Example #1
0
    def test_insert_into_multiple_products(self):
        # UIDs are global, autoincremented
        # IDs are product-scoped, incremented in the SQL translator
        self.env = ProductEnvironment(self.global_env, self.default_product)

        tid = self._insert_ticket('hello kitty', reporter='admin')
        ticket = Ticket(self.env, tid)
        self.assertEqual(tid, 1)
        self.assertEqual(self._get_ticket_uid(tid), 1)
        self.assertEqual(ticket.id, tid)
        tid = self._insert_ticket('hello kitteh', reporter='admin')
        ticket = Ticket(self.env, tid)
        self.assertEqual(tid, 2)
        self.assertEqual(self._get_ticket_uid(tid), 2)
        self.assertEqual(ticket.id, tid)

        p2 = Product(self.global_env)
        p2.prefix = 'p2'
        p2.name = 'product, too'
        p2.owner = 'admin'
        p2.insert()
        self.env = ProductEnvironment(self.global_env, p2)

        tid = self._insert_ticket('hello catty', reporter='admin')
        ticket = Ticket(self.env, tid)
        self.assertEqual(tid, 1)
        self.assertEqual(self._get_ticket_uid(tid), 3)
        self.assertEqual(ticket.id, tid)
        tid = self._insert_ticket('hello ocelot', reporter='admin')
        ticket = Ticket(self.env, tid)
        self.assertEqual(tid, 2)
        self.assertEqual(self._get_ticket_uid(tid), 4)
        self.assertEqual(ticket.id, tid)
Example #2
0
class ProductAttachmentTestCase(AttachmentTestCase, MultiproductTestCase):

    def setUp(self):
        try:
            AttachmentTestCase.setUp(self)
        except:
            self.global_env = self.env
            self.tearDown()
            raise
        else:
            self.global_env = global_env = self.env
            self._upgrade_mp(global_env)
            self._setup_test_log(global_env)
            self._load_product_from_data(global_env, self.default_product)
            self.env = ProductEnvironment(global_env, self.default_product)

            # Root folder for default product environment
            self.attachments_dir = os.path.join(self.global_env.path,
                    'products', self.default_product, 'files', 'attachments')

    def tearDown(self):
        if os.path.exists(self.global_env.path):
            shutil.rmtree(self.global_env.path)
        self.env.reset_db()

    def test_product_path_isolation(self):
        product_attachment = Attachment(self.env, 'ticket', '42')
        global_attachment = Attachment(self.global_env, 'ticket', '42')
        global_attachment.filename = product_attachment.filename = 'foo.txt'

        self.assertNotEqual(product_attachment.path, global_attachment.path)
Example #3
0
    def add_template_data(self, req, data, tickets):
        if isinstance(self.env, ProductEnvironment):
            super(ProductBatchModifyModule, self).add_template_data(req, data,
                                                                    tickets)
            return

        data['batch_modify'] = True
        data['query_href'] = req.session['query_href'] or req.href.query()

        tickets_by_product = {}
        for t in tickets:
            tickets_by_product.setdefault(t['product'], []).append(t)

        data['action_controls'] = []
        global_env = ProductEnvironment.lookup_global_env(self.env)
        cache = {}
        for k, v in tickets_by_product.iteritems():
            batch_module = cache.get(k or '')
            if batch_module is None:
                env = ProductEnvironment(global_env, k) if k else global_env
                cache[k] = batch_module = ProductBatchModifyModule(env)
            data['action_controls'] += batch_module._get_action_controls(req,
                                                                         v)
        batch_list_modes = [
            {'name': _("add"), 'value': "+"},
            {'name': _("remove"), 'value': "-"},
            {'name': _("add / remove"), 'value': "+-"},
            {'name': _("set to"), 'value': "="},
        ]
        add_script_data(req, batch_list_modes=batch_list_modes,
                        batch_list_properties=self._get_list_fields())
Example #4
0
class ProductMilestoneTestCase(MilestoneTestCase, MultiproductTestCase):
    def setUp(self):
        self.global_env = self._setup_test_env(create_folder=True)
        self._upgrade_mp(self.global_env)
        self._setup_test_log(self.global_env)
        self._load_product_from_data(self.global_env, self.default_product)

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self._load_default_data(self.env)

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

    def test_update_milestone(self):

        self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')")

        milestone = Milestone(self.env, 'Test')
        t1 = datetime(2001, 01, 01, tzinfo=utc)
        t2 = datetime(2002, 02, 02, tzinfo=utc)
        milestone.due = t1
        milestone.completed = t2
        milestone.description = 'Foo bar'
        milestone.update()

        self.assertEqual(
            [('Test', to_utimestamp(t1), to_utimestamp(t2), 'Foo bar', 
                    self.default_product)],
            self.env.db_query("SELECT * FROM milestone WHERE name='Test'"))
Example #5
0
    def setUp(self):
        self.global_env = self._setup_test_env(create_folder=True)
        self._upgrade_mp(self.global_env)
        self._setup_test_log(self.global_env)
        self._load_product_from_data(self.global_env, self.default_product)

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self._load_default_data(self.env)
Example #6
0
    def _get_product_info(self, product, href, resource, max_):
        penv = ProductEnvironment(self.env, product.prefix)
        results = []

        # some queries return a list/tuple, some a generator,
        # hence count() to get the result length
        def count(iter_):
            try:
                return len(iter_)
            except TypeError:
                return sum(1 for _ in iter_)

        query = resource['type'].select(penv)
        for q in itertools.islice(query, max_):
            q.url = href(resource['name'], q.name) \
                if resource.get('hrefurl') \
                else Query.from_string(penv,
                    '%s=%s&%s&col=%s' % (resource['name'], q.name,
                                         self.COMMON_QUERY, resource['name'])
            ).get_href(href)
            q.ticket_count = penv.db_query("""
                SELECT COUNT(*) FROM ticket WHERE ticket.%s='%s'
                AND ticket.status <> 'closed'
                """ % (resource['name'], q.name))[0][0]

            results.append(q)

        # add a '(No <milestone/component/version>)' entry if there are
        # tickets without an assigned resource in the product
        ticket_count = penv.db_query(
            """SELECT COUNT(*) FROM ticket WHERE %s=''
               AND status <> 'closed'""" % (resource['name'],))[0][0]
        if ticket_count != 0:
            q = resource['type'](penv)
            q.name = '(No %s)' % (resource['name'],)
            q.url = Query.from_string(penv,
               'status=!closed&col=id&col=summary&col=owner'
               '&col=status&col=priority&order=priority&%s='
               % (resource['name'],)
            ).get_href(href)
            q.ticket_count = ticket_count
            results.append(q)

        results.sort(key=lambda x: x.ticket_count, reverse=True)

        # add a link to the resource list if there are
        # more than max resources defined
        if count(query) > max_:
            q = resource['type'](penv)
            q.name = _('... more')
            q.ticket_count = None
            q.url = href(resource['name']) if resource.get('hrefurl') \
                else href.dashboard()
            results.append(q)

        return results
Example #7
0
 def setUp(self):
     self._mp_setup()
     self.env.abs_href = Href('http://globalenv.com/trac.cgi')
     url_pattern = getattr(
         getattr(self, self._testMethodName).im_func, 'product_base_url',
         '')
     self.env.config.set('multiproduct', 'product_base_url', url_pattern)
     self.env.config.set('trac', 'base_url',
                         'http://globalenv.com/trac.cgi')
     self.product_env = ProductEnvironment(self.env, self.default_product)
Example #8
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self._load_product_from_data(self.global_env, u'xü')

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self.env1 = ProductEnvironment(self.global_env, u'xü')

        self._load_default_data(self.global_env)
        self._load_default_data(self.env1)

        # Enable product system component in product context
        self.env.enable_component(MultiProductSystem)
Example #9
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        self.global_env.enable_component(TestRequestSpy)
        self.env.enable_component(TestRequestSpy)
        TestRequestSpy(self.global_env).testMatch = self._assert_product_match
        PermissionSystem(self.global_env).grant_permission(
            'testuser', 'PRODUCT_CREATE')
        PermissionSystem(self.global_env).grant_permission(
            'testuser', 'PRODUCT_VIEW')
        PermissionSystem(self.global_env).grant_permission(
            'testuser', 'PRODUCT_MODIFY')
Example #10
0
    def test_env_isolation(self):
        global_env = self.global_env
        env = self.env

        self._load_product_from_data(self.global_env, 'tp2')
        env1 = ProductEnvironment(self.global_env, 'tp2')

        global_store = perm.DefaultPermissionStore(global_env)
        store = perm.DefaultPermissionStore(env)
        store1 = perm.DefaultPermissionStore(env1)

        global_env.db_transaction.executemany(
            "INSERT INTO permission VALUES (%s,%s)",
            [('dev', 'WIKI_MODIFY'),
             ('dev', 'REPORT_ADMIN'),
             ('john', 'dev')])
        env.db_transaction.executemany(
            "INSERT INTO permission VALUES (%s,%s)",
            [('dev', 'WIKI_VIEW'),
             ('dev', 'REPORT_VIEW'),
             ('john', 'dev')])
        env1.db_transaction.executemany(
            "INSERT INTO permission VALUES (%s,%s)",
            [('dev', 'TICKET_CREATE'),
             ('dev', 'MILESTONE_VIEW'),
             ('john', 'dev')])

        self.assertEquals(['REPORT_ADMIN', 'WIKI_MODIFY'],
                          sorted(global_store.get_user_permissions('john')))
        self.assertEquals(['REPORT_VIEW', 'WIKI_VIEW'],
                          sorted(store.get_user_permissions('john')))
        self.assertEquals(['MILESTONE_VIEW', 'TICKET_CREATE'],
                          sorted(store1.get_user_permissions('john')))
Example #11
0
 def product_admincmd_mgr(self, prefix):
     try:
         product_env = ProductEnvironment.lookup_env(self.env, prefix)
     except LookupError:
         raise AdminCommandError('Unknown product %s' % (prefix, ))
     else:
         return AdminCommandManager(product_env)
