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)
def _render_admin_panel(self, req, cat, page, product): req.perm.require('PRODUCT_VIEW') name = req.args.get('name') description = req.args.get('description','') prefix = req.args.get('prefix') if product is None else product owner = req.args.get('owner') keys = {'prefix':prefix} field_data = {'name':name, 'description':description, 'owner':owner, } data = {} # Detail view? if product: prod = Product(self.env, keys) if req.method == 'POST': if req.args.get('save'): req.perm.require('PRODUCT_MODIFY') prod.update_field_dict(field_data) prod.update(req.authname) add_notice(req, _('Your changes have been saved.')) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) Chrome(self.env).add_wiki_toolbars(req) data = {'view': 'detail', 'product': prod} else: default = self.config.get('ticket', 'default_product') if req.method == 'POST': # Add Product if req.args.get('add'): req.perm.require('PRODUCT_CREATE') if not (prefix and name and owner): if not prefix: add_warning(req, _("You must provide a prefix " "for the product.")) if not name: add_warning(req, _("You must provide a name " "for the product.")) if not owner: add_warning(req, _("You must provide an owner " "for the product.")) data['prefix'] = prefix data['name'] = name data['owner'] = owner else: try: prod = Product(self.env, keys) except ResourceNotFound: prod = Product(self.env) prod.update_field_dict(keys) prod.update_field_dict(field_data) prod.insert() add_notice(req, _('The product "%(id)s" has been ' 'added.', id=prefix)) req.redirect(req.href.admin(cat, page)) else: if prod.prefix is None: raise TracError(_('Invalid product id.')) raise TracError(_("Product %(id)s already exists.", id=prefix)) # Remove product elif req.args.get('remove'): raise TracError(_('Product removal is not allowed!')) # Set default product elif req.args.get('apply'): prefix = req.args.get('default') if prefix and prefix != default: self.log.info("Setting default product to %s", prefix) self.config.set('ticket', 'default_product', prefix) _save_config(self.config, req, self.log) req.redirect(req.href.admin(cat, page)) # Clear default product elif req.args.get('clear'): self.log.info("Clearing default product") self.config.set('ticket', 'default_product', '') _save_config(self.config, req, self.log) req.redirect(req.href.admin(cat, page)) data['view'] = 'list' data['products'] = Product.select(self.env) data['default'] = default if self.config.getbool('ticket', 'restrict_owner'): perm = PermissionSystem(self.env) def valid_owner(username): return perm.get_user_permissions(username).get('TICKET_MODIFY') data['owners'] = [username for username, name, email in self.env.get_known_users() if valid_owner(username)] data['owners'].insert(0, '') data['owners'].sort() else: data['owners'] = None return 'admin_products.html', data
def _render_admin_panel(self, req, cat, page, product): req.perm.require('PRODUCT_VIEW') name = req.args.get('name') description = req.args.get('description', '') prefix = req.args.get('prefix') if product is None else product owner = req.args.get('owner') keys = {'prefix': prefix} field_data = { 'name': name, 'description': description, 'owner': owner, } # Detail view? if product: prod = Product(self.env, keys) if req.method == 'POST': if req.args.get('save'): req.perm.require('PRODUCT_MODIFY') prod.update_field_dict(field_data) prod.update() add_notice(req, _('Your changes have been saved.')) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) Chrome(self.env).add_wiki_toolbars(req) data = {'view': 'detail', 'product': prod} else: default = self.config.get('ticket', 'default_product') if req.method == 'POST': # Add Product if req.args.get('add') and req.args.get('prefix'): req.perm.require('PRODUCT_CREATE') if not owner: add_warning(req, _('All fields are required!')) req.redirect(req.href.admin(cat, page)) try: prod = Product(self.env, keys) except ResourceNotFound: prod = Product(self.env) prod.update_field_dict(keys) prod.update_field_dict(field_data) prod.insert() add_notice( req, _('The product "%(id)s" has been added.', id=prefix)) req.redirect(req.href.admin(cat, page)) else: if prod.prefix is None: raise TracError(_('Invalid product id.')) raise TracError( _("Product %(id)s already exists.", id=prefix)) # Remove product elif req.args.get('remove'): raise TracError(_('Product removal is not allowed!')) # Set default product elif req.args.get('apply'): prefix = req.args.get('default') if prefix and prefix != default: self.log.info("Setting default product to %s", prefix) self.config.set('ticket', 'default_product', prefix) _save_config(self.config, req, self.log) req.redirect(req.href.admin(cat, page)) products = Product.select(self.env) data = {'view': 'list', 'products': products, 'default': default} if self.config.getbool('ticket', 'restrict_owner'): perm = PermissionSystem(self.env) def valid_owner(username): return perm.get_user_permissions(username).get('TICKET_MODIFY') data['owners'] = [ username for username, name, email in self.env.get_known_users() if valid_owner(username) ] data['owners'].insert(0, '') data['owners'].sort() else: data['owners'] = None return 'admin_products.html', data
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)