def test_execute_migration_error_with_state_rollback(mock, db): mock.add_migration( 'udata', 'migration.py', '''\ def migrate(db): db.test.insert_one({'key': 'first'}) db._state['first'] = True raise ValueError('error') db.test.insert_two({'key': 'second'}) db._state['second'] = True def rollback(db): if db._state.get('first', False): db.rollback_test.insert_one({'key': 'first'}) if db._state.get('second', False): db.rollback_test.insert_one({'key': 'second'}) ''') with pytest.raises(migrations.MigrationError) as excinfo: migrations.get('udata', 'migration.py').execute() exc = excinfo.value assert isinstance(exc, migrations.RollbackError) assert exc.exc is None assert exc.msg == "Error while executing migration, rollback has been applied" assert isinstance(exc.migrate_exc, migrations.MigrationError) assert isinstance(exc.migrate_exc.exc, ValueError) assert exc.migrate_exc.msg == "Error while executing migration" # Only the first value is inserted assert db.test.count_documents({}) == 1 # Only the first rollback operation is executed assert db.rollback_test.count_documents({}) == 1 # DB is rollbacked if possible assert db.migrations.count_documents({}) == 1 record = db.migrations.find_one() assert record['plugin'] == 'udata' assert record['filename'] == 'migration.py' # Both failed migration and rollback are recorded assert len(record['ops']) == 2 # First the migration op = record['ops'][0] assert op['type'] == 'migrate' assert op['output'] == [] assert op['state'] == {'first': True} assert isinstance(op['date'], datetime) assert not op['success'] # Then the rollback op = record['ops'][1] assert op['type'] == 'rollback' assert op['output'] == [] assert op['state'] == {'first': True} assert isinstance(op['date'], datetime) assert op['success']
def test_execute_migration_error_with_rollback_error(mock, db): mock.add_migration( 'udata', 'migration.py', '''\ def migrate(db): db.test.insert_one({'key': 'value'}) raise ValueError('error') def rollback(db): db.rollback_test.insert_one({'key': 'value'}) raise ValueError('error') ''') with pytest.raises(migrations.MigrationError) as excinfo: migrations.get('udata', 'migration.py').execute() exc = excinfo.value assert isinstance(exc, migrations.RollbackError) assert isinstance(exc.exc, ValueError) assert exc.msg == "Error while executing migration rollback" assert isinstance(exc.migrate_exc, migrations.MigrationError) assert isinstance(exc.migrate_exc.exc, ValueError) assert exc.migrate_exc.msg == "Error while executing migration" # Migrate value is inserted assert db.test.count_documents({}) == 1 # Rollback should not be recorded assert db.rollback_test.count_documents({}) == 1 # DB is rollbacked if possible assert db.migrations.count_documents({}) == 1 record = db.migrations.find_one() assert record['plugin'] == 'udata' assert record['filename'] == 'migration.py' # Both failed migration and rollback are recorded assert len(record['ops']) == 2 # First the migration op = record['ops'][0] assert op['type'] == 'migrate' assert op['output'] == [] assert op['state'] == {} assert isinstance(op['date'], datetime) assert not op['success'] # Then the rollback op = record['ops'][1] assert op['type'] == 'rollback' assert op['output'] == [] assert op['state'] == {} assert isinstance(op['date'], datetime) assert not op['success']
def test_migration_execute(mock, db): mock.add_migration( 'udata', 'migration.py', '''\ import logging log = logging.getLogger(__name__) def migrate(db): db.test.insert_one({'key': 'value'}) log.info('test') ''') output = migrations.get('udata', 'migration.py').execute() inserted = db.test.find_one() assert inserted is not None assert inserted['key'] == 'value' assert output == [['info', 'test']] assert db.migrations.count_documents({}) == 1 record = db.migrations.find_one() assert record['plugin'] == 'udata' assert record['filename'] == 'migration.py' assert len(record['ops']) == 1 op = record['ops'][0] assert op['type'] == 'migrate' assert op['output'] == [['info', 'test']] assert op['state'] == {} assert isinstance(op['date'], datetime) assert op['success']
def test_get_record(db): inserted = { 'plugin': 'test', 'filename': 'filename.py', 'ops': [{ 'date': datetime.now(), 'type': 'migrate', 'script': 'script', 'output': 'output', 'success': True, }] } db.migrations.insert_one(inserted) record = migrations.get('test', 'filename.py').record assert record['plugin'] == inserted['plugin'] assert record['filename'] == inserted['filename'] op = record['ops'][0] assert op['script'] == inserted['ops'][0]['script'] assert op['output'] == inserted['ops'][0]['output'] assert op['type'] == inserted['ops'][0]['type'] assert op['success'] assert_equal_dates(op['date'], inserted['ops'][0]['date'])
def test_execute_migration_error(mock, db): mock.add_migration( 'udata', 'migration.py', '''\ import logging log = logging.getLogger(__name__) def migrate(db): db.test.insert_one({'key': 'value'}) log.info('test') raise ValueError('error') ''') with pytest.raises(migrations.MigrationError) as excinfo: migrations.get('udata', 'migration.py').execute() exc = excinfo.value assert isinstance(exc, migrations.MigrationError) assert isinstance(exc.exc, ValueError) assert exc.msg == "Error while executing migration" assert exc.output == [['info', 'test']] # Without rollback DB is left as it is assert db.test.count_documents({}) == 1 inserted = db.test.find_one() assert inserted is not None assert inserted['key'] == 'value' # Failed migration is recorded assert db.migrations.count_documents({}) == 1 record = db.migrations.find_one() assert record['plugin'] == 'udata' assert record['filename'] == 'migration.py' assert len(record['ops']) == 1 op = record['ops'][0] assert op['type'] == 'migrate' assert op['output'] == [['info', 'test']] assert op['state'] == {} assert isinstance(op['date'], datetime) assert not op['success']
def info(plugin_or_specs, filename): ''' Display detailed info about a migration ''' migration = migrations.get(plugin_or_specs, filename) log_status(migration, status_label(migration.record)) try: echo(migration.module.__doc__) except migrations.MigrationError: echo(yellow('Module not found')) for op in migration.record.get('ops', []): display_op(op)
def unrecord(plugin_or_specs, filename): ''' Remove a database migration record. \b A record can be expressed with the following syntaxes: - plugin filename - plugin filename.js - plugin:filename - plugin:fliename.js ''' migration = migrations.get(plugin_or_specs, filename) removed = migration.unrecord() if removed: log.info('Removed migration %s', migration.label) else: log.error('Migration not found %s', migration.label)
def test_execute_migration_dry_run(mock, db): mock.add_migration( 'udata', 'migration.py', '''\ import logging log = logging.getLogger(__name__) def migrate(db): db.test.insert_one({'key': 'value'}) log.info('test') ''') output = migrations.get('udata', 'migration.py').execute(dryrun=True) assert output == [] assert db.test.find_one() is None assert db.migrations.count_documents({}) == 0
def test_unrecord_migration(db): inserted = { 'plugin': 'test', 'filename': 'filename.py', 'ops': [{ 'date': datetime.now(), 'type': 'migrate', 'script': 'script', 'output': 'output', 'state': {}, 'success': True, }] } db.migrations.insert_one(inserted) migration = migrations.get('test', 'filename.py') # Remove the migration record, return True assert migration.unrecord() assert db.migrations.find_one() is None # Already removed, return False assert not migration.unrecord()
def test_record_migration(mock, db): mock.add_migration( 'test', 'filename.py', '''\ # whatever def migrate(): pass ''') expected_output = [['info', 'Recorded only']] output = migrations.get('test', 'filename.py').execute(recordonly=True) assert output == expected_output migration = db.migrations.find_one() assert migration['plugin'] == 'test' assert migration['filename'] == 'filename.py' op = migration['ops'][0] assert op['script'].startswith('# whatever\n') assert op['output'] == expected_output assert op['type'] == 'migrate' assert op['success']
def test_get_migration(args): migration = migrations.get(*args) assert isinstance(migration, migrations.Migration) assert migration.plugin == 'udata' assert migration.filename == 'test.py'
def test_execute_missing_migration(db, mock): mock.ensure_plugin('test') with pytest.raises(migrations.MigrationError): migrations.get('test', 'filename.py').execute() assert db.migrations.find_one() is None