Example #12
0
    def find_ticket(self, ticket_spec):
        ticket = None
        m = re.match(r'#?(?P<tid>\d+)', ticket_spec)
        if m:
            tid = m.group('tid')
            try:
                ticket = Ticket(self.env, tid)
            except ResourceNotFound:
                # ticket not found in current product, try all other products
                for p in Product.select(self.env):
                    if p.prefix != self.env.product.prefix:
                        # TODO: check for PRODUCT_VIEW permissions
                        penv = ProductEnvironment(self.env.parent, p.prefix)
                        try:
                            ticket = Ticket(penv, tid)
                        except ResourceNotFound:
                            pass
                        else:
                            break

        # ticket still not found, use fallback for <prefix>:ticket:<id> syntax
        if ticket is None:
            try:
                resource = ResourceIdSerializer.get_resource_by_id(ticket_spec)
                ticket = self._create_ticket_by_full_id(resource)
            except:
                raise NoSuchTicketError
        return ticket
Example #13
0
    def setUp(self):
        self._mp_setup(create_folder=True)
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        # Random component class
        self.component_class = self.DummyAdminCommand
Example #14
0
 def product_admincmd_mgr(self, prefix):
     try:
         product_env = ProductEnvironment.lookup_env(self.env, prefix)
     except LookupError:
         raise AdminCommandError('Unknown product %s' % (prefix,))
     else:
         return AdminCommandManager(product_env)
Example #15
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        # Product name inserted in RSS feed
        self.env.product._data['name'] = 'My Project'

        self.env.config.set(
            'trac', 'templates_dir',
            os.path.join(os.path.dirname(self.env.path), 'templates'))
        self.ticket_module = ProductTicketModule(self.env)
        self.mimeview = Mimeview(self.env)
        self.req = Mock(base_path='/trac.cgi',
                        path_info='',
                        href=Href('/trac.cgi'),
                        chrome={'logo': {}},
                        abs_href=Href('http://example.org/trac.cgi'),
                        environ={},
                        perm=[],
                        authname='-',
                        args={},
                        tz=None,
                        locale='',
                        session=None,
                        form_token=None)
Example #16
0
    def test_get_tickets(self):
        for pdata in (
            {'prefix': 'p2', 'name':'product, too', 'description': ''},
            {'prefix': 'p3', 'name':'strike three', 'description': ''},
        ):
            num_tickets = 5
            product = Product(self.global_env)
            product._data.update(pdata)
            product.insert()
            self.env = ProductEnvironment(self.global_env, product)
            for i in range(num_tickets):
                ticket = Ticket(self.env)
                ticket['summary'] = 'hello ticket #%s-%d' % (product.prefix, i)
                ticket['reporter'] = 'admin'
                tid = ticket.insert()

            # retrieve tickets using both global and product scope
            tickets_from_global = [(t['product'], t['id']) for t in
                Product.get_tickets(self.global_env, product.prefix)]
            self.assertEqual(len(tickets_from_global), num_tickets)
            tickets_from_product = [(t['product'], t['id']) for t in
                Product.get_tickets(self.env)]
            self.assertEqual(len(tickets_from_product), num_tickets)
            # both lists should contain same elements
            intersection = set(tickets_from_global) & set(tickets_from_product)
            self.assertEqual(len(intersection), num_tickets)
Example #17
0
    def resource_created(self, resource, context):
        import trac.db_default
        from multiproduct.env import EnvironmentStub

        # Don't populate product database when running from within test
        # environment stub as test cases really don't expect that ...
        if isinstance(self.env, EnvironmentStub):
            return

        product = resource
        self.log.debug("Adding product info (%s) to tables:" % product.prefix)
        with self.env.db_direct_transaction as db:
            # create the default entries for this Product from defaults
            for table in trac.db_default.get_data(db):
                if not table[0] in self.PRODUCT_POPULATE_TABLES:
                    continue

                self.log.debug("  -> %s" % table[0])
                cols = table[1] + ('product', )
                rows = [p + (product.prefix, ) for p in table[2]]
                db.executemany(
                    "INSERT INTO %s (%s) VALUES (%s)" %
                    (table[0], ','.join(cols), ','.join(['%s' for c in cols])),
                    rows)

        # Import default pages in product wiki
        wikiadmin = WikiAdmin(ProductEnvironment(self.env, product.prefix))
        pages = ('TitleIndex', 'RecentChanges', 'InterTrac', 'InterWiki')
        for page in pages:
            filename = resource_filename('trac.wiki', 'default-pages/' + page)
            wikiadmin.import_page(filename, page)
Example #18
0
    def add_template_data(self, req, data, tickets):
        if isinstance(self.env, ProductEnvironment):
            super(ProductBatchModifyModule, self).add_template_data(req, data,
                                                                    tickets)
            return

        data['batch_modify'] = True
        data['query_href'] = req.session['query_href'] or req.href.query()

        tickets_by_product = {}
        for t in tickets:
            tickets_by_product.setdefault(t['product'], []).append(t)

        data['action_controls'] = []
        global_env = ProductEnvironment.lookup_global_env(self.env)
        cache = {}
        for k, v in tickets_by_product.iteritems():
            batch_module = cache.get(k or '')
            if batch_module is None:
                env = ProductEnvironment(global_env, k) if k else global_env
                cache[k] = batch_module = ProductBatchModifyModule(env)
            data['action_controls'] += batch_module._get_action_controls(req,
                                                                         v)
        batch_list_modes = [
            {'name': _("add"), 'value': "+"},
            {'name': _("remove"), 'value': "-"},
            {'name': _("add / remove"), 'value': "+-"},
            {'name': _("set to"), 'value': "="},
        ]
        add_script_data(req, batch_list_modes=batch_list_modes,
                        batch_list_properties=self._get_list_fields())
Example #19
0
    def create(self, req, summary, description, attributes={}, notify=False):
        """ Create a new ticket, returning the ticket ID.

        PS: Borrowed from XmlRpcPlugin.
        """
        if 'product' in attributes:
            env = self.env.parent or self.env
            if attributes['product']:
                env = ProductEnvironment(env, attributes['product'])
        else:
            env = self.env

        t = Ticket(env)
        t['summary'] = summary
        t['description'] = description
        t['reporter'] = req.authname
        for k, v in attributes.iteritems():
            t[k] = v
        t['status'] = 'new'
        t['resolution'] = ''
        t.insert()

        if notify:
            try:
                tn = TicketNotifyEmail(env)
                tn.notify(t, newticket=True)
            except Exception, e:
                self.log.exception("Failure sending notification on creation "
                                   "of ticket #%s: %s" % (t.id, e))
Example #20
0
    def setUp(self):
        self._prepare_env()

        self._setup_test_log(self.global_env)
        formatter.WikiTestCase.setUp(self)
        if self.context.req:
            self.context.req.session = FakeSession()
        if self.mpctx:
            candidates = set(
                self.mpctx.get('load_products', []) +
                [self.mpctx.get('main_product')])
            candidates -= set(
                [self.default_product, None,
                 self.mpctx.get('setup_product')])
            for prefix in candidates:
                self._load_product_from_data(self.env, prefix)

            prefix = self.mpctx.get('main_product', NotImplemented)
            if prefix is None:
                self.env = self.global_env
            elif prefix is not NotImplemented \
                    and (self.env is self.global_env or
                         prefix != self.env.product.prefix):
                self.env = ProductEnvironment(self.global_env, prefix)
            # Enable multi-product components
            self.env.config.set('components', 'multiproduct.*', 'enabled')
Example #21
0
    def setUp(self):
        self.global_env = self._setup_test_env()
        self._upgrade_mp(self.global_env)
        self._setup_test_log(self.global_env)
        self._load_product_from_data(self.env, self.default_product)
        self.env = ProductEnvironment(self.env, self.default_product)

        self.store = perm.DefaultPermissionStore(self.env)
Example #22
0
 def process_request(self, req):
     product = req.args.get('product')
     fields_to_update = req.args.get('fields_to_update[]');
     env = ProductEnvironment(self.env.parent, req.args.get('product'))
     ticket_fields = TicketSystem(env).get_ticket_fields()
     data = dict([f['name'], f['options']]  for f in ticket_fields
         if f['type'] == 'select' and f['name'] in fields_to_update)
     req.send(to_json(data), 'application/json')
Example #23
0
    def setUp(self):
        try:
            AttachmentTestCase.setUp(self)
        except:
            self.global_env = self.env
            self.tearDown()
            raise
        else:
            self.global_env = global_env = self.env
            self._upgrade_mp(global_env)
            self._setup_test_log(global_env)
            self._load_product_from_data(global_env, self.default_product)
            self.env = ProductEnvironment(global_env, self.default_product)

            # Root folder for default product environment
            self.attachments_dir = os.path.join(self.global_env.path,
                    'products', self.default_product, 'files', 'attachments')
Example #24
0
class ProductResourceTestCase(MultiproductTestCase):
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self._load_product_from_data(self.global_env, u'xü')

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self.env1 = ProductEnvironment(self.global_env, u'xü')

        self._load_default_data(self.global_env)
        self._load_default_data(self.env1)

        # Enable product system component in product context
        self.env.enable_component(MultiProductSystem)

    def tearDown(self):
        self.global_env.reset_db()
        self.global_env = self.env = None
Example #25
0
class ProductResourceTestCase(MultiproductTestCase):
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self._load_product_from_data(self.global_env, u'xü')

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self.env1 = ProductEnvironment(self.global_env, u'xü')

        self._load_default_data(self.global_env)
        self._load_default_data(self.env1)

        # Enable product system component in product context
        self.env.enable_component(MultiProductSystem)

    def tearDown(self):
        self.global_env.reset_db()
        self.global_env = self.env = None
