Exemple #1
0
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']
Exemple #2
0
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']
Exemple #3
0
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']
Exemple #4
0
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'])
Exemple #5
0
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']
Exemple #6
0
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)
Exemple #7
0
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)
Exemple #8
0
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
Exemple #9
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()
Exemple #10
0
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']
Exemple #11
0
def test_get_migration(args):
    migration = migrations.get(*args)

    assert isinstance(migration, migrations.Migration)
    assert migration.plugin == 'udata'
    assert migration.filename == 'test.py'
Exemple #12
0
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