def abs_href(self): """The application URL""" if not self.base_url: urlpattern = MultiProductSystem(self.parent).product_base_url if not urlpattern: self.log.warn("product_base_url option not set in " "configuration, generated links may be " "incorrect") urlpattern = 'products/$(prefix)s' envname = os.path.basename(self.parent.path) prefix = unicode_quote(self.product.prefix, safe="") name = unicode_quote(self.product.name, safe="") url = urlpattern.replace('$(', '%(') \ .replace('%(envname)s', envname) \ .replace('%(prefix)s', prefix) \ .replace('%(name)s', name) if urlsplit(url).netloc: # Absolute URLs _abs_href = Href(url) else: # Relative URLs parent_href = Href(self.parent.abs_href(), path_safe="/!~*'()%", query_safe="!~*'()%") _abs_href = Href(parent_href(url)) else: _abs_href = Href(self.base_url) return _abs_href
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 _setup_multiproduct(self): try: MultiProductSystem(self.env)\ .upgrade_environment(self.env.db_transaction) except self.env.db_exc.OperationalError: # table remains but content is deleted self._add_products('@') self.env.enable_multiproduct_schema()
def _upgrade_mp(cls, env): r"""Apply multi product upgrades """ # Do not break wiki parser ( see #373 ) env.disable_component(TicketModule) env.disable_component(ReportModule) mpsystem = MultiProductSystem(env) try: mpsystem.upgrade_environment(env.db_transaction) except OperationalError: # Database is upgraded, but database version was deleted. # Complete the upgrade by inserting default product. mpsystem._insert_default_product(env.db_transaction) # assume that the database schema has been upgraded, enable # multi-product schema support in environment env.enable_multiproduct_schema(True)
def _default_product(self, envname=None): """Default product configured for a given environment @raise LookupError: if no environment matching `envname` can be found """ if envname not in ('trac', None): raise LookupError('Unable to open environment ' + envname) env = self.get_trac_environment() return MultiProductSystem(env).default_product_prefix
def setup_config(self): """Load the configuration object. """ import trac.config parent_path = MultiProductSystem(self.parent).product_config_parent if parent_path and os.path.isfile(parent_path): parents = [trac.config.Configuration(parent_path)] else: parents = [self.parent.config] self.config = Configuration(self.parent, self.product.prefix, parents) self.setup_log()
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 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 open_environment(self, environ, env_path, global_env, use_cache=False): environ.setdefault('SCRIPT_NAME', '') # bh:ticket:594 env = pid = None path_info = environ.get('PATH_INFO') if not path_info: return env m = PRODUCT_RE.match(path_info) if m: pid = m.group('pid') def create_product_env(product_prefix, script_name, path_info): if not global_env._abs_href: # make sure global environment absolute href is set before # instantiating product environment. This would normally # happen from within trac.web.main.dispatch_request req = RequestWithSession(environ, None) global_env._abs_href = req.abs_href try: env = multiproduct.env.ProductEnvironment( global_env, product_prefix) except LookupError: # bh:ticket:561 - Display product list and warning message env = global_env else: # shift WSGI environment to the left environ['SCRIPT_NAME'] = script_name environ['PATH_INFO'] = path_info return env if pid: env = create_product_env( pid, environ['SCRIPT_NAME'] + '/products/' + pid, m.group('pathinfo') or '') else: redirect = REDIRECT_DEFAULT_RE.match(path_info) if redirect: from multiproduct.api import MultiProductSystem default_product_prefix = \ MultiProductSystem(global_env).default_product_prefix env = create_product_env(default_product_prefix, environ['SCRIPT_NAME'], environ['PATH_INFO']) return env
def _upgrade_mp(cls, env): r"""Apply multi product upgrades """ # Do not break wiki parser ( see #373 ) EnvironmentStub.disable_component_in_config(env, TicketModule) EnvironmentStub.disable_component_in_config(env, ReportModule) mpsystem = MultiProductSystem(env) with env.db_transaction as db: try: mpsystem.upgrade_environment(db) except env.db_exc.OperationalError: # Database is upgraded, but database version was deleted. # Complete the upgrade by inserting default product. mpsystem._insert_default_product(db) finally: # Ensure that multiproduct DB version is set to latest value mpsystem._update_db_version(db, DB_VERSION) # assume that the database schema has been upgraded, enable # multi-product schema support in environment env.enable_multiproduct_schema(True)
def setUp(self): self.env = EnvironmentStub(enable=['trac.*', 'multiproduct.*']) self.env.path = tempfile.mkdtemp('bh-product-tempenv') self.mpsystem = MultiProductSystem(self.env) try: self.mpsystem.upgrade_environment(self.env.db_transaction) except 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.product = Product(self.env) self.product._data.update(self.default_data) self.product.insert()
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_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_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))
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)
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)