Example #26
0
 def setUp(self):
     self._mp_setup()
     self.env.path = '/path/to/env'
     self.env.abs_href = Href('http://globalenv.com/trac.cgi')
     url_pattern = getattr(getattr(self, self._testMethodName).im_func,
                           'product_base_url', '')
     self.env.config.set('multiproduct', 'product_base_url', url_pattern)
     self.env.config.set('trac', 'base_url', 'http://globalenv.com/trac.cgi')
     self.product_env = ProductEnvironment(self.env, self.default_product)
Example #27
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)
        self._load_default_data(self.env)

        self.env.config.set('ticket-custom', 'foo', 'text')
        self.env.config.set('ticket-custom', 'cbon', 'checkbox')
        self.env.config.set('ticket-custom', 'cboff', 'checkbox')
Example #28
0
 def check_permission(self, doc, context):
     product, doctype, id = doc['product'], doc['type'], doc['id']
     username = context.req.authname
     env = self.env
     if product:
         env = ProductEnvironment(self.env, product)
     perm = PermissionSystem(env)
     action = self._required_permissions[doctype]
     return perm.check_permission(action, username, id)
Example #29
0
 def setUp(self):
     self.global_env = self._setup_test_env(create_folder=True,
                                            path=os.path.join(
                                                tempfile.gettempdir(),
                                                'trac-tempenv'))
     self._upgrade_mp(self.global_env)
     self._setup_test_log(self.global_env)
     self._load_product_from_data(self.global_env, self.default_product)
     self.env = ProductEnvironment(self.global_env, self.default_product)
Example #30
0
    def post_process_request(self, req, template, data, content_type):
        """Append necessary ticket data
        """
        try:
            tm = self._get_ticket_module()
        except TracError:
            # no ticket module so no create ticket button
            return template, data, content_type

        if (template, data, content_type) != (None,) * 3:  # TODO: Check !
            if data is None:
                data = {}
            dum_req = dummy_request(self.env)
            dum_req.perm = req.perm
            ticket = Ticket(self.env)
            tm._populate(dum_req, ticket, False)
            all_fields = dict([f['name'], f]
                              for f in tm._prepare_fields(dum_req, ticket)
                              if f['type'] == 'select')

            product_field = all_fields.get('product')
            if product_field:
                # When at product scope, set the default selection to the
                # product at current scope. When at global scope the default
                # selection is determined by [ticket] default_product
                if self.env.product and \
                        self.env.product.prefix in product_field['options']:
                    product_field['value'] = self.env.product.prefix
                # Transform the options field to dictionary of product
                # attributes and filter out products for which user doesn't
                #  have TICKET_CREATE permission
                product_field['options'] = [
                    dict(value=p,
                         new_ticket_url=dum_req.href.products(p, 'newticket'),
                         description=ProductEnvironment.lookup_env(self.env, p)
                                                       .product.name
                    )
                for p in product_field['options']
                    if req.perm.has_permission('TICKET_CREATE',
                                               Neighborhood('product', p)
                                               .child(None, None))]
            else:
                msg = _("Missing ticket field '%(field)s'.", field='product')
                if ProductTicketModule is not None and \
                        self.env[ProductTicketModule] is not None:
                    # Display warning alert to users
                    add_warning(req, msg)
                else:
                    # Include message in logs since this might be a failure
                    self.log.warning(msg)
            data['qct'] = {
                'fields': [all_fields[k] for k in self.qct_fields
                           if k in all_fields],
                'hidden_fields': [all_fields[k] for k in all_fields.keys()
                                  if k not in self.qct_fields] }
        return template, data, content_type
Example #31
0
    def setUp(self):
        self.global_env = self._setup_test_env(create_folder=False)
        self._upgrade_mp(self.global_env)
        self._setup_test_log(self.global_env)
        self._load_product_from_data(self.global_env, self.default_product)
        self.env = ProductEnvironment(self.global_env, self.default_product)

        self.perm = PermissionSystem(self.env)
        self.ticket_system = TicketSystem(self.env)
        self.req = Mock()
Example #32
0
    def post_process_request(self, req, template, data, content_type):
        """Append necessary ticket data
        """
        try:
            tm = self._get_ticket_module()
        except TracError:
            # no ticket module so no create ticket button
            return template, data, content_type

        if (template, data, content_type) != (None, ) * 3:  # TODO: Check !
            if data is None:
                data = {}
            req = dummy_request(self.env)
            ticket = Ticket(self.env)
            tm._populate(req, ticket, False)
            all_fields = dict([f['name'], f]
                              for f in tm._prepare_fields(req, ticket)
                              if f['type'] == 'select')

            product_field = all_fields['product']
            if product_field:
                if self.env.product:
                    product_field['value'] = self.env.product.prefix
                else:
                    # Global scope, now check default_product_prefix is valid
                    default_prefix = self.config.get('multiproduct',
                                                     'default_product_prefix')
                    try:
                        ProductEnvironment.lookup_env(self.env, default_prefix)
                    except LookupError:
                        product_field['value'] = product_field['options'][0]
                    else:
                        product_field['value'] = default_prefix

            data['qct'] = {
                'fields':
                [all_fields[k] for k in self.qct_fields if k in all_fields],
                'hidden_fields': [
                    all_fields[k] for k in all_fields.keys()
                    if k not in self.qct_fields
                ]
            }
        return template, data, content_type
Example #33
0
    def test_creating_new_product_calls_environment_created(self):
        self._enable_component(DummyPlugin)
        self._enable_multiproduct()
        self.env.upgrade()

        prod = Product(self.env)
        prod.update_field_dict(dict(prefix='p1'))
        ProductEnvironment(self.env, prod, create=True)
        with self.env.db_direct_transaction as db:
            db('SELECT * FROM "p1_dummy_table"')
Example #34
0
 def env(self):
     env = getattr(self, '_env', None)
     if env is None:
         self.global_env = self._setup_test_env(enable=[AuthzSourcePolicy])
         self._upgrade_mp(self.global_env)
         self._setup_test_log(self.global_env)
         self._load_product_from_data(self.global_env, self.default_product)
         self._env = env = ProductEnvironment(self.global_env,
                                              self.default_product)
     return env
Example #35
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        self.global_env.enable_component(TestRequestSpy)
        self.env.enable_component(TestRequestSpy)
        TestRequestSpy(self.global_env).testMatch = self._assert_product_match
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_CREATE')
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_VIEW')
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_MODIFY')
Example #36
0
    def test_can_find_ticket_by_product_and_id(self):
        """ Can find ticket given #prefix-id"""
        product2 = "tp2"
        self._load_product_from_data(self.global_env, product2)
        p2_env = ProductEnvironment(self.global_env, product2)
        t1 = self._insert_and_load_ticket_with_env(p2_env, "T1")
        trs = TicketRelationsSpecifics(self.env)

        ticket = trs.find_ticket("#%s-%d" % (product2, t1.id))

        self.assertEqual(ticket.id, 1)
Example #37
0
    def post_process_request(self, req, template, data, content_type):
        """Append necessary ticket data
        """
        try:
            tm = self._get_ticket_module()
        except TracError:
            # no ticket module so no create ticket button
            return template, data, content_type

        if (template, data, content_type) != (None,) * 3:  # TODO: Check !
            if data is None:
                data = {}
            req = dummy_request(self.env)
            ticket = Ticket(self.env)
            tm._populate(req, ticket, False)
            all_fields = dict([f['name'], f]
                              for f in tm._prepare_fields(req, ticket)
                              if f['type'] == 'select')

            product_field = all_fields['product']
            if product_field:
                if self.env.product:
                    product_field['value'] = self.env.product.prefix
                else:
                    # Global scope, now check default_product_prefix is valid
                    default_prefix = self.config.get('multiproduct',
                                                     'default_product_prefix')
                    try:
                        ProductEnvironment.lookup_env(self.env, default_prefix)
                    except LookupError:
                        product_field['value'] = product_field['options'][0]
                    else:
                        product_field['value'] = default_prefix

            data['qct'] = {
                'fields': [all_fields[k] for k in self.qct_fields
                           if k in all_fields],
                'hidden_fields': [all_fields[k] for k in all_fields.keys()
                                  if k not in self.qct_fields]
            }
        return template, data, content_type
    def _process_doc(self, doc):
        ui_doc = dict(doc)

        if doc.get('product'):
            env = ProductEnvironment(self.env, doc['product'])
            product_href = ProductEnvironment.resolve_href(env, self.env)
            ui_doc["href"] = product_href(doc['type'], doc['id'])
        else:
            ui_doc["href"] = self.env.href(doc['type'], doc['id'])

        ui_doc['title'] = str(doc['type'] + ": " + doc['_stored_name']).title()

        return ui_doc
Example #39
0
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self._load_product_from_data(self.global_env, u'xü')

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self.env1 = ProductEnvironment(self.global_env, u'xü')

        self._load_default_data(self.global_env)
        self._load_default_data(self.env1)

        # Enable product system component in product context
        self.env.enable_component(MultiProductSystem)
Example #40
0
    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*'])
        self.env.path = tempfile.mkdtemp(prefix='bh-product-tempenv-')

        self.mpsystem = MultiProductSystem(self.env)
        try:
            self.mpsystem.upgrade_environment(self.env.db_transaction)
        except self.env.db_exc.OperationalError:
            # table remains but database version is deleted
            pass

        self.listener = self._enable_resource_change_listener()
        self.default_data = {'prefix':self.INITIAL_PREFIX,
                             'name':self.INITIAL_NAME,
                             'description':self.INITIAL_DESCRIPTION}

        self.global_env = self.env
        self.product = Product(self.env)
        self.product._data.update(self.default_data)
        self.product.insert()
