The test is actually only correct for the number of permissions
associated with user "x".
Test deletes all default permissions and creates a custom set for the
user x. Upgrading multiproduct to version 2+ was supposed to copy
existing permissions for each of the created products. Since r1489442,
upgrade also creates PRODUCT_VIEW for anonymous and authenticated
users to all products, even if they had no permissions before the
upgrade.
If this is the desired behaviour, I would fix the test to only count
permissions of user x:
Index: bloodhound_multiproduct/tests/upgrade.py
===================================================================
--- bloodhound_multiproduct/tests/upgrade.py (revision 1503125)
+++ bloodhound_multiproduct/tests/upgrade.py (working copy)
@@ -190,7 +190,7 @@
# Permissions also hold rows for global product.
- rows = db("SELECT * FROM %s" % table)
+ rows = db("SELECT * FROM %s WHERE username='x'" % table)
self.assertEqual(
On Tue, Jul 16, 2013 at 8:44 AM, Ryan Ollos <[email protected]> wrote:
> On Fri, Apr 26, 2013 at 2:10 AM, <[email protected]> wrote:
>
>> Author: astaric
>> Date: Fri Apr 26 09:10:14 2013
>> New Revision: 1476118
>>
>> URL: http://svn.apache.org/r1476118
>> Log:
>> Added more tests for multiproduct upgrade, refactoring.
>>
>> Modified:
>> bloodhound/trunk/bloodhound_multiproduct/multiproduct/api.py
>> bloodhound/trunk/bloodhound_multiproduct/tests/upgrade.py
>>
>> Modified: bloodhound/trunk/bloodhound_multiproduct/multiproduct/api.py
>> URL:
>> http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/api.py?rev=1476118&r1=1476117&r2=1476118&view=diff
>>
>> ==============================================================================
>> --- bloodhound/trunk/bloodhound_multiproduct/multiproduct/api.py (original)
>> +++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/api.py Fri Apr
>> 26 09:10:14 2013
>> @@ -30,6 +30,7 @@ from trac.attachment import Attachment
>> from trac.config import Option, PathOption
>> from trac.core import Component, TracError, implements, Interface
>> from trac.db import Table, Column, DatabaseManager, Index
>> +import trac.db_default
>> from trac.env import IEnvironmentSetupParticipant, Environment
>> from trac.perm import IPermissionRequestor, PermissionCache
>> from trac.resource import IExternalResourceConnector,
>> IResourceChangeListener,\
>> @@ -97,7 +98,7 @@ class MultiProductSystem(Component):
>> global environment configuration.
>> """)
>>
>> - SCHEMA = [mcls._get_schema() \
>> + SCHEMA = [mcls._get_schema()
>> for mcls in (Product, ProductResourceMap)]
>>
>> # Tables which should be migrated (extended with 'product' column)
>> @@ -202,171 +203,207 @@ class MultiProductSystem(Component):
>> db_installed_version = self.get_version()
>> with self.env.db_direct_transaction as db:
>> if db_installed_version < 1:
>> - # Initial installation
>> - db("ALTER TABLE ticket ADD COLUMN product TEXT")
>> - self.log.debug("creating initial db tables for %s
>> plugin." %
>> - PLUGIN_NAME)
>> - db_connector, dummy =
>> DatabaseManager(self.env)._get_connector()
>> - for table in self.SCHEMA:
>> - for statement in db_connector.to_sql(table):
>> - db(statement)
>> + self._add_column_product_to_ticket(db)
>> + self._create_multiproduct_tables(db)
>> db_installed_version = self._update_db_version(db, 1)
>>
>> if db_installed_version < 2:
>> - from multiproduct.model import Product
>> - products = Product.select(self.env)
>> - for prod in products:
>> - db("""UPDATE ticket SET product=%s
>> - WHERE product=%s""", (prod.prefix, prod.name))
>> + self._replace_product_on_ticket_with_product_prefix(db)
>> db_installed_version = self._update_db_version(db, 2)
>>
>> if db_installed_version < 3:
>> - from multiproduct.model import Product
>> - import trac.db_default
>> -
>> - def create_temp_table(table):
>> - """creates temporary table with the new schema and
>> - drops original table"""
>> - table_temp_name = '%s_temp' % table
>> - if table == 'report':
>> - cols = ','.join([c for c in table_columns[table]
>> if c != 'id'])
>> - else:
>> - cols = ','.join(table_columns[table])
>> - self.log.info("Migrating table '%s' to a new
>> schema", table)
>> - db("""CREATE TABLE %s AS SELECT %s FROM %s""" %
>> - (table_temp_name, cols, table))
>> - db("""DROP TABLE %s""" % table)
>> - db_connector, _ =
>> DatabaseManager(self.env)._get_connector()
>> - table_schema = [t for t in table_defs if t.name ==
>> table][0]
>> - for sql in db_connector.to_sql(table_schema):
>> - db(sql)
>> - return table_temp_name, cols
>> -
>> - def drop_temp_table(table):
>> - """drops specified temporary table"""
>> - db("""DROP TABLE %s""" % table)
>> -
>> - TICKET_TABLES = ['ticket_change', 'ticket_custom',
>> - 'attachment',
>> - ]
>> SYSTEM_TABLES = ['system']
>> + TICKET_TABLES = [
>> + 'ticket_change', 'ticket_custom', 'attachment',
>> + ]
>> + table_defs = self._add_product_column_to_tables(
>> + self.MIGRATE_TABLES + TICKET_TABLES + SYSTEM_TABLES,
>> + db_installed_version)
>> + table_columns = self._get_table_columns(table_defs)
>> + create_temp_table = lambda table: self._create_temp_table(
>> + db, table, table_columns, table_defs)
>> +
>> + self._insert_default_product(db)
>> + self._upgrade_tickets(db, TICKET_TABLES,
>> create_temp_table)
>> + self._upgrade_wikis(db, create_temp_table, table_columns)
>> + self._upgrade_system_tables(db, create_temp_table)
>> + self._soft_link_repositories_to_default_product(db)
>> + self._upgrade_table_system(SYSTEM_TABLES,
>> create_temp_table, db)
>> + self._enable_multiproduct_hooks()
>>
>> - # extend trac default schema by adding product column and
>> extending key with product
>> - table_defs = [copy.deepcopy(t) for t in
>> trac.db_default.schema
>> - if t.name in
>> self.MIGRATE_TABLES + TICKET_TABLES + SYSTEM_TABLES]
>> - for t in table_defs:
>> - t.columns.append(Column('product'))
>> - if isinstance(t.key, list):
>> - t.key = tuple(t.key) + tuple(['product'])
>> - elif isinstance(t.key, tuple):
>> - t.key = t.key + tuple(['product'])
>> - else:
>> - raise TracError("Invalid table '%s' schema key
>> '%s' while upgrading "
>> - "plugin '%s' from version %d to
>> %d'" %
>> - (t.name, t.key, PLUGIN_NAME,
>> db_installed_version, 3))
>> - table_columns = dict()
>> - for table in table_defs:
>> - table_columns[table.name] = [c for c in [column.namefor
>> column in
>> - [t for t
>> in table_defs if t.name == table.name][0].columns]
>> - if c
>> != 'product']
>> - # create default product
>> - self.log.info("Creating default product")
>> - db("""INSERT INTO bloodhound_product (prefix, name,
>> description, owner)
>> - VALUES ('%s', '%s', '%s', '')""" %
>> - (DEFAULT_PRODUCT, 'Default', 'Default product'))
>> -
>> - # fetch all products
>> - all_products = Product.select(self.env)
>> -
>> - # migrate tickets that don't have product assigned to
>> default product
>> - # - update ticket table product column
>> - # - update ticket related tables by:
>> - # - upgrading schema
>> - # - update product column to match ticket's product
>> - self.log.info("Migrating tickets w/o product to default
>> product")
>> - db("""UPDATE ticket SET product='%s'
>> - WHERE (product IS NULL OR product='')""" %
>> DEFAULT_PRODUCT)
>> - self._migrate_attachments(
>> - db("""SELECT a.type, a.id, a.filename
>> + db_installed_version = self._update_db_version(db, 3)
>> +
>> + if db_installed_version < 4:
>> + self._create_product_tables_for_plugins(db)
>> + db_installed_version = self._update_db_version(db, 4)
>> +
>> + self.env.enable_multiproduct_schema(True)
>> +
>> + def _add_column_product_to_ticket(self, db):
>> + self.log.debug("Adding field product to ticket table")
>> + db("ALTER TABLE ticket ADD COLUMN product TEXT")
>> +
>> + def _create_multiproduct_tables(self, db):
>> + self.log.debug("Creating initial db tables for %s plugin." %
>> + PLUGIN_NAME)
>> + db_connector, dummy = DatabaseManager(self.env)._get_connector()
>> + for table in self.SCHEMA:
>> + for statement in db_connector.to_sql(table):
>> + db(statement)
>> +
>> + def _replace_product_on_ticket_with_product_prefix(self, db):
>> + for prod in Product.select(self.env):
>> + db("""UPDATE ticket SET product=%s
>> + WHERE product=%s""", (prod.prefix, prod.name))
>> +
>> + def _create_temp_table(self, db, table, table_columns, table_defs):
>> + """creates temporary table with the new schema and
>> + drops original table"""
>> + table_temp_name = '%s_temp' % table
>> + if table == 'report':
>> + cols = ','.join([c for c in table_columns[table] if c !=
>> 'id'])
>> + else:
>> + cols = ','.join(table_columns[table])
>> + self.log.info("Migrating table '%s' to a new schema", table)
>> + db("""CREATE TABLE %s AS SELECT %s FROM %s""" %
>> + (table_temp_name, cols, table))
>> + db("""DROP TABLE %s""" % table)
>> + db_connector, _ = DatabaseManager(self.env)._get_connector()
>> + table_schema = [t for t in table_defs if t.name == table][0]
>> + for sql in db_connector.to_sql(table_schema):
>> + db(sql)
>> + return table_temp_name, cols
>> +
>> + def _drop_temp_table(self, db, table):
>> + db("""DROP TABLE %s""" % table)
>> +
>> + def _add_product_column_to_tables(self, tables, current_version):
>> + """Extend trac default schema by adding product column
>> + and extending key with product.
>> + """
>> + table_defs = [copy.deepcopy(t) for t in trac.db_default.schema
>> + if
>> + t.name in tables]
>> + for t in table_defs:
>> + t.columns.append(Column('product'))
>> + if isinstance(t.key, list):
>> + t.key = tuple(t.key) + tuple(['product'])
>> + elif isinstance(t.key, tuple):
>> + t.key = t.key + tuple(['product'])
>> + else:
>> + raise TracError(
>> + "Invalid table '%s' schema key '%s' while upgrading "
>> + "plugin '%s' from version %d to %d'" %
>> + (t.name, t.key, PLUGIN_NAME, current_version, 3))
>> + return table_defs
>> +
>> + def _get_table_columns(self, table_defs):
>> + table_columns = dict()
>> + for table in table_defs:
>> + table_definition = \
>> + [t for t in table_defs if t.name == table.name][0]
>> + column_names = \
>> + [column.name for column in table_definition.columns]
>> + table_columns[table.name] = \
>> + [c for c in column_names if c != 'product']
>> + return table_columns
>> +
>> + def _insert_default_product(self, db):
>> + self.log.info("Creating default product")
>> + db("""INSERT INTO bloodhound_product (prefix, name, description,
>> owner)
>> + VALUES ('%s', '%s', '%s', '')
>> + """ % (DEFAULT_PRODUCT, 'Default', 'Default product'))
>> +
>> + def _upgrade_tickets(self, db, TICKET_TABLES, create_temp_table):
>> + # migrate tickets that don't have product assigned to default
>> product
>> + # - update ticket table product column
>> + # - update ticket related tables by:
>> + # - upgrading schema
>> + # - update product column to match ticket's product
>> + self.log.info("Migrating tickets w/o product to default product")
>> + db("""UPDATE ticket SET product='%s'
>> + WHERE (product IS NULL OR product='')
>> + """ % DEFAULT_PRODUCT)
>> + self._migrate_attachments(
>> + db("""SELECT a.type, a.id, a.filename
>> FROM attachment a
>> INNER JOIN ticket t ON a.id = %(t.id)s
>> WHERE a.type='ticket'
>> - """ % {'t.id': db.cast('t.id', 'text')}),
>> - to_product=DEFAULT_PRODUCT
>> - )
>> -
>> - self.log.info("Migrating ticket tables to a new schema")
>> - for table in TICKET_TABLES:
>> - temp_table_name, cols = create_temp_table(table)
>> - db("""INSERT INTO %s (%s, product)
>> + """ % {'t.id': db.cast('t.id', 'text')}),
>> + to_product=DEFAULT_PRODUCT
>> + )
>> + self.log.info("Migrating ticket tables to a new schema")
>> + for table in TICKET_TABLES:
>> + temp_table_name, cols = create_temp_table(table)
>> + db("""INSERT INTO %s (%s, product)
>> SELECT %s, '' FROM %s""" %
>> - (table, cols, cols, temp_table_name))
>> - drop_temp_table(temp_table_name)
>> -
>> - for table in TICKET_TABLES:
>> - if table == 'attachment':
>> - db("""
>> - UPDATE attachment
>> - SET product=(SELECT ticket.product
>> - FROM ticket
>> - WHERE %(ticket_id)s=
>> attachment.id
>> - LIMIT 1)
>> - WHERE attachment.type='ticket'
>> - AND EXISTS(SELECT ticket.product
>> - FROM ticket
>> - WHERE %(ticket_id)s=
>> attachment.id)
>> - """ % dict(
>> - ticket_id=db.cast('attachment.id', 'text')))
>> - else:
>> - db("""UPDATE %s
>> - SET product=(SELECT ticket.product FROM
>> ticket WHERE ticket.id=%s.ticket)""" %
>> - (table, table))
>> -
>> - # migrate system table (except wiki which is handled
>> separately) to a
>> - # new schema
>> - # - create tables with the new schema
>> - # - populate system tables with global configuration for
>> each product
>> - # - exception is permission table where permissions are
>> also populated in
>> - # global scope
>> - self.log.info("Migrating system tables to a new schema")
>> - for table in self.MIGRATE_TABLES:
>> - if table == 'wiki':
>> - continue
>> - temp_table_name, cols = create_temp_table(table)
>> - for product in all_products:
>> - self.log.info("Populating table '%s' for product
>> '%s' ('%s')",
>> - table, product.name,
>> product.prefix)
>> - db("""INSERT INTO %s (%s, product) SELECT %s,'%s'
>> FROM %s""" %
>> - (table, cols, cols, product.prefix,
>> temp_table_name))
>> - if table == 'permission':
>> - self.log.info("Populating table '%s' for global
>> scope", table)
>> - db("""INSERT INTO %s (%s, product) SELECT %s,'%s'
>> FROM %s""" %
>> - (table, cols, cols, '', temp_table_name))
>> - drop_temp_table(temp_table_name)
>> -
>> - # migrate wiki table
>> - # - populate system wikis to all products + global scope
>> - # - update wiki attachment product to match wiki product
>> - table = 'wiki'
>> - temp_table_name, cols = create_temp_table(table)
>> - self.log.info("Migrating wikis to global context")
>> - db("""INSERT INTO %s (%s, product) SELECT %s, '' FROM
>> %s""" %
>> - (table, cols, cols, temp_table_name))
>> + (table, cols, cols, temp_table_name))
>> + self._drop_temp_table(db, temp_table_name)
>> + if table == 'attachment':
>> db("""UPDATE attachment
>> - SET product=''
>> - WHERE attachment.type='wiki'""")
>> -
>> - for wiki_name, wiki_version, wiki_product in db("""
>> + SET product=(SELECT ticket.product
>> + FROM ticket
>> + WHERE %(ticket.id)s=attachment.id
>> + LIMIT 1)
>> + WHERE attachment.type='ticket'
>> + AND EXISTS(SELECT ticket.product
>> + FROM ticket
>> + WHERE %(ticket.id)s=attachment.id)
>> + """ % {'ticket.id': db.cast('ticket.id', 'text')})
>> + else:
>> + db("""UPDATE %(table)s
>> + SET product=(SELECT ticket.product
>> + FROM ticket
>> + WHERE ticket.id=%(table)s.ticket)
>> + """ % {'table': table})
>> +
>> + def _upgrade_system_tables(self, db, create_temp_table):
>> + # migrate system table (except wiki which is handled separately)
>> + # to a new schema
>> + # - create tables with the new schema
>> + # - populate system tables with global configuration for each
>> product
>> + # - exception is permission table where permissions
>> + # are also populated in global scope
>> + self.log.info("Migrating system tables to a new schema")
>> + for table in self.MIGRATE_TABLES:
>> + if table == 'wiki':
>> + continue
>> + temp_table_name, cols = create_temp_table(table)
>> + for product in Product.select(self.env):
>> + self.log.info("Populating table '%s' for product '%s'
>> ('%s')",
>> + table, product.name, product.prefix)
>> + db("""INSERT INTO %s (%s, product) SELECT %s,'%s' FROM
>> %s""" %
>> + (table, cols, cols, product.prefix, temp_table_name))
>> + if table == 'permission':
>> + self.log.info("Populating table '%s' for global scope",
>> table)
>> + db("""INSERT INTO %s (%s, product) SELECT %s,'%s' FROM
>> %s""" %
>> + (table, cols, cols, '', temp_table_name))
>> + self._drop_temp_table(db, temp_table_name)
>> +
>> + def _upgrade_wikis(self, db, create_temp_table,
>> + table_columns):
>> + # migrate wiki table
>> + # - populate system wikis to all products + global scope
>> + # - update wiki attachment product to match wiki product
>> + table = 'wiki'
>> + temp_table_name, cols = create_temp_table(table)
>> + self.log.info("Migrating wikis to global context")
>> + db("""INSERT INTO %s (%s, product) SELECT %s, '' FROM %s""" %
>> + (table, cols, cols, temp_table_name))
>> + db("""UPDATE attachment
>> + SET product=''
>> + WHERE attachment.type='wiki'""")
>> + for wiki_name, wiki_version, wiki_product in db("""
>> SELECT name, version, product FROM %s""" % table):
>> - attachment_cols =
>> ','.join(table_columns['attachment'])
>> - if wiki_name in self.system_wiki_list:
>> - for product in all_products:
>> - db("""INSERT INTO %s (%s, product)
>> + attachment_cols = ','.join(table_columns['attachment'])
>> + if wiki_name in self.system_wiki_list:
>> + for product in Product.select(self.env):
>> + db("""INSERT INTO %s (%s, product)
>> SELECT %s, '%s' FROM %s
>> WHERE name='%s' AND version=%s AND
>> product='%s'""" %
>> - (table, cols, cols, product.prefix,
>> table,
>> - wiki_name, wiki_version, wiki_product))
>> - db("""INSERT INTO attachment (%(cols)s,
>> product)
>> + (table, cols, cols, product.prefix, table,
>> + wiki_name, wiki_version, wiki_product))
>> + db("""INSERT INTO attachment (%(cols)s, product)
>> SELECT %(cols)s, '%(new_product)s'
>> FROM attachment a
>> WHERE type='wiki'
>> @@ -380,89 +417,40 @@ class MultiProductSystem(Component):
>> wiki_name=wiki_name,
>> old_product=wiki_product,
>> new_product=product.prefix))
>> - self._migrate_attachments(
>> - db("""SELECT type, id, filename
>> + self._migrate_attachments(
>> + db("""SELECT type, id, filename
>> FROM attachment
>> WHERE type='wiki'
>> AND id='%s'
>> AND product='%s'
>> """ % (wiki_name, DEFAULT_PRODUCT)),
>> - to_product=DEFAULT_PRODUCT,
>> - copy=True
>> - )
>> - else:
>> - self.log.info("Moving wiki page '%s' to default
>> product", wiki_name)
>> - db("""UPDATE wiki
>> + to_product=DEFAULT_PRODUCT,
>> + copy=True
>> + )
>> + else:
>> + self.log.info("Moving wiki page '%s' to default product",
>> + wiki_name)
>> + db("""UPDATE wiki
>> SET product='%s'
>> WHERE name='%s' AND version=%s AND
>> product='%s'
>> """ % (DEFAULT_PRODUCT,
>> wiki_name, wiki_version, wiki_product))
>> - db("""UPDATE attachment
>> + db("""UPDATE attachment
>> SET product='%s'
>> WHERE type='wiki'
>> AND id='%s'
>> AND product='%s'
>> """ % (DEFAULT_PRODUCT, wiki_name,
>> wiki_product))
>> - self._migrate_attachments(
>> - db("""SELECT type, id, filename
>> + self._migrate_attachments(
>> + db("""SELECT type, id, filename
>> FROM attachment
>> WHERE type='wiki'
>> AND id='%s'
>> AND product='%s'
>> """ % (wiki_name, DEFAULT_PRODUCT)),
>> - to_product=DEFAULT_PRODUCT,
>> - )
>> -
>> - drop_temp_table(temp_table_name)
>> -
>> - # soft link existing repositories to default product
>> - repositories_linked = []
>> - for id, name in db("""SELECT id, value FROM repository
>> - WHERE name='name'"""):
>> - if id in repositories_linked:
>> - continue
>> - db("""INSERT INTO repository (id, name, value)
>> - VALUES (%s, 'product', '%s')""" %
>> - (id, DEFAULT_PRODUCT))
>> - repositories_linked.append(id)
>> - self.log.info("Repository '%s' (%s) soft linked to
>> default product", name, id)
>> -
>> - # Update system tables
>> - # Upgrade schema
>> - self.log.info("Migrating system tables to a new schema")
>> - for table in SYSTEM_TABLES:
>> - temp_table_name, cols = create_temp_table(table)
>> - db("""INSERT INTO %s (%s, product)
>> - SELECT %s,'' FROM %s""" %
>> - (table, cols, cols, temp_table_name))
>> - drop_temp_table(temp_table_name)
>> -
>> - # enable multi product hooks in environment configuration
>> - import multiproduct.hooks
>> - import inspect
>> - config_update = False
>> - hook_path =
>> os.path.realpath(inspect.getsourcefile(multiproduct.hooks))
>> - if not 'environment_factory' in self.env.config['trac']:
>> - self.env.config['trac'].set('environment_factory',
>> hook_path)
>> - config_update = True
>> - if not 'request_factory' in self.env.config['trac']:
>> - self.env.config['trac'].set('request_factory',
>> hook_path)
>> - config_update = True
>> - if config_update:
>> - self.log.info("Enabling multi product hooks in
>> environment configuration")
>> - self.env.config.save()
>> -
>> - db_installed_version = self._update_db_version(db, 3)
>> -
>> - if db_installed_version < 4:
>> - self.log.debug("creating additional db tables for %s
>> plugin." %
>> - PLUGIN_NAME)
>> - db_connector, dummy =
>> DatabaseManager(self.env)._get_connector()
>> - for statement in
>> db_connector.to_sql(ProductSetting._get_schema()):
>> - db(statement)
>> - db_installed_version = self._update_db_version(db, 4)
>> -
>> - self.env.enable_multiproduct_schema(True)
>> + to_product=DEFAULT_PRODUCT,
>> + )
>> + self._drop_temp_table(db, temp_table_name)
>>
>> def _migrate_attachments(self, attachments, to_product=None,
>> copy=False):
>> for type, id, filename in attachments:
>> @@ -498,6 +486,56 @@ class MultiProductSystem(Component):
>> filename, type, id, str(err)
>> )
>>
>> + def _soft_link_repositories_to_default_product(self, db):
>> + # soft link existing repositories to default product
>> + repositories_linked = []
>> + for id, name in db("""SELECT id, value FROM repository
>> + WHERE name='name'"""):
>> + if id in repositories_linked:
>> + continue
>> + db("""INSERT INTO repository (id, name, value)
>> + VALUES (%s, 'product', '%s')""" %
>> + (id, DEFAULT_PRODUCT))
>> + repositories_linked.append(id)
>> + self.log.info("Repository '%s' (%s) soft linked to default
>> product",
>> + name, id)
>> +
>> + def _upgrade_table_system(self, SYSTEM_TABLES, create_temp_table, db):
>> + # Update system tables
>> + # Upgrade schema
>> + self.log.info("Migrating system tables to a new schema")
>> + for table in SYSTEM_TABLES:
>> + temp_table_name, cols = create_temp_table(table)
>> + db("""INSERT INTO %s (%s, product)
>> + SELECT %s,'' FROM %s""" %
>> + (table, cols, cols, temp_table_name))
>> + self._drop_temp_table(db, temp_table_name)
>> +
>> + def _enable_multiproduct_hooks(self):
>> + # enable multi product hooks in environment configuration
>> + import multiproduct.hooks
>> + import inspect
>> +
>> + config_update = False
>> + hook_path =
>> os.path.realpath(inspect.getsourcefile(multiproduct.hooks))
>> + if not 'environment_factory' in self.env.config['trac']:
>> + self.env.config['trac'].set('environment_factory', hook_path)
>> + config_update = True
>> + if not 'request_factory' in self.env.config['trac']:
>> + self.env.config['trac'].set('request_factory', hook_path)
>> + config_update = True
>> + if config_update:
>> + self.log.info(
>> + "Enabling multi product hooks in environment
>> configuration")
>> + self.env.config.save()
>> +
>> + def _create_product_tables_for_plugins(self, db):
>> + self.log.debug("creating additional db tables for %s plugin." %
>> + PLUGIN_NAME)
>> + db_connector, dummy = DatabaseManager(self.env)._get_connector()
>> + for statement in
>> db_connector.to_sql(ProductSetting._get_schema()):
>> + db(statement)
>> +
>> # IResourceChangeListener methods
>> def match_resource(self, resource):
>> return isinstance(resource, Product)
>>
>> Modified: bloodhound/trunk/bloodhound_multiproduct/tests/upgrade.py
>> URL:
>> http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/upgrade.py?rev=1476118&r1=1476117&r2=1476118&view=diff
>>
>> ==============================================================================
>> --- bloodhound/trunk/bloodhound_multiproduct/tests/upgrade.py (original)
>> +++ bloodhound/trunk/bloodhound_multiproduct/tests/upgrade.py Fri Apr 26
>> 09:10:14 2013
>> @@ -33,6 +33,7 @@ from trac.test import Environment
>> from trac.ticket import Ticket
>> from trac.wiki import WikiPage
>>
>> +from multiproduct.api import MultiProductSystem
>> from multiproduct.env import ProductEnvironment
>> from multiproduct.model import Product
>>
>> @@ -43,13 +44,9 @@ BLOODHOUND_TABLES = (
>> )
>>
>> TABLES_WITH_PRODUCT_FIELD = (
>> - 'component',
>> - 'milestone',
>> - 'version',
>> - 'enum',
>> - 'permission',
>> - 'wiki',
>> - 'report',
>> + 'ticket', 'ticket_change', 'ticket_custom', 'attachment', 'component',
>> + 'milestone', 'wiki', 'report',
>> + 'version', 'enum', 'permission', 'system',
>> )
>>
>>
>> @@ -57,12 +54,12 @@ class EnvironmentUpgradeTestCase(unittes
>> def setUp(self):
>> self.env_path = tempfile.mkdtemp('multiproduct-tempenv')
>> self.env = Environment(self.env_path, create=True)
>> - self.enabled_components = []
>> DummyPlugin.version = 1
>>
>> - def test_upgrade_environment(self):
>> + def test_can_upgrade_environment_with_multi_product_disabled(self):
>> self.env.upgrade()
>>
>> + # Multiproduct was not enabled so multiproduct tables should not
>> exist
>> with self.env.db_direct_transaction as db:
>> for table in BLOODHOUND_TABLES:
>> with self.assertFailsWithMissingTable():
>> @@ -72,7 +69,7 @@ class EnvironmentUpgradeTestCase(unittes
>> with self.assertFailsWithMissingColumn():
>> db("SELECT product FROM %s" % table)
>>
>> - def test_upgrade_environment_to_multiproduct(self):
>> + def
>> test_upgrade_creates_multi_product_tables_and_adds_product_column(self):
>> self._enable_multiproduct()
>> self.env.upgrade()
>>
>> @@ -83,89 +80,38 @@ class EnvironmentUpgradeTestCase(unittes
>> for table in TABLES_WITH_PRODUCT_FIELD:
>> db("SELECT product FROM %s" % table)
>>
>> - 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):
>> + def test_upgrade_creates_default_product(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" """)
>> + products = Product.select(self.env)
>> + self.assertEqual(len(products), 1)
>>
>> - 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):
>> - 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_upgrade_moves_tickets_to_default_product(self):
>> + 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)
>> - VALUES ('ticket', '1')""")
>> + 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.env.db_direct_transaction as db:
>> - self.assertEqual(
>> - len(db("""SELECT * FROM ticket WHERE product='@'""")), 1)
>> - self.assertEqual(
>> - len(db("""SELECT * FROM attachment
>> - WHERE product='@'
>> - AND type='ticket'""")), 1)
>> + 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_wikis_to_default_product(self):
>> + 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)
>> @@ -205,23 +151,44 @@ class EnvironmentUpgradeTestCase(unittes
>> WHERE product=''
>> AND type='wiki'""")), 1)
>>
>> - def test_can_upgrade_database_with_orphaned_attachments(self):
>> + def
>> test_upgrade_copies_content_of_system_tables_to_all_products(self):
>> + mp = MultiProductSystem(self.env)
>> with self.env.db_direct_transaction as db:
>> - db("""INSERT INTO attachment (id, type)
>> - VALUES ('5', 'ticket')""")
>> - db("""INSERT INTO attachment (id, type)
>> - VALUES ('MyWiki', 'wiki')""")
>> + 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 wiki (name, version) VALUES ('WikiStart',
>> 1)""")
>> + db("""INSERT INTO report (title) VALUES ('x')""")
>>
>> self._enable_multiproduct()
>> self.env.upgrade()
>>
>> - def test_can_upgrade_database_with_text_attachment_ids(self):
>> with self.env.db_direct_transaction as db:
>> - db("""INSERT INTO attachment (id, type)
>> - VALUES ('abc', 'ticket')""")
>> -
>> - self._enable_multiproduct()
>> - self.env.upgrade()
>> + 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 ('wiki', 'permission'):
>> + # Permissions and wikis also hold rows for global product.
>> + rows = db("SELECT * FROM %s" % 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')
>> @@ -258,26 +225,148 @@ class EnvironmentUpgradeTestCase(unittes
>> 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)
>> + 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)
>> + VALUES ('5', 'ticket')""")
>> + db("""INSERT INTO attachment (id, type)
>> + 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):
>> + 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 _enable_multiproduct(self):
>> - self.env.config.set('components', 'multiproduct.*', 'enabled')
>> - self.env.config.save()
>> - self._reload_environment()
>> - self._reenable_components()
>> + 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.env.config.set('components',
>> - '%s.%s' % (cls.__module__, cls.__name__),
>> - 'enabled')
>> - self.enabled_components.append(cls)
>> - self.env.compmgr.enabled[cls] = True
>> + self._update_config(
>> + 'components',
>> + '%s.%s' % (cls.__module__, cls.__name__),
>> + 'enabled'
>> + )
>>
>> - def _reload_environment(self):
>> + 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 _reenable_components(self):
>> - for cls in self.enabled_components:
>> - self.env.compmgr.enabled[cls] = True
>> -
>> def _create_file_with_content(self, content):
>> filename = str(uuid.uuid4())[:6]
>> path = os.path.join(self.env_path, filename)
>>
>>
>>
>
> One of the unit tests added in this changeset is now failing on the trunk.
> It looks like the test is only correct for the number of permissions
> associated with "anonymous" and doesn't take into account the permissions
> assigned to "authenticated". Just wanted to ask what is the best way to fix
> this rather than guessing at the intention. Thanks!
>
> ======================================================================
> FAIL: test_upgrade_copies_content_of_system_tables_to_all_products
> (tests.upgrade.EnvironmentUpgradeTestCase)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File
> "/home/user/Workspace/bh579/bloodhound/bloodhound_multiproduct/tests/upgrade.py",
> line 197, in test_upgrade_copies_content_of_system_tables_to_all_products
> % (table, len(rows), 7, rows))
> AssertionError: Wrong number of lines in permission (21 instead of 7)
> [(u'anonymous', u'PRODUCT_VIEW', u''), (u'anonymous', u'PRODUCT_VIEW', u'@'),
> (u'anonymous', u'PRODUCT_VIEW', u'p1'), (u'anonymous', u'PRODUCT_VIEW',
> u'p2'), (u'anonymous', u'PRODUCT_VIEW', u'p3'), (u'anonymous',
> u'PRODUCT_VIEW', u'p4'), (u'anonymous', u'PRODUCT_VIEW', u'p5'),
> (u'authenticated', u'PRODUCT_VIEW', u''), (u'authenticated',
> u'PRODUCT_VIEW', u'@'), (u'authenticated', u'PRODUCT_VIEW', u'p1'),
> (u'authenticated', u'PRODUCT_VIEW', u'p2'), (u'authenticated',
> u'PRODUCT_VIEW', u'p3'), (u'authenticated', u'PRODUCT_VIEW', u'p4'),
> (u'authenticated', u'PRODUCT_VIEW', u'p5'), (u'x', u'TICKET_VIEW', u''),
> (u'x', u'TICKET_VIEW', u'@'), (u'x', u'TICKET_VIEW', u'p1'), (u'x',
> u'TICKET_VIEW', u'p2'), (u'x', u'TICKET_VIEW', u'p3'), (u'x',
> u'TICKET_VIEW', u'p4'), (u'x', u'TICKET_VIEW', u'p5')]