Example #1
0
File: env.py Project: miihael/trac
class EnvironmentUpgradeTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()

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

    def test_multiple_upgrade_participants(self):
        class Participant1(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return True

            def upgrade_environment(self):
                insert_value('value1', 1)

        class Participant2(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return True

            def upgrade_environment(self):
                insert_value('value2', 2)

        def insert_value(name, value):
            self.env.db_transaction(
                """
                INSERT INTO system (name, value) VALUES (%s, %s)
                """, (name, value))

        def select_value(name):
            for value, in self.env.db_query(
                    """
                    SELECT value FROM system WHERE name=%s
                    """, (name, )):
                return value

        self.env.enable_component(Participant1)
        self.env.enable_component(Participant2)

        self.assertTrue(self.env.needs_upgrade())
        self.assertTrue(self.env.upgrade())
        self.assertEqual('1', select_value('value1'))
        self.assertEqual('2', select_value('value2'))
Example #2
0
class RepositoryAdminPanelTestCase(unittest.TestCase):

    RepositoryConnector = None

    @classmethod
    def setUpClass(cls):
        class RepositoryConnector(Component):
            implements(IRepositoryConnector)

            def get_supported_types(self):
                yield 'RepositoryConnector', 1

            def get_repository(self, repos_type, repos_dir, params):
                pass

        cls.RepositoryConnector = RepositoryConnector

    def setUp(self):
        self.env = EnvironmentStub(enable=('trac.versioncontrol.admin.*', ))

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

    @classmethod
    def tearDownClass(cls):
        from trac.core import ComponentMeta
        ComponentMeta.deregister(cls.RepositoryConnector)

    def test_panel_not_exists_when_no_repository_connectors(self):
        """Repositories admin panel is not present when there are
        no repository connectors enabled.
        """
        req = MockRequest(self.env)
        rap = RepositoryAdminPanel(self.env)
        panels = [panel for panel in rap.get_admin_panels(req)]

        self.assertEqual(0, len(panels))

    def test_panel_exists_when_repository_connectors(self):
        """Repositories admin panel is present when there are
        repository connectors enabled.
        """
        self.env.enable_component(self.RepositoryConnector)
        req = MockRequest(self.env)
        rap = RepositoryAdminPanel(self.env)
        panels = [panel for panel in rap.get_admin_panels(req)]

        self.assertEqual(1, len(panels))
Example #3
0
class SystemInfoProviderTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()
        self.env.clear_component_registry()

        class SystemInfoProvider1(Component):
            implements(ISystemInfoProvider)

            def get_system_info(self):
                yield 'pkg1', 1.0
                yield 'pkg2', 2.0

        class SystemInfoProvider2(Component):
            implements(ISystemInfoProvider)

            def get_system_info(self):
                yield 'pkg1', 1.0

        self.env.enable_component(SystemInfoProvider1)
        self.env.enable_component(SystemInfoProvider2)

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

    def test_system_info_property(self):
        """The system_info property returns a list of all tuples
        generated by ISystemInfoProvider implementations.
        """
        system_info = self.env.system_info
        self.assertEqual(system_info, self.env.get_systeminfo())
        self.assertEqual(2, len(system_info))
        self.assertIn(('pkg1', 1.0), system_info)
        self.assertIn(('pkg2', 2.0), system_info)

    def test_duplicate_entries_are_removed(self):
        """Duplicate entries are removed."""
        system_info = self.env.system_info
        self.assertIn(('pkg1', 1.0), system_info)
        self.assertEqual(len(system_info), len(set(system_info)))
Example #4
0
class BatchModifyTestCase(unittest.TestCase):

    ticket_manipulators = None

    @classmethod
    def setUpClass(cls):
        class TicketValidator1(Component):
            implements(api.ITicketManipulator)

            def prepare_ticket(self, req, ticket, fields, actions):
                pass

            def validate_ticket(self, req, ticket):
                errors = []
                if ticket['component'] == 'component3':
                    errors.append(('component', 'Invalid Component'))
                return errors

        class TicketValidator2(Component):
            implements(api.ITicketManipulator)

            def prepare_ticket(self, req, ticket, fields, actions):
                pass

            def validate_ticket(self, req, ticket):
                return []

            def validate_comment(self, req, comment):
                if 'badword' in comment:
                    yield "Word is not allowed in comment"

        cls.ticket_manipulators = [TicketValidator1, TicketValidator2]

    @classmethod
    def tearDownClass(cls):
        from trac.core import ComponentMeta
        for manipulator in cls.ticket_manipulators:
            ComponentMeta.deregister(manipulator)

    def setUp(self):
        self.env = EnvironmentStub(
            default_data=True,
            enable=[
                default_workflow.ConfigurableTicketWorkflow,
                DefaultPermissionPolicy, DefaultPermissionStore,
                BatchModifyModule, api.TicketSystem, web_ui.TicketModule
            ])
        self.env.config.set('trac', 'permission_policies',
                            'DefaultPermissionPolicy')
        self.env.config.set('ticket-custom', 'text1', 'text')
        self.env.config.set('ticket-custom', 'text1.max_size', 5)
        self.env.config.set('ticket-custom', 'time1', 'time')
        self.env.config.set('ticket-custom', 'time1.format', 'date')
        self.env.config.set('ticket-workflow', 'acknowledge',
                            '* -> acknowledged')
        ps = PermissionSystem(self.env)
        ps.grant_permission('has_ta_&_bm', 'TICKET_ADMIN')
        ps.grant_permission('has_bm', 'TICKET_BATCH_MODIFY')
        ps.grant_permission('has_ta_&_bm', 'TICKET_BATCH_MODIFY')
        session = DetachedSession(self.env, 'has_ta_&_bm')
        session.set('query_href', '')
        session.save()
        session = DetachedSession(self.env, 'has_bm')
        session.set('query_href', '')
        session.save()
        self._insert_ticket('Ticket 1',
                            reporter='user1',
                            component='component1',
                            description='the desc',
                            keywords='foo one',
                            status='new')
        self._insert_ticket('Ticket 2',
                            reporter='user1',
                            component='component2',
                            description='the desc',
                            keywords='baz two',
                            status='new')

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

    def assertCommentAdded(self, ticket_id, comment):
        ticket = model.Ticket(self.env, int(ticket_id))
        changes = ticket.get_changelog()
        comment_change = [c for c in changes if c[2] == 'comment'][-1]
        self.assertEqual(comment_change[4], comment)

    def assertFieldValue(self, ticket_id, field, new_value):
        ticket = model.Ticket(self.env, int(ticket_id))
        self.assertEqual(ticket[field], new_value)

    def _insert_ticket(self, summary, **kw):
        """Helper for inserting a ticket into the database"""
        ticket = insert_ticket(self.env, summary=summary, **kw)
        return ticket.id

    def _insert_component(self, name):
        component = model.Component(self.env)
        component.name = name
        component.insert()

    def test_require_post_method(self):
        """Request must use POST method."""
        module = BatchModifyModule(self.env)
        req = MockRequest(self.env, method='GET', path_info='/batchmodify')
        req.session['query_href'] = req.href.query()

        self.assertTrue(module.match_request(req))
        with self.assertRaises(HTTPBadRequest):
            module.process_request(req)

        req = MockRequest(self.env,
                          method='POST',
                          path_info='/batchmodify',
                          args={'selected_tickets': ''})
        req.session['query_href'] = req.href.query()

        self.assertTrue(module.match_request(req))
        with self.assertRaises(RequestDone):
            module.process_request(req)

    def test_redirect_to_query_href_in_req_args(self):
        redirect_listener_args = []

        def redirect_listener(req, url, permanent):
            redirect_listener_args[:] = (url, permanent)

        module = BatchModifyModule(self.env)
        req = MockRequest(self.env, method='POST', path_info='/batchmodify')
        query_opened_tickets = req.href.query(status='!closed')
        query_default = req.href.query()
        req.args = {'selected_tickets': '', 'query_href': query_opened_tickets}
        req.session['query_href'] = query_default
        req.add_redirect_listener(redirect_listener)

        self.assertTrue(module.match_request(req))
        self.assertRaises(RequestDone, module.process_request, req)
        self.assertEqual([query_opened_tickets, False], redirect_listener_args)

    def test_save_comment(self):
        """Comments are saved to all selected tickets."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_comment': 'the comment',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertCommentAdded(1, 'the comment')
        self.assertCommentAdded(2, 'the comment')

    def test_save_values(self):
        """Changed values are saved to all tickets."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_component': 'component1',
                              'batchmod_value_comment': '',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'component', 'component1')
        self.assertFieldValue(2, 'component', 'component1')

    def test_list_fields_add(self):
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_mode_keywords': '+',
                              'batchmod_primary_keywords': 'baz new',
                              'batchmod_secondary_keywords': '*****',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'keywords', 'foo, one, baz, new')
        self.assertFieldValue(2, 'keywords', 'baz, two, new')

    def test_list_fields_addrem(self):
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_mode_keywords': '+-',
                              'batchmod_primary_keywords': 'one three four',
                              'batchmod_secondary_keywords': 'baz missing',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'keywords', 'foo, one, three, four')
        self.assertFieldValue(2, 'keywords', 'two, one, three, four')

    def test_list_fields_rem(self):
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_mode_keywords': '-',
                              'batchmod_primary_keywords': 'foo two',
                              'batchmod_secondary_keywords': '*****',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'keywords', 'one')
        self.assertFieldValue(2, 'keywords', 'baz')

    def test_list_fields_set(self):
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_mode_keywords': '=',
                              'batchmod_primary_keywords': 'orange',
                              'batchmod_secondary_keywords': '*****',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'keywords', 'orange')
        self.assertFieldValue(2, 'keywords', 'orange')

    def test_action_with_state_change(self):
        """Actions can have change status."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'action': 'acknowledge',
                              'batchmod_value_comment': '',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'status', 'acknowledged')
        self.assertFieldValue(2, 'status', 'acknowledged')

    def test_action_with_side_effects(self):
        """Actions can have operations with side effects."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'action': 'reassign',
                              'action_reassign_reassign_owner': 'user3',
                              'batchmod_value_comment': '',
                              'selected_tickets': '1,2',
                          })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req))
        with self.assertRaises(RequestDone):
            batch.process_request(req)

        self.assertFieldValue(1, 'owner', 'user3')
        self.assertFieldValue(2, 'owner', 'user3')
        self.assertFieldValue(1, 'status', 'assigned')
        self.assertFieldValue(2, 'status', 'assigned')

    def test_timeline_events(self):
        """Regression test for #11288"""
        req1 = MockRequest(self.env)
        tktmod = web_ui.TicketModule(self.env)
        now = datetime_now(utc)
        start = now - timedelta(hours=1)
        stop = now + timedelta(hours=1)
        events = tktmod.get_timeline_events(req1, start, stop,
                                            ['ticket_details'])
        self.assertTrue(all(ev[0] != 'batchmodify' for ev in events))

        prio_ids = {}
        for i in xrange(20):
            priority = ('', 'minor', 'major', 'critical')[i % 4]
            t = insert_ticket(self.env,
                              summary='Ticket %d' % i,
                              priority=priority)
            prio_ids.setdefault(t['priority'], []).append(t.id)
        tktids = prio_ids['critical'] + prio_ids['major'] + \
                 prio_ids['minor'] + prio_ids['']

        req2 = MockRequest(self.env,
                           method='POST',
                           authname='has_ta_&_bm',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_summary':
                               'batch updated ticket',
                               'batchmod_value_owner':
                               'ticket11288',
                               'batchmod_value_reporter':
                               'ticket11288',
                               'action':
                               'leave',
                               'selected_tickets':
                               ','.join(str(t) for t in tktids),
                           })

        batch = BatchModifyModule(self.env)
        self.assertTrue(batch.match_request(req2))
        with self.assertRaises(RequestDone):
            batch.process_request(req2)

        # shuffle ticket_change records
        with self.env.db_transaction as db:
            rows = db('SELECT * FROM ticket_change')
            db.execute('DELETE FROM ticket_change')
            rows = rows[0::4] + rows[1::4] + rows[2::4] + rows[3::4]
            db.executemany(
                'INSERT INTO ticket_change VALUES (%s)' % ','.join(
                    ('%s', ) * len(rows[0])), rows)

        events = tktmod.get_timeline_events(req1, start, stop,
                                            ['ticket_details'])
        events = [ev for ev in events if ev[0] == 'batchmodify']
        self.assertEqual(1, len(events))
        batch_ev = events[0]
        self.assertEqual('has_ta_&_bm', batch_ev[2])
        self.assertEqual(tktids, batch_ev[3][0])
        self.assertEqual('updated', batch_ev[3][1])

        context = web_context(req2)
        self.assertEqual(
            req2.href.query(id=','.join(str(t) for t in tktids)),
            tktmod.render_timeline_event(context, 'url', batch_ev))

    def test_modify_summary_and_description(self):
        """The ticket summary and description cannot be modified."""
        req = MockRequest(self.env,
                          authname='has_ta_&_bm',
                          method='POST',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_summary': 'the new summary',
                              'batchmod_value_description':
                              'the new description',
                              'batchmod_value_comment': '',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        module = BatchModifyModule(self.env)
        self.assertTrue(module.match_request(req))
        with self.assertRaises(RequestDone):
            module.process_request(req)

        self.assertFieldValue(1, 'description', 'the desc')
        self.assertFieldValue(1, 'summary', 'Ticket 1')
        self.assertFieldValue(2, 'description', 'the desc')
        self.assertFieldValue(2, 'summary', 'Ticket 2')

    def test_modify_reporter_with_ticket_admin(self):
        """User with TICKET_ADMIN can batch modify the reporter."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_ta_&_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_reporter': 'user2',
                              'batchmod_value_comment': '',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        module = BatchModifyModule(self.env)
        self.assertTrue(module.match_request(req))
        with self.assertRaises(RequestDone):
            module.process_request(req)

        self.assertFieldValue(1, 'reporter', 'user2')
        self.assertFieldValue(2, 'reporter', 'user2')

    def test_modify_reporter_without_ticket_admin(self):
        """User without TICKET_ADMIN cannot batch modify the reporter."""
        req = MockRequest(self.env,
                          method='POST',
                          authname='has_bm',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_reporter': 'user2',
                              'batchmod_value_comment': '',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        module = BatchModifyModule(self.env)
        self.assertTrue(module.match_request(req))
        with self.assertRaises(RequestDone):
            module.process_request(req)

        self.assertFieldValue(1, 'reporter', 'user1')
        self.assertFieldValue(2, 'reporter', 'user1')

    def test_validate_ticket_comment_size(self):
        """The [ticket] max_comment_size value is enforced."""
        module = BatchModifyModule(self.env)
        self.env.config.set('ticket', 'max_comment_size', 5)
        req1 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_comment': '12345',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })
        req2 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_comment': '123456',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })

        self.assertTrue(module.match_request(req1))
        with self.assertRaises(RequestDone):
            module.process_request(req1)

        self.assertEqual([], req1.chrome['warnings'])
        self.assertCommentAdded(1, '12345')
        self.assertCommentAdded(2, '12345')

        self.assertTrue(module.match_request(req2))
        with self.assertRaises(RequestDone):
            module.process_request(req2)

        self.assertEqual(1, len(req2.chrome['warnings']))
        self.assertEqual(
            "The ticket comment is invalid: Must be less than or "
            "equal to 5 characters", unicode(req2.chrome['warnings'][0]))
        self.assertEqual(1, len(model.Ticket(self.env, 1).get_changelog()))
        self.assertEqual(1, len(model.Ticket(self.env, 2).get_changelog()))

    def test_validate_select_fields(self):
        """The select field values are validated."""
        req = MockRequest(self.env,
                          authname='has_bm',
                          method='POST',
                          path_info='/batchmodify',
                          args={
                              'batchmod_value_component': 'component3',
                              'action': 'leave',
                              'selected_tickets': '1,2',
                          })

        module = BatchModifyModule(self.env)
        self.assertTrue(module.match_request(req))
        with self.assertRaises(RequestDone):
            module.process_request(req)

        self.assertEqual(1, len(req.chrome['warnings']))
        self.assertEqual(
            'The ticket field <strong>component</strong> is '
            'invalid: "component3" is not a valid value',
            unicode(req.chrome['warnings'][0]))

    def test_validate_ticket_custom_field_max_size(self):
        """The [ticket-custom] max_size attribute is enforced."""
        module = BatchModifyModule(self.env)
        req1 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_text1': '12345',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })
        req2 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_text1': '123456',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })

        self.assertTrue(module.match_request(req1))
        with self.assertRaises(RequestDone):
            module.process_request(req1)

        self.assertEqual([], req1.chrome['warnings'])
        self.assertEqual('12345', model.Ticket(self.env, 1)['text1'])
        self.assertEqual('12345', model.Ticket(self.env, 2)['text1'])

        self.assertTrue(module.match_request(req2))
        with self.assertRaises(RequestDone):
            module.process_request(req2)

        self.assertEqual(1, len(req2.chrome['warnings']))
        self.assertEqual(
            "The ticket field <strong>Text1</strong> is "
            "invalid: Must be less than or equal to 5 "
            "characters", unicode(req2.chrome['warnings'][0]))
        self.assertEqual('12345', model.Ticket(self.env, 1)['text1'])
        self.assertEqual('12345', model.Ticket(self.env, 2)['text1'])

    def test_validate_time_fields(self):
        """The time fields are validated."""
        module = BatchModifyModule(self.env)
        req1 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_time1': '2016-01-02T12:34:56Z',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })
        req2 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_time1': 'invalid',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })
        dt = datetime(2016, 1, 2, 12, 34, 56, tzinfo=utc)

        self.assertTrue(module.match_request(req1))
        with self.assertRaises(RequestDone):
            module.process_request(req1)

        self.assertEqual(dt, model.Ticket(self.env, 1)['time1'])
        self.assertEqual(dt, model.Ticket(self.env, 2)['time1'])
        self.assertEqual([], req1.chrome['warnings'])

        self.assertTrue(module.match_request(req2))
        with self.assertRaises(RequestDone):
            module.process_request(req2)

        self.assertEqual(1, len(req2.chrome['warnings']))
        self.assertRegexpMatches(
            unicode(req2.chrome['warnings'][0]),
            'The ticket field <strong>Time1</strong> is invalid: "invalid" '
            'is an invalid date, or the date format is not known. '
            'Try "[^"]+" or "[^"]+" instead.')
        self.assertEqual(dt, model.Ticket(self.env, 1)['time1'])
        self.assertEqual(dt, model.Ticket(self.env, 2)['time1'])

    def test_ticket_manipulators(self):
        """The ticket manipulators are called to valid the ticket."""
        module = BatchModifyModule(self.env)
        self._insert_component('component3')
        self._insert_component('component4')
        self.env.enable_component(self.ticket_manipulators[0])
        self.env.enable_component(self.ticket_manipulators[1])
        req1 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_component': 'component3',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })

        self.assertTrue(module.match_request(req1))
        with self.assertRaises(RequestDone):
            module.process_request(req1)

        self.assertEqual(1, len(req1.chrome['warnings']))
        self.assertEqual(
            "The ticket field <strong>component</strong> is "
            "invalid: Invalid Component", unicode(req1.chrome['warnings'][0]))
        self.assertFieldValue(1, 'component', 'component1')
        self.assertFieldValue(2, 'component', 'component2')

        req2 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_component': 'component4',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })

        self.assertTrue(module.match_request(req2))
        with self.assertRaises(RequestDone):
            module.process_request(req2)

        self.assertEqual([], req2.chrome['warnings'])
        self.assertFieldValue(1, 'component', 'component4')
        self.assertFieldValue(2, 'component', 'component4')

        req3 = MockRequest(self.env,
                           authname='has_bm',
                           method='POST',
                           path_info='/batchmodify',
                           args={
                               'batchmod_value_comment':
                               'this comment has the badword!',
                               'batchmod_value_component': 'component3',
                               'action': 'leave',
                               'selected_tickets': '1,2',
                           })

        self.assertTrue(module.match_request(req3))
        with self.assertRaises(RequestDone):
            module.process_request(req3)

        self.assertEqual(
            "The ticket comment is invalid: Word is not allowed "
            "in comment", unicode(req3.chrome['warnings'][0]))
        self.assertFieldValue(1, 'component', 'component4')
        self.assertFieldValue(2, 'component', 'component4')

    def test_post_process_request_add_template_data(self):
        """Template data added by post_process_request."""
        self._insert_ticket("Ticket 1", status='new')
        self._insert_ticket("Ticket 2", status='new')
        req = MockRequest(self.env, path_info='/query')
        req.session['query_href'] = '/query?status=!closed'
        batch = BatchModifyModule(self.env)
        data_in = {'tickets': [{'id': 1}, {'id': 2}]}

        data_out = batch.post_process_request(req, 'query.html', data_in,
                                              'text/html')[1]

        self.assertTrue(data_out['batch_modify'])
        self.assertEqual(
            ['leave', 'resolve', 'reassign', 'acknowledge', 'accept'],
            [a[0] for a in data_out['action_controls']])

    def test_actions_added_by_additional_ticket_action_controllers(self):
        """Actions added by custom ticket action controller.

        Regression test for #12938.
        """
        class TestOperation(Component):
            """TicketActionController that directly provides an action."""
            implements(api.ITicketActionController)

            def get_ticket_actions(self, req, ticket):
                return [(0, 'test')]

            def get_all_status(self):
                return []

            def render_ticket_action_control(self, req, ticket, action):
                return "test", '', "This is a null action."

            def get_ticket_changes(self, req, ticket, action):
                return {}

            def apply_action_side_effects(self, req, ticket, action):
                pass

        self._insert_ticket("Ticket 1", status='new')
        self._insert_ticket("Ticket 2", status='new')
        req = MockRequest(self.env, path_info='/query')
        req.session['query_href'] = '/query?status=!closed'
        batch = BatchModifyModule(self.env)
        data_in = {'tickets': [{'id': 1}, {'id': 2}]}
        self.env.config.set('ticket', 'workflow',
                            'ConfigurableTicketWorkflow, TestOperation')
        self.env.enable_component(TestOperation)

        data_out = batch.post_process_request(req, 'query.html', data_in,
                                              'text/html')[1]

        self.assertEqual(
            ['leave', 'test', 'resolve', 'reassign', 'acknowledge', 'accept'],
            [a[0] for a in data_out['action_controls']])

    def test_post_process_request_error_handling(self):
        """Exception not raised in post_process_request error handling.
        """
        module = BatchModifyModule(self.env)
        req = MockRequest(self.env, path_info='/query')
        self.assertEqual((None, None, None),
                         module.post_process_request(req, None, None, None))
Example #5
0
class RenderResourceLinkTestCase(unittest.TestCase):

    class FakeResourceManager(Component):
        implements(resource.IResourceManager)

        def get_resource_realms(self):
            yield 'fake'

        def resource_exists(self, resource):
            return False if resource.id == 'missing' else True

    def setUp(self):
        self.env = EnvironmentStub(default_data=True)
        self.env.enable_component(self.FakeResourceManager)
        self.req = Mock(perm=MockPerm(), href=Href('/trac.cgi'))
        self.context = web_context(self.req)

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

    def test_resource_exists_default_format(self):
        res = resource.Resource('fake', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('fake:exists',  class_='fake',
                     href='/trac.cgi/fake/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_exists_summary_format(self):
        res = resource.Resource('fake', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context,
                                             res, 'summary')
        html = tag.a('fake:exists at version 1', class_='fake',
                     href='/trac.cgi/fake/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_missing_default_format(self):
        res = resource.Resource('fake', 'missing', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('fake:missing', class_='fake missing',
                     rel='nofollow', href='/trac.cgi/fake/missing?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_missing_summary_format(self):
        res = resource.Resource('fake', 'missing', version=1)
        link = resource.render_resource_link(self.env, self.context,
                                             res, 'summary')
        html = tag.a('fake:missing at version 1', class_='fake missing',
                     rel='nofollow', href='/trac.cgi/fake/missing?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_has_no_manager_default_format(self):
        res = resource.Resource('unmanaged', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('unmanaged:exists', class_='unmanaged',
                     href='/trac.cgi/unmanaged/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_has_no_manager_summary_format(self):
        res = resource.Resource('unmanaged', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context,
                                             res, 'summary')
        html = tag.a('unmanaged:exists at version 1', class_='unmanaged',
                     href='/trac.cgi/unmanaged/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))
Example #6
0
class RenderResourceLinkTestCase(unittest.TestCase):
    class FakeResourceManager(Component):
        implements(resource.IResourceManager)

        def get_resource_realms(self):
            yield 'fake'

        def resource_exists(self, resource):
            return False if resource.id == 'missing' else True

    def setUp(self):
        self.env = EnvironmentStub(default_data=True)
        self.env.enable_component(self.FakeResourceManager)
        self.req = Mock(perm=MockPerm(), href=Href('/trac.cgi'))
        self.context = web_context(self.req)

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

    def test_resource_exists_default_format(self):
        res = resource.Resource('fake', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('fake:exists',
                     class_='fake',
                     href='/trac.cgi/fake/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_exists_summary_format(self):
        res = resource.Resource('fake', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res,
                                             'summary')
        html = tag.a('fake:exists at version 1',
                     class_='fake',
                     href='/trac.cgi/fake/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_missing_default_format(self):
        res = resource.Resource('fake', 'missing', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('fake:missing',
                     class_='fake missing',
                     rel='nofollow',
                     href='/trac.cgi/fake/missing?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_missing_summary_format(self):
        res = resource.Resource('fake', 'missing', version=1)
        link = resource.render_resource_link(self.env, self.context, res,
                                             'summary')
        html = tag.a('fake:missing at version 1',
                     class_='fake missing',
                     rel='nofollow',
                     href='/trac.cgi/fake/missing?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_has_no_manager_default_format(self):
        res = resource.Resource('unmanaged', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res)
        html = tag.a('unmanaged:exists',
                     class_='unmanaged',
                     href='/trac.cgi/unmanaged/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))

    def test_resource_has_no_manager_summary_format(self):
        res = resource.Resource('unmanaged', 'exists', version=1)
        link = resource.render_resource_link(self.env, self.context, res,
                                             'summary')
        html = tag.a('unmanaged:exists at version 1',
                     class_='unmanaged',
                     href='/trac.cgi/unmanaged/exists?version=1')
        self.assertEqual(unicode(html), unicode(link))
Example #7
0
class AuthenticateTestCase(unittest.TestCase):

    authenticators = {}
    request_handlers = []

    @classmethod
    def setUpClass(cls):
        class UnsuccessfulAuthenticator(Component):
            implements(IAuthenticator)

            def authenticate(self, req):
                return None

        class RaisingAuthenticator(Component):
            implements(IAuthenticator)

            def authenticate(self, req):
                raise TracError("Bad attempt")

        class SuccessfulAuthenticator1(Component):
            implements(IAuthenticator)

            def authenticate(self, req):
                return 'user1'

        class SuccessfulAuthenticator2(Component):
            implements(IAuthenticator)

            def authenticate(self, req):
                return 'user2'

        class AuthenticateRequestHandler(Component):
            implements(IRequestHandler)

            def __init__(self):
                self.calls = 0

            def match_request(self, req):
                return bool(req.perm)

            def process_request(self, req):
                self.calls += 1
                req.authname
                req.send('')

        cls.authenticators['success1'] = SuccessfulAuthenticator1
        cls.authenticators['success2'] = SuccessfulAuthenticator2
        cls.authenticators['unsuccess'] = UnsuccessfulAuthenticator
        cls.authenticators['raising'] = RaisingAuthenticator
        cls.request_handlers = [AuthenticateRequestHandler]

    @classmethod
    def tearDownClass(cls):
        from trac.core import ComponentMeta
        for component in cls.authenticators.values() + cls.request_handlers:
            ComponentMeta.deregister(component)

    def setUp(self):
        self.env = EnvironmentStub(enable=('trac.web.main.*', ))
        self.req = MockRequest(self.env)
        self.request_dispatcher = RequestDispatcher(self.env)

    def test_authenticate_returns_first_successful(self):
        self.env.enable_component(self.authenticators['success1'])
        self.env.enable_component(self.authenticators['success2'])
        self.assertEqual(2, len(self.request_dispatcher.authenticators))
        self.assertIsInstance(self.request_dispatcher.authenticators[0],
                              self.authenticators['success1'])
        self.assertIsInstance(self.request_dispatcher.authenticators[1],
                              self.authenticators['success2'])
        self.assertEqual('user1',
                         self.request_dispatcher.authenticate(self.req))

    def test_authenticate_skips_unsuccessful(self):
        self.env.enable_component(self.authenticators['unsuccess'])
        self.env.enable_component(self.authenticators['success1'])
        self.assertEqual(2, len(self.request_dispatcher.authenticators))
        self.assertIsInstance(self.request_dispatcher.authenticators[0],
                              self.authenticators['unsuccess'])
        self.assertIsInstance(self.request_dispatcher.authenticators[1],
                              self.authenticators['success1'])
        self.assertEqual('user1',
                         self.request_dispatcher.authenticate(self.req))

    def test_authenticate_raises(self):
        self.env.enable_component(self.authenticators['raising'])
        self.env.enable_component(self.authenticators['success1'])
        self.assertEqual(2, len(self.request_dispatcher.authenticators))
        self.assertIsInstance(self.request_dispatcher.authenticators[0],
                              self.authenticators['raising'])
        self.assertIsInstance(self.request_dispatcher.authenticators[1],
                              self.authenticators['success1'])
        self.assertEqual('anonymous',
                         self.request_dispatcher.authenticate(self.req))
        self.assertEqual(1, len(self.req.chrome['warnings']))
        expected = "Can't authenticate using RaisingAuthenticator: "
        for level, message in self.env.log_messages:
            if expected in message.split('\n'):
                self.assertEqual('ERROR', level)
                break
        else:
            self.fail("Expected log message not found: \"%s\"" % expected)

    def test_authenticate_once(self):
        self.env.enable_component(self.authenticators['success1'])
        self.env.enable_component(self.request_handlers[0])
        self.env.config.set('trac', 'default_handler',
                            'AuthenticateRequestHandler')
        self.request_dispatcher.set_default_callbacks(self.req)

        with self.assertRaises(RequestDone):
            self.request_dispatcher.dispatch(self.req)

        self.assertEqual(1, len(self.request_dispatcher.authenticators))
        self.assertEqual(1, len(self.request_dispatcher.handlers))
        self.assertEqual(1, self.request_dispatcher.handlers[0].calls)
Example #8
0
class PostProcessRequestTestCase(unittest.TestCase):
    """Test cases for handling of the optional `method` argument in
    RequestDispatcher._post_process_request."""

    request_filter = {}

    @classmethod
    def setUpClass(cls):
        class RequestFilter4Arg(Component):
            implements(IRequestFilter)

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self, req, template, data, metadata):
                return template, data, metadata

        class RequestFilter5Arg(Component):
            implements(IRequestFilter)

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self,
                                     req,
                                     template,
                                     data,
                                     metadata,
                                     method=None):
                return template, data, metadata, method

        class RequestFilter5ArgXml(Component):
            implements(IRequestFilter)

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self,
                                     req,
                                     template,
                                     data,
                                     metadata,
                                     method=None):
                return template, data, metadata, 'xml'

        class RequestFilterRedirectOnPermError(Component):
            implements(IRequestHandler, IRequestFilter)

            def match_request(self, req):
                return re.match(r'/perm-error', req.path_info)

            def process_request(self, req):
                req.entered_process_request = True
                raise PermissionError("No permission to view")

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self, req, template, data, content_type):
                if (template, data, content_type) == (None, None, None):
                    req.entered_post_process_request = True
                    req.redirect(req.href('/redirect-target'))
                return template, data, content_type

        cls.request_filter['4Arg'] = RequestFilter4Arg
        cls.request_filter['5Arg'] = RequestFilter5Arg
        cls.request_filter['5ArgXml'] = RequestFilter5ArgXml
        cls.request_filter['RedirectOnPermError'] = \
            RequestFilterRedirectOnPermError

    @classmethod
    def tearDownClass(cls):
        from trac.core import ComponentMeta
        for component in cls.request_filter.values():
            ComponentMeta.deregister(component)

    def setUp(self):
        self.env = EnvironmentStub(enable=('trac.web.main.*', ))
        self.req = MockRequest(self.env)

    def test_no_request_filters_request_handler_returns_method_false(self):
        """IRequestHandler doesn't return `method` and no IRequestFilters
        are registered. The `method` is set to `None`.
        """
        args = ('template.html', {}, 'text/html')
        request_dispatcher = RequestDispatcher(self.env)
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(0, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args + (None, ), resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'})
        request_dispatcher = RequestDispatcher(self.env)
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(0, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args + (None, ), resp)

    def test_no_request_filters_request_handler_returns_method_true(self):
        """IRequestHandler returns `method` and no IRequestFilters
        are registered. The `method` is forwarded.
        """
        args = ('template.html', {}, 'text/html', 'xhtml')
        request_dispatcher = RequestDispatcher(self.env)
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(0, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(0, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)

    def test_4arg_post_process_request_request_handler_returns_method_false(
            self):
        """IRequestHandler doesn't return `method` and IRequestFilter doesn't
        accept `method` as an argument. The `method` is set to `None`.
        """
        self.env.enable_component(self.request_filter['4Arg'])
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {}, 'text/html')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args + (None, ), resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'})
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args + (None, ), resp)

    def test_4arg_post_process_request_request_handler_returns_method_true(
            self):
        """IRequestHandler returns `method` and IRequestFilter doesn't accept
        the argument. The `method` argument is forwarded over IRequestFilter
        implementations that don't accept the argument.
        """
        self.env.enable_component(self.request_filter['4Arg'])
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {}, 'text/html', 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)

    def test_5arg_post_process_request_request_handler_returns_method_false(
            self):
        """IRequestHandler doesn't return `method` and IRequestFilter accepts
        `method` as an argument. The `method` is set to `None`.
        """
        self.env.enable_component(self.request_filter['5Arg'])
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {}, 'text/html')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args[:3] + (None, ), resp)

    def test_5arg_post_process_request_request_handler_returns_method_true(
            self):
        """IRequestHandler returns `method` and IRequestFilter accepts
        the argument. The `method` argument is passed through IRequestFilter
        implementations.
        """
        self.env.enable_component(self.request_filter['5Arg'])
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {}, 'text/html', 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args, resp)

    def test_5arg_post_process_request_request_handler_adds_method(self):
        """IRequestFilter adds `method` not returned by IRequestHandler.
        """
        self.env.enable_component(self.request_filter['5ArgXml'])
        args = ('template.html', {}, 'text/html')
        request_dispatcher = RequestDispatcher(self.env)
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args[:3] + ('xml', ), resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'})
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args[:3] + ('xml', ), resp)

    def test_5arg_post_process_request_request_handler_modifies_method(self):
        """IRequestFilter modifies `method` returned by IRequestHandler.
        """
        self.env.enable_component(self.request_filter['5ArgXml'])
        args = ('template.html', {}, 'text/html', 'xhtml')
        request_dispatcher = RequestDispatcher(self.env)
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args[:3] + ('xml', ), resp)
        # TODO (1.5.1) remove old API (genshi style)
        args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml')
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(1, len(request_dispatcher.filters))
        self.assertEqual(4, len(resp))
        self.assertEqual(args[:3] + ('xml', ), resp)

    def test_redirect_on_permission_error(self):
        """The post_process_request method can redirect during exception
        handling from an exception raised in process_request.
        """
        self.env.enable_component(self.request_filter['RedirectOnPermError'])
        dispatcher = RequestDispatcher(self.env)
        req = MockRequest(self.env, method='GET', path_info='/perm-error')
        req.entered_process_request = False
        req.entered_post_process_request = False

        try:
            dispatcher.dispatch(req)
        except RequestDone:
            pass
        else:
            self.fail("RequestDone not raised")

        self.assertTrue(req.entered_process_request)
        self.assertTrue(req.entered_post_process_request)
Example #9
0
class UpgradeTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(path=mkdtemp())
        self.env.config.filename = os.path.join(self.env.path, 'trac.ini')
        self.env.config.set('trac', 'repository_sync_per_request',
                            'repos1, repos3, repos5')

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

    def test_saves_backup(self):
        """Backup file is saved during upgrade."""
        config = self.env.config
        db32.do_upgrade(self.env, VERSION, None)

        self.assertTrue(os.path.exists(config.filename + '.db32.bak'))

    def test_repository_sync_per_request_default_value(self):
        """The default repository sync_per_request attribute is set to true
        when repository_sync_per_request is not set in trac.ini.
        """
        self.env.config.remove('trac', 'repository_sync_per_request')
        repositories = self.env.config['repositories']
        repositories.set('.dir', '/var/svn')
        repositories.set('.type', 'svn')
        repositories.set('git.dir', '/var/git')
        repositories.set('git.type', 'git')

        db32.do_upgrade(self.env, VERSION, None)

        repositories = self.env.config['repositories']
        self.assertIn('.sync_per_request', repositories)
        self.assertTrue(repositories.getbool('.sync_per_request'))
        self.assertNotIn('git.sync_per_request', repositories)
        self.assertFalse(repositories.getbool('git.sync_per_request'))

    def test_repository_sync_per_request_default_value_with_db(self):
        """The default repository sync_per_request attribute is set to
        true when repository_sync_per_request is not set in trac.ini.
        """
        self.env.config.remove('trac', 'repository_sync_per_request')
        # directly insert repository records instead of DbRepositoryProvider
        # to avoid a TracError "The repository type 'svn' is not supported"
        with self.env.db_transaction as db:
            db.executemany(
                """INSERT INTO repository (id,name,value)
                              VALUES (%s,%s,%s)""", [(1, 'name', ''),
                                                     (1, 'dir', '/var/svn'),
                                                     (1, 'type', 'svn'),
                                                     (2, 'name', 'git'),
                                                     (2, 'dir', '/var/git'),
                                                     (2, 'type', 'git')])

        db32.do_upgrade(self.env, VERSION, None)

        repos = RepositoryManager(self.env).get_all_repositories()
        self.assertIn('', repos)
        self.assertTrue(repos['']['sync_per_request'])
        self.assertEqual(
            '1',
            self.env.db_query("""
            SELECT value FROM repository
            WHERE id=1 AND name='sync_per_request'""")[0][0])
        self.assertIn('git', repos)
        self.assertFalse(repos['git']['sync_per_request'])
        self.assertIsNone(
            self.env.db_query("""
            SELECT value FROM repository
            WHERE id=2 AND name='sync_per_request'""")[0][0])

    def test_gitweb_configuration_moved(self):
        """The Gitweb configuration is moved from the [git] section to
        the [gitweb-repositories] section.
        """
        projects_list = os.path.join(self.env.path, 'projects_list')
        projects_base = os.path.dirname(projects_list)
        projects_url = 'http://localhost/%s'
        with open(projects_list, 'w') as f:
            f.write("""
            repos1 user1+<*****@*****.**>
            repos2
            """)
        config = self.env.config['git']
        config.set('projects_list', projects_list)
        config.set('projects_base', projects_base)
        config.set('projects_url', projects_url)
        repos1_dir = os.path.join(projects_base, 'repos1')
        repos2_dir = os.path.join(projects_base, 'repos2')

        db32.do_upgrade(self.env, VERSION, None)

        repos = RepositoryManager(self.env).get_all_repositories()
        self.assertIn('repos1', repos)
        self.assertTrue(repos['repos1']['sync_per_request'])
        self.assertEqual(repos1_dir, repos['repos1']['dir'])
        self.assertEqual('http://localhost/repos1', repos['repos1']['url'])
        self.assertIn('repos2', repos)
        self.assertFalse(repos['repos2']['sync_per_request'])
        self.assertEqual(repos2_dir, repos['repos2']['dir'])
        self.assertEqual('http://localhost/repos2', repos['repos2']['url'])
        config = self.env.config['gitweb-repositories']
        self.assertNotIn('projects_list', self.env.config)
        self.assertNotIn('projects_base', self.env.config)
        self.assertNotIn('projects_url', self.env.config)
        self.assertNotIn('repository_sync_per_request', self.env.config)
        self.assertEqual(projects_list, config.get('projects_list'))
        self.assertEqual(projects_base, config.get('projects_base'))
        self.assertEqual(projects_url, config.get('projects_url'))
        self.assertEqual('repos1', config.get('sync_per_request'))

    def test_repository_providers_disabled(self):
        """Repository configuration is rewritten when repository providers
        are disabled.
        """
        projects_list = os.path.join(self.env.path, 'projects_list')
        projects_base = os.path.dirname(projects_list)
        projects_url = 'http://localhost/%s'
        with open(projects_list, 'w') as f:
            f.write("""
            repos1 user1+<*****@*****.**>
            repos2
            """)
        config = self.env.config['git']
        config.set('projects_list', projects_list)
        config.set('projects_base', projects_base)
        config.set('projects_url', projects_url)
        db_provider = DbRepositoryProvider(self.env)
        db_provider.add_repository('repos3', '/var/git/repos3', 'git')
        db_provider.add_repository('repos4', '/var/git/repos4', 'git')
        config = self.env.config['repositories']
        config.set('repos5.dir', '/var/svn/repos4')
        config.set('repos5.type', 'svn')
        config.set('repos6.dir', '/var/svn/repos5')
        config.set('repos6.type', 'svn')
        self.env.disable_component(GitwebProjectsRepositoryProvider)
        self.env.disable_component(DbRepositoryProvider)
        self.env.disable_component(RepositoryManager)

        db32.do_upgrade(self.env, VERSION, None)

        self.env.enable_component(GitwebProjectsRepositoryProvider)
        self.env.enable_component(DbRepositoryProvider)
        self.env.enable_component(RepositoryManager)
        repos = RepositoryManager(self.env).get_all_repositories()
        config = self.env.config['gitweb-repositories']
        self.assertEqual(projects_list, config.get('projects_list'))
        self.assertEqual(projects_base, config.get('projects_base'))
        self.assertEqual(projects_url, config.get('projects_url'))
        self.assertEqual('repos1', config.get('sync_per_request'))
        self.assertIn('repos1', repos)
        self.assertTrue(repos['repos1']['sync_per_request'])
        self.assertIn('repos2', repos)
        self.assertFalse(repos['repos2']['sync_per_request'])
        self.assertIn('repos3', repos)
        self.assertTrue(repos['repos3']['sync_per_request'])
        self.assertIn('repos4', repos)
        self.assertFalse(repos['repos4']['sync_per_request'])
        self.assertIn('repos5', repos)
        self.assertTrue(repos['repos5']['sync_per_request'])
        self.assertIn('repos6', repos)
        self.assertFalse(repos['repos6']['sync_per_request'])
Example #10
0
class EnvironmentUpgradeTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()

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

    def test_multiple_upgrade_participants(self):
        class Participant1(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return True

            def upgrade_environment(self):
                insert_value('value1', 1)

        class Participant2(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return True

            def upgrade_environment(self):
                insert_value('value2', 2)

        def insert_value(name, value):
            with self.env.db_transaction as db:
                db(
                    """
                    INSERT INTO {0} (name, value) VALUES (%s, %s)
                    """.format(db.quote('system')), (name, value))

        def select_value(name):
            with self.env.db_query as db:
                for value, in db(
                        """
                        SELECT value FROM {0} WHERE name=%s
                        """.format(db.quote('system')), (name, )):
                    return value

        self.env.enable_component(Participant1)
        self.env.enable_component(Participant2)

        self.assertTrue(self.env.needs_upgrade())
        self.assertTrue(self.env.upgrade())
        self.assertEqual('1', select_value('value1'))
        self.assertEqual('2', select_value('value2'))

    def test_upgrade_environment(self):
        """EnvironmentSetupParticipants are called only if
        environment_needs_upgrade returns True for the participant.
        """
        class SetupParticipantA(Component):
            implements(IEnvironmentSetupParticipant)

            called = False

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return True

            def upgrade_environment(self):
                self.called = True

        class SetupParticipantB(Component):
            implements(IEnvironmentSetupParticipant)

            called = False

            def environment_created(self):
                pass

            def environment_needs_upgrade(self):
                return False

            def upgrade_environment(self):
                self.called = True

        self.env.enable_component(SetupParticipantA)
        self.env.enable_component(SetupParticipantB)
        participant_a = SetupParticipantA(self.env)
        participant_b = SetupParticipantB(self.env)

        self.assertTrue(self.env.needs_upgrade())
        self.env.upgrade()
        self.assertTrue(participant_a.called)
        self.assertFalse(participant_b.called)
Example #11
0
class AttachmentTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()
        self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-')
        self.attachments_dir = os.path.join(self.env.path, 'files',
                                            'attachments')
        self.env.enable_component(TicketOnlyViewsTicket)
        self.env.config.set('trac', 'permission_policies',
                            'TicketOnlyViewsTicket, LegacyAttachmentPolicy')
        self.env.config.set('attachment', 'max_size', 512)

        self.perm = PermissionCache(self.env)
        self.datetime = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
        with self.env.db_transaction as db:
            db("INSERT INTO wiki (name,version) VALUES ('WikiStart',1)")
            db("INSERT INTO wiki (name,version) VALUES ('SomePage',1)")
            db("INSERT INTO ticket (id) VALUES (42)")
            db("INSERT INTO ticket (id) VALUES (43)")
            db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
               ('ticket', '43', 'foo.txt', 8, to_utimestamp(
                   self.datetime), 'A comment', 'joe', '::1'))

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

    def test_new_attachment(self):
        attachment = Attachment(self.env, 'ticket', 42)
        self.assertEqual(None, attachment.filename)
        self.assertEqual(None, attachment.description)
        self.assertEqual(None, attachment.size)
        self.assertEqual(None, attachment.date)
        self.assertEqual(None, attachment.author)
        self.assertEqual(None, attachment.ipnr)
        self.assertEqual('<Attachment None>', repr(attachment))

    def test_existing_attachment(self):
        attachment = Attachment(self.env, 'ticket', 43, 'foo.txt')
        self.assertEqual('foo.txt', attachment.filename)
        self.assertEqual('A comment', attachment.description)
        self.assertEqual(8, attachment.size)
        self.assertEqual(self.datetime, attachment.date)
        self.assertEqual('joe', attachment.author)
        self.assertEqual('::1', attachment.ipnr)
        self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))

    def test_existing_attachment_from_resource(self):
        resource = Resource('ticket', 43).child('attachment', 'foo.txt')
        attachment = Attachment(self.env, resource)
        self.assertEqual('foo.txt', attachment.filename)
        self.assertEqual('A comment', attachment.description)
        self.assertEqual(8, attachment.size)
        self.assertEqual(self.datetime, attachment.date)
        self.assertEqual('joe', attachment.author)
        self.assertEqual('::1', attachment.ipnr)
        self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))

    def test_get_path(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'foo.txt'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'ticket', hashes['42'][0:3],
                         hashes['42'], hashes['foo.txt'] + '.txt'),
            attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = 'bar.jpg'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'wiki', hashes['SomePage'][0:3],
                         hashes['SomePage'], hashes['bar.jpg'] + '.jpg'),
            attachment.path)

    def test_path_extension(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'Foo.Mp3'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'ticket', hashes['42'][0:3],
                         hashes['42'], hashes['Foo.Mp3'] + '.Mp3'),
            attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = 'bar.7z'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'wiki', hashes['SomePage'][0:3],
                         hashes['SomePage'], hashes['bar.7z'] + '.7z'),
            attachment.path)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'foo.$$$'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'ticket', hashes['42'][0:3],
                         hashes['42'], hashes['foo.$$$']), attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = u'bar.aäc'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'wiki', hashes['SomePage'][0:3],
                         hashes['SomePage'], hashes[u'bar.aäc']),
            attachment.path)

    def test_get_path_encoded(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'Teh foo.txt'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'ticket', hashes['42'][0:3],
                         hashes['42'], hashes['Teh foo.txt'] + '.txt'),
            attachment.path)
        attachment = Attachment(self.env, 'wiki', u'ÜberSicht')
        attachment.filename = 'Teh bar.jpg'
        self.assertEqual(
            os.path.join(self.attachments_dir, 'wiki',
                         hashes[u'ÜberSicht'][0:3], hashes[u'ÜberSicht'],
                         hashes['Teh bar.jpg'] + '.jpg'), attachment.path)

    def test_select_empty(self):
        self.assertRaises(StopIteration,
                          Attachment.select(self.env, 'ticket', 42).next)
        self.assertRaises(StopIteration,
                          Attachment.select(self.env, 'wiki', 'SomePage').next)

    def test_insert(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0, 1)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('bar.jpg', StringIO(''), 0, 2)

        attachments = Attachment.select(self.env, 'ticket', 42)
        self.assertEqual('foo.txt', attachments.next().filename)
        self.assertEqual('bar.jpg', attachments.next().filename)
        self.assertRaises(StopIteration, attachments.next)

    def test_insert_unique(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0)
        self.assertEqual('foo.txt', attachment.filename)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0)
        self.assertEqual('foo.2.txt', attachment.filename)
        self.assertEqual(
            os.path.join(self.attachments_dir, 'ticket', hashes['42'][0:3],
                         hashes['42'], hashes['foo.2.txt'] + '.txt'),
            attachment.path)
        self.assertTrue(os.path.exists(attachment.path))

    def test_insert_outside_attachments_dir(self):
        attachment = Attachment(self.env, '../../../../../sth/private', 42)
        self.assertRaises(TracError, attachment.insert, 'foo.txt',
                          StringIO(''), 0)

    def test_delete(self):
        attachment1 = Attachment(self.env, 'wiki', 'SomePage')
        attachment1.insert('foo.txt', StringIO(''), 0)
        attachment2 = Attachment(self.env, 'wiki', 'SomePage')
        attachment2.insert('bar.jpg', StringIO(''), 0)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(2, len(list(attachments)))

        attachment1.delete()
        attachment2.delete()

        self.assertFalse(os.path.exists(attachment1.path))
        self.assertFalse(os.path.exists(attachment2.path))

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(0, len(list(attachments)))

    def test_delete_file_gone(self):
        """
        Verify that deleting an attachment works even if the referenced file
        doesn't exist for some reason.
        """
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.insert('foo.txt', StringIO(''), 0)
        os.unlink(attachment.path)

        attachment.delete()

    def test_reparent(self):
        attachment1 = Attachment(self.env, 'wiki', 'SomePage')
        attachment1.insert('foo.txt', StringIO(''), 0)
        path1 = attachment1.path
        attachment2 = Attachment(self.env, 'wiki', 'SomePage')
        attachment2.insert('bar.jpg', StringIO(''), 0)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(2, len(list(attachments)))
        attachments = Attachment.select(self.env, 'ticket', 123)
        self.assertEqual(0, len(list(attachments)))
        self.assertTrue(
            os.path.exists(path1) and os.path.exists(attachment2.path))

        attachment1.reparent('ticket', 123)
        self.assertEqual('ticket', attachment1.parent_realm)
        self.assertEqual('ticket', attachment1.resource.parent.realm)
        self.assertEqual('123', attachment1.parent_id)
        self.assertEqual('123', attachment1.resource.parent.id)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(1, len(list(attachments)))
        attachments = Attachment.select(self.env, 'ticket', 123)
        self.assertEqual(1, len(list(attachments)))
        self.assertFalse(
            os.path.exists(path1) and os.path.exists(attachment1.path))
        self.assertTrue(os.path.exists(attachment2.path))

    def test_legacy_permission_on_parent(self):
        """Ensure that legacy action tests are done on parent.  As
        `ATTACHMENT_VIEW` maps to `TICKET_VIEW`, the `TICKET_VIEW` is tested
        against the ticket's resource."""
        attachment = Attachment(self.env, 'ticket', 42)
        self.assertTrue('ATTACHMENT_VIEW' in self.perm(attachment.resource))

    def test_resource_exists(self):
        att = Attachment(self.env, 'wiki', 'WikiStart')
        att.insert('file.txt', StringIO(''), 1)
        self.assertTrue(resource_exists(self.env, att.resource))
Example #12
0
class EnvironmentUpgradeTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub()

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

    def test_multiple_upgrade_participants(self):
        class Participant1(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self, db):
                return True

            def upgrade_environment(self, db):
                insert_value('value1', 1)

        class Participant2(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self, db):
                return True

            def upgrade_environment(self, db):
                insert_value('value2', 2)

        def insert_value(name, value):
            self.env.db_transaction(
                """
                INSERT INTO system (name, value) VALUES (%s, %s)
                """, (name, value))

        def select_value(name):
            for value, in self.env.db_query(
                    """
                    SELECT value FROM system WHERE name=%s
                    """, (name, )):
                return value

        self.env.enable_component(Participant1)
        self.env.enable_component(Participant2)

        self.assertTrue(self.env.needs_upgrade())
        self.assertTrue(self.env.upgrade())
        self.assertEqual('1', select_value('value1'))
        self.assertEqual('2', select_value('value2'))

    def test_needs_upgrade_legacy_participant(self):
        """For backward compatibility with plugin, environment_needs_upgrade
        with a `db` argument is deprecated but still allowed."""
        participants = self.env.setup_participants
        needs_upgrade = self.env.needs_upgrade()

        class LegacyParticipant(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self, db):
                return True

            def upgrade_environment(self, db):
                pass

        self.env.enable_component(LegacyParticipant)

        self.assertFalse(needs_upgrade)
        self.assertEqual(
            len(participants) + 1, len(self.env.setup_participants))
        self.assertTrue(self.env.needs_upgrade())

    def test_upgrade_legacy_participant(self):
        """For backward compatibility with plugin, upgrade with a `db`
        argument is deprecated but still allowed."""
        participants = self.env.setup_participants

        class LegacyParticipant(Component):
            implements(IEnvironmentSetupParticipant)

            def environment_created(self):
                pass

            def environment_needs_upgrade(self, db):
                return True

            def upgrade_environment(self, db):
                pass

        self.env.enable_component(LegacyParticipant)

        self.assertEqual(
            len(participants) + 1, len(self.env.setup_participants))
        self.assertTrue(self.env.needs_upgrade())
        self.assertTrue(self.env.upgrade())
Example #13
0
class PostProcessRequestTestCase(unittest.TestCase):
    """Test cases for handling of the optional `method` argument in
    RequestDispatcher._post_process_request."""

    request_filter = {}

    @classmethod
    def setUpClass(cls):
        class RequestFilterReturns2Args(Component):
            implements(IRequestFilter)

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self, req, template, data, metadata):
                if metadata is not None:
                    metadata['text'] = True
                return template, data

        class RequestFilterReturns3Args(Component):
            implements(IRequestFilter)

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self, req, template, data, metadata):
                if metadata is not None:
                    metadata['domain'] = 'en_US'
                return template, data, metadata

        class RequestFilterRedirectOnPermError(Component):
            implements(IRequestHandler, IRequestFilter)

            def match_request(self, req):
                return re.match(r'/perm-error', req.path_info)

            def process_request(self, req):
                req.entered_process_request = True
                raise PermissionError("No permission to view")

            def pre_process_request(self, req, handler):
                return handler

            def post_process_request(self, req, template, data, content_type):
                if (template, data, content_type) == (None, None, None):
                    req.entered_post_process_request = True
                    req.redirect(req.href('/redirect-target'))
                return template, data, content_type

        cls.request_filter['2Arg'] = RequestFilterReturns2Args
        cls.request_filter['3Arg'] = RequestFilterReturns3Args
        cls.request_filter['RedirectOnPermError'] = \
            RequestFilterRedirectOnPermError

    @classmethod
    def tearDownClass(cls):
        from trac.core import ComponentMeta
        for component in cls.request_filter.values():
            ComponentMeta.deregister(component)

    def setUp(self):
        self.env = EnvironmentStub(enable=('trac.web.main.*',
                                           self.request_filter['2Arg'],
                                           self.request_filter['3Arg']))
        self.req = MockRequest(self.env)

    def test_post_process_request_error_handling(self):
        """The post_process_request methods are called with a triple of
        `None` values when an exception is raised in process_request or
        post_process_request, or an empty response is returned by
        process_request.
        """
        request_dispatcher = RequestDispatcher(self.env)
        args = (None, ) * 3
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(2, len(request_dispatcher.filters))
        self.assertEqual((None, None, None), resp)

    def test_post_process_request_with_2_args(self):
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {})
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(2, len(request_dispatcher.filters))
        self.assertEqual(3, len(resp))
        self.assertEqual(2, len(resp[2]))
        self.assertEqual('en_US', resp[2]['domain'])
        self.assertTrue(resp[2]['text'])

    def test_post_process_request_with_3_args(self):
        request_dispatcher = RequestDispatcher(self.env)
        args = ('template.html', {}, {'content_type': 'text/html'})
        resp = request_dispatcher._post_process_request(self.req, *args)
        self.assertEqual(2, len(request_dispatcher.filters))
        self.assertEqual(3, len(resp))
        self.assertEqual('text/html', resp[2]['content_type'])
        self.assertEqual('en_US', resp[2]['domain'])
        self.assertTrue(resp[2]['text'])
        self.assertEqual(args, resp)

    def test_redirect_on_permission_error(self):
        """The post_process_request method can redirect during exception
        handling from an exception raised in process_request.
        """
        self.env.enable_component(self.request_filter['RedirectOnPermError'])
        dispatcher = RequestDispatcher(self.env)
        req = MockRequest(self.env, method='GET', path_info='/perm-error')
        req.entered_process_request = False
        req.entered_post_process_request = False

        try:
            dispatcher.dispatch(req)
        except RequestDone:
            pass
        else:
            self.fail("RequestDone not raised")

        self.assertTrue(req.entered_process_request)
        self.assertTrue(req.entered_post_process_request)
