;-*- mode: doctest; -*-
Migration tester
================

This is *definitively* not end-user documentation.

Here I'm setting up a whole lot of mock objects to test the actual migration
in a controlled manner. It is a unit test (as I'm not using zopetestcase or
plonetestcase, but it sure is a big one due to the nature of the task).

To prevent cluttering up the migration.py with a huge doctest this is in a
separate file.

    >>> import Acquisition
    >>> class MockSomething(Acquisition.Implicit):
    ...     # just to allow us to set values on something.
    ...     pass
    >>> class MockPortalTypes:
    ...     def getTypeInfo(self, typename):
    ...         fti = MockSomething()
    ...         fti.product = typename
    ...         fti.factory = typename
    ...         return fti
    >>> class MockPortal(Acquisition.Implicit):
    ...     portal_types = MockPortalTypes()
    ...     def _checkId(self, id):
    ...         return True
    ...     def _getOb(self, id):
    ...         return getattr(self, id)
    ...     def _delObject(self, id):
    ...         delattr(self, id)
    ...     def manage_delObjects(self, ids):
    ...         for id in ids:
    ...             self._delObject(id)
    ...     def _setObject(self, new_id, ob, set_owner=0):
    ...         setattr(self, new_id, ob)
    ...     def __init__(self):
    ...         self.aq_explicit = self
    ...     manage_addProduct = {}
    ...     manage_addProduct['Residential'] = MockSomething()
    ...     manage_addProduct['Commercial'] = MockSomething()
    >>> portal = MockPortal()
    >>> class MockContentType(Acquisition.Implicit):
    ...     aq_parent = portal
    ...     typename = 'mock'
    ...     def __init__(self, id):
    ...         self.id = id
    ...     def getTypeInfo(self):
    ...         fti = MockSomething()
    ...         fti.product = self.typename
    ...         fti.factory = self.typename
    ...         return fti
    ...     def objectValues(self):
    ...         # cmfphotoalbum content migration is tested in migration.py itself
    ...         return []
    ...     def getObject(self):
    ...         # Double as a brain ;-)
    ...         return self
    ...     def getPhysicalPath(self):
    ...         return ['root', self.id]
    ...     def getId(self):
    ...         return self.id
    ...     def absolute_url(self, *args):
    ...         return 'http://site/' + self.id
    ...     def CreationDate(self):
    ...         return 1
    ...     def ModificationDate(self):
    ...         return 1
    ...     def cb_isMoveable(self):
    ...         return True
    ...     def _notifyOfCopyTo(self, *args, **kw):
    ...         pass
    ...     def _setId(self, id):
    ...         self.id = id
    ...     def _postCopy(self, *args, **kw):
    ...         pass
    >>> class MockREHome(MockContentType):
    ...     typename = 'REHome'
    >>> class MockResidential(MockContentType):
    ...     typename = 'Residential'
    >>> class MockREBusiness(MockContentType):
    ...     typename = 'REBusiness'
    >>> class MockCommercial(MockContentType):
    ...     typename = 'Commercial'
    >>> def addResidential(id):
    ...     portal._setObject(id, MockResidential(id))
    >>> portal.manage_addProduct['Residential'].Residential = addResidential
    >>> def addCommercial(id):
    ...     portal._setObject(id, MockCommercial(id))
    >>> portal.manage_addProduct['Commercial'].Commercial = addCommercial
    >>> portal._setObject('one', MockREHome('one'))
    >>> portal._setObject('two', MockREBusiness('two'))
    >>> class MockCatalog:
    ...     threshold = None
    ...     def __call__(self, query):
    ...         # Is expected to return brains.
    ...         if query['portal_type'] == 'REHome':
    ...             return [portal.one]
    ...         if query['portal_type'] == 'REBusiness':
    ...             return [portal.two]
    >>> portal.portal_catalog = MockCatalog()
    >>> class DisabledMigration:
    ...     # To prevent too much Mock* work, we'll disable migration of
    ...     # Title and Description and so. That'll just work.
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __call__(self):
    ...         print "Dummy migration: " + self.name
    >>> from collective.realestatebroker.migration import ResidentialMigrator
    >>> ResidentialMigrator.migrate_dc = DisabledMigration('DC')
    >>> ResidentialMigrator.migrate_localroles = DisabledMigration('local roles')
    >>> ResidentialMigrator.migrate_owner = DisabledMigration('owner')
    >>> ResidentialMigrator.migrate_permission_settings = DisabledMigration('permission_settings')
    >>> ResidentialMigrator.last_migrate_date = DisabledMigration('migration date')
    >>> from collective.realestatebroker.migration import CommercialMigrator
    >>> CommercialMigrator.migrate_dc = DisabledMigration('DC')
    >>> CommercialMigrator.migrate_localroles = DisabledMigration('local roles')
    >>> CommercialMigrator.migrate_owner = DisabledMigration('owner')
    >>> CommercialMigrator.migrate_permission_settings = DisabledMigration('permission_settings')
    >>> CommercialMigrator.last_migrate_date = DisabledMigration('migration date')

We have set up a dummy portal and dummy content types and we've disabled a
couple of non-interesting migration steps. So we can now run the migration.

    >>> from collective.realestatebroker.migration import migrate
    >>> migrate(portal, migrators=(ResidentialMigrator, CommercialMigrator))
    Dummy migration: DC
    Dummy migration: local roles
    Dummy migration: owner
    Dummy migration: permission_settings
    Dummy migration: migration date
    Dummy migration: DC
    Dummy migration: local roles
    Dummy migration: owner
    Dummy migration: permission_settings
    Dummy migration: migration date
    'Starting migration\nMigrating root/one (REHome -> Residential)\n\nMigrating root/two (REBusiness -> Commercial)\n\nMigration finished\n'

Both items should now be of the new type:

    >>> portal.one.typename
    'Residential'
    >>> portal.two.typename
    'Commercial'
