class TicketModuleTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.ticket_module = TicketModule(self.env) def tearDown(self): self.env.reset_db() def _create_request(self, authname='anonymous', **kwargs): kw = { 'path_info': '/', 'perm': MockPerm(), 'args': _RequestArgs(), 'href': self.env.href, 'abs_href': self.env.abs_href, 'tz': utc, 'locale': None, 'lc_time': locale_en, 'session': DetachedSession(self.env, authname), 'authname': authname, 'chrome': { 'notices': [], 'warnings': [] }, 'method': None, 'get_header': lambda v: None, 'is_xhr': False, 'form_token': None } if 'args' in kwargs: kw['args'].update(kwargs.pop('args')) kw.update(kwargs) def redirect(url, permanent=False): raise RequestDone return Mock(add_redirect_listener=lambda x: [].append(x), redirect=redirect, **kw) def _create_ticket_with_change(self, old_props, new_props, author='anonymous'): """Create a ticket with `old_props` and apply properties in `new_props`. """ t = Ticket(self.env) t.populate(old_props) t.insert() comment = new_props.pop('comment', None) t.populate(new_props) t.save_changes(author, comment=comment) return t def _insert_ticket(self, **kw): """Helper for inserting a ticket into the database""" ticket = Ticket(self.env) for k, v in kw.items(): ticket[k] = v return ticket.insert() def test_ticket_module_as_default_handler(self): """The New Ticket mainnav entry is active when TicketModule is the `default_handler` and navigating to the base url. Test for regression of http://trac.edgewall.org/ticket/8791. """ req = self._create_request() chrome = Chrome(self.env).prepare_request(req, self.ticket_module) name = None for item in chrome['nav']['mainnav']: if item['active'] is True: name = item['name'] break self.assertEqual('newticket', name) def test_reporter_and_owner_full_name_is_displayed(self): """Full name of reporter and owner are used in ticket properties.""" self.env.insert_known_users([('user1', 'User One', None), ('user2', 'User Two', None)]) tkt_id = self._insert_ticket(reporter='user1', owner='user2') req = self._create_request(authname='user2', method='GET', args={ 'id': tkt_id, 'replyto': '1' }) data = self.ticket_module.process_request(req)[1] self.assertEqual( u'<a class="trac-author" href="/trac.cgi/query?' u'status=!closed&reporter=user1">User One</a>', unicode(data['reporter_link'])) self.assertEqual( u'<a class="trac-author-user" href="/trac.cgi/query?' u'status=!closed&owner=user2">User Two</a>', unicode(data['owner_link'])) def test_quoted_reply_author_is_obfuscated(self): """Reply-to author is obfuscated in a quoted reply.""" author = 'author <*****@*****.**>' tkt = self._create_ticket_with_change({}, {'comment': 'the comment'}, author) req = self._create_request(method='GET', args={ 'id': tkt.id, 'replyto': '1' }) data = self.ticket_module.process_request(req)[1] comment = u"Replying to [comment:1 author <author@\u2026>]:\n> " \ u"the comment\n" self.assertEqual(comment, data['comment']) self.assertEqual(comment, data['change_preview']['comment']) def test_quoted_reply_author_full_name_is_displayed(self): """Full name of reply-to author is used in quoted reply.""" self.env.insert_known_users([('author', 'The Author', '*****@*****.**')]) tkt = self._create_ticket_with_change({}, {'comment': 'the comment'}, 'author') req = self._create_request(method='GET', args={ 'id': tkt.id, 'replyto': '1' }) data = self.ticket_module.process_request(req)[1] comment = u"Replying to [comment:1 The Author]:\n> " \ u"the comment\n" self.assertEqual(comment, data['comment']) self.assertEqual(comment, data['change_preview']['comment']) def test_ticket_property_diff_owner_change(self): """Property diff message when ticket owner is changed.""" t = self._create_ticket_with_change({'owner': 'owner1'}, {'owner': 'owner2'}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['owner'] self.assertEqual("changed from <em>owner1</em> to <em>owner2</em>", str(field['rendered'])) def test_ticket_property_diff_owner_add(self): """Property diff message when ticket owner is added.""" t = self._create_ticket_with_change({'owner': ''}, {'owner': 'owner2'}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['owner'] self.assertEqual("set to <em>owner2</em>", str(field['rendered'])) def test_ticket_property_diff_owner_remove(self): """Property diff message when ticket owner is removed.""" t = self._create_ticket_with_change({'owner': 'owner1'}, {'owner': ''}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['owner'] self.assertEqual("<em>owner1</em> deleted", str(field['rendered'])) def test_ticket_property_diff_reporter_change(self): """Property diff message when ticket reporter is changed.""" t = self._create_ticket_with_change({'reporter': 'reporter1'}, {'reporter': 'reporter2'}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['reporter'] self.assertEqual( "changed from <em>reporter1</em> to " "<em>reporter2</em>", str(field['rendered'])) def test_ticket_property_diff_reporter_add(self): """Property diff message when ticket reporter is added.""" t = self._create_ticket_with_change({'reporter': ''}, {'reporter': 'reporter2'}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['reporter'] self.assertEqual("set to <em>reporter2</em>", str(field['rendered'])) def test_ticket_property_diff_reporter_remove(self): """Property diff message when ticket reporter is removed.""" t = self._create_ticket_with_change({'reporter': 'reporter1'}, {'reporter': ''}) req = self._create_request(args={'id': t.id}) data = self.ticket_module.process_request(req)[1] field = data['changes'][0]['fields']['reporter'] self.assertEqual("<em>reporter1</em> deleted", str(field['rendered'])) def _test_invalid_cnum_raises(self, action, cnum=None): self._insert_ticket() req = self._create_request(args={'action': action, 'id': '1'}) if cnum is not None: req.args.update({'cnum': cnum}) self.assertRaises(TracError, self.ticket_module.process_request, req) def test_comment_history_cnum_missing_raises(self): self._test_invalid_cnum_raises('comment-history') def test_comment_history_cnum_invalid_type_raises(self): self._test_invalid_cnum_raises('comment-history', 'a') def test_comment_history_cnum_empty_raises(self): self._test_invalid_cnum_raises('comment-history', '') def test_comment_history_cnum_out_of_range(self): """Out of range cnum returns an empty history.""" self._insert_ticket() req = self._create_request(args={ 'action': 'comment-history', 'id': '1', 'cnum': '1' }) resp = self.ticket_module.process_request(req) self.assertEqual([], resp[1]['history']) def test_comment_diff_cnum_missing_raises(self): self._test_invalid_cnum_raises('comment-diff') def test_comment_diff_cnum_invalid_type_raises(self): self._test_invalid_cnum_raises('comment-diff', 'a') def test_comment_diff_cnum_empty_raises(self): self._test_invalid_cnum_raises('comment-diff', '') def test_comment_diff_cnum_out_of_range_raises(self): self._insert_ticket() req = self._create_request(args={ 'action': 'comment-diff', 'id': '1', 'cnum': '1' }) self.assertRaises(ResourceNotFound, self.ticket_module.process_request, req)
class RestrictOwnerTestCase(unittest.TestCase): def setUp(self): tmpdir = os.path.realpath(tempfile.gettempdir()) self.env = EnvironmentStub(enable=['trac.*', AuthzPolicy], path=tmpdir) self.env.config.set('trac', 'permission_policies', 'AuthzPolicy, DefaultPermissionPolicy') self.env.config.set('ticket', 'restrict_owner', True) self.perm_sys = PermissionSystem(self.env) self.env.insert_known_users([ ('user1', '', ''), ('user2', '', ''), ('user3', '', ''), ('user4', '', '') ]) self.perm_sys.grant_permission('user1', 'TICKET_MODIFY') self.perm_sys.grant_permission('user2', 'TICKET_VIEW') self.perm_sys.grant_permission('user3', 'TICKET_MODIFY') self.perm_sys.grant_permission('user4', 'TICKET_MODIFY') self.authz_file = os.path.join(tmpdir, 'trac-authz-policy') create_file(self.authz_file) self.env.config.set('authz_policy', 'authz_file', self.authz_file) self.ctlr = TicketSystem(self.env).action_controllers[0] self.req1 = Mock(authname='user1', args={}, perm=PermissionCache(self.env, 'user1')) self.ticket = Ticket(self.env) self.ticket['status'] = 'new' self.ticket.insert() def tearDown(self): self.env.reset_db() os.remove(self.authz_file) def _reload_workflow(self): self.ctlr.actions = self.ctlr.get_all_actions() def test_set_owner(self): """Restricted owners list contains users with TICKET_MODIFY. """ ctrl = self.ctlr.render_ticket_action_control(self.req1, self.ticket, 'reassign') self.assertEqual('reassign', ctrl[0]) self.assertIn('value="user1">user1</option>', str(ctrl[1])) self.assertNotIn('value="user2">user2</option>', str(ctrl[1])) self.assertIn('value="user3">user3</option>', str(ctrl[1])) self.assertIn('value="user4">user4</option>', str(ctrl[1])) def test_set_owner_fine_grained_permissions(self): """Fine-grained permission checks when populating the restricted owners list (#10833). """ create_file(self.authz_file, """\ [ticket:1] user4 = !TICKET_MODIFY """) ctrl = self.ctlr.render_ticket_action_control(self.req1, self.ticket, 'reassign') self.assertEqual('reassign', ctrl[0]) self.assertIn('value="user1">user1</option>', str(ctrl[1])) self.assertNotIn('value="user2">user2</option>', str(ctrl[1])) self.assertIn('value="user3">user3</option>', str(ctrl[1])) self.assertNotIn('value="user4">user4</option>', str(ctrl[1]))
class TicketSystemTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.perm = PermissionSystem(self.env) self.ticket_system = TicketSystem(self.env) self.req = Mock() def tearDown(self): self.env.reset_db() def _get_actions(self, ticket_dict): ts = TicketSystem(self.env) ticket = Ticket(self.env) ticket.populate(ticket_dict) id = ticket.insert() return ts.get_available_actions(self.req, Ticket(self.env, id)) def _get_ticket_field(self, field_name): fields = TicketSystem(self.env).get_ticket_fields() return (i for i in fields if i['name'] == field_name).next() def test_custom_field_text(self): self.env.config.set('ticket-custom', 'test', 'text') self.env.config.set('ticket-custom', 'test.label', 'Test') self.env.config.set('ticket-custom', 'test.value', 'Foo bar') self.env.config.set('ticket-custom', 'test.format', 'wiki') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual({'name': 'test', 'type': 'text', 'label': 'Test', 'value': 'Foo bar', 'order': 0, 'format': 'wiki', 'custom': True}, fields[0]) def test_custom_field_select(self): self.env.config.set('ticket-custom', 'test', 'select') self.env.config.set('ticket-custom', 'test.label', 'Test') self.env.config.set('ticket-custom', 'test.value', '1') self.env.config.set('ticket-custom', 'test.options', 'option1|option2') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test', 'value': '1', 'options': ['option1', 'option2'], 'order': 0, 'custom': True}, fields[0]) def test_custom_field_optional_select(self): self.env.config.set('ticket-custom', 'test', 'select') self.env.config.set('ticket-custom', 'test.label', 'Test') self.env.config.set('ticket-custom', 'test.value', '1') self.env.config.set('ticket-custom', 'test.options', '|option1|option2') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual({'name': 'test', 'type': 'select', 'label': 'Test', 'value': '1', 'options': ['option1', 'option2'], 'order': 0, 'optional': True, 'custom': True}, fields[0]) def test_custom_field_textarea(self): self.env.config.set('ticket-custom', 'test', 'textarea') self.env.config.set('ticket-custom', 'test.label', 'Test') self.env.config.set('ticket-custom', 'test.value', 'Foo bar') self.env.config.set('ticket-custom', 'test.rows', '4') self.env.config.set('ticket-custom', 'test.format', 'wiki') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual({'name': 'test', 'type': 'textarea', 'label': 'Test', 'value': 'Foo bar', 'height': 4, 'order': 0, 'format': 'wiki', 'custom': True}, fields[0]) def test_custom_field_time(self): self.env.config.set('ticket-custom', 'test', 'time') self.env.config.set('ticket-custom', 'test.label', 'Test') self.env.config.set('ticket-custom', 'test.value', '') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual({'name': 'test', 'type': 'time', 'label': 'Test', 'value': '', 'order': 0, 'format': 'datetime', 'custom': True}, fields[0]) def test_custom_field_order(self): self.env.config.set('ticket-custom', 'test1', 'text') self.env.config.set('ticket-custom', 'test1.order', '2') self.env.config.set('ticket-custom', 'test2', 'text') self.env.config.set('ticket-custom', 'test2.order', '1') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual('test2', fields[0]['name']) self.assertEqual('test1', fields[1]['name']) def test_custom_field_label(self): self.env.config.set('ticket-custom', '_test_one', 'text') self.env.config.set('ticket-custom', 'test_two', 'text') self.env.config.set('ticket-custom', 'test_two.label', 'test_2') fields = TicketSystem(self.env).get_custom_fields() self.assertEqual('Test one', fields[0]['label']) self.assertEqual('test_2', fields[1]['label']) def test_available_actions_full_perms(self): self.perm.grant_permission('anonymous', 'TICKET_CREATE') self.perm.grant_permission('anonymous', 'TICKET_MODIFY') self.req.perm = PermissionCache(self.env) self.assertEqual(['leave', 'resolve', 'reassign', 'accept'], self._get_actions({'status': 'new'})) self.assertEqual(['leave', 'resolve', 'reassign', 'accept'], self._get_actions({'status': 'assigned'})) self.assertEqual(['leave', 'resolve', 'reassign', 'accept'], self._get_actions({'status': 'accepted'})) self.assertEqual(['leave', 'resolve', 'reassign', 'accept'], self._get_actions({'status': 'reopened'})) self.assertEqual(['leave', 'reopen'], self._get_actions({'status': 'closed'})) def test_available_actions_no_perms(self): self.req.perm = PermissionCache(self.env) self.assertEqual(['leave'], self._get_actions({'status': 'new'})) self.assertEqual(['leave'], self._get_actions({'status': 'assigned'})) self.assertEqual(['leave'], self._get_actions({'status': 'accepted'})) self.assertEqual(['leave'], self._get_actions({'status': 'reopened'})) self.assertEqual(['leave'], self._get_actions({'status': 'closed'})) def test_available_actions_create_only(self): self.perm.grant_permission('anonymous', 'TICKET_CREATE') self.req.perm = PermissionCache(self.env) self.assertEqual(['leave'], self._get_actions({'status': 'new'})) self.assertEqual(['leave'], self._get_actions({'status': 'assigned'})) self.assertEqual(['leave'], self._get_actions({'status': 'accepted'})) self.assertEqual(['leave'], self._get_actions({'status': 'reopened'})) self.assertEqual(['leave', 'reopen'], self._get_actions({'status': 'closed'})) def test_available_actions_chgprop_only(self): # CHGPROP is not enough for changing a ticket's state (#3289) self.perm.grant_permission('anonymous', 'TICKET_CHGPROP') self.req.perm = PermissionCache(self.env) self.assertEqual(['leave'], self._get_actions({'status': 'new'})) self.assertEqual(['leave'], self._get_actions({'status': 'assigned'})) self.assertEqual(['leave'], self._get_actions({'status': 'accepted'})) self.assertEqual(['leave'], self._get_actions({'status': 'reopened'})) self.assertEqual(['leave'], self._get_actions({'status': 'closed'})) def test_get_allowed_owners_restrict_owner_false(self): self.env.config.set('ticket', 'restrict_owner', False) self.assertIsNone(self.ticket_system.get_allowed_owners()) def test_get_allowed_owners_restrict_owner_true(self): self.env.config.set('ticket', 'restrict_owner', True) self.env.insert_known_users([('user3', None, None), ('user1', None, None)]) self.perm.grant_permission('user4', 'TICKET_MODIFY') self.perm.grant_permission('user3', 'TICKET_MODIFY') self.perm.grant_permission('user2', 'TICKET_VIEW') self.perm.grant_permission('user1', 'TICKET_MODIFY') self.assertEqual(['user1', 'user3'], self.ticket_system.get_allowed_owners()) def test_get_ticket_fields_version_rename(self): """Cached ticket fields are updated when version is renamed.""" fields = self.ticket_system.get_ticket_fields() version_field = self._get_ticket_field('version') v2 = Version(self.env, '2.0') v2.name = '0.0' v2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_version_field = self._get_ticket_field('version') self.assertNotEqual(fields, updated_fields) self.assertEqual(['2.0', '1.0'], version_field['options']) self.assertEqual(['1.0', '0.0'], updated_version_field['options']) def test_get_ticket_fields_version_update_time(self): """Cached ticket fields are updated when version release time is changed. """ fields = self.ticket_system.get_ticket_fields() version_field = self._get_ticket_field('version') v1 = Version(self.env, '1.0') v1.time = datetime.now(utc) v2 = Version(self.env, '2.0') v2.time = v1.time - timedelta(seconds=1) v1.update() v2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_version_field = self._get_ticket_field('version') self.assertNotEqual(fields, updated_fields) self.assertEqual(['2.0', '1.0'], version_field['options']) self.assertEqual(['1.0', '2.0'], updated_version_field['options']) def test_get_ticket_fields_milestone_rename(self): """Cached ticket fields are updated when milestone is renamed.""" fields = self.ticket_system.get_ticket_fields() milestone_field = self._get_ticket_field('milestone') m2 = Milestone(self.env, 'milestone2') m2.name = 'milestone5' m2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_milestone_field = self._get_ticket_field('milestone') self.assertNotEqual(fields, updated_fields) self.assertEqual(['milestone1', 'milestone2', 'milestone3', 'milestone4'], milestone_field['options']) self.assertEqual(['milestone1', 'milestone3', 'milestone4', 'milestone5'], updated_milestone_field['options']) def test_get_ticket_fields_milestone_update_completed(self): """Cached ticket fields are updated when milestone is completed date is changed. """ fields = self.ticket_system.get_ticket_fields() milestone_field = self._get_ticket_field('milestone') m2 = Milestone(self.env, 'milestone2') m2.completed = datetime.now(utc) m2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_milestone_field = self._get_ticket_field('milestone') self.assertNotEqual(fields, updated_fields) self.assertEqual(['milestone1', 'milestone2', 'milestone3', 'milestone4'], milestone_field['options']) self.assertEqual(['milestone2', 'milestone1', 'milestone3', 'milestone4'], updated_milestone_field['options']) def test_get_ticket_fields_milestone_update_due(self): """Cached ticket fields are updated when milestone due date is changed. """ fields = self.ticket_system.get_ticket_fields() milestone_field = self._get_ticket_field('milestone') m2 = Milestone(self.env, 'milestone2') m2.due = datetime.now(utc) m2.update() updated_fields = self.ticket_system.get_ticket_fields() updated_milestone_field = self._get_ticket_field('milestone') self.assertNotEqual(fields, updated_fields) self.assertEqual(['milestone1', 'milestone2', 'milestone3', 'milestone4'], milestone_field['options']) self.assertEqual(['milestone2', 'milestone1', 'milestone3', 'milestone4'], updated_milestone_field['options']) def test_resource_exists_valid_resource_id(self): Ticket(self.env).insert() r1 = Resource('ticket', 1) r2 = Resource('ticket', 2) self.assertTrue(self.ticket_system.resource_exists(r1)) self.assertFalse(self.ticket_system.resource_exists(r2)) def test_resource_exists_invalid_resource_id(self): """Exception is trapped from resource with invalid id.""" r1 = Resource('ticket', None) r2 = Resource('ticket', 'abc') r3 = Resource('ticket', '2.') r4 = Resource('ticket', r2) self.assertFalse(self.ticket_system.resource_exists(r1)) self.assertFalse(self.ticket_system.resource_exists(r2)) self.assertFalse(self.ticket_system.resource_exists(r3)) self.assertFalse(self.ticket_system.resource_exists(r4))