Example #41
0
    def _process_doc(self, doc):
        ui_doc = dict(doc)
        if doc.get('product'):
            env = ProductEnvironment(self.env, doc['product'])
            product_href = ProductEnvironment.resolve_href(env, self.env)
            # pylint: disable=too-many-function-args
            ui_doc["href"] = product_href(doc['type'], doc['id'])
        else:
            ui_doc["href"] = self.req.href(doc['type'], doc['id'])

        if doc['content']:
            ui_doc['content'] = shorten_result(doc['content'])

        if doc['time']:
            ui_doc['date'] = user_time(self.req, format_datetime, doc['time'])

        is_free_text_view = self.view is None
        if is_free_text_view:
            participant = self.allowed_participants[doc['type']]
            ui_doc['title'] = participant.format_search_results(doc)
        return ui_doc
Example #42
0
    def render_widget(self, name, context, options):
        """Gather product list and render data in compact view
        """
        data = {}
        req = context.req
        title = ''
        params = ('max', 'cols')
        max_, cols = self.bind_params(name, options, *params)

        if not isinstance(self.env, ProductEnvironment):
            for p in Product.select(self.env):
                if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix)):
                    penv = ProductEnvironment(self.env, p.prefix)
                    phref = ProductEnvironment.resolve_href(penv, self.env)
                    for resource in (
                        {'type': Milestone, 'name': 'milestone', 'hrefurl': True},
                        {'type': Component, 'name': 'component'},
                        {'type': Version, 'name': 'version'},
                    ):
                        setattr(p, resource['name'] + 's',
                                self._get_product_info(p, phref, resource, max_))
                    p.owner_link = Query.from_string(self.env,
                        'status!=closed&col=id&col=summary&col=owner'
                        '&col=status&col=priority&order=priority'
                        '&group=product&owner=%s' % (p._data['owner'] or '', )
                    ).get_href(phref)
                    p.href = phref()
                    data.setdefault('product_list', []).append(p)
            title = _('Products')

        data['colseq'] = itertools.cycle(xrange(cols - 1, -1, -1)) if cols \
            else itertools.repeat(1)

        return 'widget_product.html', {
            'title': title,
            'data': data,
            'ctxtnav': [tag.a(_('More'), href=req.href('products'))],
        }, context
Example #43
0
class ProductTestCase(unittest.TestCase):
    """Unit tests covering the Product model"""
    INITIAL_PREFIX = 'tp'
    INITIAL_NAME = 'test project'
    INITIAL_DESCRIPTION = 'a test project'

    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*'])
        self.env.path = tempfile.mkdtemp(prefix='bh-product-tempenv-')

        self.mpsystem = MultiProductSystem(self.env)
        try:
            self.mpsystem.upgrade_environment(self.env.db_transaction)
        except self.env.db_exc.OperationalError:
            # table remains but database version is deleted
            pass

        self.listener = self._enable_resource_change_listener()
        self.default_data = {'prefix':self.INITIAL_PREFIX,
                             'name':self.INITIAL_NAME,
                             'description':self.INITIAL_DESCRIPTION}

        self.global_env = self.env
        self.product = Product(self.env)
        self.product._data.update(self.default_data)
        self.product.insert()

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

    def _enable_resource_change_listener(self):
        listener = TestResourceChangeListener(self.env)
        listener.resource_type = Product
        listener.callback = self.listener_callback
        return listener

    def listener_callback(self, action, resource, context, old_values = None):
        # pylint: disable=unused-argument
        # pylint: disable=attribute-defined-outside-init
        self.prefix = resource.prefix
        self.name = resource.name
        self.description = resource.description

    def test_set_table_field(self):
        """tests that table.field style update works"""
        test = {'prefix': 'td',
                'name': 'test field access',
                'description': 'product to test field setting'}

        product = Product(self.env)

        # attempt to set the fields from the data
        product.prefix = test['prefix']
        product.name = test['name']
        product.description = test['description']

        self.assertEqual(product._data['prefix'], test['prefix'])
        self.assertEqual(product._data['name'], test['name'])
        self.assertEqual(product._data['description'], test['description'])

    def test_select(self):
        """tests that select can search Products by fields"""

        p2_data = {'prefix':'tp2',
                   'name':'test project 2',
                   'description':'a different test project'}
        p3_data = {'prefix':'tp3',
                   'name':'test project 3',
                   'description':'test project'}

        product2 = Product(self.env)
        product2._data.update(p2_data)
        product3 = Product(self.env)
        product3._data.update(p3_data)

        product2.insert()
        product3.insert()

        products = list(Product.select(self.env, where={'prefix':'tp'}))
        self.assertEqual(1, len(products))
        products = list(Product.select(self.env,
            where={'name':'test project'}))
        self.assertEqual(1, len(products))
        products = list(Product.select(self.env,
            where={'prefix':'tp3', 'name':'test project 3'}))
        self.assertEqual(1, len(products))

    def test_update(self):
        """tests that we can use update to push data to the database"""
        product = list(Product.select(self.env, where={'prefix':'tp'}))[0]
        self.assertEqual('test project', product._data['name'])

        new_data = {'prefix':'tp',
                    'name':'updated',
                    'description':'nothing'}
        product._data.update(new_data)
        product.update()

        comp_product = list(Product.select(self.env, where={'prefix':'tp'}))[0]
        self.assertEqual('updated', comp_product._data['name'])

    def test_update_key_change(self):
        """tests that we raise an error for attempting to update key fields"""
        bad_data = {'prefix':'tp0',
                    'name':'update',
                    'description':'nothing'}
        product = list(Product.select(self.env, where={'prefix':'tp'}))[0]
        product._data.update(bad_data)
        self.assertRaises(TracError, product.update)

    def test_insert(self):
        """test saving new Product"""
        data = {'prefix':'new', 'name':'new', 'description':'new'}
        product = Product(self.env)
        product._data.update(data)
        product.insert()

        check_products = list(Product.select(self.env, where={'prefix':'new'}))

        self.assertEqual(product._data['prefix'],
                         check_products[0]._data['prefix'])
        self.assertEqual(1, len(check_products))

    def test_insert_duplicate_key(self):
        """test attempted saving of Product with existing key fails"""
        dupe_key_data = {'prefix':'tp',
                         'name':'dupe',
                         'description':'dupe primary key'}
        product2 = Product(self.env)
        product2._data.update(dupe_key_data)
        self.assertRaises(TracError, product2.insert)

    def test_delete(self):
        """test that we are able to delete Products"""
        product = list(Product.select(self.env, where={'prefix':'tp'}))[0]
        product.delete()

        post = list(Product.select(self.env, where={'prefix':'tp'}))
        self.assertEqual(0, len(post))

    def test_delete_twice(self):
        """test that we error when deleting twice on the same key"""
        product = list(Product.select(self.env, where={'prefix':'tp'}))[0]
        product.delete()

        self.assertRaises(TracError, product.delete)

    def test_field_data_get(self):
        """tests that we can use table.field syntax to get to the field data"""
        prefix = self.default_data['prefix']
        name = self.default_data['name']
        description = self.default_data['description']
        product = list(Product.select(self.env, where={'prefix':prefix}))[0]
        self.assertEqual(prefix, product.prefix)
        self.assertEqual(name, product.name)
        self.assertEqual(description, product.description)

    def test_field_set(self):
        """tests that we can use table.field = something to set field data"""
        prefix = self.default_data['prefix']
        product = list(Product.select(self.env, where={'prefix':prefix}))[0]

        new_description = 'test change of description'
        product.description = new_description
        self.assertEqual(new_description, product.description)

    def test_missing_unique_fields(self):
        """ensure that that insert method works when _meta does not specify
        unique fields when inserting more than one ProductResourceMap instances
        """
        class TestModel(ModelBase):
            """A test model with no unique_fields"""
            _meta = {'table_name': 'bloodhound_testmodel',
                     'object_name': 'TestModelObject',
                     'key_fields': ['id',],
                     'non_key_fields': ['value'],
                     'unique_fields': [],}

        from trac.db import DatabaseManager
        schema = [TestModel._get_schema(), ]
        with self.env.db_transaction as db:
            db_connector, dummy = DatabaseManager(self.env)._get_connector()
            for table in schema:
                for statement in db_connector.to_sql(table):
                    db(statement)

        structure =  dict([(table.name, [col.name for col in table.columns])
                           for table in schema])
        tm1 = TestModel(self.env)
        tm1._data.update({'id':1, 'value':'value1'})
        tm1.insert()
        tm2 = TestModel(self.env)
        tm2._data.update({'id':2, 'value':'value2'})
        tm2.insert()

    def test_change_listener_created(self):
        self.assertEqual('created', self.listener.action)
        self.assertIsInstance(self.listener.resource, Product)
        self.assertEqual(self.INITIAL_PREFIX, self.prefix)
        self.assertEqual(self.INITIAL_NAME, self.name)
        self.assertEqual(self.INITIAL_DESCRIPTION, self.description)

    def test_change_listener_changed(self):
        CHANGED_NAME = "changed name"
        self.product.name = CHANGED_NAME
        self.product.update()
        self.assertEqual('changed', self.listener.action)
        self.assertIsInstance(self.listener.resource, Product)
        self.assertEqual(CHANGED_NAME, self.name)
        self.assertEqual({"name":self.INITIAL_NAME}, self.listener.old_values)

    def test_change_listener_deleted(self):
        self.product.delete()
        self.assertEqual('deleted', self.listener.action)
        self.assertIsInstance(self.listener.resource, Product)
        self.assertEqual(self.INITIAL_PREFIX, self.prefix)

    def test_get_tickets(self):
        for pdata in (
            {'prefix': 'p2', 'name':'product, too', 'description': ''},
            {'prefix': 'p3', 'name':'strike three', 'description': ''},
        ):
            num_tickets = 5
            product = Product(self.global_env)
            product._data.update(pdata)
            product.insert()
            self.env = ProductEnvironment(self.global_env, product)
            for i in range(num_tickets):
                ticket = Ticket(self.env)
                ticket['summary'] = 'hello ticket #%s-%d' % (product.prefix, i)
                ticket['reporter'] = 'admin'
                tid = ticket.insert()

            # retrieve tickets using both global and product scope
            tickets_from_global = [(t['product'], t['id']) for t in
                Product.get_tickets(self.global_env, product.prefix)]
            self.assertEqual(len(tickets_from_global), num_tickets)
            tickets_from_product = [(t['product'], t['id']) for t in
                Product.get_tickets(self.env)]
            self.assertEqual(len(tickets_from_product), num_tickets)
            # both lists should contain same elements
            intersection = set(tickets_from_global) & set(tickets_from_product)
            self.assertEqual(len(intersection), num_tickets)
