Skip to content

Commit 9cf9fce

Browse files
committed
more unit tests
1 parent 9888627 commit 9cf9fce

2 files changed

Lines changed: 154 additions & 18 deletions

File tree

src/mas/devops/users.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def manage_internal_ca_pem_file_path(self):
172172
@property
173173
def manage_maxadmin_api_key(self):
174174
if self._manage_maxadmin_api_key is None:
175-
self._manage_maxadmin_api_key = self.create_or_get_manage_api_key_for_user(MASUserUtils.MAXADMIN)
175+
self._manage_maxadmin_api_key = self.create_or_get_manage_api_key_for_user(MASUserUtils.MAXADMIN, temporary=True)
176176
return self._manage_maxadmin_api_key
177177

178178
@property
@@ -502,7 +502,11 @@ def resync_users(self, user_ids):
502502
user = self.get_user(user_id)
503503
self.update_user_display_name(user_id, user["displayName"])
504504

505-
def create_or_get_manage_api_key_for_user(self, user_id):
505+
def create_or_get_manage_api_key_for_user(self, user_id, temporary=False):
506+
'''
507+
Get singleton API for user_id if it already exists, create it if not
508+
if temporary is True AND we created the API key, delete it on exit
509+
'''
506510
self.logger.debug(f"Attempting to create Manage API Key for user {user_id}")
507511
url = f"{self.manage_api_url_internal}/maximo/api/os/mxapiapikey"
508512
querystring = {
@@ -531,14 +535,19 @@ def create_or_get_manage_api_key_for_user(self, user_id):
531535
)
532536

533537
if response.status_code == 400:
534-
error_json = response.json()
538+
# Assisted by watsonx Code Assistant
539+
try:
540+
error_json = response.json()
541+
except ValueError:
542+
raise Exception(f"{response.status_code} {response.text}")
543+
535544
if "Error" in error_json and "reasonCode" in error_json["Error"] and error_json["Error"]["reasonCode"] == "BMXAA10051E":
536545
# BMXAA10051E - Only one API key allowed per user.
537546
self.logger.info(f"Reusing existing Manage API Key for user {user_id}")
538547
pass
539548
else:
540549
# any other 400 error is unexpected
541-
raise Exception(response.text)
550+
raise Exception(f"{response.status_code} {response.text}")
542551

543552
elif response.status_code == 201:
544553
self.logger.info(f"Creating new Manage API Key for user {user_id}")
@@ -554,11 +563,7 @@ def create_or_get_manage_api_key_for_user(self, user_id):
554563
# so we expect the get call to find it
555564
raise Exception("API key was unexpectedly not found")
556565

557-
if response.status_code == 201:
558-
# We created this api key, register a handler to delete it when we're done
559-
# TODO: is there a danger of some other process adopting this singleton api key
560-
# which may then fail if we subsequently delete it?
561-
# NOTE: the manage sanity test seems to create maxadmin apikey and not clean it up
566+
if temporary and response.status_code == 201:
562567
atexit.register(self.delete_manage_api_key, apikey)
563568

564569
return apikey
@@ -592,7 +597,7 @@ def get_manage_api_key_for_user(self, user_id):
592597

593598
return None
594599

595-
raise Exception(response.text)
600+
raise Exception(f"{response.status_code} {response.text}")
596601

597602
def delete_manage_api_key(self, manage_api_key):
598603
self.logger.info(f"Deleting Manage API Key for user {manage_api_key['userid']}")

test/src/test_users.py

Lines changed: 139 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,20 @@
3636

3737
ADMIN_DASHBOARD_PORT = 1
3838
COREAPI_PORT = 2
39+
MANAGE_API_PORT = 3
3940

