Example #1
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 #2
0
    def process_request(self, req):
        """process request handler"""

        req.perm.require('PRODUCT_VIEW')
        pid = req.args.get('productid', None)
        action = req.args.get('action', 'view')

        products = [
            p for p in Product.select(self.env)
            if 'PRODUCT_VIEW' in req.perm(p.resource)
        ]

        if pid is not None:
            add_link(req, 'up', req.href.products(), _('Products'))

        try:
            product = Product(self.env, {'prefix': pid})
        except ResourceNotFound:
            product = Product(self.env)

        data = {'product': product}

        if req.method == 'POST':
            if req.args.has_key('cancel'):
                req.redirect(req.href.products(product.prefix))
            elif action == 'edit':
                return self._do_save(req, product)
            elif action == 'delete':
                req.perm(product.resource).require('PRODUCT_DELETE')
                retarget_to = req.args.get('retarget', None)
                name = product.name
                product.delete(resources_to=retarget_to)
                add_notice(req,
                           _('The product "%(n)s" has been deleted.', n=name))
                req.redirect(req.href.products())
        elif action in ('new', 'edit'):
            return self._render_editor(req, product)
        elif action == 'delete':
            req.perm(product.resource).require('PRODUCT_DELETE')
            return 'product_delete.html', data, None

        if pid is None:
            data = {'products': products}
            return 'product_list.html', data, None

        def add_product_link(rel, product):
            href = req.href.products(product.prefix)
            add_link(req, rel, href, _('Product "%(name)s"',
                                       name=product.name))

        idx = [i for i, p in enumerate(products) if p.name == product.name]
        if idx:
            idx = idx[0]
            if idx > 0:
                add_product_link('first', products[0])
                add_product_link('prev', products[idx - 1])
            if idx < len(products) - 1:
                add_product_link('next', products[idx + 1])
                add_product_link('last', products[-1])
        prevnext_nav(req, _('Previous Product'), _('Next Product'),
                     _('Back to Product List'))
        return 'product_view.html', data, None
Example #3
0
 def process_request(self, req):
     """process request handler"""
     
     req.perm.require('PRODUCT_VIEW')
     pid = req.args.get('productid', None)
     if pid:
         req.perm('product', pid).require('PRODUCT_VIEW')
     action = req.args.get('action', 'view')
     
     products = [p for p in Product.select(self.env)
                 if 'PRODUCT_VIEW' in req.perm(p.resource)]
     
     if pid is not None:
         add_link(req, 'up', req.href.products(), _('Products'))
     
     try:
         product = Product(self.env, {'prefix': pid})
     except ResourceNotFound:
         product = Product(self.env)
     
     data = {'product': product, 
             'context': web_context(req, product.resource)}
     
     if req.method == 'POST':
         if req.args.has_key('cancel'):
             req.redirect(req.href.products(product.prefix))
         elif action == 'edit':
             return self._do_save(req, product)
         elif action == 'delete':
             req.perm(product.resource).require('PRODUCT_DELETE')
             retarget_to = req.args.get('retarget', None)
             name = product.name
             product.delete(resources_to=retarget_to)
             add_notice(req, _('The product "%(n)s" has been deleted.',
                               n = name))
             req.redirect(req.href.products())
     elif action in ('new', 'edit'):
         return self._render_editor(req, product)
     elif action == 'delete':
         req.perm(product.resource).require('PRODUCT_DELETE')
         return 'product_delete.html', data, None
     
     if pid is None:
         data = {'products': products,
                 'context': web_context(req, Resource('products', None))}
         return 'product_list.html', data, None
     
     def add_product_link(rel, product):
         href = req.href.products(product.prefix)
         add_link(req, rel, href, _('Product "%(name)s"',
                                    name=product.name))
     
     idx = [i for i, p in enumerate(products) if p.name == product.name]
     if idx:
         idx = idx[0]
         if idx > 0:
             add_product_link('first', products[0])
             add_product_link('prev', products[idx - 1])
         if idx < len(products) - 1:
             add_product_link('next', products[idx + 1])
             add_product_link('last', products[-1])        
     prevnext_nav(req, _('Previous Product'), _('Next Product'),
                  _('Back to Product List'))
     return 'product_view.html', data, None
Example #4
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)