class TestRestClient(unittest.TestCase): def setUp(self): self.ret_chn = ReturnChannel(stdout_callback, {}) command_list = load_modules() self.conf_path = os.path.join(get_directory_of_current_module(self), CONF_FILE_LOCATION) self.ret_chn = ReturnChannel(stdout_callback, {}) self.config = Config('help', '', command_list, [], self.ret_chn, 'shell') self.rest_client = RESTBase('sde', 'conf_name', self.config) self.config.import_custom_options() Config.parse_config_file(self.config, self.conf_path) def test_context_root_is_empty(self): self._check_context_root_matches_target('', '') def test_context_root_is_slash(self): self._check_context_root_matches_target('/', '') def test_context_root_is_notempty(self): self._check_context_root_matches_target('branch', '/branch') def _check_context_root_matches_target(self, context_root, target): self.rest_client.config['sde_context_root'] = context_root self.assertTrue(self.rest_client._get_context_root() == target, 'Expected an empty context root')
class LDAPHelperTests(unittest.TestCase): def setUp(self): ret_chn = ReturnChannel(stdout_callback, {}) command_list = load_modules() self.config = Config('help', '', command_list, [], ret_chn, 'shell') self.ldap_helper = LDAPHelper(self.config) self.config.import_custom_options() self.init_config() def init_config(self): self.config['ldap_method'] = 'LDAP' self.config['page_size'] = 1 self.config['bind_dn'] = 'cn=foo,dc=example,dc=com' def test_normalize_dc(self): self.assertEquals(self.ldap_helper.normalize_dc('dc=abc'), 'DC=abc') self.assertEquals(self.ldap_helper.normalize_dc('DC=def'), 'DC=def') def test_compute_base_dn(self): self.assertEquals( self.ldap_helper.compute_base_dn('cn=foo,ou=bar,dc=abc,dc=com'), 'DC=abc,DC=com' ) self.assertEquals( self.ldap_helper.compute_base_dn('ou=bar,dc=foo,dc=example,dc=org'), 'DC=foo,DC=example,DC=org' ) def test_initialize_basedn(self): self.config['base_dn'] = '' self.ldap_helper.initialize() self.assertEquals(self.ldap_helper.basedn, 'DC=example,DC=com') self.config['base_dn'] = 'DC=foo,DC=org' self.ldap_helper.initialize() self.assertEquals(self.ldap_helper.basedn, 'DC=foo,DC=org') def test_initialize_checks_ldap_method(self): self.config['ldap_method'] = 'foo' self.assertRaises(LDAPConnectorError, self.ldap_helper.initialize) def test_bind_dn_required(self): self.config['bind_dn'] = '' self.assertRaises(LDAPConnectorError, self.ldap_helper.initialize) def test_search(self): self.ldap_helper.initialize() self.ldap_helper.connection = MockLDAP(range(10)) results = self.ldap_helper.search(None, None, None) self.assertEquals(results, range(10)) def test_get_group_from_ldap_tuple(self): self.ldap_helper.initialize() self.ldap_helper.connection = MockLDAP('') self.ldap_helper.connect() group = self.ldap_helper.get_group_from_ldap_tuple( ('CN=mathematicians,OU=SyncGroups,DC=labs,DC=sdelements,DC=com', {'member': ['CN=Simeon Poisson,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=PGJ Dirichlet,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=James Bond,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=analyst,OU=SyncGroups,DC=labs,DC=sdelements,DC=com', 'CN=bind user,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=David Hilbert,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=john neumann,CN=Users,DC=labs,DC=sdelements,DC=com'], 'name': ['mathematicians']}) ) self.assertEquals( group, ('mathematicians', ['CN=Simeon Poisson,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=PGJ Dirichlet,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=James Bond,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=analyst,OU=SyncGroups,DC=labs,DC=sdelements,DC=com', 'CN=bind user,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=David Hilbert,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=john neumann,CN=Users,DC=labs,DC=sdelements,DC=com'], 'CN=mathematicians,OU=SyncGroups,DC=labs,DC=sdelements,DC=com') ) def test_get_group_users(self): self.ldap_helper.initialize() self.ldap_helper.connection = MockLDAP([ ('CN=john neumann,CN=Users,DC=labs,DC=sdelements,DC=com', {'givenName': ['john'], 'cn': ['john neumann'], 'userAccountControl': ['512'], 'sn': ['neumann']}), ('CN=David Hilbert,CN=Users,DC=labs,DC=sdelements,DC=com', {'mail': ['*****@*****.**'], 'givenName': ['David'], 'cn': ['David Hilbert'], 'userAccountControl': ['512'], 'sn': ['Hilbert']}), ('CN=bind user,CN=Users,DC=labs,DC=sdelements,DC=com', {'mail': ['*****@*****.**'], 'givenName': ['bind'], 'cn': ['bind user'], 'userAccountControl': ['66048'], 'sn': ['user']}) ]) self.ldap_helper.connect() group = 'CN=mathematicians,OU=SyncGroups,DC=labs,DC=sdelements,DC=com' members = ['CN=bind user,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=David Hilbert,CN=Users,DC=labs,DC=sdelements,DC=com', 'CN=john neumann,CN=Users,DC=labs,DC=sdelements,DC=com'] group_users = self.ldap_helper.get_group_users(group, members) self.assertEquals(group_users, [ {'givenName': ['john'], 'cn': ['john neumann'], 'userAccountControl': ['512'], 'sn': ['neumann']}, {'mail': ['*****@*****.**'], 'givenName': ['David'], 'cn': ['David Hilbert'], 'userAccountControl': ['512'], 'sn': ['Hilbert']}, {'mail': ['*****@*****.**'], 'givenName': ['bind'], 'cn': ['bind user'], 'userAccountControl': ['66048'], 'sn': ['user']} ]) def test_get_user_email(self): self.ldap_helper.initialize() users = [{ # active AD 'mail': ['*****@*****.**'], 'userAccountControl': ['512'], 'cn': ['James Bond'], 'sn': ['Bond'] }, { # disabled AD 'mail': ['*****@*****.**'], 'userAccountControl': ['514'], 'cn': ['James Bond'], 'sn': ['Bond'] }, { # AD - not a user 'cn': ['Administrators'] }, { # AD - user but no email 'userAccountControl': ['512'], 'cn': ['James Bond'], 'sn': ['Bond'] }, { # LDAP - user 'mail': ['*****@*****.**'], 'cn': ['James Bond'], 'sn': ['Bond'] }] expected_responses = [ '*****@*****.**', '', '', '', '*****@*****.**', ] for user, expected in zip(users, expected_responses): self.assertEqual(self.ldap_helper.get_user_email(user), expected)
class LDAPConnectorTests(unittest.TestCase): def setUp(self): ret_chn = ReturnChannel(stdout_callback, {}) command_list = load_modules() self.config = Config('help', '', command_list, [], ret_chn, 'shell') self.connector = LDAPConnector(self.config) self.config.import_custom_options() self.init_config() def init_config(self): self.config['ldap_method'] = 'LDAP' self.config['page_size'] = 1 self.config['bind_dn'] = 'cn=foo,dc=example,dc=com' self.config['group_mapping'] = { "scientists": "group1", "mathematicians": "group2", "analyst": "group3" } self.david = 'CN=David Hilbert,CN=Users,DC=example,DC=org' self.jon = 'CN=Jon Snow,CN=Users,DC=example,DC=org' self.james = 'CN=James Bond,CN=Users,DC=example,DC=org' self.users = [self.david, self.jon] def expected_directory_users(self): expected_users = {} for user, group in zip(self.users, ['mathematicians', 'scientists']): expected_users[DIRECTORY[user]['mail'][0]] = { 'full_name': DIRECTORY[user]['cn'][0], 'groups': [group], 'last_name': DIRECTORY[user]['sn'][0], 'email': DIRECTORY[user]['mail'][0], 'exists': False } return expected_users def test_translate_ldap_group_to_sde_group(self): self.connector._groups = { 'g1': [1, 2, 3], 'g2': [4, 5, 6] } mapping = {'mathematicians': 'g1', 'scientists': 'g2'} ldap_groups = ['mathematicians', 'programmers'] expected = [[1, 2, 3]] actual = self.connector.translate_ldap_group_to_sde_group(ldap_groups, mapping) self.assertEqual(actual, expected) def test_calculate_name(self): user = { 'first_name': 'John', 'last_name': 'Smith', 'full_name': 'John Smith' } new_user = self.connector.calculate_name(user) self.assertEqual(user, new_user) # Missing first name and last name is inferred for missing_field in ['first_name', 'last_name']: other_user = user.copy() del other_user[missing_field] new_user = self.connector.calculate_name(other_user) self.assertEqual(user, new_user) # only full name present with space in it other_user = {'full_name': 'John Smith'} new_user = self.connector.calculate_name(other_user) self.assertEqual(user, new_user) # only full name present - no spaces other_user = {'full_name': 'Aouda'} expected_user = { 'first_name': 'Aouda', 'last_name': 'Aouda', 'full_name': 'Aouda' } new_user = self.connector.calculate_name(other_user) self.assertEqual(new_user, expected_user) # either first or last name only for field in ['first_name', 'last_name']: other_user = {field: 'Aouda'} new_user = self.connector.calculate_name(other_user) self.assertEqual(new_user['first_name'], new_user['last_name']) def test_filter_groups(self): groups = { 'g1': ['a', 'b', 'c'], 'g2': ['d', 'e', 'f'] } # no filtering actual = self.connector.filter_groups(groups, {}) self.assertEqual(actual, groups) # only whitelisted groups should appear actual = self.connector.filter_groups(groups, {'groups': ['g1']}) def test_filter_users(self): # only those with emails are returned groups = { 'mathematicians': [DIRECTORY[self.david]], 'scientists': [DIRECTORY[self.jon]], 'spies': [DIRECTORY[self.james]] } self.connector.initialize() actual = self.connector.filter_users(groups, {}) expected = self.expected_directory_users() self.assertEqual(actual, expected) # only whitelisted ones are returned user = self.jon email = DIRECTORY[user]['mail'][0] filters = {'users': [email]} actual = self.connector.filter_users(groups, filters) expected = { email: { 'full_name': DIRECTORY[user]['cn'][0], 'groups': ['scientists'], 'last_name': DIRECTORY[user]['sn'][0], 'email': DIRECTORY[user]['mail'][0], 'exists': False } } self.assertEqual(actual, expected) def test_get_users_diff(self): dict_a = {'a': 1, 'b': 2, 'c': 3} dict_b = {'c': 1, 'd': 2, 'e': 3} actual = self.connector._get_users_diff(dict_a, dict_b) expected = ( dict_a, dict_b, set(['c']), set(['d', 'e']), set(['a', 'b']) ) self.assertEquals(actual, expected)
class AlmPluginTestBase(object): @classmethod def setUpClass(cls, test_dir=None, conf_file_location=CONF_FILE_LOCATION, alm_classes=[None, None, None]): """ test_dir - The directory where we will look for the test config file. Default is the directory of the calling class conf_file_location - The relative path from the test_dir to the conf file. Default is the value of CONF_FILE_LOCATION classes - Pass the class name of the connector, api and response generator to use the default initializer. Expects: [alm_connector, alm_api, alm_response_generator """ if test_dir is None: test_dir = get_directory_of_current_module(cls) cls.connector_cls, cls.api_cls, cls.generator_cls = alm_classes cls.test_dir = test_dir cls.conf_path = os.path.join(cls.test_dir, conf_file_location) cls.mock_sde_response = MOCK_SDE_RESPONSE cls.mock_alm_response = MOCK_ALM_RESPONSE cls.ret_chn = ReturnChannel(stdout_callback, {}) def init_alm_connector(self): if self.connector_cls is None: raise Error('No alm connector found') elif self.api_cls is None: raise Error('No alm api found') else: self.connector = self.connector_cls(self.config, self.api_cls(self.config)) def init_response_generator(self, resp_generator_cls=None): resp_generator_cls = resp_generator_cls or self.generator_cls if not resp_generator_cls: raise Error('No response generator found') self.response_generator = resp_generator_cls(self.config, self.test_dir) def pre_parse_config(self): pass def post_parse_config(self): pass def setUp(self): """ Plugin setup mirrors the setup that occurs during a sync_alm call. Also initializes the mock responses. """ command_list = load_modules() self.config = Config('help', '', command_list, [], self.ret_chn, 'shell') self.init_alm_connector() self.pre_parse_config() self.config.import_custom_options() self.config['alm_reference_context'] = 2 Config.parse_config_file(self.config, self.conf_path) self.post_parse_config() self.init_response_generator() self.mock_sde_response.initialize(self.config) self.mock_alm_response.initialize(self.response_generator) self.connector.initialize() AlmConnector.init_statuses(self.connector) def tearDown(self): self.mock_alm_response.teardown() self.mock_sde_response.teardown() def test_parsing_alm_task(self): # Verify that none of the abstract methods inherited from AlmTask will break. # This test can be extended to verify the contents of task. self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) test_task_result = self.connector.alm_get_task(test_task) task_id = test_task_result.get_task_id() alm_id = test_task_result.get_alm_id() alm_status = test_task_result.get_status() alm_timestamp = test_task_result.get_timestamp() task_id_regex = '^[^\d]+\d+$' self.assertMatch(task_id_regex, task_id, 'Task id does not match the expected pattern. pattern:%s, task_id:%s' % (task_id_regex, task_id)) self.assertEqual(type(alm_timestamp), datetime, 'Expected a datetime object') self.assertEqual(test_task['status']['id'], alm_status, 'Expected %s status, got %s' % (test_task['status']['id'], alm_status)) self.assertNotNone(alm_id, 'Expected a value for alm_id') return [test_task, test_task_result] def test_alm_connect(self): self.connector.alm_connect() def test_add_and_get_task(self): # The plugin may initialize variables during alm_connect() so we need # to call alm_connect() before proceeding self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) test_task_result = self.connector.alm_get_task(test_task) self.assertNotNone(test_task_result, 'Failed retrieve newly added task') def test_alm_priority_map(self): priority_map_tests = [ { 'description': 'Single mapping', 'map': { '1-10': 'Simple' }, 'expected_pass': True }, { 'description': 'Three mappings', 'map': { '1-3': 'Low', '4-6': 'Medium', '7-10': 'High', }, 'expected_pass': True }, { 'description': 'Four mappings, out of order', 'map': { '1-3': 'Low', '7-8': 'High', '4-6': 'Medium', '9-10': 'Critical', }, 'expected_pass': True }, { 'description': 'Priority 3 duplicated', 'map': { '1-3': 'Low', '3-10': 'High', }, 'expected_pass': False }, { 'description': 'Priority 3 duplicated', 'map': { '1-3': 'Low', '3': 'Medium', '4-10': 'High', }, 'expected_pass': False }, { 'description': 'Priority 2 in wrong order', 'map': { '2-1': 'Low', '3-10': 'High', }, 'expected_pass': False }, { 'description': 'Missing priority 3', 'map': { '1-2': 'Low', '4-10': 'High', }, 'expected_pass': False }, { 'description': 'Priority 12 is out of range 1-10', 'map': { '1-12': 'Low', '4-10': 'High', }, 'expected_pass': False }, { 'description': 'Priority 0 is out of range 1-10', 'map': { '0': 'Low', '4-10': 'High', }, 'expected_pass': False }, { 'description': 'Priority 11 is out of range 1-10', 'map': { '1-10': 'Low', '11': 'Super High', }, 'expected_pass': False } ] for priority_map_test in priority_map_tests: self.connector.config['test_alm'] = 'project' self.connector.config['alm_priority_map'] = priority_map_test['map'] try: self.connector.initialize() if not priority_map_test['expected_pass']: raise AssertionError('Error not detected for %s' % priority_map_test['description']) except AlmException: if priority_map_test['expected_pass']: raise AssertionError('Unexpected exception thrown for %s' % priority_map_test['description']) def test_alm_test_connect(self): self.connector.config['test_alm'] = 'project' self.connector.synchronize() def test_sde_valid_phases(self): self.connector.initialize() self.connector.config['alm_phases'] = ['development', 'requirements'] self.connector.synchronize() def test_sde_invalid_phases(self): self.connector.initialize() self.connector.config['alm_phases'] = ['development', 'invalid_phase'] exception_msg = 'Incorrect alm_phase configuration: invalid_phase is not a valid phase' self.assert_exception(AlmException, '', exception_msg, self.connector.synchronize) def test_sde_invalid_selected_tasks(self): self.connector.config['selected_tasks'] = ['T123', '123'] try: self.connector.initialize() raise Exception('Expected an exception to be thrown') except UsageError: pass def test_alm_fixed_title(self): task = { 'id': '1099-T12', 'title': 'T12: Sample Title' } config = { 'alm_context': 'Coffee', 'sde_application': 'Breakfast', 'sde_project': 'Sandwich', 'alm_title_format': '[$application ${project} $context] $task_id ${title}' } title = '[Breakfast Sandwich Coffee] T12:' task_title = AlmConnector.get_alm_task_title(config, task, True) self.assertEqual(title, task_title, 'Incorrect fixed alm title: %s' % task_title) def test_alm_full_title(self): task = { 'id': '1099-T12', 'title': 'T12: Sample Title' } config = { 'alm_context': 'Coffee', 'sde_application': 'Breakfast', 'sde_project': 'Sandwich', 'alm_title_format': '[$application ${project} $context] $task_id ${title}' } title = '[Breakfast Sandwich Coffee] T12: Sample Title' task_title = AlmConnector.get_alm_task_title(config, task) self.assertEqual(title, task_title, 'Incorrect full alm title: %s' % task_title) def test_malformed_alm_title_format(self): self.connector.config['alm_title_format'] = 'BAD' exception_msg = 'Incorrect alm_title_format configuration' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_missing_title_in_alm_title_format(self): self.connector.config['alm_title_format'] = '${task_id}' exception_msg = 'Incorrect alm_title_format configuration. Missing ${title}' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_missing_task_id_in_alm_title_format(self): self.connector.config['alm_title_format'] = '${title}' exception_msg = 'Incorrect alm_title_format configuration. Missing ${task_id}' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_missing_context_in_alm_title_format(self): self.connector.config['alm_context'] = '' self.connector.config['alm_title_format'] = '${context} ${task_id} ${title}' exception_msg = 'Missing alm_context in configuration' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_sde_invalid_min_priority(self): self.connector.config['sde_min_priority'] = 'BAD' exception_msg = 'Incorrect sde_min_priority specified in configuration. Valid values are > 0 and <= 10' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_sde_too_small_min_priority(self): self.connector.config['sde_min_priority'] = 0 exception_msg = 'Incorrect sde_min_priority specified in configuration. Valid values are > 0 and <= 10' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_sde_too_large_min_priority(self): self.connector.config['sde_min_priority'] = 11 exception_msg = 'Incorrect sde_min_priority specified in configuration. Valid values are > 0 and <= 10' self.assert_exception(AlmException, '', exception_msg, self.connector.initialize) def test_tasks_filter_tags(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_min_priority'] = 1 self.connector.config['sde_tags_filter'] = ['one', 'two'] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # We expect these tasks to match self.mock_sde_response.generate_sde_task(priority=8, tags=['one', 'two', 'three']) self.mock_sde_response.generate_sde_task(priority=1, tags=['two', 'one']) # These tasks do not match the tags filter criteria self.mock_sde_response.generate_sde_task(priority=7, tags=['one']) self.mock_sde_response.generate_sde_task(priority=7, tags=['two']) self.mock_sde_response.generate_sde_task(priority=6, tags=['four', 'five']) tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 5, 'Expected 5 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 2, 'Expected 2 tasks') for task in tasks: self.assertTrue(set(self.connector.config['sde_tags_filter']).issubset(task['tags']), 'Task %s has unexpected priority %d' % (task['id'], task['priority'])) def test_tasks_filter_empty_tags(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_min_priority'] = 1 self.connector.config['sde_tags_filter'] = [] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # All these tasks should match self.mock_sde_response.generate_sde_task(priority=8, tags=['one', 'two', 'three']) self.mock_sde_response.generate_sde_task(priority=7, tags=['one']) self.mock_sde_response.generate_sde_task(priority=7, tags=['two']) self.mock_sde_response.generate_sde_task(priority=1, tags=['two', 'one']) self.mock_sde_response.generate_sde_task(priority=6, tags=['four', 'five']) tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 5, 'Expected 5 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 5, 'Expected 5 tasks') def test_tasks_filter_priority(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_min_priority'] = 7 self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # These tasks should match self.mock_sde_response.generate_sde_task(priority=8) self.mock_sde_response.generate_sde_task(priority=7) self.mock_sde_response.generate_sde_task(priority=7) # These tasks should not match self.mock_sde_response.generate_sde_task(priority=1) self.mock_sde_response.generate_sde_task(priority=6) all_tasks = self.connector.sde_get_tasks() self.assertTrue(len(all_tasks) == 5, 'Expected 5 tasks') tasks = self.connector.filter_tasks(all_tasks) self.assertTrue(len(tasks) == 3, 'Expected 3 filtered tasks') for task in tasks: self.assertTrue(task['priority'] >= self.connector.config['sde_min_priority'], 'Task %s has an unexpected priority %d' % (task['id'], task['priority'])) def test_tasks_filter_verification(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_verification_filter'] = ['partial', 'fail'] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # These tasks should match self.mock_sde_response.generate_sde_task(verification_status='partial') self.mock_sde_response.generate_sde_task(verification_status='partial') self.mock_sde_response.generate_sde_task(verification_status='fail') # These tasks should not match self.mock_sde_response.generate_sde_task() self.mock_sde_response.generate_sde_task(verification_status='pass') tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 5, 'Expected 5 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 3, 'Expected 3 tasks') for task in tasks: self.assertTrue(task['verification_status'] in self.config['sde_verification_filter'], 'Task %s has unexpected verification status %s' % (task['id'], task['verification_status'])) def test_tasks_filter_no_verification(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_verification_filter'] = ['none', 'pass'] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # These tasks should match self.mock_sde_response.generate_sde_task(verification_status='pass') self.mock_sde_response.generate_sde_task() # These tasks should not match self.mock_sde_response.generate_sde_task(verification_status='fail') self.mock_sde_response.generate_sde_task(verification_status='partial') tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 4, 'Expected 4 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 2, 'Expected 2 tasks') for task in tasks: self.assertTrue(task['verification_status'] in self.config['sde_verification_filter'], 'Task %s has unexpected verification status %s' % (task['id'], task['verification_status'])) def test_tasks_filter_empty_verification(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_verification_filter'] = [] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # These tasks should match self.mock_sde_response.generate_sde_task(verification_status='pass') self.mock_sde_response.generate_sde_task(verification_status='partial') self.mock_sde_response.generate_sde_task(verification_status='fail') self.mock_sde_response.generate_sde_task() tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 4, 'Expected 4 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 4, 'Expected 4 tasks') for task in tasks: self.assertTrue(task['verification_status'] in AlmConnector.VERIFICATION_STATUSES + [None], 'Task %s has unexpected verification status %s' % (task['id'], task['verification_status'])) def test_tasks_filter_phase(self): self.connector.config['conflict_policy'] = 'sde' self.connector.config['sde_min_priority'] = 7 self.connector.config['alm_phases'] = ['requirements', 'testing'] self.connector.sde_connect() self.connector.alm_connect() # clear out default tasks self.mock_sde_response.clear_tasks() # These tasks should match self.mock_sde_response.generate_sde_task(priority=7, phase='requirements') self.mock_sde_response.generate_sde_task(priority=7, phase='testing') # This task should not match self.mock_sde_response.generate_sde_task(priority=7, phase='development') tasks = self.connector.sde_get_tasks() self.assertTrue(len(tasks) == 3, 'Expected 3 tasks') tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(tasks) == 2, 'Expected 2 tasks') for task in tasks: self.assertTrue(task['phase']['slug'] in self.config['alm_phases'], 'Task %s has unexpected phase %s' % (task['id'], task['phase']['slug'])) def test_update_existing_task_sde(self): # The plugin may initialize variables during alm_connect() so we need # to call alm_connect() before proceeding self.connector.config['conflict_policy'] = 'sde' self.connector.config['alm_phases'] = ['requirements', 'testing', 'development'] self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.synchronize() alm_task = self.connector.alm_get_task(test_task) self.assertNotNone(alm_task, 'Expected alm task to be generated') self.connector.alm_update_task_status(alm_task, STATUS.DONE) def test_update_task_status_to_done(self): self.connector.config['conflict_policy'] = 'alm' self.connector.config['alm_phases'] = ['requirements', 'testing', 'development'] self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) alm_task = self.connector.alm_get_task(test_task) self.assertNotEqual(alm_task.get_status(), STATUS.DONE, 'Cannot update task status to DONE because status is already DONE') self.connector.alm_update_task_status(alm_task, STATUS.DONE) test_task_result = self.connector.alm_get_task(test_task) self.assertEqual(test_task_result.get_status(), STATUS.DONE, 'Failed to update task status to DONE') self.connector.synchronize() the_task = self.connector.sde_get_task(test_task['id']) self.assertEqual(the_task['status'], STATUS.DONE, 'Failed to update SDE task to DONE') def test_update_task_status_to_na(self): self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) alm_task = self.connector.alm_get_task(test_task) self.assertNotEqual(alm_task.get_status(), STATUS.NA, 'Cannot update task status to NA because status is already NA') self.connector.alm_update_task_status(alm_task, STATUS.NA) test_task_result = self.connector.alm_get_task(test_task) self.assertIn(test_task_result.get_status(), [STATUS.DONE, STATUS.NA], 'Failed to update task status to NA') def test_update_task_status_to_todo(self): self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) test_task['status']['id'] = STATUS.DONE self.connector.alm_add_task(test_task) alm_task = self.connector.alm_get_task(test_task) self.assertNotEqual(alm_task.get_status(), STATUS.TODO, 'Cannot update task status to TODO because status is already TODO') self.connector.alm_update_task_status(alm_task, STATUS.TODO) test_task_result = self.connector.alm_get_task(test_task) self.assertEqual(test_task_result.get_status(), STATUS.TODO, 'Failed to update task status to TODO') def test_remove_alm_task(self): if not self.connector.alm_supports_delete(): return self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) test_task_result = self.connector.alm_get_task(test_task) self.assertNotNone(test_task_result, 'Task added to ALM') self.connector.alm_remove_task(test_task_result) test_task_result = self.connector.alm_get_task(test_task) self.assertEqual(test_task_result, None, 'Failed to remove task from ALM') def test_synchronize(self): # Verify no exceptions are thrown self.connector.synchronize() def test_api_exceptions_are_handled(self): # Check that all api exceptions are properly handled for api_target, mock_flag in self.response_generator.rest_api_targets.iteritems(): self.tearDown() self.setUp() self.connector.config['conflict_policy'] = 'sde' self.connector.config['alm_phases'] = ['requirements', 'testing', 'development'] self.mock_sde_response.get_response_generator().generator_clear_resources() # All response methods should throw an exception if the fail flag is encountered self.mock_alm_response.set_response_flags({mock_flag: 'fail'}) try: # This will invoke add, get and update task self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) test_task['status']['id'] = STATUS.DONE self.connector.alm_add_task(test_task) self.connector.synchronize() test_task = self.mock_sde_response.generate_sde_task(phase='testing') requirement_task = self.mock_sde_response.generate_sde_task(phase='requirements') self.assertNotNone(requirement_task, 'Expected requirements task to be generated') self.connector.synchronize() raise Exception('Expected an AlmException to be thrown for the following target: %s' % api_target) except AlmException: pass def test_alm_timestamp_non_naive(self): self.connector.alm_connect() test_task = self.mock_sde_response.generate_sde_task() test_task = self.connector.transform_task(test_task) self.connector.alm_add_task(test_task) alm_task = self.connector.alm_get_task(test_task) assert(alm_task.get_timestamp().tzinfo is not None) @staticmethod def assertIn(value, expectations, msg=None): if value not in expectations: if not msg: msg = 'Value %s is not one of the following expected values: %s' % (value, expectations) raise AssertionError(msg) @staticmethod def assertNotNone(obj, msg=None): if obj is None: if not msg: msg = 'Expected a value other than None' raise AssertionError(msg) @staticmethod def assertMatch(regex, str, msg): pattern = re.compile(regex) result = pattern.match(str) if not result: if not msg: msg = 'String %s did not match the following regular expression: %s' % (str, regex) raise AssertionError(msg) def assert_exception(self, exception, error_code, reason, fn, *args): if not exception: raise AssertionError('No exception type specified') if not fn: raise AssertionError('No function specified') if not exception and not error_code: raise AssertionError('No error code or error message to assert against') try: fn.__call__(*args) raise AssertionError('Expected an exception to be thrown') except Error as err: self.assertEqual(type(err), exception, 'Expected exception type %s, Got %s' % (exception, type(err))) if error_code: self.assertEqual(err.code, error_code, 'Expected error code %s, Got %s' % (error_code, err.code)) if reason: self.assertTrue(reason in err.value, "Expected error message '%s', Got '%s'" % (reason, err.value))
class AlmPluginLiveTestBase(object): @classmethod def setUpClass(cls, connector, api): if 'sdetools' not in config: raise Exception("Missing configuration for sdetools config_path") cls.connector_cls = connector cls.api_cls = api cls.conf_path = config['sdetools']['config_path'] cls.ret_chn = ReturnChannel(stdout_callback, {}) def setUp(self): command_list = load_modules() self.config = Config('help', '', command_list, [], self.ret_chn, 'shell') self.init_alm_connector() self.config.import_custom_options() Config.parse_config_file(self.config, self.conf_path) def init_alm_connector(self): if self.connector_cls is None: raise Error('No alm connector found') elif self.api_cls is None: raise Error('No alm api found') else: self.connector = self.connector_cls(self.config, self.api_cls(self.config)) def test_bad_user(self): self.config['alm_user'] = '******' self.connector.config = self.config self.connector.initialize() try: self.connector.alm_connect() except AlmException: pass def test_bad_server(self): self.config['alm_server'] = 'XXXXX' self.connector.config = self.config self.connector.initialize() try: self.connector.alm_connect() except AlmException: pass def test_sde_connect(self): self.config['test_alm'] = 'server' self.connector.config = self.config self.connector.initialize() self.connector.sde_connect() self.assertTrue(self.connector.is_sde_connected, True) def test_alm_connect_server(self): self.config['test_alm'] = 'server' self.connector.config = self.config self.connector.initialize() self.connector.alm_connect() def test_alm_connect_project(self): self.config['test_alm'] = 'project' self.connector.config = self.config self.connector.initialize() self.connector.alm_connect() def test_alm_connect_settings(self): self.config['test_alm'] = 'settings' self.connector.config = self.config self.connector.initialize() self.connector.alm_connect() def test_alm_task_delete(self): self.config['test_alm'] = '' self.connector.config = self.config self.connector.initialize() if not self.connector.alm_supports_delete() or not self.config['start_fresh']: return self.connector.config['start_fresh'] = True self.connector.sde_connect() self.connector.alm_connect() tasks = self.connector.sde_get_tasks() filtered_tasks = self.connector.filter_tasks(tasks) if not filtered_tasks: return test_task = filtered_tasks[0] alm_task1 = self.connector.alm_get_task(test_task) if not alm_task1: ref = self.connector.alm_add_task(test_task) self.assertNotNone(ref, 'Could not create ALM issue for %s' % test_task['id']) alm_task1 = self.connector.alm_get_task(test_task) self.assertNotNone(alm_task1, 'Missing Alm task for %s' % test_task['id']) self.connector.alm_remove_task(alm_task1) alm_task2 = self.connector.alm_get_task(test_task) if alm_task2: self.assertNotEqual(alm_task1.get_alm_id(), alm_task2.get_alm_id(), 'Could not delete task %s in alm' % test_task['id']) def synchronize(self, options): options['test_alm'] = '' self._update_config(options) self.connector.initialize() # Only refresh tasks if the configuration has this option explicitly set refresh_tasks = self.connector.config['start_fresh'] and self.connector.alm_supports_delete() alm_tasks = {} for i in xrange(2): # clean out any existing issues on the first run, if possible if i == 1 and refresh_tasks: self.connector.config['start_fresh'] = True else: self.connector.config['start_fresh'] = False # synchronize the two systems self.connector.synchronize() tasks = self.connector.sde_get_tasks() filtered_tasks = self.connector.filter_tasks(tasks) for task in filtered_tasks: alm_task = self.connector.alm_get_task(task) self.assertNotNone(alm_task, 'Missing Alm task for %s' % task['id']) if self.config['alm_standard_workflow']: self.assertTrue(self.connector.status_match(alm_task.get_status(), task['status']['id'])) # invert the status and get ready to synchronize again self.connector.sde_update_task_status(task, self._inverted_status(task['status']['id'])) # Remaining tests are only applicable if connector supports delete if not refresh_tasks: continue # Make sure we're creating new ALM tasks if i == 0: alm_tasks[task['id']] = alm_task.get_alm_id() elif i == 1: self.assertNotEqual(alm_tasks[task['id']], alm_task.get_alm_id()) # we don't need the alm task anymore self.connector.alm_remove_task(alm_task) def test_synchronize_sde_as_master(self): self.synchronize({'conflict_policy': 'sde'}) def test_synchronize_alm_as_master(self): self.synchronize({'conflict_policy': 'alm'}) def _update_config(self, options): for key in options: self.config[key] = options[key] self.connector.config = self.config def test_synchronize_similar_tasks(self): self.connector.initialize() self.connector.sde_connect() self.connector.alm_connect() tasks = self.connector.sde_get_tasks() filtered_tasks = self.connector.filter_tasks(tasks) self.assertTrue(len(filtered_tasks) > 0) task = filtered_tasks[0] test_task = task.copy() task_id = test_task['id'] title = test_task['title'] # construct a similar task (T21 => T21222222) and add to the ALM task['id'] = '%s222222' % test_task['id'] task['title'] = '%s: Sample Title' % AlmConnector._extract_task_id(task['id']) task = self.connector.transform_task(task) # re-use an issue if one already exists in the ALM alm_task1 = self.connector.alm_get_task(task) if not alm_task1: ref = self.connector.alm_add_task(task) self.assertNotNone(ref, 'Could not add issue to ALM for %s' % task['id']) alm_task1 = self.connector.alm_get_task(task) self.assertNotNone(alm_task1, 'Missing ALM task for %s' % task['id']) # Try to sync a similar task test_task['id'] = task_id test_task['title'] = title test_task = self.connector.transform_task(test_task) alm_task2 = self.connector.alm_get_task(test_task) if not alm_task2: ref = self.connector.alm_add_task(test_task) self.assertNotNone(ref, 'Could not add issue to ALM for %s' % test_task['id']) alm_task2 = self.connector.alm_get_task(test_task) self.assertNotNone(alm_task2, 'Missing ALM task for %s' % test_task['id']) self.assertNotEqual(alm_task1.get_alm_id(), alm_task2.get_alm_id()) # clean-up issues in the ALM if not self.connector.alm_supports_delete() or not self.connector.config['start_fresh']: return if alm_task1: self.connector.alm_remove_task(alm_task1) if alm_task2: self.connector.alm_remove_task(alm_task2) def test_custom_titles(self): scenario_options = { 'alm_standard_workflow': False, 'alm_context': 'Context', } scenario1_alm_title_format = '[$application-$project] $title' scenario2_alm_title_format = '[$context] $title' self._update_config(scenario_options) self.connector.initialize() self.connector.sde_connect() self.connector.alm_connect() tasks = self.connector.sde_get_tasks() filtered_tasks = self.connector.filter_tasks(tasks) for task in filtered_tasks: # Find the corresponding scenario1 alm task # setup the alm_title_format and initialize everything self.connector.config['alm_title_format'] = scenario1_alm_title_format scenario1_task = self.connector.transform_task(task.copy()) scenario1_alm_task = self.connector.alm_get_task(scenario1_task) if not scenario1_alm_task: ref = self.connector.alm_add_task(scenario1_task) self.assertNotNone(ref, 'Could not add issue to ALM for %s' % scenario1_task['id']) scenario1_alm_task = self.connector.alm_get_task(scenario1_task) self.assertNotNone(scenario1_alm_task, 'Could not retrieve ALM issue with title: %s' % scenario1_task['alm_full_title']) # Find the corresponding scenario2 alm task # setup the alm_title_format and initialize everything self.connector.config['alm_title_format'] = scenario2_alm_title_format scenario2_task = self.connector.transform_task(task.copy()) scenario2_alm_task = self.connector.alm_get_task(scenario2_task) if not scenario2_alm_task: ref = self.connector.alm_add_task(scenario2_task) self.assertNotNone(ref, 'Could not add issue to ALM for %s' % scenario2_task['id']) scenario2_alm_task = self.connector.alm_get_task(scenario2_task) self.assertNotNone(scenario2_alm_task, 'Could not retrieve ALM issue with title: %s' % scenario2_task['alm_full_title']) # Check that these alm tasks are distinct for the same sde task self.assertNotEqual(scenario1_alm_task.get_alm_id(), scenario2_alm_task.get_alm_id()) # Update the first alm task to the opposite of the second alm task's status scenario2_status = scenario2_alm_task.get_status() self.connector.alm_update_task_status(scenario1_alm_task, self._inverted_status(scenario2_status)) scenario1_alm_task = self.connector.alm_get_task(scenario1_task) scenario2_alm_task = self.connector.alm_get_task(scenario2_task) # Make sure the first alm task status updated and the second one remained the same self.assertTrue(self.connector.status_match(scenario1_alm_task.get_status(), self._inverted_status(scenario2_status))) self.assertTrue(self.connector.status_match(scenario2_alm_task.get_status(), scenario2_status)) # Update the second alm task to the opposite of the first alm task's status scenario1_status = scenario1_alm_task.get_status() self.connector.alm_update_task_status(scenario2_alm_task, self._inverted_status(scenario1_status)) scenario2_alm_task = self.connector.alm_get_task(scenario2_task) scenario1_alm_task = self.connector.alm_get_task(scenario1_task) # Make sure the second alm task status updated and the first one remained the same self.assertTrue(self.connector.status_match(scenario2_alm_task.get_status(), self._inverted_status(scenario1_status))) self.assertTrue(self.connector.status_match(scenario1_alm_task.get_status(), scenario1_status)) # clean-up issues in the ALM if not self.connector.alm_supports_delete(): return if scenario1_alm_task: self.connector.alm_remove_task(scenario1_alm_task) if scenario2_alm_task: self.connector.alm_remove_task(scenario2_alm_task) def _inverted_status(self, status): return STATUS.DONE if status == STATUS.TODO else STATUS.TODO @staticmethod def assertNotNone(obj, msg=None): if obj is None: if not msg: msg = 'Expected a value other than None' raise AssertionError(msg)