4041
MAS_ADMIN_URL = f"https://admin-dashboard.{MAS_CORE_NAMESPACE}.svc.cluster.local:{ADMIN_DASHBOARD_PORT}"
4142
MAS_API_URL = f'https://coreapi.{MAS_CORE_NAMESPACE}.svc.cluster.local:{COREAPI_PORT}'
43+
MANAGE_API_URL = f'https://{MAS_INSTANCE_ID}-{MAS_WORKSPACE_ID}.{MANAGE_NAMESPACE}.svc.cluster.local:{MANAGE_API_PORT}'
4244

4345
PEM_PATH = "pempath"
4446

4547

46-
def additional_matcher(req, json=None, verify=PEM_PATH):
48+
def additional_matcher(req, json=None, verify=PEM_PATH, cert=None):
4749
if json is not None:
4850
assert req.json() == json
4951
assert req.verify == verify
52+
assert req.cert == cert
5053
return True
5154

5255

@@ -120,7 +123,8 @@ def user_utils(mock_v1_secrets, mock_logininitial_endpoint, mock_named_temporary
120123
MAS_WORKSPACE_ID,
121124
k8s_client,
122125
coreapi_port=COREAPI_PORT,
123-
admin_dashboard_port=ADMIN_DASHBOARD_PORT
126+
admin_dashboard_port=ADMIN_DASHBOARD_PORT,
127+
manage_api_port=MANAGE_API_PORT
124128
)
125129

126130
yield user_utils
@@ -905,14 +909,141 @@ def test_check_user_sync_appstate_persistent_error(user_utils, requests_mock):
905909
assert patche.call_count == get.call_count / 2
906910

907911

908-
def test_create_or_get_manage_api_key_for_user(user_utils, requests_mock):
909-
pass
910-
# TODO
912+
def test_get_manage_api_key_for_user_exists(user_utils, requests_mock):
913+
user_id = "user1"
914+
apikey = {"userid": user_id, "href": f"https://{MANAGE_API_URL}/maximo/api/os/mxapikey/theapikeyid"}
911915

916+
get = requests_mock.get(
917+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
918+
request_headers={"accept": "application/json"},
919+
json={"member": [apikey]},
920+
status_code=200,
921+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
922+
)
912923

