def test_can_serialize_task_to_dict(self): task = AgiloTicket(self.env, t_type=Type.TASK) self.assertNotEqual('fixed', task[Key.RESOLUTION]) task[Key.SUMMARY] = 'My Summary' task.insert() expected = { # required Key.ID: task.id, Key.TYPE: Type.TASK, Key.SUMMARY: 'My Summary', Key.DESCRIPTION: '', Key.STATUS: '', Key.RESOLUTION: '', Key.REPORTER: '', Key.OWNER: '', # type specific Key.SPRINT: '', Key.REMAINING_TIME: '', Key.RESOURCES: '', # Key.Options is not used in order to reduce required data to # transfer for a backlog load. 'outgoing_links': [], 'incoming_links': [], 'time_of_last_change': to_timestamp(task.time_changed), 'ts': str(task.time_changed), } if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp expected.update( {'view_time': str(to_utimestamp(task.time_changed))}) self.assert_equals(expected, task.as_dict())
def test_resource_list_can_include_owner(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) story[Key.OWNER] = 'Foo' self.assert_equals(0, len(story.get_resource_list())) self.assert_equals(['Foo'], story.get_resource_list(include_owner=True)) story[Key.OWNER] = '' self.assert_equals(0, len(story.get_resource_list(include_owner=True)))
def test_changing_type_restore_correct_fields(self): """Tests that changing a ticket type restores the correct fields for that type""" t = AgiloTicket(self.env, t_type=Type.TASK) self.assert_true(self._matching_properties(t)) # Now change that to a story t[Key.TYPE] = Type.USER_STORY self.assert_true(self._matching_properties(t)) # Now reload config and change to bug ac = AgiloConfig(self.env) ac.reload() t[Key.TYPE] = Type.BUG self.assert_true(self._matching_properties(t)) # Now add a field on the fly and see if it is adapting to it agilo_types = ac.get_section(ac.AGILO_TYPES) ticket_custom = ac.get_section(ac.TICKET_CUSTOM) ticket_custom.change_option('customer', 'text') ticket_custom.change_option('customer.label', 'Customer') fields = agilo_types.get_list(Type.BUG) fields.append('customer') # notice the save to force the reload of the config agilo_types.change_option('story', ','.join(fields), save=True) t[Key.TYPE] = Type.USER_STORY self.assert_true('customer' in t.fields_for_type, \ "No 'customer' in %s" % t.fields_for_type) t['customer'] = 'My Own Customer' self.assert_true(self._matching_properties(t)) t.insert() self.assert_true(self._matching_properties(t)) self.assert_equals('My Own Customer', t['customer'])
def test_always_serialize_id_as_int(self): task = AgiloTicket(self.env, t_type=Type.TASK) task.insert() task_id = task.id task = AgiloTicket(self.env, str(task_id)) self.assert_equals(task_id, task.as_dict()['id'])
def test_can_serialize_task_to_dict(self): task = AgiloTicket(self.env, t_type=Type.TASK) self.assertNotEqual('fixed', task[Key.RESOLUTION]) task[Key.SUMMARY] = 'My Summary' task.insert() expected = { # required Key.ID: task.id, Key.TYPE: Type.TASK, Key.SUMMARY: 'My Summary', Key.DESCRIPTION: '', Key.STATUS: '', Key.RESOLUTION: '', Key.REPORTER: '', Key.OWNER: '', # type specific Key.SPRINT: '', Key.REMAINING_TIME: '', Key.RESOURCES: '', # Key.Options is not used in order to reduce required data to # transfer for a backlog load. 'outgoing_links': [], 'incoming_links': [], 'time_of_last_change': to_timestamp(task.time_changed), 'ts': str(task.time_changed), } if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp expected.update({'view_time': str(to_utimestamp(task.time_changed))}) self.assert_equals(expected, task.as_dict())
def test_can_serialize_links(self): task = AgiloTicket(self.env, t_type=Type.TASK) task.insert() story = AgiloTicket(self.env, t_type=Type.USER_STORY) story.link_to(task) story.insert() self.assert_equals([task.id], story.as_dict()['outgoing_links']) self.assert_equals([story.id], task.as_dict()['incoming_links'])
def test_empty_resource_list_for_empty_resource_string(self): task = AgiloTicket(self.env, t_type=Type.TASK) task[Key.RESOURCES] = '' self.assert_equals([], task.get_resource_list()) task[Key.RESOURCES] = ' ' self.assert_equals([], task.get_resource_list()) task[Key.RESOURCES] = ' , , , ' self.assert_equals([], task.get_resource_list())
def test_can_set_basic_properties_before_creating_agilo_ticket(self): ticket = AgiloTicket(self.env) ticket[Key.TYPE] = "story" ticket[Key.SUMMARY] = "This is an AgiloTicket" ticket[Key.DESCRIPTION] = "This is the description of the ticket..." ticket_id = ticket.insert() story = AgiloTicket(self.env, ticket_id) for attribute_name in (Key.TYPE, Key.SUMMARY, Key.DESCRIPTION): self.assert_equals(ticket[attribute_name], story[attribute_name])
def test_saving_of_custom_properties_works_on_model(self): """Tests that the custom properties are saved on insert""" us = AgiloTicket(self.env) us[Key.TYPE] = Type.USER_STORY us[Key.SUMMARY] = "This is a story with 5 points" us[Key.DESCRIPTION] = "As a user..." # Now a custom property us[Key.STORY_POINTS] = '5' us_id = us.insert() # Now reload the story same_us = AgiloTicket(self.env, tkt_id=us_id) self.assert_equals(Type.USER_STORY, same_us[Key.TYPE]) self.assert_equals('5', same_us[Key.STORY_POINTS])
def test_cannot_change_to_value_to_empty(self): self._change_ticket(Usernames.team_member, component='component1') task_from_db = AgiloTicket(self.env, self.task.id) self.assert_equals('component1', task_from_db[Key.COMPONENT]) req = self._request_for_ticket_change(Usernames.team_member, component='') response = self.assert_method_returns_error( TicketUpdateView(self.env).do_post, req, req.args) self.assert_contains('must be set', response.errors[0]) task_from_db = AgiloTicket(self.env, self.task.id) self.assert_equals('component1', task_from_db[Key.COMPONENT])
def test_can_serialize_calculated_fields(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) task = AgiloTicket(self.env, t_type=Type.TASK) task[Key.REMAINING_TIME] = '5' story.link_to(task) story.insert() self.assert_equals(5, story.as_dict()[Key.TOTAL_REMAINING_TIME])
def test_new_agilo_tickets_dont_have_a_resolution_by_default(self): # This bug was triggered by declaring the default resolution option in # trac's TicketModule so we make sure that the option is always known # when running this test Option('ticket', 'default_resolution', 'fixed', '') ticket = AgiloTicket(self.env, t_type=Type.TASK) self.assert_equals('', ticket[Key.RESOLUTION])
def test_get_field_with_field_name(self): """Tests the method get_field of a ticket, that returns the field dictionary""" task = AgiloTicket(self.env, t_type=Type.TASK) f = task.get_field(Key.REMAINING_TIME) self.assert_not_none(f) self.assert_equals(Key.REMAINING_TIME, f[Key.NAME]) us = AgiloTicket(self.env, t_type=Type.USER_STORY) f = us.get_field(Key.STORY_POINTS) self.assert_not_none(f) self.assert_equals(Key.STORY_POINTS, f[Key.NAME]) # Now calculated fields c = us.get_field(Key.TOTAL_REMAINING_TIME) self.assert_not_none(c) self.assert_equals(Key.TOTAL_REMAINING_TIME, c[Key.NAME])
def get_all_open_tickets(env): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("select id from ticket where status != 'closed'") tickets = (AgiloTicket(env, tkt_id=row[0], db=db) for row in cursor.fetchall()) return tickets
def testCreateNewLinkedTicket(self): """Test the creation of a new linked ticket""" # Get db and handle the transaction db = self.teh.get_env().get_db_cnx() new = AgiloTicket(self.teh.get_env()) new[Key.SUMMARY] = 'This is a new ticket, never saved' new[Key.DESCRIPTION] = 'This will be created and linked in one shot ;-)' new[Key.TYPE] = Type.TASK self.assert_true(new.link_from(self.t1, db)) self.assert_not_none(new.insert(db=db)) # Now commit the link and the insert db.commit() # Check that has been linked self.assert_true(new.is_linked_from(self.t1)) self.assert_true(self.t1.is_linked_to(new)) # Now test the autoinsert and link of the agilo ticket new2 = AgiloTicket(self.teh.get_env(), t_type=Type.TASK) new2[Key.SUMMARY] = "This is a linked new ticket" new2[Key.DESCRIPTION] = "description" self.assert_true(self.t1.link_to(new2)) self.assert_true(new2.is_linked_from(self.t1)) self.assert_true(self.t1.is_linked_to(new2)) # Now test the link failure self.assert_false(new2.link_from(self.t1))
def test_dragging_task_to_in_progress_set_status_accepted(self): self.assert_not_equals(Usernames.team_member, self.task[Key.OWNER]) json = self._set_status_with_json(Usernames.team_member, simple_status='in_progress') self.assert_equals(Status.ACCEPTED, json.status) self.assert_equals(Usernames.team_member, json.owner) # load from db explicitly, our cached self.task still has old values task_from_db = AgiloTicket(self.env, self.task.id) self.assert_equals(Usernames.team_member, task_from_db[Key.OWNER])
def test_new_ticket_uses_default_type_if_none_given(self): # This is the unit test covering bug #611 (see also regression test # TestOnlyTasksFieldsAreShownInPreview) def field_names(ticket): return [f['name'] for f in ticket.fields] old_default_type = self._set_default_type(Type.TASK) try: story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_equals(Type.USER_STORY, story[Key.TYPE]) self.assert_true(Key.STORY_POINTS in field_names(story)) self.assert_false(Key.REMAINING_TIME in field_names(story)) self.assert_false(Key.BUSINESS_VALUE in field_names(story)) ticket = AgiloTicket(self.env) self.assert_equals(Type.TASK, ticket[Key.TYPE]) self.assert_true(Key.REMAINING_TIME in field_names(ticket)) self.assert_false(Key.STORY_POINTS in field_names(ticket)) self.assert_false(Key.BUSINESS_VALUE in field_names(ticket)) finally: self._set_default_type(old_default_type)
def test_milestone_for_task_is_deleted_when_task_gets_removed_from_sprint( self): sprint = self.teh.create_sprint('fnord') task = self.teh.create_task(sprint=sprint) self.assert_equals(sprint.milestone, task[Key.MILESTONE]) task[Key.SPRINT] = None task.save_changes(None, None) self.assert_equals('', task[Key.MILESTONE]) self.assert_equals( '', AgiloTicket(self.env, tkt_id=task.id)[Key.MILESTONE])
def test_correct_handling_of_ticket_types(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) story[Key.SUMMARY] = "This is a story..." story[Key.DESCRIPTION] = "A very nice one..." self.assert_true(self._matching_properties(story)) story.insert() self.assert_true(self._matching_properties(story)) # Not existing type should show all the properties nonex = AgiloTicket(self.env, t_type="nonex") nonex[Key.SUMMARY] = "This is a story..." nonex[Key.DESCRIPTION] = "A very nice one..." nonex.insert() self.assert_true(self._matching_properties(nonex)) # Setting type on the fly should work task = AgiloTicket(self.env, t_type=Type.TASK) task[Key.SUMMARY] = "This should be a task" self.assert_true(self._matching_properties(task)) task.insert() self.assert_true(self._matching_properties(task))
def _request_for_ticket_change(self, username, **kwargs): # We need to load the ticket again to get the correct time of last # change - otherwise trac will reject the edit... task = AgiloTicket(self.env, self.task.id) args = dict( ticket_id=task.id, time_of_last_change=to_timestamp(task.time_changed), ts=str(task.time_changed), ) from agilo.ticket import AgiloTicketSystem if AgiloTicketSystem.is_trac_1_0(): from trac.util.datefmt import to_utimestamp args.update({ 'view_time': str(to_utimestamp(task.time_changed)), 'submit': True }) args.update(kwargs) req = self.teh.mock_request(username, method='POST') req.args = args return req
def test_throws_exception_if_not_persisted_ticket_should_be_serialized( self): task = AgiloTicket(self.env, t_type=Type.TASK) self.assert_raises(ValueError, task.as_dict)
def test_owner_is_only_listed_once_in_resource_list(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) story[Key.OWNER] = 'Foo Bar' story[Key.RESOURCES] = 'Foo Bar' self.assert_equals(['Foo Bar'], story.get_resource_list(include_owner=True))
def test_resource_list_with_owner_and_resources(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) story[Key.OWNER] = 'Foo' story[Key.RESOURCES] = 'Bar, Baz' self.assert_equals(['Foo', 'Bar', 'Baz'], story.get_resource_list(include_owner=True))
def test_ticket_is_task_like_if_it_has_remaining_time_field(self): self.teh.add_field_for_type(Key.REMAINING_TIME, Type.USER_STORY) story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_true(story.is_readable_field(Key.REMAINING_TIME)) self.assert_true(story.is_task_like())
def test_cannot_change_custom_field_that_is_disabled_for_tasks(self): json = self._change_ticket(Usernames.team_member, businessvalue='100') self.assert_not_contains(Key.BUSINESS_VALUE, json) task_from_db = AgiloTicket(self.env, self.task.id) self.assert_equals('', task_from_db[Key.BUSINESS_VALUE])
def test_can_use_numbers_for_ticket_properties(self): task = AgiloTicket(self.env, t_type=Type.TASK) # trac tries to strip() ticket properties so AgiloTicket needs convert # the value to a str task[Key.REMAINING_TIME] = 5 task[Key.SUMMARY] = u'Should work with unicode values: äöüß'
def test_ticket_knows_if_it_is_task_like(self): task = AgiloTicket(self.env, t_type=Type.TASK) self.assert_true(task.is_readable_field(Key.REMAINING_TIME)) self.assert_true(task.is_task_like())
def test_strip_usernames_for_resource_list(self): task = AgiloTicket(self.env, t_type=Type.TASK) task[Key.RESOURCES] = ' foo, bar,baz ' self.assert_equals(['foo', 'bar', 'baz'], task.get_resource_list())
def test_ticket_knows_if_not_task_like(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_false(story.is_readable_field(Key.REMAINING_TIME)) self.assert_false(story.is_task_like())
def test_resource_list_when_no_resource_field_present(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_equals('', story[Key.RESOURCES]) self.assert_equals(0, len(story.get_resource_list()))
def test_story_has_total_remaining_time_and_estimated_remaining_time_by_default(self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_equals([Key.TOTAL_REMAINING_TIME, Key.ESTIMATED_REMAINING_TIME], story.get_calculated_fields_names())
def test_do_not_stringify_none_for_ticket_properties(self): task = AgiloTicket(self.env, t_type=Type.TASK) task[Key.REMAINING_TIME] = None task.insert() task[Key.REMAINING_TIME] = 3 self.assert_none(task._old[Key.REMAINING_TIME])
def test_story_has_total_remaining_time_and_estimated_remaining_time_by_default( self): story = AgiloTicket(self.env, t_type=Type.USER_STORY) self.assert_equals( [Key.TOTAL_REMAINING_TIME, Key.ESTIMATED_REMAINING_TIME], story.get_calculated_fields_names())