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')]

Reply via email to