913-
def test_get_manage_api_key_for_user(user_utils, requests_mock):
914-
pass
915-
# TODO
924+
assert user_utils.get_manage_api_key_for_user(user_id) == apikey
925+
assert get.call_count == 1
926+
927+
928+
def test_get_manage_api_key_for_user_notfound(user_utils, requests_mock):
929+
user_id = "user1"
930+
931+
get = requests_mock.get(
932+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
933+
request_headers={"accept": "application/json"},
934+
json={"member": []},
935+
status_code=200,
936+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
937+
)
938+
939+
assert user_utils.get_manage_api_key_for_user(user_id) is None
940+
assert get.call_count == 1
941+
942+
943+
def test_get_manage_api_key_for_user_error(user_utils, requests_mock):
944+
user_id = "user1"
945+
946+
get = requests_mock.get(
947+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
948+
request_headers={"accept": "application/json"},
949+
text="boom",
950+
status_code=500,
951+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
952+
)
953+
954+
with pytest.raises(Exception) as excinfo:
955+
user_utils.get_manage_api_key_for_user(user_id)
956+
assert str(excinfo.value) == "500 boom"
957+
assert get.call_count == 1
958+
959+
960+
@pytest.mark.parametrize("temporary", [(True), (False)])
961+
def test_create_or_get_manage_api_key_for_user_new_api_key(temporary, user_utils, requests_mock, mock_atexit):
962+
user_id = "user1"
963+
apikey = {"userid": user_id, "href": f"https://{MANAGE_API_URL}/maximo/api/os/mxapikey/theapikeyid"}
964+
965+
post = requests_mock.post(
966+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1",
967+
request_headers={"content-type": "application/json"},
968+
json={"id": user_id},
969+
status_code=201,
970+
additional_matcher=lambda req: additional_matcher(req, json={"expiration": -1, "userid": user_id}, cert=PEM_PATH)
971+
)
972+
973+
get = requests_mock.get(
974+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
975+
request_headers={"accept": "application/json"},
976+
json={"member": [apikey]},
977+
status_code=200,
978+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
979+
)
980+
981+
assert user_utils.create_or_get_manage_api_key_for_user(user_id, temporary=temporary) == apikey
982+
assert post.call_count == 1
983+
assert get.call_count == 1
984+
985+
# if temporary, check we registered the exit hook to delete the temporary Manage API Key
986+
if temporary:
987+
assert call(user_utils.delete_manage_api_key, apikey) in mock_atexit.mock_calls, "delete_manage_api_key exit hook not registered for temporary api key that we created"
988+
else:
989+
assert call(user_utils.delete_manage_api_key, apikey) not in mock_atexit.mock_calls, "delete_manage_api_key exit hook registered unexpectedly for non-temporary api key that we created"
990+
991+
992+
@pytest.mark.parametrize("temporary", [(True), (False)])
993+
def test_create_or_get_manage_api_key_for_user_existing_api_key(temporary, user_utils, requests_mock, mock_atexit):
994+
user_id = "user1"
995+
apikey = {"userid": user_id, "href": f"https://{MANAGE_API_URL}/maximo/api/os/mxapikey/theapikeyid"}
996+
997+
post = requests_mock.post(
998+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1",
999+
request_headers={"content-type": "application/json"},
1000+
json={"Error": {"reasonCode": "BMXAA10051E"}},
1001+
status_code=400,
1002+
additional_matcher=lambda req: additional_matcher(req, json={"expiration": -1, "userid": user_id}, cert=PEM_PATH)
1003+
)
1004+
1005+
get = requests_mock.get(
1006+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
1007+
request_headers={"accept": "application/json"},
1008+
json={"member": [apikey]},
1009+
status_code=200,
1010+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
1011+
)
1012+
1013+
assert user_utils.create_or_get_manage_api_key_for_user(user_id, temporary=temporary) == apikey
1014+
assert post.call_count == 1
1015+
assert get.call_count == 1
1016+
1017+
# even if temporary is set, because we did not create the api key, we should not registered a hook to delete it
1018+
assert call(user_utils.delete_manage_api_key, apikey) not in mock_atexit.mock_calls, "delete_manage_api_key exit hook registered unexpectedly for existing API Key that we did not create"
1019+
1020+
1021+
def test_create_or_get_manage_api_key_for_user_error(user_utils, requests_mock, mock_atexit):
1022+
user_id = "user1"
1023+
apikey = {"userid": user_id, "href": f"https://{MANAGE_API_URL}/maximo/api/os/mxapikey/theapikeyid"}
1024+
1025+
post = requests_mock.post(
1026+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1",
1027+
request_headers={"content-type": "application/json"},
1028+
text="boom",
1029+
status_code=400,
1030+
additional_matcher=lambda req: additional_matcher(req, json={"expiration": -1, "userid": user_id}, cert=PEM_PATH)
1031+
)
1032+
1033+
get = requests_mock.get(
1034+
f"{MANAGE_API_URL}/maximo/api/os/mxapiapikey?ccm=1&lean=1&oslc.select=*&oslc.where=userid=\"{user_id}\"",
1035+
request_headers={"accept": "application/json"},
1036+
json={"member": [apikey]},
1037+
status_code=200,
1038+
additional_matcher=lambda req: additional_matcher(req, cert=PEM_PATH)
1039+
)
1040+
1041+
with pytest.raises(Exception) as excinfo:
1042+
user_utils.create_or_get_manage_api_key_for_user(user_id, temporary=True)
1043+
assert str(excinfo.value) == "400 boom"
1044+
assert post.call_count == 1
1045+
assert get.call_count == 0
1046+
assert call(user_utils.delete_manage_api_key, apikey) not in mock_atexit.mock_calls, "delete_manage_api_key exit hook not registered even though we failed to create the api key"
9161047

9171048

9181049
def test_delete_manage_api_key(user_utils, requests_mock):

0 commit comments

Comments
 (0)