Example #44
0
class ProductEnvHrefTestCase(MultiproductTestCase):
    """Assertions for resolution of product environment's base URL 
    [https://issues.apache.org/bloodhound/wiki/Proposals/BEP-0003 BEP 3]
    """

    def product_base_url(url_template):
        def decorator(f):
            f.product_base_url = url_template
            return f

        return decorator

    def setUp(self):
        self._mp_setup()
        self.env.path = '/path/to/env'
        self.env.abs_href = Href('http://globalenv.com/trac.cgi')
        url_pattern = getattr(getattr(self, self._testMethodName).im_func,
                              'product_base_url', '')
        self.env.config.set('multiproduct', 'product_base_url', url_pattern)
        self.env.config.set('trac', 'base_url', 'http://globalenv.com/trac.cgi')
        self.product_env = ProductEnvironment(self.env, self.default_product)

    def tearDown(self):
        # Release reference to transient environment mock object
        if self.env is not None:
            try:
                self.env.reset_db()
            except OperationalError:
                # "Database not found ...",
                # "OperationalError: no such table: system" or the like
                pass
        self.env = None
        self.product_env = None

    @product_base_url('http://$(prefix)s.domain.tld/')
    def test_href_subdomain(self):
        """Test product sub domain base URL
        """
        self.assertEqual('/', self.product_env.href())
        self.assertEqual('http://tp1.domain.tld', self.product_env.abs_href())

    @product_base_url('/path/to/bloodhound/$(prefix)s')
    def test_href_sibling_paths(self):
        """Test product base URL at sibling paths
        """
        self.assertEqual('/trac.cgi/path/to/bloodhound/tp1',
                         self.product_env.href())
        self.assertEqual('http://globalenv.com/trac.cgi/path/to/bloodhound/tp1',
                         self.product_env.abs_href())

    @product_base_url('/$(envname)s/$(prefix)s')
    def test_href_inherit_sibling_paths(self):
        """Test product base URL at sibling paths inheriting configuration.
        """
        self.assertEqual('/trac.cgi/env/tp1', self.product_env.href())
        self.assertEqual('http://globalenv.com/trac.cgi/env/tp1',
                         self.product_env.abs_href())

    @product_base_url('')
    def test_href_default(self):
        """Test product base URL is to a default
        """
        self.assertEqual('/trac.cgi/products/tp1', self.product_env.href())
        self.assertEqual('http://globalenv.com/trac.cgi/products/tp1',
                         self.product_env.abs_href())

    @product_base_url('/products/$(prefix)s')
    def test_href_embed(self):
        """Test default product base URL /products/prefix
        """
        self.assertEqual('/trac.cgi/products/tp1', self.product_env.href())
        self.assertEqual('http://globalenv.com/trac.cgi/products/tp1',
                         self.product_env.abs_href())

    @product_base_url('http://$(envname)s.tld/bh/$(prefix)s')
    def test_href_complex(self):
        """Test complex product base URL
        """
        self.assertEqual('/bh/tp1', self.product_env.href())
        self.assertEqual('http://env.tld/bh/tp1', self.product_env.abs_href())

    @product_base_url('http://$(prefix)s.$(envname)s.tld/')
    def test_product_href_uses_multiproduct_product_base_url(self):
        """Test that [multiproduct] product_base_url is used to compute
        abs_href for the product environment when [trac] base_url for
        the product environment is an empty string (the default).
        """
        # Global URLs
        self.assertEqual('http://globalenv.com/trac.cgi', self.env.base_url)
        self.assertEqual('/trac.cgi', self.env.href())
        self.assertEqual('http://globalenv.com/trac.cgi', self.env.abs_href())

        # Product URLs
        self.assertEqual('', self.product_env.base_url)
        self.assertEqual('/', self.product_env.href())
        self.assertEqual('http://tp1.env.tld', self.product_env.abs_href())

    @product_base_url('http://$(prefix)s.$(envname)s.tld/')
    def test_product_href_uses_products_base_url(self):
        """Test that [trac] base_url for the product environment is used to
        compute abs_href for the product environment when [trac] base_url
        for the product environment is different than [trac] base_url for
        the global environment.
        """
        self.product_env.config.set('trac', 'base_url', 'http://productenv.com')
        self.product_env.config.save()

        self.assertEqual('http://productenv.com', self.product_env.base_url)
        self.assertEqual('/', self.product_env.href())
        self.assertEqual('http://productenv.com', self.product_env.abs_href())

    @product_base_url('http://$(prefix)s.$(envname)s.tld/')
    def test_product_href_global_and_product_base_urls_same(self):
        """Test that [multiproduct] product_base_url is used to compute
        abs_href for the product environment when [trac] base_url is the same
        for the product and global environment.
        """
        self.product_env.config.set('trac', 'base_url',
                                    self.env.config.get('trac', 'base_url'))
        self.product_env.config.save()

        self.assertEqual('', self.product_env.base_url)
        self.assertEqual('/', self.product_env.href())
        self.assertEqual('http://tp1.env.tld', self.product_env.abs_href())

    product_base_url = staticmethod(product_base_url)
Example #45
0
class ProductMilestoneTestCase(MilestoneTestCase, MultiproductTestCase):
    def setUp(self):
        self.global_env = self._setup_test_env(create_folder=True)
        self._upgrade_mp(self.global_env)
        self._setup_test_log(self.global_env)
        self._load_product_from_data(self.global_env, self.default_product)

        self.env = ProductEnvironment(self.global_env, self.default_product)
        self._load_default_data(self.env)

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

    @unittest.skipUnless(threading, 'Threading required for test')
    def test_milestone_threads(self):
        """ Ensure that in threaded (e.g. mod_wsgi) situations, we get
        an accurate list of milestones from Milestone.list

        The basic strategy is:
            thread-1 requests a list of milestones
            thread-2 adds a milestone
            thread-1 requests a new list of milestones
        To pass, thread-1 should have a list of milestones that matches
        those that are in the database.
        """
        lock = threading.RLock()
        results = []
        # two events to coordinate the workers and ensure that the threads
        # alternate appropriately
        e1 = threading.Event()
        e2 = threading.Event()

        def task(add):
            """the thread task - either we are discovering or adding events"""
            with lock:
                env = ProductEnvironment(self.global_env,
                                         self.default_product)
                if add:
                    name = 'milestone_from_' + threading.current_thread().name
                    milestone = Milestone(env)
                    milestone.name = name
                    milestone.insert()
                else:
                    # collect the names of milestones reported by Milestone and
                    # directly from the db - as sets to ease comparison later
                    results.append({
                        'from_t': set([m.name for m in Milestone.select(env)]),
                        'from_db': set(
                            [v[0] for v in self.env.db_query(
                                "SELECT name FROM milestone")])})

        def worker1():
            """ check milestones in this thread twice either side of ceding
            control to worker2
            """
            task(False)
            e1.set()
            e2.wait()
            task(False)

        def worker2():
            """ adds a milestone when worker1 allows us to then cede control
            back to worker1
            """
            e1.wait()
            task(True)
            e2.set()

        t1, t2 = [threading.Thread(target=f) for f in (worker1, worker2)]
        t1.start()
        t2.start()
        t1.join()
        t2.join()

        r = results[-1]  # note we only care about the final result
        self.assertEqual(r['from_t'], r['from_db'])

    def test_update_milestone(self):

        self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')")

        milestone = Milestone(self.env, 'Test')
        t1 = datetime(2001, 01, 01, tzinfo=utc)
        t2 = datetime(2002, 02, 02, tzinfo=utc)
        milestone.due = t1
        milestone.completed = t2
        milestone.description = 'Foo bar'
        milestone.update()

        self.assertEqual(
            [('Test', to_utimestamp(t1), to_utimestamp(t2), 'Foo bar',
                    self.default_product)],
            self.env.db_query("SELECT * FROM milestone WHERE name='Test'"))