Example #14
0
class AttachmentTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub()
        self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-')
        self.attachments_dir = os.path.join(self.env.path, 'files',
                                            'attachments')
        self.env.enable_component(TicketOnlyViewsTicket)
        self.env.config.set('trac', 'permission_policies',
                            'TicketOnlyViewsTicket, LegacyAttachmentPolicy')
        self.env.config.set('attachment', 'max_size', 512)

        self.perm = PermissionCache(self.env)
        self.datetime = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
        with self.env.db_transaction as db:
            db("INSERT INTO wiki (name,version) VALUES ('WikiStart',1)")
            db("INSERT INTO wiki (name,version) VALUES ('SomePage',1)")
            db("INSERT INTO ticket (id) VALUES (42)")
            db("INSERT INTO ticket (id) VALUES (43)")
            db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
               ('ticket', '43', 'foo.txt', 8, to_utimestamp(self.datetime),
                'A comment', 'joe', '::1'))

    def tearDown(self):
        shutil.rmtree(self.env.path)
        self.env.reset_db()

    def test_new_attachment(self):
        attachment = Attachment(self.env, 'ticket', 42)
        self.assertEqual(None, attachment.filename)
        self.assertEqual(None, attachment.description)
        self.assertEqual(None, attachment.size)
        self.assertEqual(None, attachment.date)
        self.assertEqual(None, attachment.author)
        self.assertEqual(None, attachment.ipnr)
        self.assertEqual('<Attachment None>', repr(attachment))

    def test_existing_attachment(self):
        attachment = Attachment(self.env, 'ticket', 43, 'foo.txt')
        self.assertEqual('foo.txt', attachment.filename)
        self.assertEqual('A comment', attachment.description)
        self.assertEqual(8, attachment.size)
        self.assertEqual(self.datetime, attachment.date)
        self.assertEqual('joe', attachment.author)
        self.assertEqual('::1', attachment.ipnr)
        self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))

    def test_existing_attachment_from_resource(self):
        resource = Resource('ticket', 43).child('attachment', 'foo.txt')
        attachment = Attachment(self.env, resource)
        self.assertEqual('foo.txt', attachment.filename)
        self.assertEqual('A comment', attachment.description)
        self.assertEqual(8, attachment.size)
        self.assertEqual(self.datetime, attachment.date)
        self.assertEqual('joe', attachment.author)
        self.assertEqual('::1', attachment.ipnr)
        self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))

    def test_get_path(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'foo.txt'
        self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
                                      hashes['42'][0:3], hashes['42'],
                                      hashes['foo.txt'] + '.txt'),
                         attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = 'bar.jpg'
        self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
                                      hashes['SomePage'][0:3],
                                      hashes['SomePage'],
                                      hashes['bar.jpg'] + '.jpg'),
                         attachment.path)

    def test_path_extension(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'Foo.Mp3'
        self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
                                      hashes['42'][0:3], hashes['42'],
                                      hashes['Foo.Mp3'] + '.Mp3'),
                         attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = 'bar.7z'
        self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
                                      hashes['SomePage'][0:3],
                                      hashes['SomePage'],
                                      hashes['bar.7z'] + '.7z'),
                         attachment.path)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'foo.$$$'
        self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
                                      hashes['42'][0:3], hashes['42'],
                                      hashes['foo.$$$']),
                         attachment.path)
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.filename = u'bar.aäc'
        self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
                                      hashes['SomePage'][0:3],
                                      hashes['SomePage'],
                                      hashes[u'bar.aäc']),
                         attachment.path)

    def test_get_path_encoded(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.filename = 'Teh foo.txt'
        self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
                                      hashes['42'][0:3], hashes['42'],
                                      hashes['Teh foo.txt'] + '.txt'),
                         attachment.path)
        attachment = Attachment(self.env, 'wiki', u'ÜberSicht')
        attachment.filename = 'Teh bar.jpg'
        self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
                                      hashes[u'ÜberSicht'][0:3],
                                      hashes[u'ÜberSicht'],
                                      hashes['Teh bar.jpg'] + '.jpg'),
                         attachment.path)

    def test_select_empty(self):
        self.assertRaises(StopIteration,
                          Attachment.select(self.env, 'ticket', 42).next)
        self.assertRaises(StopIteration,
                          Attachment.select(self.env, 'wiki', 'SomePage').next)

    def test_insert(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0, 1)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('bar.jpg', StringIO(''), 0, 2)

        attachments = Attachment.select(self.env, 'ticket', 42)
        self.assertEqual('foo.txt', attachments.next().filename)
        self.assertEqual('bar.jpg', attachments.next().filename)
        self.assertRaises(StopIteration, attachments.next)

    def test_insert_unique(self):
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0)
        self.assertEqual('foo.txt', attachment.filename)
        attachment = Attachment(self.env, 'ticket', 42)
        attachment.insert('foo.txt', StringIO(''), 0)
        self.assertEqual('foo.2.txt', attachment.filename)
        self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
                                      hashes['42'][0:3], hashes['42'],
                                      hashes['foo.2.txt'] + '.txt'),
                         attachment.path)
        self.assertTrue(os.path.exists(attachment.path))

    def test_insert_outside_attachments_dir(self):
        attachment = Attachment(self.env, '../../../../../sth/private', 42)
        self.assertRaises(TracError, attachment.insert, 'foo.txt',
                          StringIO(''), 0)

    def test_delete(self):
        attachment1 = Attachment(self.env, 'wiki', 'SomePage')
        attachment1.insert('foo.txt', StringIO(''), 0)
        attachment2 = Attachment(self.env, 'wiki', 'SomePage')
        attachment2.insert('bar.jpg', StringIO(''), 0)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(2, len(list(attachments)))

        attachment1.delete()
        attachment2.delete()

        self.assertFalse(os.path.exists(attachment1.path))
        self.assertFalse(os.path.exists(attachment2.path))

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(0, len(list(attachments)))

    def test_delete_file_gone(self):
        """
        Verify that deleting an attachment works even if the referenced file
        doesn't exist for some reason.
        """
        attachment = Attachment(self.env, 'wiki', 'SomePage')
        attachment.insert('foo.txt', StringIO(''), 0)
        os.unlink(attachment.path)

        attachment.delete()

    def test_reparent(self):
        attachment1 = Attachment(self.env, 'wiki', 'SomePage')
        attachment1.insert('foo.txt', StringIO(''), 0)
        path1 = attachment1.path
        attachment2 = Attachment(self.env, 'wiki', 'SomePage')
        attachment2.insert('bar.jpg', StringIO(''), 0)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(2, len(list(attachments)))
        attachments = Attachment.select(self.env, 'ticket', 123)
        self.assertEqual(0, len(list(attachments)))
        self.assertTrue(os.path.exists(path1) and os.path.exists(attachment2.path))

        attachment1.reparent('ticket', 123)
        self.assertEqual('ticket', attachment1.parent_realm)
        self.assertEqual('ticket', attachment1.resource.parent.realm)
        self.assertEqual('123', attachment1.parent_id)
        self.assertEqual('123', attachment1.resource.parent.id)

        attachments = Attachment.select(self.env, 'wiki', 'SomePage')
        self.assertEqual(1, len(list(attachments)))
        attachments = Attachment.select(self.env, 'ticket', 123)
        self.assertEqual(1, len(list(attachments)))
        self.assertFalse(os.path.exists(path1) and os.path.exists(attachment1.path))
        self.assertTrue(os.path.exists(attachment2.path))

    def test_legacy_permission_on_parent(self):
        """Ensure that legacy action tests are done on parent.  As
        `ATTACHMENT_VIEW` maps to `TICKET_VIEW`, the `TICKET_VIEW` is tested
        against the ticket's resource."""
        attachment = Attachment(self.env, 'ticket', 42)
        self.assertTrue('ATTACHMENT_VIEW' in self.perm(attachment.resource))

    def test_resource_doesnt_exist(self):
        r = Resource('wiki', 'WikiStart').child('attachment', 'file.txt')
        self.assertFalse(AttachmentModule(self.env).resource_exists(r))

    def test_resource_exists(self):
        att = Attachment(self.env, 'wiki', 'WikiStart')
        att.insert('file.txt', StringIO(''), 1)
        self.assertTrue(resource_exists(self.env, att.resource))
