class TestDirectoryMetaDirective(UpgradeTestCase): def setUp(self): super(TestDirectoryMetaDirective, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_upgrade_steps_are_registered(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_action')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 2, 2, 8)) .named('remove_action')) with self.package_created(): self.assert_upgrades([ {'source': ('10000000000000',), 'dest': ('20110101080000',), 'title': u'Add action.'}, {'source': ('20110101080000',), 'dest': ('20110202080000',), 'title': u'Remove action.'}]) def test_first_source_version_is_last_regulare_upgrade_step(self): self.profile.with_upgrade(Builder('plone upgrade step') .upgrading('1000', to='1001') .titled(u'Register foo utility.')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_action')) with self.package_created(): self.assert_upgrades([ {'source': ('1000',), 'dest': ('1001',), 'title': u'Register foo utility.'}, {'source': ('1001',), 'dest': ('20110101080000',), 'title': u'Add action.'}]) def test_registers_migration_generic_setup_profile_foreach_step(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_an_action')) with self.package_created() as package: upgrade_path = package.package_path.joinpath( 'upgrades', '20110101080000_add_an_action') self.assert_profile( {'id': 'the.package.upgrades:default-upgrade-20110101080000', 'title': 'Upgrade the.package:default ' + \ 'to 20110101080000: Add an action.', 'description': '', 'path': upgrade_path, 'product': 'the.package.upgrades', 'type': EXTENSION, 'for': IMigratingPloneSiteRoot}) def test_package_modules_is_not_corrupted(self): # Regression: when the upgrade-step:directory directive is used from # the package-directory with a relative path (directory="upgrades"), # it corrupted the sys.modules entry of the package. package_builder = ( Builder('python package') .named('other.package') .at_path(self.layer['temp_directory']) .with_file('__init__.py', 'PACKAGE = "package root"') .with_directory('profiles/default') .with_zcml_node('genericsetup:registerProfile', name='default', title='other.package:default', directory='profiles/default', provides='Products.GenericSetup.interfaces.EXTENSION') .with_directory('upgrades') .with_file('upgrades/__init__.py', 'PACKAGE = "upgrades package"') .with_zcml_include('ftw.upgrade', file='meta.zcml') .with_zcml_node('upgrade-step:directory', profile='other.package:default', directory='upgrades')) with create(package_builder).zcml_loaded(self.layer['configurationContext']): import other.package self.assertEquals('package root', other.package.PACKAGE) def test_profile_must_be_registed_before_registering_upgrade_directory(self): package_builder = (Builder('python package') .named('other.package') .at_path(self.layer['temp_directory']) .with_zcml_include('ftw.upgrade', file='meta.zcml') .with_zcml_node('upgrade-step:directory', profile='other.package:default', directory='.')) with create(package_builder) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'The profile "other.package:default" needs to be registered' ' before registering its upgrade step directory.', str(cm.exception).splitlines()[0]) def test_profile_version_is_set_to_latest_profile_version(self): self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 2, 2, 8))) with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile( {'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': profile_path, 'version': '20110202080000', 'product': 'the.package', 'type': EXTENSION, 'for': None}) def test_profile_version_is_set_to_latest_old_school_profile_version(self): self.profile.with_upgrade(Builder('plone upgrade step') .upgrading('1000', to='1001') .titled(u'Register foo utility.')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 2, 2, 8))) package = create(self.package) # Remove upgrade-step directory upgrade in order to have the # manually created upgrade step as last version # but still declaring an upgrade-step:directory: package.package_path.joinpath( 'upgrades', '20110202080000_upgrade').rmtree() profile_path = package.package_path.joinpath('profiles', 'default') self.assertNotIn('<version', profile_path.joinpath('metadata.xml').text()) with package.zcml_loaded(self.layer['configurationContext']): from ftw.upgrade.directory.zcml import find_start_version find_start_version(u'the.package:default') self.assert_profile( {'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': str(profile_path), 'version': '1001', 'product': 'the.package', 'type': EXTENSION, 'for': None}) def test_version_set_to_default_when_no_upgrades_defined(self): upgrades = self.package.package.get_subpackage('upgrades') upgrades.with_zcml_include('ftw.upgrade', file='meta.zcml') upgrades.with_zcml_node('upgrade-step:directory', profile='the.package:default', directory='.') with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile( {'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': profile_path, 'version': u'10000000000000', 'product': 'the.package', 'type': EXTENSION, 'for': None}) def test_profile_must_not_have_a_metadata_version_defined(self): self.profile.with_fs_version('1000') self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) with create(self.package) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'Registering an upgrades directory for "the.package:default" requires' ' this profile to not define a version in its metadata.xml.' ' The version is automatically set to the latest upgrade.', str(cm.exception).splitlines()[0]) def test_declaring_upgrades_dependency(self): self.package.with_profile( Builder('genericsetup profile') .named('bar') .with_upgrade( Builder('ftw upgrade step') .to(datetime(2010, 1, 1, 1, 1)) .with_zcml_directory_options( soft_dependencies="the.package:baz"))) self.package.with_profile( Builder('genericsetup profile') .named('foo') .with_upgrade( Builder('ftw upgrade step') .to(datetime(2010, 1, 1, 1, 1)) .with_zcml_directory_options( soft_dependencies="the.package:bar the.package:baz"))) self.package.with_profile( Builder('genericsetup profile') .named('baz') .with_upgrade( Builder('ftw upgrade step') .to(datetime(2010, 1, 1, 1, 1)) .with_zcml_directory_options( soft_dependencies="the.package:default"))) with self.package_created() as package: self.assert_profile( {'id': u'the.package:foo', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': [u'the.package:bar', u'the.package:baz'], 'path': package.package_path.joinpath('profiles', 'foo'), 'version': u'20100101010100', 'product': 'the.package', 'type': EXTENSION, 'for': None}) portal_setup = getToolByName(self.portal, 'portal_setup') self.assertEquals( [u'the.package:default', u'the.package:baz', u'the.package:bar', u'the.package:foo'], filter(lambda profile_id: profile_id.startswith('the.package:'), get_sorted_profile_ids(portal_setup))) def test_handler_step_provides_interfaces_implemented_by_upgrade_step_class(self): code = '\n'.join(( 'from ftw.upgrade import UpgradeStep', 'from ftw.upgrade.tests.test_directory_meta_directive import IFoo', 'from zope.interface import implementer', '', '@implementer(IFoo)', 'class Foo(UpgradeStep):', ' pass')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 1)) .with_code(code)) with self.package_created(): portal_setup = getToolByName(self.portal, 'portal_setup') steps = listUpgradeSteps(portal_setup, 'the.package:default', '10000000000000') self.assertEquals(1, len(steps)) self.assertItemsEqual( (IRecordableHandler, IUpgradeStep, IFoo), tuple(providedBy(steps[0]['step'].handler))) self.assertTrue(steps[0]['step'].handler.handler) def assert_upgrades(self, expected): upgrades = self.portal_setup.listUpgrades('the.package:default') got = [dict((key, value) for (key, value) in step.items() if key in ('source', 'dest', 'title')) for step in upgrades] self.maxDiff = None self.assertItemsEqual(expected, got) def assert_profile(self, expected): self.assertTrue( self.portal_setup.profileExists(expected['id']), 'Profile "{0}" does not exist. Profiles: {1}'.format( expected['id'], [profile['id'] for profile in self.portal_setup.listProfileInfo()])) got = self.portal_setup.getProfileInfo(expected['id']).copy() # Ignore pre_handler and post_handler, only available in Plone >= 4.3.8 got.pop('pre_handler', None) got.pop('post_handler', None) self.maxDiff = None self.assertDictEqual(expected, got)
class TestUpgradeStepBuilder(UpgradeTestCase): def setUp(self): super(TestUpgradeStepBuilder, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_upgrade_step_directory_and_file_is_created(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1)) .named('migrate file content type')) with self.package_created() as package: upgrade_path = package.package_path.joinpath( 'upgrades', '20110101000000_migrate_file_content_type') self.assertTrue(upgrade_path.isdir(), 'Upgrade directory was not created {0}'.format(upgrade_path)) self.assertMultiLineEqual( '\n'.join(('from ftw.upgrade import UpgradeStep', '', '', 'class MigrateFileContentType(UpgradeStep):', ' """Migrate file content type.', ' """', '', ' def __call__(self):', ' self.install_upgrade_profile()', '')), upgrade_path.joinpath('upgrade.py').text()) def test_executing_upgrade_step_with_custom_code(self): class AddExcludeFromNavIndex(UpgradeStep): def __call__(self): self.catalog_add_index('excludeFromNav', 'KeywordIndex') self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1)) .calling(AddExcludeFromNavIndex)) catalog = getToolByName(self.portal, 'portal_catalog') with self.package_created(): self.install_profile('the.package:default', '0') self.assertNotIn('excludeFromNav', catalog.indexes(), 'Index excludeFromNav already exists.') self.install_profile_upgrades('the.package:default') self.assertIn('excludeFromNav', catalog.indexes(), 'Index excludeFromNav was not created.') def test_add_files_and_directories_to_profile(self): self.profile.with_upgrade( Builder('ftw upgrade step') .to(datetime(2011, 1, 1)) .with_file('foo.txt', 'FOO') .with_directory('bar') .with_file('baz/baz.txt', 'BAZ', makedirs=True)) with self.package_created() as package: upgrade_path = package.package_path.joinpath('upgrades', '20110101000000_upgrade') self.assertTrue(upgrade_path.isdir(), 'Upgrade directory was not created {0}'.format(upgrade_path)) self.assertEqual('FOO', upgrade_path.joinpath('foo.txt').text()) self.assertTrue(upgrade_path.joinpath('bar').isdir(), 'directory "bar" was not created.') self.assertEqual('BAZ', upgrade_path.joinpath('baz', 'baz.txt').text()) def test_importing_upgrade_step_with_import_profile_files(self): self.profile.with_upgrade( Builder('ftw upgrade step') .to(datetime(2011, 1, 1)) .with_file('properties.xml', self.asset('foo-property.xml'))) with self.package_created(): self.install_profile('the.package:default', '0') self.assertFalse(self.portal.getProperty('foo'), 'Expected property "foo" to not yet exist.') self.install_profile_upgrades('the.package:default') self.assertEqual('bar', self.portal.getProperty('foo'), 'Property "foo" was not created.')
class TestDirectoryMetaDirective(UpgradeTestCase): def setUp(self): super(TestDirectoryMetaDirective, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_upgrade_steps_are_registered(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_action')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 2, 2, 8)) .named('remove_action')) with self.package_created(): self.assert_upgrades([ {'source': ('10000000000000',), 'dest': ('20110101080000',), 'title': u'Add action.'}, {'source': ('20110101080000',), 'dest': ('20110202080000',), 'title': u'Remove action.'}]) def test_first_source_version_is_last_regulare_upgrade_step(self): self.profile.with_upgrade(Builder('plone upgrade step') .upgrading('1000', to='1001') .titled('Register foo utility.')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_action')) with self.package_created(): self.assert_upgrades([ {'source': ('1000',), 'dest': ('1001',), 'title': u'Register foo utility.'}, {'source': ('1001',), 'dest': ('20110101080000',), 'title': u'Add action.'}]) def test_registers_migration_generic_setup_profile_foreach_step(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add_an_action')) with self.package_created() as package: upgrade_path = package.package_path.joinpath( 'upgrades', '20110101080000_add_an_action') self.assert_profile( {'id': 'the.package.upgrades:default-upgrade-20110101080000', 'title': 'Upgrade the.package:default ' + \ 'to 20110101080000: Add an action.', 'description': '', 'path': upgrade_path, 'product': 'the.package.upgrades', 'type': EXTENSION, 'for': IMigratingPloneSiteRoot}) def test_package_modules_is_not_corrupted(self): # Regression: when the upgrade-step:directory directive is used from # the package-directory with a relative path (directory="upgrades"), # it corrupted the sys.modules entry of the package. package_builder = ( Builder('python package') .named('other.package') .at_path(self.layer['temp_directory']) .with_file('__init__.py', 'PACKAGE = "package root"') .with_directory('profiles/default') .with_zcml_node('genericsetup:registerProfile', name='default', title='other.package:default', directory='profiles/default', provides='Products.GenericSetup.interfaces.EXTENSION') .with_directory('upgrades') .with_file('upgrades/__init__.py', 'PACKAGE = "upgrades package"') .with_zcml_include('ftw.upgrade', file='meta.zcml') .with_zcml_node('upgrade-step:directory', profile='other.package:default', directory='upgrades')) with create(package_builder).zcml_loaded(self.layer['configurationContext']): import other.package self.assertEquals('package root', other.package.PACKAGE) def test_profile_must_be_registed_before_registering_upgrade_directory(self): package_builder = (Builder('python package') .named('other.package') .at_path(self.layer['temp_directory']) .with_zcml_include('ftw.upgrade', file='meta.zcml') .with_zcml_node('upgrade-step:directory', profile='other.package:default', directory='.')) with create(package_builder) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'The profile "other.package:default" needs to be registered' ' before registering its upgrade step directory.', str(cm.exception).splitlines()[0]) def test_profile_version_is_set_to_latest_profile_version(self): self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 2, 2, 8))) with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile( {'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'path': profile_path, 'version': '20110202080000', 'product': 'the.package', 'type': EXTENSION, 'for': None}) def test_version_set_to_default_when_no_upgrades_defined(self): upgrades = self.package.package.get_subpackage('upgrades') upgrades.with_zcml_include('ftw.upgrade', file='meta.zcml') upgrades.with_zcml_node('upgrade-step:directory', profile='the.package:default', directory='.') with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile( {'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'path': profile_path, 'version': u'10000000000000', 'product': 'the.package', 'type': EXTENSION, 'for': None}) def test_profile_must_not_have_a_metadata_version_defined(self): self.profile.with_fs_version('1000') self.profile.with_upgrade(Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) with create(self.package) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'Registering an upgrades directory for "the.package:default" requires' ' this profile to not define a version in its metadata.xml.' ' The version is automatically set to the latest upgrade.', str(cm.exception).splitlines()[0]) def assert_upgrades(self, expected): upgrades = self.portal_setup.listUpgrades('the.package:default') got = [dict((key, value) for (key, value) in step.items() if key in ('source', 'dest', 'title')) for step in upgrades] self.maxDiff = None self.assertItemsEqual(expected, got) def assert_profile(self, expected): self.assertTrue( self.portal_setup.profileExists(expected['id']), 'Profile "{0}" does not exist. Profiles: {1}'.format( expected['id'], [profile['id'] for profile in self.portal_setup.listProfileInfo()])) got = self.portal_setup.getProfileInfo(expected['id']) self.maxDiff = None self.assertDictEqual(expected, got)
class TestDirectoryScanner(UpgradeTestCase): def setUp(self): super(TestDirectoryScanner, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_returns_chained_upgrade_infos(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add an action')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 2, 2, 8)) .named('update the action')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 3, 3, 8)) .named('remove the action')) with self.scanned() as upgrade_infos: map(lambda info: (info.__delitem__('path'), info.__delitem__('callable')), upgrade_infos) self.maxDiff = None self.assertEqual( [{'source-version': None, 'target-version': '20110101080000', 'title': 'Add an action.'}, {'source-version': '20110101080000', 'target-version': '20110202080000', 'title': 'Update the action.'}, {'source-version': '20110202080000', 'target-version': '20110303080000', 'title': 'Remove the action.'}], upgrade_infos) def test_exception_raised_when_upgrade_has_no_code(self): self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add action') .with_code('')) with create(self.package) as package: with self.assertRaises(UpgradeStepDefinitionError) as cm: self.scan(package) self.assertEqual( 'The upgrade step 20110101080000_add_action has no upgrade class' ' in the upgrade.py module.', str(cm.exception)) def test_exception_raised_when_multiple_upgrade_steps_detected(self): code = '\n'.join(( 'from ftw.upgrade import UpgradeStep', 'class Foo(UpgradeStep): pass', 'class Bar(UpgradeStep): pass')) self.profile.with_upgrade(Builder('ftw upgrade step') .to(datetime(2011, 1, 1, 8)) .named('add action') .with_code(code)) with create(self.package) as package: with self.assertRaises(UpgradeStepDefinitionError) as cm: self.scan(package) self.assertEqual( 'The upgrade step 20110101080000_add_action has more than one upgrade' ' class in the upgrade.py module.', str(cm.exception)) def test_does_not_fail_when_no_upgrades_present(self): self.package.with_zcml_include('ftw.upgrade', file='meta.zcml') self.package.with_zcml_node('upgrade-step:directory', profile='the.package:default', directory='.') with self.scanned() as upgrade_infos: self.assertEqual( [], upgrade_infos) @contextmanager def scanned(self): with create(self.package) as package: yield self.scan(package) def scan(self, package): upgrades = package.package_path.joinpath('upgrades') return Scanner('the.package.upgrades', upgrades).scan()
class TestDirectoryMetaDirective(UpgradeTestCase): def setUp(self): super(TestDirectoryMetaDirective, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_upgrade_steps_are_registered(self): self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8)).named('add_action')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 2, 2, 8)).named('remove_action')) with self.package_created(): self.assert_upgrades([{ 'source': ('10000000000000', ), 'dest': ('20110101080000', ), 'title': u'Add action.' }, { 'source': ('20110101080000', ), 'dest': ('20110202080000', ), 'title': u'Remove action.' }]) def test_first_source_version_is_last_regulare_upgrade_step(self): self.profile.with_upgrade( Builder('plone upgrade step').upgrading( '1000', to='1001').titled(u'Register foo utility.')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8)).named('add_action')) with self.package_created(): self.assert_upgrades([{ 'source': ('1000', ), 'dest': ('1001', ), 'title': u'Register foo utility.' }, { 'source': ('1001', ), 'dest': ('20110101080000', ), 'title': u'Add action.' }]) def test_registers_migration_generic_setup_profile_foreach_step(self): self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8)).named('add_an_action')) with self.package_created() as package: upgrade_path = package.package_path.joinpath( 'upgrades', '20110101080000_add_an_action') self.assert_profile( {'id': 'the.package.upgrades:default-upgrade-20110101080000', 'title': 'Upgrade the.package:default ' + \ 'to 20110101080000: Add an action.', 'description': '', 'path': upgrade_path, 'product': 'the.package.upgrades', 'type': EXTENSION, 'for': IMigratingPloneSiteRoot}) def test_package_modules_is_not_corrupted(self): # Regression: when the upgrade-step:directory directive is used from # the package-directory with a relative path (directory="upgrades"), # it corrupted the sys.modules entry of the package. package_builder = ( Builder('python package').named('other.package').at_path( self.layer['temp_directory']).with_file( '__init__.py', 'PACKAGE = "package root"' ).with_directory('profiles/default').with_zcml_node( 'genericsetup:registerProfile', name='default', title='other.package:default', directory='profiles/default', provides='Products.GenericSetup.interfaces.EXTENSION'). with_directory('upgrades').with_file( 'upgrades/__init__.py', 'PACKAGE = "upgrades package"').with_zcml_include( 'ftw.upgrade', file='meta.zcml').with_zcml_node( 'upgrade-step:directory', profile='other.package:default', directory='upgrades')) with create(package_builder).zcml_loaded( self.layer['configurationContext']): import other.package self.assertEquals('package root', other.package.PACKAGE) def test_profile_must_be_registed_before_registering_upgrade_directory( self): package_builder = ( Builder('python package').named('other.package').at_path( self.layer['temp_directory']).with_zcml_include( 'ftw.upgrade', file='meta.zcml').with_zcml_node( 'upgrade-step:directory', profile='other.package:default', directory='.')) with create(package_builder) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'The profile "other.package:default" needs to be registered' ' before registering its upgrade step directory.', str(cm.exception).splitlines()[0]) def test_profile_version_is_set_to_latest_profile_version(self): self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 2, 2, 8))) with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile({ 'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': profile_path, 'version': '20110202080000', 'product': 'the.package', 'type': EXTENSION, 'for': None }) def test_profile_version_is_set_to_latest_old_school_profile_version(self): self.profile.with_upgrade( Builder('plone upgrade step').upgrading( '1000', to='1001').titled(u'Register foo utility.')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 2, 2, 8))) package = create(self.package) # Remove upgrade-step directory upgrade in order to have the # manually created upgrade step as last version # but still declaring an upgrade-step:directory: package.package_path.joinpath('upgrades', '20110202080000_upgrade').rmtree() profile_path = package.package_path.joinpath('profiles', 'default') self.assertNotIn('<version', profile_path.joinpath('metadata.xml').text()) with package.zcml_loaded(self.layer['configurationContext']): from ftw.upgrade.directory.zcml import find_start_version find_start_version(u'the.package:default') self.assert_profile({ 'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': str(profile_path), 'version': '1001', 'product': 'the.package', 'type': EXTENSION, 'for': None }) def test_version_set_to_default_when_no_upgrades_defined(self): upgrades = self.package.package.get_subpackage('upgrades') upgrades.with_zcml_include('ftw.upgrade', file='meta.zcml') upgrades.with_zcml_node('upgrade-step:directory', profile='the.package:default', directory='.') with self.package_created() as package: profile_path = package.package_path.joinpath('profiles', 'default') self.assert_profile({ 'id': u'the.package:default', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': None, 'path': profile_path, 'version': u'10000000000000', 'product': 'the.package', 'type': EXTENSION, 'for': None }) def test_profile_must_not_have_a_metadata_version_defined(self): self.profile.with_fs_version('1000') self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8))) with create(self.package) as package: with self.assertRaises(ConfigurationExecutionError) as cm: package.load_zcml(self.layer['configurationContext']) self.assertEqual( "<class 'ftw.upgrade.exceptions.UpgradeStepConfigurationError'>: " 'Registering an upgrades directory for "the.package:default" requires' ' this profile to not define a version in its metadata.xml.' ' The version is automatically set to the latest upgrade.', str(cm.exception).splitlines()[0]) def test_declaring_upgrades_dependency(self): self.package.with_profile( Builder('genericsetup profile').named('bar').with_upgrade( Builder('ftw upgrade step').to(datetime( 2010, 1, 1, 1, 1)).with_zcml_directory_options( soft_dependencies="the.package:baz"))) self.package.with_profile( Builder('genericsetup profile').named('foo').with_upgrade( Builder('ftw upgrade step').to(datetime( 2010, 1, 1, 1, 1)).with_zcml_directory_options( soft_dependencies="the.package:bar the.package:baz"))) self.package.with_profile( Builder('genericsetup profile').named('baz').with_upgrade( Builder('ftw upgrade step').to(datetime( 2010, 1, 1, 1, 1)).with_zcml_directory_options( soft_dependencies="the.package:default"))) with self.package_created() as package: self.assert_profile({ 'id': u'the.package:foo', 'title': u'the.package', 'description': u'', 'ftw.upgrade:dependencies': [u'the.package:bar', u'the.package:baz'], 'path': package.package_path.joinpath('profiles', 'foo'), 'version': u'20100101010100', 'product': 'the.package', 'type': EXTENSION, 'for': None }) portal_setup = getToolByName(self.portal, 'portal_setup') self.assertEquals( [ u'the.package:default', u'the.package:baz', u'the.package:bar', u'the.package:foo' ], filter( lambda profile_id: profile_id.startswith('the.package:'), get_sorted_profile_ids(portal_setup))) def test_handler_step_provides_interfaces_implemented_by_upgrade_step_class( self): code = '\n'.join(( 'from ftw.upgrade import UpgradeStep', 'from ftw.upgrade.tests.test_directory_meta_directive import IFoo', 'from zope.interface import implementer', '', '@implementer(IFoo)', 'class Foo(UpgradeStep):', ' pass')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 1)).with_code(code)) with self.package_created(): portal_setup = getToolByName(self.portal, 'portal_setup') steps = listUpgradeSteps(portal_setup, 'the.package:default', '10000000000000') self.assertEquals(1, len(steps)) self.assertItemsEqual((IRecordableHandler, IUpgradeStep, IFoo), tuple(providedBy(steps[0]['step'].handler))) self.assertTrue(steps[0]['step'].handler.handler) def assert_upgrades(self, expected): upgrades = self.portal_setup.listUpgrades('the.package:default') got = [ dict((key, value) for (key, value) in step.items() if key in ('source', 'dest', 'title')) for step in upgrades ] self.maxDiff = None self.assertItemsEqual(expected, got) def assert_profile(self, expected): self.assertTrue( self.portal_setup.profileExists(expected['id']), 'Profile "{0}" does not exist. Profiles: {1}'.format( expected['id'], [ profile['id'] for profile in self.portal_setup.listProfileInfo() ])) got = self.portal_setup.getProfileInfo(expected['id']).copy() # Ignore pre_handler and post_handler, only available in Plone >= 4.3.8 got.pop('pre_handler', None) got.pop('post_handler', None) self.maxDiff = None self.assertDictEqual(expected, got)
class TestDirectoryScanner(UpgradeTestCase): def setUp(self): super(TestDirectoryScanner, self).setUp() self.profile = Builder('genericsetup profile') self.package.with_profile(self.profile) def test_returns_chained_upgrade_infos(self): self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime(2011, 1, 1, 8)).named('add an action')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime( 2011, 2, 2, 8)).named('update the action')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime( 2011, 3, 3, 8)).named('remove the action')) with self.scanned() as upgrade_infos: map( lambda info: (info.__delitem__('path'), info.__delitem__('callable')), upgrade_infos) self.maxDiff = None self.assertEqual([{ 'source-version': None, 'target-version': '20110101080000', 'title': 'Add an action.' }, { 'source-version': '20110101080000', 'target-version': '20110202080000', 'title': 'Update the action.' }, { 'source-version': '20110202080000', 'target-version': '20110303080000', 'title': 'Remove the action.' }], upgrade_infos) def test_exception_raised_when_upgrade_has_no_code(self): self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime( 2011, 1, 1, 8)).named('add action').with_code('')) with create(self.package) as package: with self.assertRaises(UpgradeStepDefinitionError) as cm: self.scan(package) self.assertEqual( 'The upgrade step 20110101080000_add_action has no upgrade class' ' in the upgrade.py module.', str(cm.exception)) def test_exception_raised_when_multiple_upgrade_steps_detected(self): code = '\n'.join( ('from ftw.upgrade import UpgradeStep', 'class Foo(UpgradeStep): pass', 'class Bar(UpgradeStep): pass')) self.profile.with_upgrade( Builder('ftw upgrade step').to(datetime( 2011, 1, 1, 8)).named('add action').with_code(code)) with create(self.package) as package: with self.assertRaises(UpgradeStepDefinitionError) as cm: self.scan(package) self.assertEqual( 'The upgrade step 20110101080000_add_action has more than one upgrade' ' class in the upgrade.py module.', str(cm.exception)) def test_does_not_fail_when_no_upgrades_present(self): self.package.with_zcml_include('ftw.upgrade', file='meta.zcml') self.package.with_zcml_node('upgrade-step:directory', profile='the.package:default', directory='.') with self.scanned() as upgrade_infos: self.assertEqual([], upgrade_infos) @contextmanager def scanned(self): with create(self.package) as package: yield self.scan(package) def scan(self, package): upgrades = package.package_path.joinpath('upgrades') return Scanner('the.package.upgrades', upgrades).scan()