Example #46
0
class EnvironmentUpgradeTestCase(unittest.TestCase):
    def setUp(self, options=()):
        env_path = tempfile.mkdtemp(prefix='bh-product-tempenv-')
        self.env = Environment(env_path, create=True, options=options)
        DummyPlugin.version = 1

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

    def test_can_upgrade_environment_with_multi_product_disabled(self):
        self.env.upgrade()

        # Multiproduct was not enabled so multiproduct tables should not exist
        for table in BLOODHOUND_TABLES:
            with self.assertFailsWithMissingTable():
                self.env.db_direct_query("SELECT * FROM %s" % table)

        for table in TABLES_WITH_PRODUCT_FIELD:
            with self.assertFailsWithMissingColumn():
                self.env.db_direct_query("SELECT product FROM %s" % table)

    def test_upgrade_creates_multi_product_tables_and_adds_product_column(self):
        self._enable_multiproduct()
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            for table in BLOODHOUND_TABLES:
                db("SELECT * FROM %s" % table)

            for table in TABLES_WITH_PRODUCT_FIELD:
                db("SELECT product FROM %s" % table)

    def test_upgrade_creates_default_product(self):
        self._enable_multiproduct()
        self.env.upgrade()

        products = Product.select(self.env)
        self.assertEqual(len(products), 1)

    def test_upgrade_moves_tickets_and_related_objects_to_default_prod(self):
        self._add_custom_field('custom_field')
        with self.env.db_direct_transaction as db:
            db("""INSERT INTO ticket (id) VALUES (1)""")
            db("""INSERT INTO attachment (type, id, filename)
                       VALUES ('ticket', '1', '')""")
            db("""INSERT INTO ticket_custom (ticket, name, value)
                       VALUES (1, 'custom_field', '42')""")
            db("""INSERT INTO ticket_change (ticket, time, field)
                       VALUES (1, 42, 'summary')""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.product('@'):
            ticket = Ticket(self.env, 1)
            attachments = list(Attachment.select(self.env,
                                                 ticket.resource.realm,
                                                 ticket.resource.id))
            self.assertEqual(len(attachments), 1)
            self.assertEqual(ticket['custom_field'], '42')
            changes = ticket.get_changelog()
            self.assertEqual(len(changes), 3)

    def test_upgrade_moves_custom_wikis_to_default_product(self):
        with self.env.db_direct_transaction as db:
            db("""INSERT INTO wiki (name, version) VALUES ('MyPage', 1)""")
            db("""INSERT INTO attachment (type, id, filename)
                         VALUES ('wiki', 'MyPage', '')""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            self.assertEqual(
                len(db("""SELECT * FROM wiki WHERE product='@'""")), 1)
            self.assertEqual(
                len(db("""SELECT * FROM attachment
                           WHERE product='@'
                             AND type='wiki'""")), 1)

    def test_upgrade_moves_system_wikis_to_products(self):
        with self.env.db_direct_transaction as db:
            db("""INSERT INTO wiki (name, version) VALUES ('WikiStart', 1)""")
            db("""INSERT INTO attachment (type, id, filename)
                         VALUES ('wiki', 'WikiStart', '')""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            self.assertEqual(
                len(db("""SELECT * FROM wiki WHERE product='@'""")), 1)
            self.assertEqual(
                len(db("""SELECT * FROM attachment
                           WHERE product='@'
                             AND type='wiki'""")), 1)
            self.assertEqual(
                len(db("""SELECT * FROM wiki WHERE product=''""")), 0)
            self.assertEqual(
                len(db("""SELECT * FROM attachment
                           WHERE product=''
                             AND type='wiki'""")), 0)

    def test_upgrade_copies_content_of_system_tables_to_all_products(self):
        mp = MultiProductSystem(self.env)
        with self.env.db_direct_transaction as db:
            mp._add_column_product_to_ticket(db)
            mp._create_multiproduct_tables(db)
            mp._update_db_version(db, 1)
            for i in range(1, 6):
                db("""INSERT INTO bloodhound_product (prefix, name)
                           VALUES ('p%d', 'Product 1')""" % i)
            for table in ('component', 'milestone', 'enum', 'version',
                          'permission', 'report'):
                db("""DELETE FROM %s""" % table)
            db("""INSERT INTO component (name) VALUES ('foobar')""")
            db("""INSERT INTO milestone (name) VALUES ('foobar')""")
            db("""INSERT INTO version (name) VALUES ('foobar')""")
            db("""INSERT INTO enum (type, name) VALUES ('a', 'b')""")
            db("""INSERT INTO permission VALUES ('x', 'TICKET_VIEW')""")
            db("""INSERT INTO report (title) VALUES ('x')""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            for table in ('component', 'milestone', 'version', 'enum',
                          'report'):
                rows = db("SELECT * FROM %s" % table)
                self.assertEqual(
                    len(rows), 6,
                    "Wrong number of lines in %s (%d instead of %d)\n%s"
                    % (table, len(rows), 6, rows))
            for table in ('permission',):
                # Permissions also hold rows for global product.
                rows = db("SELECT * FROM %s WHERE username='******'" % table)
                self.assertEqual(
                    len(rows), 7,
                    "Wrong number of lines in %s (%d instead of %d)\n%s"
                    % (table, len(rows), 7, rows))

    def test_upgrading_database_moves_attachment_to_correct_product(self):
        ticket = self.insert_ticket('ticket')
        wiki = self.insert_wiki('MyWiki')
        attachment = self._create_file_with_content('Hello World!')
        self.add_attachment(ticket.resource, attachment)
        self.add_attachment(wiki.resource, attachment)

        self._enable_multiproduct()
        self.env.upgrade()

        with self.product('@'):
            attachments = list(
                Attachment.select(self.env, 'ticket', ticket.id))
            attachments.extend(
                Attachment.select(self.env, 'wiki', wiki.name))
        self.assertEqual(len(attachments), 2)
        for attachment in attachments:
            self.assertEqual(attachment.open().read(), 'Hello World!')

    def test_can_upgrade_database_with_ticket_attachment_with_text_ids(self):
        with self.env.db_direct_transaction as db:
            db("""INSERT INTO attachment (id, type, filename)
                       VALUES ('abc', 'ticket', '')""")

        self._enable_multiproduct()
        self.env.upgrade()

    def test_can_upgrade_database_with_orphaned_attachments(self):
        with self.env.db_direct_transaction as db:
            db("""INSERT INTO attachment (id, type, filename)
                       VALUES ('5', 'ticket', '')""")
            db("""INSERT INTO attachment (id, type, filename)
                       VALUES ('MyWiki', 'wiki', '')""")

        self._enable_multiproduct()
        self.env.upgrade()

    def test_can_upgrade_multi_product_from_v1(self):
        mp = MultiProductSystem(self.env)
        with self.env.db_direct_transaction as db:
            mp._add_column_product_to_ticket(db)
            mp._create_multiproduct_tables(db)
            mp._update_db_version(db, 1)

            db("""INSERT INTO bloodhound_product (prefix, name)
                       VALUES ('p1', 'Product 1')""")
            db("""INSERT INTO ticket (id, product)
                       VALUES (1, 'Product 1')""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.product('p1'):
            Ticket(self.env, 1)

    def test_can_upgrade_multi_product_from_v2(self):
        mp = MultiProductSystem(self.env)
        with self.env.db_direct_transaction as db:
            mp._add_column_product_to_ticket(db)
            mp._create_multiproduct_tables(db)
            mp._replace_product_on_ticket_with_product_prefix(db)
            mp._update_db_version(db, 2)

            db("""INSERT INTO bloodhound_product (prefix, name)
                       VALUES ('p1', 'Product 1')""")
            db("""INSERT INTO ticket (id, product)
                       VALUES (1, 'p1')""")
            db("""INSERT INTO ticket (id)
                       VALUES (2)""")

        self._enable_multiproduct()
        self.env.upgrade()

        with self.product('p1'):
            Ticket(self.env, 1)
        with self.product('@'):
            Ticket(self.env, 2)

    def test_upgrade_plugin(self):
        self._enable_component(DummyPlugin)
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            db("SELECT v1 FROM dummy_table")
            with self.assertFailsWithMissingColumn():
                db("SELECT v2 FROM dummy_table")

        DummyPlugin.version = 2
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            db("SELECT v2 FROM dummy_table")

    def test_upgrade_plugin_to_multiproduct(self):
        self._enable_multiproduct()
        self._enable_component(DummyPlugin)
        self.env.upgrade()

        with self.env.db_direct_transaction as db:
            db("SELECT * FROM dummy_table")
            db("""SELECT * FROM "@_dummy_table" """)

    def test_upgrade_existing_plugin_to_multiproduct(self):
        self._enable_component(DummyPlugin)
        self.env.upgrade()
        with self.env.db_direct_transaction as db:
            with self.assertFailsWithMissingTable():
                db("""SELECT * FROM "@_dummy_table" """)

        self._enable_multiproduct()
        self.env.upgrade()
        with self.env.db_direct_transaction as db:
            db("SELECT * FROM dummy_table")
            db("""SELECT * FROM "@_dummy_table" """)

    def test_upgrading_existing_plugin_leaves_data_in_global_env(self):
        DummyPlugin.version = 2
        self._enable_component(DummyPlugin)
        self.env.upgrade()
        with self.env.db_direct_transaction as db:
            for i in range(5):
                db("INSERT INTO dummy_table (v1) VALUES ('%d')" % i)
            self.assertEqual(
                len(db("SELECT * FROM dummy_table")), 5)

        self._enable_multiproduct()
        self.env.upgrade()
        with self.env.db_direct_transaction as db:
            self.assertEqual(
                len(db('SELECT * FROM "dummy_table"')), 5)
            self.assertEqual(
                len(db('SELECT * FROM "@_dummy_table"')), 0)

    def test_creating_new_product_calls_environment_created(self):
        self._enable_component(DummyPlugin)
        self._enable_multiproduct()
        self.env.upgrade()

        prod = Product(self.env)
        prod.update_field_dict(dict(prefix='p1'))
        ProductEnvironment(self.env, prod, create=True)
        with self.env.db_direct_transaction as db:
            db('SELECT * FROM "p1_dummy_table"')

    def test_migrating_to_multiproduct_with_custom_default_prefix(self):
        ticket = self.insert_ticket('ticket')

        self.env.config.set('multiproduct', 'default_product_prefix', 'xxx')
        self._enable_multiproduct()
        self.env.upgrade()

        products = Product.select(self.env)
        self.assertEqual(len(products), 1)
        self.assertEqual(products[0].prefix, 'xxx')

    def test_migration_to_multiproduct_preserves_ticket_ids(self):
        for ticket_id in (1, 3, 5, 7):
            with self.env.db_transaction as db:
                cursor = db.cursor()
                cursor.execute("INSERT INTO ticket (id) VALUES (%i)" % ticket_id)
                db.update_sequence(cursor, 'ticket')

        self._enable_multiproduct()
        self.env.upgrade()

        for ticket_id in (1, 3, 5, 7):
            with self.product('@'):
                ticket = Ticket(self.env, ticket_id)
            self.assertEqual(ticket.id, ticket_id)

    def test_can_insert_tickets_after_upgrade(self):
        t1 = Ticket(self.env)
        t1.summary = "test"
        t1.insert()
        self.assertEqual(t1.id, 1)

        self._enable_multiproduct()
        self.env.upgrade()

        with self.product('@'):
            ticket = Ticket(self.env)
            ticket.summary = 'test'
            ticket.insert()
            self.assertEqual(ticket.id, 2)

    def test_can_insert_tickets_with_same_id_to_different_products(self):
        self._enable_multiproduct()
        self.env.upgrade()

        self.env.db_transaction("INSERT INTO ticket (id, summary)"
                                "            VALUES (1, 'first product')")
        t1 = Ticket(self.env, 1)

        with self.product('@'):
            self.env.db_transaction("INSERT INTO ticket (id, summary)"
                                    "            VALUES (1, 'second product')")
            t2 = Ticket(self.env, 1)

        self.assertEqual(t1.id, t2.id)
        self.assertNotEqual(t1['summary'], t2['summary'])

    def test_batch_ticket_insert_after_upgrade(self):
        self._enable_multiproduct()
        self.env.upgrade()
        with self.env.db_direct_transaction as db:
            db("""CREATE TABLE "@_tmp" (summary text, product text)""")
            for summary in "abcdef":
                db("""INSERT INTO "@_tmp" VALUES ('%s', '@')""" % (summary,))

        with self.product('@'):
            with self.env.db_transaction as db:
                db("""INSERT INTO ticket (summary) SELECT summary FROM tmp""")



    def _enable_multiproduct(self):
        self._update_config('components', 'multiproduct.*', 'enabled')

    def _add_custom_field(self, field_name):
        self._update_config('ticket-custom', field_name, 'text')

    def _enable_component(self, cls):
        self._update_config(
            'components',
            '%s.%s' % (cls.__module__, cls.__name__),
            'enabled'
        )

    def _update_config(self, section, key, value):
        self.env.config.set(section, key, value)
        self.env.config.save()
        self.env = Environment(self.env.path)

    def _create_file_with_content(self, content):
        filename = str(uuid.uuid4())[:6]
        path = os.path.join(self.env.path, filename)
        with open(path, 'wb') as f:
            f.write(content)
        return path

    @contextmanager
    def assertFailsWithMissingTable(self):
        with self.assertRaises(self.env.db_exc.OperationalError) as cm:
            yield
        self.assertIn('no such table', str(cm.exception))

    @contextmanager
    def assertFailsWithMissingColumn(self):
        with self.assertRaises(self.env.db_exc.OperationalError) as cm:
            yield
        self.assertIn('no such column', str(cm.exception))

    def create_ticket(self, summary, **kw):
        ticket = Ticket(self.env)
        ticket["summary"] = summary
        for k, v in kw.items():
            ticket[k] = v
        return ticket

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

    def create_wiki(self, name, text,  **kw):
        page = WikiPage(self.env, name)
        page.text = text
        for k, v in kw.items():
            page[k] = v
        return page

    def insert_wiki(self, name, text = None, **kw):
        text = text or "Dummy text"
        page = self.create_wiki(name, text, **kw)
        page.save("dummy author", "dummy comment", "::1")
        return page

    def add_attachment(self, resource, path):
        resource = '%s:%s' % (resource.realm, resource.id)
        AttachmentAdmin(self.env)._do_add(resource, path)

    @contextmanager
    def product(self, prefix):
        old_env = self.env
        self.env = ProductEnvironment(self.env, prefix)
        yield
        self.env = old_env
Example #47
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        query_string, kwargs, format = self.parse_args(content)
        if query_string:
            query_string += '&'
        query_string += '&'.join('%s=%s' % item
                                 for item in kwargs.iteritems())

        env = ProductEnvironment.lookup_global_env(self.env)
        query = ProductQuery.from_string(env, query_string)

        if format == 'count':
            cnt = query.count(req)
            return tag.span(cnt, title='%d tickets for which %s' %
                            (cnt, query_string), class_='query_count')

        tickets = query.execute(req)

        if format == 'table':
            data = query.template_data(formatter.context, tickets,
                                       req=formatter.context.req)

            add_stylesheet(req, 'common/css/report.css')

            return Chrome(env).render_template(
                req, 'query_results.html', data, None, fragment=True)

        if format == 'progress':
            from trac.ticket.roadmap import (RoadmapModule,
                                             apply_ticket_permissions,
                                             get_ticket_stats,
                                             grouped_stats_data)

            add_stylesheet(req, 'common/css/roadmap.css')

            def query_href(extra_args, group_value = None):
                q = ProductQuery.from_string(env, query_string)
                if q.group:
                    extra_args[q.group] = group_value
                    q.group = None
                for constraint in q.constraints:
                    constraint.update(extra_args)
                if not q.constraints:
                    q.constraints.append(extra_args)
                return q.get_href(formatter.context)
            chrome = Chrome(env)
            tickets = apply_ticket_permissions(env, req, tickets)
            stats_provider = RoadmapModule(env).stats_provider
            by = query.group
            if not by:
                stat = get_ticket_stats(stats_provider, tickets)
                data = {
                    'stats': stat,
                    'stats_href': query_href(stat.qry_args),
                    'interval_hrefs': [query_href(interval['qry_args'])
                                       for interval in stat.intervals],
                    'legend': True,
                }
                return tag.div(
                    chrome.render_template(req, 'progress_bar.html', data,
                                           None, fragment=True),
                    class_='trac-progress')

            def per_group_stats_data(gstat, group_name):
                return {
                    'stats': gstat,
                    'stats_href': query_href(gstat.qry_args,  group_name),
                    'interval_hrefs': [query_href(interval['qry_args'],
                                                  group_name)
                                       for interval in gstat.intervals],
                    'percent': '%d / %d' % (gstat.done_count,
                                            gstat.count),
                    'legend': False,
                }

            groups = grouped_stats_data(env, stats_provider, tickets, by,
                                        per_group_stats_data)
            data = {
                'groups': groups, 'grouped_by': by,
                'summary': _("Ticket completion status for each %(group)s",
                             group=by),
            }
            return tag.div(
                chrome.render_template(req, 'progress_bar_grouped.html', data,
                                       None, fragment=True),
                class_='trac-groupprogress')

        # Formats above had their own permission checks, here we need to
        # do it explicitly:

        tickets = [t for t in tickets
                   if 'TICKET_VIEW' in req.perm('ticket', t['id'])]

        if not tickets:
            return tag.span(_("No results"), class_='query_no_results')

        # Cache resolved href targets
        hrefcache = {}

        def ticket_anchor(ticket):
            try:
                pvalue = ticket.get('product') or GLOBAL_PRODUCT
                envhref = hrefcache[pvalue]
            except KeyError:
                try:
                    env = lookup_product_env(self.env, prefix= pvalue,
                                             name=pvalue)
                except LookupError:
                    return tag.a('#%s' % ticket['id'], 
                                 class_='missing product')
                hrefcache[pvalue] = envhref = resolve_product_href(
                         to_env=env, at_env=self.env)
            return tag.a('#%s' % ticket['id'],
                         class_=ticket['status'],
                         href=envhref.ticket(int(ticket['id'])),
                         title=shorten_line(ticket['summary']))

        def ticket_groups():
            groups = []
            for v, g in groupby(tickets, lambda t: t[query.group]):
                q = ProductQuery.from_string(env, query_string)
                # produce the hint for the group
                q.group = q.groupdesc = None
                order = q.order
                q.order = None
                title = _("%(groupvalue)s %(groupname)s tickets matching "
                          "%(query)s", groupvalue=v, groupname=query.group,
                          query=q.to_string())
                # produce the href for the query corresponding to the group
                for constraint in q.constraints:
                    constraint[str(query.group)] = v
                q.order = order
                href = q.get_href(formatter.context)
                groups.append((v, [t for t in g], href, title))
            return groups

        if format == 'compact':
            if query.group:
                groups = [(v, ' ',
                           tag.a('#%s' % u',\u200b'.join(str(t['id'])
                                                         for t in g),
                                 href=href, class_='query', title=title))
                          for v, g, href, title in ticket_groups()]
                return tag(groups[0], [(', ', g) for g in groups[1:]])
            else:
                alist = [ticket_anchor(ticket) for ticket in tickets]
                return tag.span(alist[0], *[(', ', a) for a in alist[1:]])
        else:
            if query.group:
                return tag.div(
                    [(tag.p(tag_('%(groupvalue)s %(groupname)s tickets:',
                                 groupvalue=tag.a(v, href=href, class_='query',
                                                  title=title),
                                 groupname=query.group)),
                      tag.dl([(tag.dt(ticket_anchor(t)),
                               tag.dd(t['summary'])) for t in g],
                             class_='wiki compact'))
                     for v, g, href, title in ticket_groups()])
            else:
                return tag.div(tag.dl([(tag.dt(ticket_anchor(ticket)),
                                        tag.dd(ticket['summary']))
                                       for ticket in tickets],
                                      class_='wiki compact'))
Example #48
0
class ProductModuleTestCase(RequestHandlerTestCase):
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        self.global_env.enable_component(TestRequestSpy)
        self.env.enable_component(TestRequestSpy)
        TestRequestSpy(self.global_env).testMatch = self._assert_product_match
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_CREATE')
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_VIEW')
        PermissionSystem(self.global_env).grant_permission('testuser', 'PRODUCT_MODIFY')

    def tearDown(self):
        self.global_env.reset_db()
        self.env = self.global_env = None

    expectedPrefix = None
    expectedPathInfo = None

    def _assert_product_match(self, req, handler):
        self.assertIs(ProductModule(self.global_env), handler)
        self.assertEqual(self.expectedPrefix, req.args['productid'],
                         "Unexpected product prefix")
        self.assertEqual(self.expectedPathInfo, req.args['pathinfo'],
                         "Unexpected sub path")

    def test_product_list(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products'

        mps = MultiProductSystem(self.global_env)
        def assert_product_list(req, template, data, content_type):
            self.assertEquals('product_list.html', template)
            self.assertIs(None, content_type)
            self.assertEquals([mps.default_product_prefix,
                               self.default_product],
                              [p.prefix for p in data.get('products')])
            self.assertTrue('context' in data)
            ctx = data['context']
            self.assertEquals('product', ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_list
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_product_new(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products'
        req.environ['QUERY_STRING'] = 'action=new'

        def assert_product_new(req, template, data, content_type):
            self.assertEquals('product_edit.html', template)
            self.assertIs(None, content_type)
            self.assertFalse('products' in data)
            self.assertTrue('context' in data)
            ctx = data['context']
            self.assertEquals('product', ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_new
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_product_view(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        def assert_product_view(req, template, data, content_type):
            self.assertEquals('product_view.html', template)
            self.assertIs(None, content_type)
            self.assertFalse('products' in data)

            self.assertTrue('context' in data)
            ctx = data['context']
            self.assertEquals('product', ctx.resource.realm)
            self.assertEquals(real_prefix, ctx.resource.id)

            self.assertTrue('product' in data)
            self.assertEquals(real_prefix, data['product'].prefix)

        spy.testProcessing = assert_product_view

        # Existing product
        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products/%s' % (self.default_product,)

        real_prefix = self.default_product
        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ''
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_missing_product(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        mps = MultiProductSystem(self.global_env)
        def assert_product_list(req, template, data, content_type):
            self.assertEquals('product_list.html', template)
            self.assertIs(None, content_type)
            self.assertEquals([mps.default_product_prefix,
                               self.default_product],
                              [p.prefix for p in data.get('products')])
            self.assertTrue('context' in data)
            ctx = data['context']
            self.assertEquals('product', ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_list

        # Missing product
        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products/missing'

        self.expectedPrefix = 'missing'
        self.expectedPathInfo = ''
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)
        self.assertEqual(1, len(req.chrome['warnings']))
        self.assertEqual('Product missing not found',
                         req.chrome['warnings'][0].unescape())

    def test_product_edit(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        # HTTP GET
        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products/%s' % (self.default_product,)
        req.environ['QUERY_STRING'] = 'action=edit'

        real_prefix = self.default_product

        def assert_product_edit(req, template, data, content_type):
            self.assertEquals('product_edit.html', template)
            self.assertIs(None, content_type)
            self.assertFalse('products' in data)

            self.assertTrue('context' in data)
            ctx = data['context']
            self.assertEquals('product', ctx.resource.realm)
            self.assertEquals(real_prefix, ctx.resource.id)

            self.assertTrue('product' in data)
            self.assertEquals(real_prefix, data['product'].prefix)

        spy.testProcessing = assert_product_edit

        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ''
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

        # HTTP POST
        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['REQUEST_METHOD'] = 'POST'
        req.environ['PATH_INFO'] = '/products/%s' % (self.default_product,)
        req.args = dict(action='edit', description='New description',
                        prefix=self.default_product,
                        name=self.env.product.name)

        spy.testProcessing = assert_product_edit

        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ''
        self.record_response = True
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

        try:
            product = Product(self.global_env,
                              {'prefix' : self.env.product.prefix})
        except ResourceNotFound:
            self.fail('Default test product deleted ?')
        else:
            self.assertEquals('New description', product.description)

        product_url = Href(req.base_path).products(self.default_product)
        self.assertRedirect(req, product_url)

    def test_product_delete(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = 'testuser'
        req.environ['PATH_INFO'] = '/products/%s' % (self.default_product,)
        req.environ['QUERY_STRING'] = 'action=delete'
        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ''
        spy.testProcessing = lambda *args, **kwargs: None

        with self.assertRaises(HTTPInternalError) as test_cm:
            self._dispatch(req, self.global_env)

        self.assertEqual('500 Trac Error (Product removal is not allowed!)',
                         unicode(test_cm.exception))
Example #49
0
 def setUp(self, options=()):
     self.env_path = tempfile.mkdtemp('multiproduct-tempenv')
     self.env = Environment(self.env_path, create=True, options=options)
     DummyPlugin.version = 1
Example #50
0
class ProductModuleTestCase(RequestHandlerTestCase):
    def setUp(self):
        self._mp_setup()
        self.global_env = self.env
        self.env = ProductEnvironment(self.global_env, self.default_product)

        self.global_env.enable_component(TestRequestSpy)
        self.env.enable_component(TestRequestSpy)
        TestRequestSpy(self.global_env).testMatch = self._assert_product_match
        PermissionSystem(self.global_env).grant_permission("testuser", "PRODUCT_CREATE")
        PermissionSystem(self.global_env).grant_permission("testuser", "PRODUCT_VIEW")
        PermissionSystem(self.global_env).grant_permission("testuser", "PRODUCT_MODIFY")

    def tearDown(self):
        self.global_env.reset_db()
        self.env = self.global_env = None

    expectedPrefix = None
    expectedPathInfo = None

    def _assert_product_match(self, req, handler):
        self.assertIs(ProductModule(self.global_env), handler)
        self.assertEqual(self.expectedPrefix, req.args["productid"], "Unexpected product prefix")
        self.assertEqual(self.expectedPathInfo, req.args["pathinfo"], "Unexpected sub path")

    def test_product_list(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products"

        mps = MultiProductSystem(self.global_env)

        def assert_product_list(req, template, data, content_type):
            self.assertEquals("product_list.html", template)
            self.assertIs(None, content_type)
            self.assertEquals(
                [mps.default_product_prefix, self.default_product], [p.prefix for p in data.get("products")]
            )
            self.assertTrue("context" in data)
            ctx = data["context"]
            self.assertEquals("product", ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_list
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_product_new(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products"
        req.environ["QUERY_STRING"] = "action=new"

        def assert_product_new(req, template, data, content_type):
            self.assertEquals("product_edit.html", template)
            self.assertIs(None, content_type)
            self.assertFalse("products" in data)
            self.assertTrue("context" in data)
            ctx = data["context"]
            self.assertEquals("product", ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_new
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_product_view(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        def assert_product_view(req, template, data, content_type):
            self.assertEquals("product_view.html", template)
            self.assertIs(None, content_type)
            self.assertFalse("products" in data)

            self.assertTrue("context" in data)
            ctx = data["context"]
            self.assertEquals("product", ctx.resource.realm)
            self.assertEquals(real_prefix, ctx.resource.id)

            self.assertTrue("product" in data)
            self.assertEquals(real_prefix, data["product"].prefix)

        spy.testProcessing = assert_product_view

        # Existing product
        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products/%s" % (self.default_product,)

        real_prefix = self.default_product
        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ""
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

    def test_missing_product(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        mps = MultiProductSystem(self.global_env)

        def assert_product_list(req, template, data, content_type):
            self.assertEquals("product_list.html", template)
            self.assertIs(None, content_type)
            self.assertEquals(
                [mps.default_product_prefix, self.default_product], [p.prefix for p in data.get("products")]
            )
            self.assertTrue("context" in data)
            ctx = data["context"]
            self.assertEquals("product", ctx.resource.realm)
            self.assertEquals(None, ctx.resource.id)

        spy.testProcessing = assert_product_list

        # Missing product
        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products/missing"

        self.expectedPrefix = "missing"
        self.expectedPathInfo = ""
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)
        self.assertEqual(1, len(req.chrome["warnings"]))
        self.assertEqual("Product missing not found", req.chrome["warnings"][0].unescape())

    def test_product_edit(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        # HTTP GET
        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products/%s" % (self.default_product,)
        req.environ["QUERY_STRING"] = "action=edit"

        real_prefix = self.default_product

        def assert_product_edit(req, template, data, content_type):
            self.assertEquals("product_edit.html", template)
            self.assertIs(None, content_type)
            self.assertFalse("products" in data)

            self.assertTrue("context" in data)
            ctx = data["context"]
            self.assertEquals("product", ctx.resource.realm)
            self.assertEquals(real_prefix, ctx.resource.id)

            self.assertTrue("product" in data)
            self.assertEquals(real_prefix, data["product"].prefix)

        spy.testProcessing = assert_product_edit

        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ""
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

        # HTTP POST
        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["REQUEST_METHOD"] = "POST"
        req.environ["PATH_INFO"] = "/products/%s" % (self.default_product,)
        req.args = dict(
            action="edit", description="New description", prefix=self.default_product, name=self.env.product.name
        )

        spy.testProcessing = assert_product_edit

        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ""
        self.record_response = True
        with self.assertRaises(RequestDone):
            self._dispatch(req, self.global_env)

        try:
            product = Product(self.global_env, {"prefix": self.env.product.prefix})
        except ResourceNotFound:
            self.fail("Default test product deleted ?")
        else:
            self.assertEquals("New description", product.description)

        product_url = Href(req.base_path).products(self.default_product)
        self.assertRedirect(req, product_url)

    def test_product_delete(self):
        spy = self.global_env[TestRequestSpy]
        self.assertIsNot(None, spy)

        req = self._get_request_obj(self.global_env)
        req.authname = "testuser"
        req.environ["PATH_INFO"] = "/products/%s" % (self.default_product,)
        req.environ["QUERY_STRING"] = "action=delete"
        self.expectedPrefix = self.default_product
        self.expectedPathInfo = ""
        spy.testProcessing = lambda *args, **kwargs: None

        with self.assertRaises(HTTPInternalError) as test_cm:
            self._dispatch(req, self.global_env)

        self.assertEqual("500 Trac Error (Product removal is not allowed!)", unicode(test_cm.exception))
Example #51
0
 def _update_config(self, section, key, value):
     self.env.config.set(section, key, value)
     self.env.config.save()
     self.env = Environment(self.env.path)
Example #52
0
 def setUp(self, options=()):
     env_path = tempfile.mkdtemp(prefix='bh-product-tempenv-')
     self.env = Environment(env_path, create=True, options=options)
     DummyPlugin.version = 1
Example #53
0
 def admin_url(prefix):
     env = ProductEnvironment.lookup_env(self.env, prefix)
     href = ProductEnvironment.resolve_href(env, self.env)
     return href.admin()
Example #54
0
 def product(self, prefix):
     old_env = self.env
     self.env = ProductEnvironment(self.env, prefix)
     yield
     self.env = old_env