def test_delete_project(self): rest = _RestProxyForTest() projects = ProjectsClient(rest) rest.expect_delete(f"{ProjectsClient._REST_ENDPOINT_PREFIX}/aaa", 204) projects.delete_project('aaa') # 200 isn't a valid return value for delete calls right now rest.expect_delete(f"{ProjectsClient._REST_ENDPOINT_PREFIX}/aaa", 200) with self.assertRaises(RuntimeError): projects.delete_project('aaa') rest.expect_delete(f"{ProjectsClient._REST_ENDPOINT_PREFIX}/aaa", 404) with self.assertRaises(RuntimeError): projects.delete_project('aaa')
def test_get_json(self): rest = _RestProxyForTest() adam_objects = AdamObjects(rest, 'MyType') rest.expect_get("/adam_object/single/MyType/uuid1", 200, {'anyKey': 'anyVal'}) json = adam_objects._get_json('uuid1') self.assertEqual({'anyKey': 'anyVal'}, json) rest.expect_get("/adam_object/single/MyType/uuid1", 404, {}) json = adam_objects._get_json('uuid1') self.assertEqual(json, None) rest.expect_get("/adam_object/single/MyType/uuid1", 403, {}) with self.assertRaises(RuntimeError): adam_objects._get_json('uuid1')
def test_authentication_empty_token(self): # Use REST proxy for testing rest = _RestProxyForTest() auth = Auth(rest) # Authenticate in order to fill in email/logged_in so that next test # can verify that these are cleared. rest.expect_get('/me', 200, {'email': '*****@*****.**', 'loggedIn': True}) auth.authenticate() # Authentication with an empty token should be no problem and result in an empty # auth object. rest.expect_get('/me', 200, {"loggedIn": False}) auth.authenticate() self.assertEqual(auth.get_user(), '') self.assertEqual(auth.get_logged_in(), False)
def test_is_ready_fails_no_uuid(self): """Test that no specified UUID will raise a KeyError when retrieved This function tests that checking if a job is ready without specifying the UUID will result in a KeyError. """ # Use REST proxy for testing rest = _RestProxyForTest() # Initiate Batch class batch = Batch(rest) # Assert that a KeyError is raised when checking if it is ready with self.assertRaises(KeyError): batch.is_ready()
def test_revoke_user_permission(self): rest = _RestProxyForTest() permissions = Permissions(rest) rest.expect_delete( '/user_permission/u1?right=ADMIN&target_type=PROJECT&target_id=id', 204) p1 = Permission('ADMIN', 'PROJECT', 'id') permissions.revoke_user_permission('u1', p1) rest.expect_delete( '/user_permission/u1?right=ADMIN&target_type=PROJECT&target_id=id', 200) with self.assertRaises(RuntimeError): permissions.revoke_user_permission('u1', p1)
def test_authentication_server_error(self): # Use REST proxy for testing rest = _RestProxyForTest() auth = Auth(rest) # Authenticate in order to fill in email/logged_in/token so that next test # can verify that these are not cleared. rest.expect_get('/me', 200, {'email': '*****@*****.**', 'loggedIn': True}) auth.authenticate() # Authentication should throw on a non-200 response and leave auth contents # unchanged. rest.expect_get('/me', 404, {}) with self.assertRaises(RuntimeError): auth.authenticate() self.assertEqual(auth.get_user(), '*****@*****.**') self.assertEqual(auth.get_logged_in(), True)
def test_get_unsafe_url_characters(self): rest = _RestProxyForTest() # All the unsafe and reserved characters #pylint: disable=W1401 # NOQA token = '; / ? : @ = & < > # % { } | \\ ^ ~ [ ]' auth_rest = AuthenticatingRestProxy(rest, token) # handle urlencode modules with and without the bugfix for # https://bugs.python.org/issue16285 (Python >= 3.7). # see also: https://stackoverflow.com/questions/51334226/python-why-is-now-included-in-the-set-of-reserved-characters-in-urllib-pars # noqa: E501 import urllib tilde = urllib.parse.quote('~') rest.expect_get( "/test?a=1&b=2&token=%3B+%2F+%3F+%3A+%40+%3D+%26+++%3C+%3E" "+%23+%25+%7B+%7D+%7C+%5C+%5E+{}+%5B+%5D".format(tilde), 200, {}) auth_rest.get("/test?a=1&b=2")
def test_add_group_member(self): rest = _RestProxyForTest() groups = Groups(rest) expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True expected_data = {'member_id': "aaa", 'member_type': "USER"} rest.expect_post("/group/g1/member", check_input, 200, {}) groups.add_user_to_group('aaa', 'g1') expected_data = {'member_id': "bbb", 'member_type': "GROUP"} rest.expect_post("/group/g1/member", check_input, 200, {}) groups.add_group_to_group('bbb', 'g1')
def test_get_ephemeris(self): """Test that an ephemeris is returned if the job has completed successfully This function tests that a job that is completed successfully will return the expected ephemeris. """ # Dummy UUID and part number for testing uuid = 'BLAH' part = 3 # Use REST proxy for testing rest = _RestProxyForTest() # Set expected 'GET' request with calc_state as 'COMPLETED' for specific part rest.expect_get( self._base + '/batch/' + uuid + '/' + str(part), 200, { 'calc_state': 'COMPLETED', 'error': 'No error!', 'stk_ephemeris': 'something', 'part_index': part }) # Initiate Batch class batch = Batch() # Set UUID, parts count, and overall calc state (as 'COMPLETED') batch._uuid = uuid batch._parts_count = 10 batch._calc_state = 'COMPLETED' # Override network access with proxy batch.set_rest_accessor(rest) # Assert that an overall calc state as 'COMPLETED' will return the expected ephemeris self.assertEqual(batch.get_part_ephemeris(part), 'something') # Assert that the error is as expected self.assertEqual(batch.get_part_error(part), 'No error!') # Assert that the calc state for the specific part's run is as expected self.assertEqual(batch.get_part_state(part), 'COMPLETED') # Assert that checking if the part is ready will return True self.assertTrue(batch.is_ready_part(part))
def test_delete_config(self): rest = _RestProxyForTest() configs = PropagatorConfigs(rest) rest.expect_delete(f"{PropagatorConfigs.REST_ENDPOINT_PREFIX}/aaa", 204) configs.delete_config('aaa') # 200 isn't a valid return value for delete calls right now rest.expect_delete(f"{PropagatorConfigs.REST_ENDPOINT_PREFIX}/aaa", 200) with self.assertRaises(RuntimeError): configs.delete_config('aaa') rest.expect_delete(f"{PropagatorConfigs.REST_ENDPOINT_PREFIX}/aaa", 404) with self.assertRaises(RuntimeError): configs.delete_config('aaa')
def test_get_summary(self): rest = _RestProxyForTest() batches = Batches(rest) # Successful request. rest.expect_get('/batch/aaa', 200, {'uuid': 'aaa', 'calc_state': 'RUNNING'}) summary = batches.get_summary('aaa') self.assertEqual('aaa', summary.get_uuid()) self.assertEqual('RUNNING', summary.get_calc_state()) # Successfully found missing. rest.expect_get('/batch/aaa', 404, 'Not JSON wat') self.assertIsNone(batches.get_summary('aaa')) # Unsuccessful request. rest.expect_get('/batch/aaa', 503, 'Also not JSON wat') with self.assertRaises(RuntimeError): batches.get_summary('aaa')
def test_grant_group_permission(self): rest = _RestProxyForTest() permissions = Permissions(rest) expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True expected_data = {'right': 'ADMIN', 'target_type': 'PROJECT', 'target_id': 'id'} rest.expect_post('/group_permission/g1', check_input, 200, expected_data) p1 = Permission('ADMIN', 'PROJECT', 'id') permissions.grant_group_permission('g1', p1) rest.expect_post('/group_permission/g1', check_input, 404, {}) with self.assertRaises(RuntimeError): permissions.grant_group_permission('g1', p1)
def test_post(self): rest = _RestProxyForTest() token = 'my_token' auth_rest = AuthenticatingRestProxy(rest, token) expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True # Token should be added to empty data in post. expected_data = {'token': token} rest.expect_post("/test", check_input, 200, {}) auth_rest.post("/test", {}) # Token should be added to non-empty data in post, preserving rest of data. expected_data = {'a': 1, 'token': token, 'b': 2} rest.expect_post("/test", check_input, 200, {}) auth_rest.post("/test", {'b': 2, 'a': 1})
def test_get_runnable_states(self): rest = _RestProxyForTest() adam_objects = AdamObjects(rest, 'MyType') # Empty return value rest.expect_get( "/adam_object/runnable_state/by_project/MyType/project_uuid", 200, {'items': []}) runnable_states = adam_objects.get_runnable_states('project_uuid') self.assertEqual(0, len(runnable_states)) rest.expect_get( "/adam_object/runnable_state/by_project/MyType/project_uuid", 200, { 'items': [{ 'uuid': 'uuid1', 'calculationState': 'PENDING', }, { 'uuid': 'uuid2', 'calculationState': 'FAILED', 'error': 'some error' }] }) runnable_states = adam_objects.get_runnable_states('project_uuid') self.assertEqual(2, len(runnable_states)) self.assertEqual('uuid1', runnable_states[0].get_uuid()) self.assertEqual('PENDING', runnable_states[0].get_calc_state()) self.assertIsNone(runnable_states[0].get_error()) self.assertEqual('uuid2', runnable_states[1].get_uuid()) self.assertEqual('FAILED', runnable_states[1].get_calc_state()) self.assertEqual('some error', runnable_states[1].get_error()) rest.expect_get( "/adam_object/runnable_state/by_project/MyType/project_uuid", 404, {}) runnable_states = adam_objects.get_runnable_states('project_uuid') self.assertEqual(runnable_states, []) rest.expect_get( "/adam_object/runnable_state/by_project/MyType/project_uuid", 403, {}) with self.assertRaises(RuntimeError): adam_objects.get_runnable_states('project_uuid')
def test_insert(self): rest = _RestProxyForTest() adam_objects = AdamObjects(rest, 'MyType') expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True expected_data = {'anyKey': 'anyVal'} rest.expect_post("/adam_object/single/MyType", check_input, 200, {'uuid': 'uuid1'}) uuid = adam_objects._insert({'anyKey': 'anyVal'}) self.assertEqual('uuid1', uuid) rest.expect_post("/adam_object/single/MyType", check_input, 404, {}) with self.assertRaises(RuntimeError): adam_objects._insert({'anyKey': 'anyVal'})
def test_bad_step_size_unit(self): """Tests an invalid step size unit This function tests that an invalid defined step size unit will raise a KeyError. """ # Use REST proxy for testing rest = _RestProxyForTest() # Initiate Batch class batch = Batch() # Set start time, end time, and state vector with epoch batch.set_start_time("AAA") batch.set_end_time("BBB") batch.set_state_vector('CCC', [1, 2, 3, 4, 5, 6]) with self.assertRaises(KeyError): batch.set_step_size(3600, 'blah')
def test_successful_authentication(self): # Use REST proxy for testing rest = _RestProxyForTest() auth = Auth(rest) # Before authenticating, auth should reflect not logged in. self.assertEqual(auth.get_token(), '') self.assertEqual(auth.get_user(), '') self.assertEqual(auth.get_logged_in(), False) # A successful authentication should store token and set user to returned value. good_token = 'good' rest.expect_get('/me?token=' + good_token, 200, { 'email': '*****@*****.**', 'loggedIn': True }) auth.authenticate(good_token) self.assertEqual(auth.get_token(), good_token) self.assertEqual(auth.get_user(), '*****@*****.**') self.assertEqual(auth.get_logged_in(), True)
def test_is_ready_failed_part(self): """Test when an individual part has failed This function tests that a job will correctly indicate when a part has failed, that it returns the expected error, and that it returns None for ephemeris. """ # Dummy UUID and part number for testing uuid = 'BLAH' part = 3 # Use REST proxy for testing rest = _RestProxyForTest() # Set expected 'GET' request with calc_state as 'FAILED' rest.expect_get(self._base + '/batch/' + uuid + '/' + str(part), 200, { 'calc_state': 'FAILED', 'error': 'Some error', 'part_index': part }) # Initiate Batch class batch = Batch() # Set UUID, parts count, and overall calc state (as 'FAILED') batch._uuid = uuid batch._parts_count = 10 batch._calc_state = 'FAILED' # Override network access with proxy batch.set_rest_accessor(rest) # Assert that the calc state for the specific part's run is as expected self.assertEqual(batch.get_part_state(part), 'FAILED') # Assert that the error returned is as expected self.assertEqual(batch.get_part_error(part), 'Some error') # Assert that attempting to retrieve a part's ephemeris will return None self.assertEqual(batch.get_part_ephemeris(part), None)
def test_delete_group_member(self): rest = _RestProxyForTest() groups = Groups(rest) rest.expect_delete("/group/g1/member?member_id=aaa&member_type=USER", 204) groups.remove_user_from_group('aaa', 'g1') rest.expect_delete("/group/g1/member?member_id=aaa&member_type=GROUP", 204) groups.remove_group_from_group('aaa', 'g1') rest.expect_delete("/group/g1/member?member_id=aaa&member_type=USER", 404) with self.assertRaises(RuntimeError): groups.remove_user_from_group('aaa', 'g1') rest.expect_delete("/group/g1/member?member_id=aaa&member_type=GROUP", 404) with self.assertRaises(RuntimeError): groups.remove_group_from_group('aaa', 'g1')
def test_get_config(self): rest = _RestProxyForTest() configs = PropagatorConfigs(rest) rest.expect_get("/config/aaa", 200, {'uuid': 'aaa', 'project': 'bbb'}) config = configs.get_config('aaa') self.assertEqual('aaa', config.get_uuid()) self.assertEqual('bbb', config.get_project()) rest.expect_get("/config/00000000-0000-0000-0000-000000000003", 200, FULL_CONFIG_JSON_OUTPUT) config = configs.get_config("00000000-0000-0000-0000-000000000003") self.assertEqual(FULL_CONFIG_JSON_OUTPUT, config.get_config_json()) rest.expect_get("/config/aaa", 404, {}) config = configs.get_config('aaa') self.assertIsNone(config) rest.expect_get("/config/aaa", 403, {}) with self.assertRaises(RuntimeError): config = configs.get_config('aaa')
def test_is_ready_failed_part(self): """Test when an individual part has failed This function tests that a job will correctly indicate when a part has failed, that it returns the expected error, and that it returns None for ephemeris. """ # Dummy UUID and part number for testing uuid = 'BLAH' part = 3 num_parts = 10 # Use REST proxy for testing rest = _RestProxyForTest() # Set expected 'GET' requests with calc_state as 'FAILED' for i in range(1, num_parts + 1): rest.expect_get('/batch/' + uuid + '/' + str(i), 200, { 'calc_state': 'FAILED', 'error': 'Some error', 'part_index': i }) # Initiate Batch class batch = Batch(rest) # Set UUID, parts count, and overall calc state (as 'FAILED') batch.set_uuid_for_testing(uuid) batch.set_parts_count_for_testing(num_parts) batch.set_calc_state_for_testing('FAILED') # Assert that the calc state for the specific part's run is as expected self.assertEqual(batch.get_part_state(part), 'FAILED') # Assert that the error returned is as expected self.assertEqual(batch.get_part_error(part), 'Some error') # Assert that attempting to retrieve a part's ephemeris will return None self.assertEqual(batch.get_part_ephemeris(part), None)
def test_group_memberships(self): rest = _RestProxyForTest() groups = Groups(rest) rest.expect_get( '/group_membership?recursive=true&expand=true&group_uuid=g1', 200, {'items': []}) memberships = groups.get_group_memberships('g1') self.assertTrue(len(memberships) == 0) rest.expect_get( '/group_membership?recursive=true&expand=true&group_uuid=g1', 200, { 'items': [{ 'uuid': 'g2', 'name': 'n', 'description': 'd' }, { 'uuid': 'g3' }] }) memberships = groups.get_group_memberships('g1') self.assertTrue(len(memberships) == 2) self.assertTrue('g2' in [g.get_uuid() for g in memberships]) self.assertTrue('g3' in [g.get_uuid() for g in memberships]) rest.expect_get( '/group_membership?recursive=true&expand=true', 200, { 'items': [{ 'uuid': 'g2', 'name': 'n', 'description': 'd' }, { 'uuid': 'g3' }] }) memberships = groups.get_my_memberships() self.assertTrue(len(memberships) == 2) self.assertTrue('g2' in [g.get_uuid() for g in memberships]) self.assertTrue('g3' in [g.get_uuid() for g in memberships])
def test_unsuccessful_authentication(self): # Use REST proxy for testing rest = _RestProxyForTest() auth = Auth(rest) # Authenticate in order to fill in email/logged_in/token so that next test # can verify that these are cleared. good_token = 'good' rest.expect_get('/me?token=' + good_token, 200, { 'email': '*****@*****.**', 'loggedIn': True }) auth.authenticate(good_token) # An unsuccessful authentication should clear token and other values. bad_token = 'bad' # An example of the few ways that the server might reject a user. Others look # like this with different messages. server_error_on_bad_token = """ { "error": { "errors": [ { "domain": "global", "reason": "backendError", "message": "org.apache.shiro.authc.IncorrectCredentialsException" } ], "code": 503, "message": "org.apache.shiro.authc.IncorrectCredentialsException" } } """ rest.expect_get('/me?token=' + bad_token, 503, json.loads(server_error_on_bad_token)) auth.authenticate(bad_token) self.assertEqual(auth.get_token(), '') self.assertEqual(auth.get_user(), '') self.assertEqual(auth.get_logged_in(), False)
def test_is_ready_failed(self): """Test that job is ready when failed This function tests that a job will indicate that it is ready if it has failed. """ # Dummy UUID for testing uuid = 'BLAH' # Use REST proxy for testing rest = _RestProxyForTest() # Set expected 'GET' request with calc_state as 'FAILED' rest.expect_get( self._base + '/batch/' + uuid, 200, { 'calc_state': 'FAILED', 'parts_count': 42, 'summary': "ZQZ", 'error': 'No error!' }) # Initiate Batch class batch = Batch() # Set UUID batch._uuid = uuid # Override network access with proxy batch.set_rest_accessor(rest) # Assert that checking if the batch is ready will return True self.assertTrue(batch.is_ready()) # Assert that the calc state is as expected self.assertEqual(batch.get_calc_state(), 'FAILED') # Assert that the number of expected parts is returned self.assertEqual(batch.get_parts_count(), 42)
def test_get_runnable_state(self): rest = _RestProxyForTest() adam_objects = AdamObjects(rest, 'MyType') rest.expect_get("/adam_object/runnable_state/single/MyType/uuid1", 200, { 'uuid': 'uuid1', 'calculationState': 'PENDING', }) runnable_state = adam_objects.get_runnable_state('uuid1') self.assertEqual('uuid1', runnable_state.get_uuid()) self.assertEqual('PENDING', runnable_state.get_calc_state()) self.assertIsNone(runnable_state.get_error()) rest.expect_get("/adam_object/runnable_state/single/MyType/uuid1", 404, {}) runnable_state = adam_objects.get_runnable_state('uuid1') self.assertEqual(runnable_state, None) rest.expect_get("/adam_object/runnable_state/single/MyType/uuid1", 403, {}) with self.assertRaises(RuntimeError): adam_objects.get_runnable_state('uuid1')
def test_new_batch(self): rest = _RestProxyForTest() batches = Batches(rest) # A successful run. rest.expect_post("/batch", self._check_input, 200, { 'calc_state': 'PENDING', 'uuid': '1' }) state = batches.new_batch(self.dummy_propagation_params, self.dummy_opm_params) self.assertEqual('1', state.get_uuid()) self.assertEqual('PENDING', state.get_calc_state()) # Unsuccessful run. rest.expect_post("/batch", self._check_input, 400, { 'calc_state': 'PENDING', 'uuid': '1' }) with self.assertRaises(RuntimeError): batches.new_batch(self.dummy_propagation_params, self.dummy_opm_params)
def test_good_response(self): rest = _RestProxyForTest() retrying_rest = RetryingRestProxy(rest) expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True # A 200 (or 204 for deletes) results in no retrying. rest.expect_post("/test", check_input, 200, {'a': 1}) code, response = retrying_rest.post("/test", {}) self.assertEqual(200, code) self.assertDictEqual({'a': 1}, response) rest.expect_get("/test?a=1&b=2", 200, {'a': 1}) code, response = retrying_rest.get("/test?a=1&b=2") self.assertEqual(200, code) self.assertDictEqual({'a': 1}, response) rest.expect_delete("/test?a=1&b=2", 204) code = retrying_rest.delete("/test?a=1&b=2") self.assertEqual(204, code)
def test_no_retry_on_nonretryable_error(self): rest = _RestProxyForTest() retrying_rest = RetryingRestProxy(rest) expected_data = {} def check_input(data_dict): self.assertEqual(expected_data, data_dict) return True # An error other than 403, 502, and 503 results in no retrying. rest.expect_post("/test", check_input, 404, {'a': 1}) code, response = retrying_rest.post("/test", {}) self.assertEqual(404, code) self.assertDictEqual({'a': 1}, response) rest.expect_get("/test?a=1&b=2", 418, {'a': 1}) code, response = retrying_rest.get("/test?a=1&b=2") self.assertEqual(418, code) self.assertDictEqual({'a': 1}, response) rest.expect_delete("/test?a=1&b=2", 500) code = retrying_rest.delete("/test?a=1&b=2") self.assertEqual(500, code)
def test_get_sub_projects(self): rest = _RestProxyForTest() projects_module = ProjectsClient(rest) projects_response = { 'items': [{ 'uuid': 'aaa', 'parent': 'p1' }, { 'uuid': 'bbb', 'parent': 'p2' }, { 'uuid': 'ccc' }, { 'uuid': 'ddd', 'parent': 'p2' }] } rest.expect_get(ProjectsClient._REST_ENDPOINT_PREFIX, 200, projects_response) projects = projects_module.get_sub_projects('p1') self.assertEqual(1, len(projects)) self.assertEqual('aaa', projects[0].get_uuid()) rest.expect_get(ProjectsClient._REST_ENDPOINT_PREFIX, 200, projects_response) projects = projects_module.get_sub_projects('p2') self.assertEqual(2, len(projects)) self.assertEqual('bbb', projects[0].get_uuid()) self.assertEqual('ddd', projects[1].get_uuid()) rest.expect_get(ProjectsClient._REST_ENDPOINT_PREFIX, 200, projects_response) projects = projects_module.get_sub_projects(None) self.assertEqual(1, len(projects)) self.assertEqual('ccc', projects[0].get_uuid())
def test_is_ready_completed(self): """Test that job is ready when completed This function tests that a job will indicate that it is ready if it has completed. """ # Dummy UUID for testing uuid = 'BLAH' # Use REST proxy for testing rest = _RestProxyForTest() # Set expected 'GET' request with calc_state as 'COMPLETED' rest.expect_get( '/batch/' + uuid, 200, { 'uuid': uuid, 'calc_state': 'COMPLETED', 'parts_count': 42, 'summary': "ZQZ", 'error': 'No error!' }) # Initiate Batch class batch = Batch(rest) # Set UUID batch.set_uuid_for_testing(uuid) # Assert that checking if the batch is ready will return True self.assertTrue(batch.is_ready()) # Assert that the calc state is as expected self.assertEqual(batch.get_calc_state(), 'COMPLETED') # Assert that the number of expected parts is returned self.assertEqual(batch.get_parts_count(), 42)