Example #15
0
class RecursivePolicyTestCase(unittest.TestCase):
    """Test case for policies that perform recursive permission checks."""

    def setUp(self):
        self.env = EnvironmentStub()
        self.env.clear_component_registry()
        decisions = []
        self.decisions = decisions

        class PermissionPolicy1(Component):

            implements(perm.IPermissionPolicy)

            def __init__(self):
                self.call_count = 0

            def check_permission(self, action, username, resource, perm):
                self.call_count += 1
                decision = None
                if 'ACTION_2' in perm(resource):
                    decision = None
                elif action == 'ACTION_1':
                    decision = username == 'user1'
                decisions.append(('policy1', action, decision))
                return decision

        class PermissionPolicy2(Component):

            implements(perm.IPermissionPolicy)

            def __init__(self):
                self.call_count = 0

            def check_permission(self, action, username, resource, perm):
                self.call_count += 1
                decision = None
                if action == 'ACTION_2':
                    decision = username == 'user2'
                decisions.append(('policy2', action, decision))
                return decision

        self.env.enable_component(PermissionPolicy1)
        self.env.enable_component(PermissionPolicy2)
        self.env.config.set('trac', 'permission_policies',
                            'PermissionPolicy1, PermissionPolicy2')
        self.ps = perm.PermissionSystem(self.env)

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

    def test_user1_allowed_by_policy1(self):
        """policy1 consulted for ACTION_1. policy1 and policy2 consulted
        for ACTION_2.
        """
        perm_cache = perm.PermissionCache(self.env, 'user1')
        self.assertTrue('ACTION_1' in perm_cache)
        self.assertEqual(2, self.ps.policies[0].call_count)
        self.assertEqual(1, self.ps.policies[1].call_count)
        self.assertEqual([
            ('policy1', 'ACTION_2', None),
            ('policy2', 'ACTION_2', False),
            ('policy1', 'ACTION_1', True),
        ], self.decisions)

    def test_user2_denied_by_no_decision(self):
        """policy1 and policy2 consulted for ACTION_1. policy1 and
        policy2 consulted for ACTION_2.
        """
        perm_cache = perm.PermissionCache(self.env, 'user2')
        self.assertFalse('ACTION_1' in perm_cache)
        self.assertEqual(2, self.ps.policies[0].call_count)
        self.assertEqual(2, self.ps.policies[1].call_count)
        self.assertEqual([
            ('policy1', 'ACTION_2', None),
            ('policy2', 'ACTION_2', True),
            ('policy1', 'ACTION_1', None),
            ('policy2', 'ACTION_1', None),
        ], self.decisions)

    def test_user1_denied_by_policy2(self):
        """policy1 consulted for ACTION_2. policy2 consulted for ACTION_2.
        """
        perm_cache = perm.PermissionCache(self.env, 'user1')
        self.assertFalse('ACTION_2' in perm_cache)
        self.assertEqual(1, self.ps.policies[0].call_count)
        self.assertEqual(1, self.ps.policies[1].call_count)
        self.assertEqual([
            ('policy1', 'ACTION_2', None),
            ('policy2', 'ACTION_2', False),
        ], self.decisions)

    def test_user1_allowed_by_policy2(self):
        """policy1 consulted for ACTION_2. policy2 consulted for ACTION_2.
        """
        perm_cache = perm.PermissionCache(self.env, 'user2')
        self.assertTrue('ACTION_2' in perm_cache)
        self.assertEqual(1, self.ps.policies[0].call_count)
        self.assertEqual(1, self.ps.policies[1].call_count)
        self.assertEqual([
            ('policy1', 'ACTION_2', None),
            ('policy2', 'ACTION_2', True),
        ], self.decisions)