Ejemplo n.º 1
0
class ConfigurableTicketWorkflowTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub()
        config = self.env.config
        config.set('ticket-workflow', 'change_owner', 'new -> new')
        config.set('ticket-workflow', 'change_owner.operations', 'set_owner')
        self.ctlr = TicketSystem(self.env).action_controllers[0]
        self.ticket_module = TicketModule(self.env)

    def tearDown(self):
        self.env.reset_db()

    def _add_component(self, name='test', owner='owner1'):
        component = Component(self.env)
        component.name = name
        component.owner = owner
        component.insert()

    def _reload_workflow(self):
        self.ctlr.actions = self.ctlr.get_all_actions()

    def test_get_all_actions_custom_attribute(self):
        """Custom attribute in ticket-workflow."""
        config = self.env.config['ticket-workflow']
        config.set('resolve.set_milestone', 'reject')
        all_actions = self.ctlr.get_all_actions()

        resolve_action = None
        for name, attrs in all_actions.items():
            if name == 'resolve':
                resolve_action = attrs

        self.assertIsNotNone(resolve_action)
        self.assertIn('set_milestone', list(resolve_action))
        self.assertEqual('reject', resolve_action['set_milestone'])

    def test_owner_from_component(self):
        """Verify that the owner of a new ticket is set to the owner
        of the component.
        """
        self._add_component('component3', 'cowner3')

        req = MockRequest(self.env, method='POST', args={
            'field_reporter': 'reporter1',
            'field_summary': 'the summary',
            'field_component': 'component3',
        })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, 1)

        self.assertEqual('component3', ticket['component'])
        self.assertEqual('cowner3', ticket['owner'])

    def test_component_change(self):
        """New ticket owner is updated when the component is changed.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env, reporter='reporter1',
                               summary='the summary', component='component3',
                               owner='cowner3', status='new')

        req = MockRequest(self.env, method='POST', args={
            'id': ticket.id,
            'field_component': 'component4',
            'submit': True,
            'action': 'leave',
            'view_time': str(to_utimestamp(ticket['changetime'])),
        })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('cowner4', ticket['owner'])

    def test_component_change_and_owner_change(self):
        """New ticket owner is not updated if owner is explicitly
        changed.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env, reporter='reporter1',
                               summary='the summary', component='component3',
                               status='new')

        req = MockRequest(self.env, method='POST', args={
            'id': ticket.id,
            'field_component': 'component4',
            'submit': True,
            'action': 'change_owner',
            'action_change_owner_reassign_owner': 'owner1',
            'view_time': str(to_utimestamp(ticket['changetime'])),
        })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('owner1', ticket['owner'])

    def test_old_owner_not_old_component_owner(self):
        """New ticket owner is not updated if old owner is not the owner
        of the old component.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env, reporter='reporter1',
                               summary='the summary', component='component3',
                               owner='owner1', status='new')

        req = MockRequest(self.env, method='POST', args={
            'id': ticket.id,
            'field_component': 'component4',
            'submit': True,
            'action': 'leave',
            'view_time': str(to_utimestamp(ticket['changetime'])),
        })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('owner1', ticket['owner'])

    def test_new_component_has_no_owner(self):
        """Ticket is not disowned when the component is changed to a
        component with no owner.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', '')

        ticket = insert_ticket(self.env, reporter='reporter1',
                               summary='the summary', component='component3',
                               owner='cowner3', status='new')

        req = MockRequest(self.env, method='POST', args={
            'id': ticket.id,
            'field_component': 'component4',
            'submit': True,
            'action': 'leave',
            'view_time': str(to_utimestamp(ticket['changetime'])),
        })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('cowner3', ticket['owner'])

    def _test_get_allowed_owners(self):
        ticket = insert_ticket(self.env, summary='Ticket 1')
        self.env.insert_users([('user1', None, None, 1),
                               ('user2', None, None, 1),
                               ('user3', None, None, 1)])
        ps = PermissionSystem(self.env)
        for user in ('user1', 'user3'):
            ps.grant_permission(user, 'TICKET_MODIFY')
        self.env.config.set('ticket', 'restrict_owner', True)
        return ticket

    def test_get_allowed_owners_returns_set_owner_list(self):
        """Users specified in `set_owner` for the action are returned."""
        req = None
        action = {'set_owner': ['user4', 'user5']}
        ticket = self._test_get_allowed_owners()
        self.assertEqual(['user4', 'user5'],
                         self.ctlr.get_allowed_owners(req, ticket, action))

    def test_get_allowed_owners_returns_user_with_ticket_modify(self):
        """Users with TICKET_MODIFY are are returned if `set_owner` is
        not specified for the action.
        """
        req = None
        action = {}
        ticket = self._test_get_allowed_owners()
        self.assertEqual(['user1', 'user3'],
                         self.ctlr.get_allowed_owners(req, ticket, action))

    def test_transition_to_star(self):
        """Workflow hint is not be added in a workflow transition to *,
        for example: <none> -> *

        AdvancedTicketWorkflow uses the behavior for the triage operation
        (see #12823)
        """
        config = self.env.config
        config.set('ticket-workflow', 'create_and_triage', '<none> -> *')
        config.set('ticket-workflow', 'create_and_triage.operations', 'triage')
        self._reload_workflow()
        ticket = Ticket(self.env)
        req = MockRequest(self.env, path_info='/newticket', method='POST')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket,
                                                   'create_and_triage')

        self.assertEqual('create and triage', label)
        self.assertEqual('', unicode(control))
        self.assertEqual('', unicode(hints))

    def test_get_actions_by_operation_for_req(self):
        """Request with no permission checking."""
        req = MockRequest(self.env, path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(req, ticket,
                                                             'set_owner')
        self.assertEqual([(0, u'change_owner'), (0, u'reassign')], actions)

    def test_get_actions_by_operation_for_req_with_ticket_modify(self):
        """User without TICKET_MODIFY won't have reassign action."""
        req = MockRequest(self.env, authname='user1', path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(req, ticket,
                                                             'set_owner')
        self.assertEqual([(0, u'change_owner')], actions)

    def test_get_actions_by_operation_for_req_without_ticket_modify(self):
        """User with TICKET_MODIFY will have reassign action."""
        PermissionSystem(self.env).grant_permission('user1', 'TICKET_MODIFY')
        req = MockRequest(self.env, authname='user1', path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(req, ticket,
                                                             'set_owner')
        self.assertEqual([(0, u'change_owner'), (0, u'reassign')], actions)

    def test_ignores_other_operations(self):
        """Ignores operations not defined by ConfigurableTicketWorkflow.
        """
        self.env.config.set('ticket-workflow', 'review', 'assigned -> review')
        self.env.config.set('ticket-workflow', 'review.operations',
                            'CodeReview')
        ctw = ConfigurableTicketWorkflow(self.env)
        ticket = Ticket(self.env)
        ticket.populate({'summary': '#13013', 'status': 'assigned'})
        ticket.insert()
        req = MockRequest(self.env)

        self.assertNotIn((0, 'review'), ctw.get_ticket_actions(req, ticket))
Ejemplo n.º 2
0
class ConfigurableTicketWorkflowTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()
        config = self.env.config
        config.set('ticket-workflow', 'change_owner', 'new -> new')
        config.set('ticket-workflow', 'change_owner.operations', 'set_owner')
        self.ctlr = TicketSystem(self.env).action_controllers[0]
        self.ticket_module = TicketModule(self.env)

    def tearDown(self):
        self.env.reset_db()

    def _add_component(self, name='test', owner='owner1'):
        component = Component(self.env)
        component.name = name
        component.owner = owner
        component.insert()

    def _reload_workflow(self):
        self.ctlr.actions = self.ctlr.get_all_actions()

    def test_get_all_actions_custom_attribute(self):
        """Custom attribute in ticket-workflow."""
        config = self.env.config['ticket-workflow']
        config.set('resolve.set_milestone', 'reject')
        all_actions = self.ctlr.get_all_actions()

        resolve_action = None
        for name, attrs in all_actions.items():
            if name == 'resolve':
                resolve_action = attrs

        self.assertIsNotNone(resolve_action)
        self.assertIn('set_milestone', list(resolve_action))
        self.assertEqual('reject', resolve_action['set_milestone'])

    def test_owner_from_component(self):
        """Verify that the owner of a new ticket is set to the owner
        of the component.
        """
        self._add_component('component3', 'cowner3')

        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'field_reporter': 'reporter1',
                              'field_summary': 'the summary',
                              'field_component': 'component3',
                          })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, 1)

        self.assertEqual('component3', ticket['component'])
        self.assertEqual('cowner3', ticket['owner'])

    def test_component_change(self):
        """New ticket owner is updated when the component is changed.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env,
                               reporter='reporter1',
                               summary='the summary',
                               component='component3',
                               owner='cowner3',
                               status='new')

        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'id':
                              ticket.id,
                              'field_component':
                              'component4',
                              'submit':
                              True,
                              'action':
                              'leave',
                              'view_time':
                              str(to_utimestamp(ticket['changetime'])),
                          })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('cowner4', ticket['owner'])

    def test_component_change_and_owner_change(self):
        """New ticket owner is not updated if owner is explicitly
        changed.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env,
                               reporter='reporter1',
                               summary='the summary',
                               component='component3',
                               status='new')

        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'id':
                              ticket.id,
                              'field_component':
                              'component4',
                              'submit':
                              True,
                              'action':
                              'change_owner',
                              'action_change_owner_reassign_owner':
                              'owner1',
                              'view_time':
                              str(to_utimestamp(ticket['changetime'])),
                          })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('owner1', ticket['owner'])

    def test_old_owner_not_old_component_owner(self):
        """New ticket owner is not updated if old owner is not the owner
        of the old component.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', 'cowner4')

        ticket = insert_ticket(self.env,
                               reporter='reporter1',
                               summary='the summary',
                               component='component3',
                               owner='owner1',
                               status='new')

        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'id':
                              ticket.id,
                              'field_component':
                              'component4',
                              'submit':
                              True,
                              'action':
                              'leave',
                              'view_time':
                              str(to_utimestamp(ticket['changetime'])),
                          })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('owner1', ticket['owner'])

    def test_new_component_has_no_owner(self):
        """Ticket is not disowned when the component is changed to a
        component with no owner.
        """
        self._add_component('component3', 'cowner3')
        self._add_component('component4', '')

        ticket = insert_ticket(self.env,
                               reporter='reporter1',
                               summary='the summary',
                               component='component3',
                               owner='cowner3',
                               status='new')

        req = MockRequest(self.env,
                          method='POST',
                          args={
                              'id':
                              ticket.id,
                              'field_component':
                              'component4',
                              'submit':
                              True,
                              'action':
                              'leave',
                              'view_time':
                              str(to_utimestamp(ticket['changetime'])),
                          })
        self.assertRaises(RequestDone, self.ticket_module.process_request, req)
        ticket = Ticket(self.env, ticket.id)

        self.assertEqual('component4', ticket['component'])
        self.assertEqual('cowner3', ticket['owner'])

    def _test_get_allowed_owners(self):
        ticket = insert_ticket(self.env, summary='Ticket 1')
        self.env.insert_users([('user1', None, None, 1),
                               ('user2', None, None, 1),
                               ('user3', None, None, 1)])
        ps = PermissionSystem(self.env)
        for user in ('user1', 'user3'):
            ps.grant_permission(user, 'TICKET_MODIFY')
        self.env.config.set('ticket', 'restrict_owner', True)
        return ticket

    def test_get_allowed_owners_returns_set_owner_list(self):
        """Users specified in `set_owner` for the action are returned."""
        req = None
        action = {'set_owner': ['user4', 'user5']}
        ticket = self._test_get_allowed_owners()
        self.assertEqual(['user4', 'user5'],
                         self.ctlr.get_allowed_owners(req, ticket, action))

    def test_get_allowed_owners_returns_user_with_ticket_modify(self):
        """Users with TICKET_MODIFY are are returned if `set_owner` is
        not specified for the action.
        """
        req = None
        action = {}
        ticket = self._test_get_allowed_owners()
        self.assertEqual(['user1', 'user3'],
                         self.ctlr.get_allowed_owners(req, ticket, action))

    def test_status_change_with_operation(self):
        """Status change with operation."""
        ticket = Ticket(self.env)
        ticket['new'] = 'status1'
        ticket['owner'] = 'user1'
        ticket.insert()
        req = MockRequest(self.env,
                          path_info='/ticket',
                          authname='user2',
                          method='POST')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket, 'accept')

        self.assertEqual('accept', label)
        self.assertEqual('', unicode(control))
        self.assertEqual(
            "The owner will be <span class=\"trac-author-user\">"
            "user2</span>. The status will be 'accepted'.", unicode(hints))

    def test_status_change_with_no_operation(self):
        """Existing ticket status change with no operation."""
        config = self.env.config
        config.set('ticket-workflow', 'change_status', 'status1 -> status2')
        self._reload_workflow()
        ticket = Ticket(self.env)
        ticket['status'] = 'status1'
        ticket.insert()
        req = MockRequest(self.env, path_info='/ticket', method='POST')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket,
                                                   'change_status')

        self.assertEqual('change status', label)
        self.assertEqual('', unicode(control))
        self.assertEqual("Next status will be 'status2'.", unicode(hints))

    def test_new_ticket_status_change_with_no_operation(self):
        """New ticket status change with no operation."""
        config = self.env.config
        config.set('ticket-workflow', 'change_status', '<none> -> status1')
        self._reload_workflow()
        ticket = Ticket(self.env)
        req = MockRequest(self.env, path_info='/newticket', method='POST')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket,
                                                   'change_status')

        self.assertEqual('change status', label)
        self.assertEqual('', unicode(control))
        self.assertEqual("The status will be 'status1'.", unicode(hints))

    def test_operation_with_no_status_change(self):
        """Operation with no status change."""
        config = self.env.config
        config.set('ticket-workflow', 'change_owner', 'closed -> closed')
        config.set('ticket-workflow', 'change_owner.operations', 'set_owner')

        self._reload_workflow()
        ticket = Ticket(self.env)
        ticket['status'] = 'closed'
        ticket['owner'] = 'user2'
        ticket.insert()
        req = MockRequest(self.env,
                          path_info='/ticket',
                          method='POST',
                          authname='user1')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket,
                                                   'change_owner')

        self.assertEqual('change owner', label)
        self.assertEqual(
            'to <input id="action_change_owner_reassign_owner" '
            'name="action_change_owner_reassign_owner" type="text" '
            'value="user1" />', unicode(control))
        self.assertEqual(
            'The owner will be changed from <span class="trac-author">'
            'user2</span> to the specified user.', unicode(hints))

    def test_transition_to_star(self):
        """Action not rendered by CTW for transition to *

        AdvancedTicketWorkflow uses the behavior for the triage operation
        (see #12823)
        """
        config = self.env.config
        config.set('ticket-workflow', 'create_and_triage', '<none> -> *')
        config.set('ticket-workflow', 'create_and_triage.operations', 'triage')
        self._reload_workflow()
        ticket = Ticket(self.env)
        req = MockRequest(self.env, path_info='/newticket', method='POST')

        actions = self.ctlr.get_ticket_actions(req, ticket)

        # create_and_triage not in actions
        self.assertEqual([(1, 'create'), (0, 'create_and_assign')], actions)

    def test_transition_to_star_with_leave_operation(self):
        """Action is rendered by CTW for transition to * with leave_status
        """
        config = self.env.config
        config.set('ticket-workflow', 'change_owner', 'assigned,closed -> *')
        config.set('ticket-workflow', 'change_owner.operations',
                   'leave_status,set_owner')
        self._reload_workflow()
        status = ['assigned', 'closed']
        for s in status:
            ticket = Ticket(self.env)
            ticket['status'] = s
            ticket['owner'] = 'user2'
            ticket.insert()
            req = MockRequest(self.env,
                              path_info='/ticket',
                              method='POST',
                              authname='user1')

            label, control, hints = \
                self.ctlr.render_ticket_action_control(req, ticket,
                                                       'change_owner')
            self.assertEqual('change owner', label)
            self.assertEqual(
                'to <input id="action_change_owner_reassign_owner" '
                'name="action_change_owner_reassign_owner" type="text" '
                'value="user1" />', unicode(control))
            self.assertEqual(
                'The owner will be changed from <span class="trac-author">'
                'user2</span> to the specified user.', unicode(hints))

    def test_leave_operation(self):
        ticket = Ticket(self.env)
        ticket['status'] = 'assigned'
        ticket['owner'] = 'user2'
        ticket.insert()
        req = MockRequest(self.env,
                          path_info='/ticket',
                          method='POST',
                          authname='user1')

        label, control, hints = \
            self.ctlr.render_ticket_action_control(req, ticket, 'leave')

        self.assertEqual('leave', label)
        self.assertEqual('as assigned', unicode(control))
        self.assertEqual(
            'The owner will remain <span class="trac-author">'
            'user2</span>.', unicode(hints))

    def test_get_actions_by_operation_for_req(self):
        """Request with no permission checking."""
        req = MockRequest(self.env, path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(
            req, ticket, 'set_owner')
        self.assertEqual([(0, u'change_owner'), (0, u'reassign')], actions)

    def test_get_actions_by_operation_for_req_with_ticket_modify(self):
        """User without TICKET_MODIFY won't have reassign action."""
        req = MockRequest(self.env, authname='user1', path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(
            req, ticket, 'set_owner')
        self.assertEqual([(0, u'change_owner')], actions)

    def test_get_actions_by_operation_for_req_without_ticket_modify(self):
        """User with TICKET_MODIFY will have reassign action."""
        PermissionSystem(self.env).grant_permission('user1', 'TICKET_MODIFY')
        req = MockRequest(self.env, authname='user1', path_info='/ticket/1')
        ticket = insert_ticket(self.env, status='new')
        actions = self.ctlr.get_actions_by_operation_for_req(
            req, ticket, 'set_owner')
        self.assertEqual([(0, u'change_owner'), (0, u'reassign')], actions)