text:
var should = require('should'),
    sinon = require('sinon'),
    rewire = require('rewire'),
    _ = require('lodash'),
    Promise = require('bluebird'),
    crypto = require('crypto'),
    fs = require('fs'),

    // Stuff we are testing
    db = require('../../server/data/db'),
    errors = require('../../server/errors'),
    models = require('../../server/models'),
    exporter = require('../../server/data/export'),
    schema = require('../../server/data/schema'),

    migration = rewire('../../server/data/migration'),
    fixtures = require('../../server/data/migration/fixtures'),
    populate = require('../../server/data/migration/populate'),
    update = rewire('../../server/data/migration/update'),
    updates004 = require('../../server/data/migration/004'),
    updates005 = require('../../server/data/migration/005'),

    defaultSettings = schema.defaultSettings,
    schemaTables = Object.keys(schema.tables),

    sandbox = sinon.sandbox.create();

// Check version integrity
// These tests exist to ensure that developers are not able to modify the database schema, or permissions fixtures
// without knowing that they also need to update the default database version,
// both of which are required for migrations to work properly.
describe('DB version integrity', function () {
    // Only these variables should need updating
    var currentDbVersion = '006',
        currentSchemaHash = 'f63f41ac97b5665a30c899409bbf9a83',
        currentFixturesHash = '56f781fa3bba0fdbf98da5f232ec9b11';

    // If this test is failing, then it is likely a change has been made that requires a DB version bump,
    // and the values above will need updating as confirmation
    it('should not change without fixing this test', function () {
        var tablesNoValidation = _.cloneDeep(schema.tables),
            schemaHash,
            fixturesHash;

        _.each(tablesNoValidation, function (table) {
            return _.each(table, function (column, name) {
                table[name] = _.omit(column, 'validations');
            });
        });

        schemaHash = crypto.createHash('md5').update(JSON.stringify(tablesNoValidation)).digest('hex');
        fixturesHash = crypto.createHash('md5').update(JSON.stringify(fixtures.fixtures)).digest('hex');

        // Test!
        defaultSettings.core.databaseVersion.defaultValue.should.eql(currentDbVersion);
        schemaHash.should.eql(currentSchemaHash);
        fixturesHash.should.eql(currentFixturesHash);
        schema.versioning.canMigrateFromVersion.should.eql('003');
    });
});

describe('Migrations', function () {
    var loggerStub, resetLogger;

    before(function () {
        models.init();
    });

    afterEach(function () {
        sandbox.restore();
        resetLogger();
    });

    beforeEach(function () {
        loggerStub = {
            info: sandbox.stub(),
            warn: sandbox.stub()
        };

        resetLogger = update.__set__('logger', loggerStub);
    });

    describe('Backup', function () {
        var exportStub, filenameStub, fsStub;

        beforeEach(function () {
            exportStub = sandbox.stub(exporter, 'doExport').returns(new Promise.resolve());
            filenameStub = sandbox.stub(exporter, 'fileName').returns(new Promise.resolve('test'));
            fsStub = sandbox.stub(fs, 'writeFile').yields();
        });

        it('should create a backup JSON file', function (done) {
            migration.backupDatabase(loggerStub).then(function () {
                exportStub.calledOnce.should.be.true();
                filenameStub.calledOnce.should.be.true();
                fsStub.calledOnce.should.be.true();
                loggerStub.info.calledTwice.should.be.true();

                done();
            }).catch(done);
        });

        it('should fall back to console.log if no logger provided', function (done) {
            var noopStub = sandbox.stub(_, 'noop');

            migration.backupDatabase().then(function () {
                exportStub.calledOnce.should.be.true();
                filenameStub.calledOnce.should.be.true();
                fsStub.calledOnce.should.be.true();
                noopStub.calledTwice.should.be.true();
                // restore early so we get the test output
                noopStub.restore();

                done();
            }).catch(done);
        });
    });

    describe('Reset', function () {
        var deleteStub;

        beforeEach(function () {
            deleteStub = sandbox.stub(schema.commands, 'deleteTable').returns(new Promise.resolve());
        });

        it('should delete all tables in reverse order', function (done) {
            migration.reset().then(function (result) {
                should.exist(result);
                result.should.be.an.Array().with.lengthOf(schemaTables.length);

                deleteStub.called.should.be.true();
                deleteStub.callCount.should.be.eql(schemaTables.length);
                // First call should be called with the last table
                deleteStub.firstCall.calledWith(schemaTables[schemaTables.length - 1]).should.be.true();
                // Last call should be called with the first table
                deleteStub.lastCall.calledWith(schemaTables[0]).should.be.true();

                done();
            }).catch(done);
        });

        it('should delete all tables in reverse order when called twice in a row', function (done) {
            migration.reset().then(function (result) {
                should.exist(result);
                result.should.be.an.Array().with.lengthOf(schemaTables.length);

                deleteStub.called.should.be.true();
                deleteStub.callCount.should.be.eql(schemaTables.length);
                // First call should be called with the last table
                deleteStub.firstCall.calledWith(schemaTables[schemaTables.length - 1]).should.be.true();
                // Last call should be called with the first table
                deleteStub.lastCall.calledWith(schemaTables[0]).should.be.true();

                return migration.reset();
            }).then(function (result) {
                should.exist(result);
                result.should.be.an.Array().with.lengthOf(schemaTables.length);

                deleteStub.called.should.be.true();
                deleteStub.callCount.should.be.eql(schemaTables.length * 2);
                // First call (second set) should be called with the last table
                deleteStub.getCall(schemaTables.length).calledWith(schemaTables[schemaTables.length - 1]).should.be.true();
                // Last call (second Set) should be called with the first table
                deleteStub.getCall(schemaTables.length * 2 - 1).calledWith(schemaTables[0]).should.be.true();

                done();
            }).catch(done);
        });
    });

    describe('Populate', function () {
        var createStub, fixturesStub;

        beforeEach(function () {
            createStub = sandbox.stub(schema.commands, 'createTable').returns(new Promise.resolve());
            fixturesStub = sandbox.stub(fixtures, 'populate').returns(new Promise.resolve());
        });

        it('should create all tables, and populate fixtures', function (done) {
            populate().then(function (result) {
                should.not.exist(result);

                createStub.called.should.be.true();
                createStub.callCount.should.be.eql(schemaTables.length);
                createStub.firstCall.calledWith(schemaTables[0]).should.be.true();
                createStub.lastCall.calledWith(schemaTables[schemaTables.length - 1]).should.be.true();
                fixturesStub.calledOnce.should.be.true();
                done();
            }).catch(done);
        });

        it('should should only create tables, with tablesOnly setting', function (done) {
            populate({tablesOnly: true}).then(function (result) {
                should.exist(result);
                result.should.be.an.Array().with.lengthOf(schemaTables.length);

                createStub.called.should.be.true();
                createStub.callCount.should.be.eql(schemaTables.length);
                createStub.firstCall.calledWith(schemaTables[0]).should.be.true();
                createStub.lastCall.calledWith(schemaTables[schemaTables.length - 1]).should.be.true();
                fixturesStub.called.should.be.false();
                done();
            }).catch(done);
        });
    });

    describe('isDatabaseOutOfDate', function () {
        var updateDatabaseSchemaStub, updateDatabaseSchemaReset, versionsSpy;

        beforeEach(function () {
            versionsSpy = sandbox.spy(schema.versioning, 'getMigrationVersions');

            // For these tests, stub out the actual update task
            updateDatabaseSchemaStub = sandbox.stub().returns(new Promise.resolve());
            updateDatabaseSchemaReset = update.__set__('updateDatabaseSchema', updateDatabaseSchemaStub);
        });

        afterEach(function () {
            updateDatabaseSchemaReset();
        });

        it('should throw error if versions are too old', function () {
            var response = update.isDatabaseOutOfDate({fromVersion: '000', toVersion: '002'});
            updateDatabaseSchemaStub.calledOnce.should.be.false();
            (response.error instanceof errors.DatabaseVersion).should.eql(true);
        });

        it('should just return if versions are the same', function () {
            var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
                migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub),
                response = update.isDatabaseOutOfDate({fromVersion: '004', toVersion: '004'});

            response.migrate.should.eql(false);
            versionsSpy.calledOnce.should.be.false();
            migrateToDatabaseVersionStub.callCount.should.eql(0);
            migrateToDatabaseVersionReset();
        });

        it('should throw an error if the database version is higher than the default', function () {
            var response = update.isDatabaseOutOfDate({fromVersion: '010', toVersion: '004'});
            updateDatabaseSchemaStub.calledOnce.should.be.false();
            (response.error instanceof errors.DatabaseVersion).should.eql(true);
        });
    });

    describe('Update', function () {
        describe('Update function', function () {
            var resetBackup, backupStub, fixturesStub, setDbStub, versionsSpy, tasksSpy, transactionStub, transaction;

            beforeEach(function () {
                transaction = {
                    rollback: sandbox.stub(),
                    commit: sandbox.stub()
                };

                // Stubs
                backupStub = sandbox.stub().returns(new Promise.resolve());
                fixturesStub = sandbox.stub(fixtures, 'update').returns(new Promise.resolve());
                setDbStub = sandbox.stub(schema.versioning, 'setDatabaseVersion').returns(new Promise.resolve());

                transactionStub = sandbox.stub(db.knex, 'transaction', function (transactionStart) {
                    return new Promise(function () {
                        transactionStart(transaction);
                    });
                });

                // Spys
                versionsSpy = sandbox.spy(schema.versioning, 'getMigrationVersions');
                tasksSpy = sandbox.spy(schema.versioning, 'getUpdateDatabaseTasks');

                // Internal overrides
                resetBackup = update.__set__('backup', backupStub);
            });

            afterEach(function () {
                resetBackup();
            });

            describe('Pre & post update process', function () {
                var updateDatabaseSchemaStub, updateDatabaseSchemaReset;

                beforeEach(function () {
                    // For these tests, stub out the actual update task
                    updateDatabaseSchemaStub = sandbox.stub().returns(new Promise.resolve());
                    updateDatabaseSchemaReset = update.__set__('updateDatabaseSchema', updateDatabaseSchemaStub);
                });

                afterEach(function () {
                    updateDatabaseSchemaReset();
                });

                it('should attempt to run the pre & post update tasks correctly', function (done) {
                    // Execute
                    update.execute({fromVersion: '100', toVersion: '102'}).then(function () {
                        // getMigrationVersions should be called with the correct versions
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('100', '102').should.be.true();

                        // It should attempt to do a backup
                        backupStub.calledOnce.should.be.true();

                        // Now it's going to try to actually do the update...
                        updateDatabaseSchemaStub.calledTwice.should.be.true();
                        updateDatabaseSchemaStub.firstCall.calledWith([], loggerStub, {
                            transacting: transaction,
                            context: {internal: true}
                        }).should.be.true();
                        updateDatabaseSchemaStub.secondCall.calledWith([], loggerStub, {
                            transacting: transaction,
                            context: {internal: true}
                        }).should.be.true();

                        // Then fixture updates
                        fixturesStub.calledTwice.should.be.true();
                        // And finally, set the new DB version
                        setDbStub.calledTwice.should.be.true();

                        transaction.commit.called.should.eql(true);
                        transaction.rollback.called.should.eql(false);

                        // Just to be sure, lets assert the call order
                        sinon.assert.callOrder(
                            versionsSpy, backupStub, updateDatabaseSchemaStub, fixturesStub, setDbStub
                        );

                        done();
                    }).catch(done);
                });

                it('should upgrade from minimum version, if force migration is set', function (done) {
                    var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
                        migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);

                    update.execute({fromVersion: '005', toVersion: '006', forceMigration: true}).then(function () {
                        // getMigrationVersions should be called with the correct versions
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('003', '006').should.be.true();
                        versionsSpy.returned(['003', '004', '005', '006']).should.be.true();

                        // It should try to do the update
                        migrateToDatabaseVersionStub.callCount.should.eql(3);
                        migrateToDatabaseVersionStub.firstCall.args[0].should.eql('004');
                        migrateToDatabaseVersionStub.secondCall.args[0].should.eql('005');
                        migrateToDatabaseVersionStub.thirdCall.args[0].should.eql('006');

                        migrateToDatabaseVersionReset();
                        done();
                    }).catch(done);
                });

                it('should do an UPDATE if newer database version is higher', function (done) {
                    var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
                        migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);

                    update.execute({fromVersion: '004', toVersion: '005'}).then(function () {
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('004', '005').should.be.true();
                        versionsSpy.returned(['004', '005']).should.be.true();

                        migrateToDatabaseVersionStub.callCount.should.eql(1);
                        migrateToDatabaseVersionStub.firstCall.args[0].should.eql('005');

                        migrateToDatabaseVersionReset();

                        done();
                    }).catch(done);
                });

                it('should do an UPDATE if default version is significantly higher', function (done) {
                    var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
                        migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);

                    update.execute({fromVersion: '004', toVersion: '010'}).then(function () {
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('004', '010').should.be.true();
                        versionsSpy.returned(['004', '005', '006', '007', '008', '009', '010']).should.be.true();

                        migrateToDatabaseVersionStub.callCount.should.eql(6);
                        migrateToDatabaseVersionStub.firstCall.args[0].should.eql('005');
                        migrateToDatabaseVersionStub.lastCall.args[0].should.eql('010');

                        migrateToDatabaseVersionReset();

                        done();
                    }).catch(done);
                });

                it('should do an UPDATE even if versions are the same, when FORCE_MIGRATION set', function (done) {
                    var migrateToDatabaseVersionStub = sandbox.stub().returns(new Promise.resolve()),
                        migrateToDatabaseVersionReset = update.__set__('migrateToDatabaseVersion', migrateToDatabaseVersionStub);

                    update.execute({fromVersion: '004', toVersion: '004', forceMigration: true}).then(function () {
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('003', '004').should.be.true();
                        versionsSpy.returned(['003', '004']).should.be.true();

                        migrateToDatabaseVersionStub.callCount.should.eql(1);
                        migrateToDatabaseVersionStub.firstCall.args[0].should.eql('004');
                        migrateToDatabaseVersionReset();

                        done();
                    }).catch(done);
                });
            });

            describe('Update to 004', function () {
                it('should call all the 004 database upgrade tasks', function (done) {
                    // Setup
                    // Create a new stub, this will replace sequence, so that db calls don't actually get run
                    var sequenceStub = sandbox.stub(),
                        sequenceReset = update.__set__('sequence', sequenceStub);

                    sequenceStub.returns(Promise.resolve([]));

                    // Execute
                    update.execute({fromVersion: '003', toVersion: '004'}).then(function () {
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('003', '004').should.be.true();

                        // returns the same, but get sliced afterwards!
                        versionsSpy.returned(['003', '004']).should.be.true();

                        tasksSpy.calledOnce.should.be.true();
                        tasksSpy.calledWith('004', loggerStub).should.be.true();
                        tasksSpy.firstCall.returnValue.should.be.an.Array().with.lengthOf(5);

                        transaction.commit.called.should.eql(true);
                        transaction.rollback.called.should.eql(false);

                        sequenceStub.calledOnce.should.be.true();

                        sequenceStub.firstCall.calledWith(sinon.match.array, {
                            transacting: transaction,
                            context: {
                                internal: true
                            }
                        }, loggerStub).should.be.true();

                        sequenceStub.firstCall.args[0].should.be.an.Array().with.lengthOf(5);
                        sequenceStub.firstCall.args[0][0].should.be.a.Function().with.property('name', 'addTourColumnToUsers');
                        sequenceStub.firstCall.args[0][1].should.be.a.Function().with.property('name', 'addSortOrderColumnToPostsTags');
                        sequenceStub.firstCall.args[0][2].should.be.a.Function().with.property('name', 'addManyColumnsToClients');
                        sequenceStub.firstCall.args[0][3].should.be.a.Function().with.property('name', 'addClientTrustedDomainsTable');
                        sequenceStub.firstCall.args[0][4].should.be.a.Function().with.property('name', 'dropUniqueOnClientsSecret');

                        // Reset sequence
                        sequenceReset();
                        done();
                    }).catch(done);
                });

                describe('Tasks:', function () {
                    var addColumnStub, createTableStub, getIndexesStub, dropUniqueStub,
                        knexStub, knexMock;

                    beforeEach(function () {
                        knexMock = sandbox.stub().returns({});
                        knexMock.schema = {
                            hasTable: sandbox.stub(),
                            hasColumn: sandbox.stub()
                        };
                        // this MUST use sinon, not sandbox, see sinonjs/sinon#781
                        knexStub = sinon.stub(db, 'knex', {
                            get: function () {
                                return knexMock;
                            }
                        });

                        addColumnStub = sandbox.stub(schema.commands, 'addColumn');
                        createTableStub = sandbox.stub(schema.commands, 'createTable');
                        getIndexesStub = sandbox.stub(schema.commands, 'getIndexes');
                        dropUniqueStub = sandbox.stub(schema.commands, 'dropUnique');
                    });

                    afterEach(function () {
                        knexStub.restore();
                    });

                    it('should have tasks for 004', function () {
                        should.exist(updates004);
                        updates004.should.be.an.Array().with.lengthOf(5);
                    });

                    describe('01-add-tour-column-to-users', function () {
                        var addTourColumn = updates004[0];

                        it('does not try to add a new column if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(new Promise.resolve(false));

                            // Execute
                            addTourColumn({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('users').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add a new column if the column already exists', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'tour').returns(new Promise.resolve(true));

                            // Execute
                            addTourColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'tour').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add a new column if table is present but column is not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'tour').returns(new Promise.resolve(false));

                            // Execute
                            addTourColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'tour').should.be.true();

                                addColumnStub.calledOnce.should.be.true();
                                addColumnStub.calledWith('users', 'tour').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('02-add-sortorder-column-to-poststags', function () {
                        var addSortOrderColumn = updates004[1];

                        it('does not try to add a new column if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts_tags').returns(new Promise.resolve(false));

                            // Execute
                            addSortOrderColumn({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('posts_tags').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add a new column if the column already exists', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts_tags').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('posts_tags', 'sort_order').returns(new Promise.resolve(true));

                            // Execute
                            addSortOrderColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts_tags').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts_tags', 'sort_order').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add a new column if table is present but column is not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts_tags').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('posts_tags', 'sort_order').returns(new Promise.resolve(false));

                            // Execute
                            addSortOrderColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts_tags').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts_tags', 'sort_order').should.be.true();

                                addColumnStub.calledOnce.should.be.true();
                                addColumnStub.calledWith('posts_tags', 'sort_order').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('03-add-many-columns-to-clients', function () {
                        var addClientColumns = updates004[2];

                        it('does not try to add new columns if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(false));

                            // Execute
                            addClientColumns({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add new columns if the columns already exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'redirection_uri').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'logo').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'status').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'type').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'description').returns(new Promise.resolve(true));

                            // Execute
                            addClientColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                knexMock.schema.hasColumn.callCount.should.eql(5);
                                knexMock.schema.hasColumn.calledWith('clients', 'redirection_uri').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'logo').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'status').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'type').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'description').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.callCount.should.eql(5);

                                done();
                            }).catch(done);
                        });

                        it('tries to add new columns if table is present but columns are not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'redirection_uri').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'logo').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'status').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'type').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'description').returns(new Promise.resolve(false));

                            // Execute
                            addClientColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                knexMock.schema.hasColumn.callCount.should.eql(5);
                                knexMock.schema.hasColumn.calledWith('clients', 'redirection_uri').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'logo').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'status').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'type').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'description').should.be.true();

                                addColumnStub.callCount.should.eql(5);
                                addColumnStub.calledWith('clients', 'redirection_uri').should.be.true();
                                addColumnStub.calledWith('clients', 'logo').should.be.true();
                                addColumnStub.calledWith('clients', 'status').should.be.true();
                                addColumnStub.calledWith('clients', 'type').should.be.true();
                                addColumnStub.calledWith('clients', 'description').should.be.true();

                                loggerStub.info.callCount.should.eql(5);
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });

                        it('will only try to add columns that do not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'redirection_uri').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'logo').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'status').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('clients', 'type').returns(new Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('clients', 'description').returns(new Promise.resolve(true));

                            // Execute
                            addClientColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                knexMock.schema.hasColumn.callCount.should.eql(5);
                                knexMock.schema.hasColumn.calledWith('clients', 'redirection_uri').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'logo').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'status').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'type').should.be.true();
                                knexMock.schema.hasColumn.calledWith('clients', 'description').should.be.true();

                                addColumnStub.callCount.should.eql(2);
                                addColumnStub.calledWith('clients', 'logo').should.be.true();
                                addColumnStub.calledWith('clients', 'type').should.be.true();

                                loggerStub.info.callCount.should.eql(2);
                                loggerStub.warn.callCount.should.eql(3);

                                done();
                            }).catch(done);
                        });
                    });

                    describe('04-add-clienttrusteddomains-table', function () {
                        var addTrustedDomains = updates004[3];

                        it('does not try to add a new table if the table already exists', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('client_trusted_domains').returns(new Promise.resolve(true));

                            // Execute
                            addTrustedDomains({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('client_trusted_domains').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.true();
                                    done();
                                })
                                .catch(done);
                        });

                        it('tries to add a new table if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('client_trusted_domains').returns(new Promise.resolve(false));

                            // Execute
                            addTrustedDomains({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('client_trusted_domains').should.be.true();

                                createTableStub.calledOnce.should.be.true();
                                createTableStub.calledWith('client_trusted_domains').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('05-drop-unique-on-clients-secret', function () {
                        var dropUnique = updates004[4];

                        it('does not try to drop unique if the table does not exist', function (done) {
                            // Setup
                            getIndexesStub.withArgs('clients').returns(new Promise.resolve(
                                ['clients_slug_unique', 'clients_name_unique', 'clients_secret_unique'])
                            );
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(false));

                            // Execute
                            dropUnique({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to drop unique if the index does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(true));

                            getIndexesStub.withArgs('clients').returns(new Promise.resolve(
                                ['clients_slug_unique', 'clients_name_unique'])
                            );

                            // Execute
                            dropUnique({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                getIndexesStub.calledOnce.should.be.true();
                                getIndexesStub.calledWith('clients').should.be.true();

                                dropUniqueStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add a drop unique if table and index both exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('clients').returns(new Promise.resolve(true));

                            getIndexesStub.withArgs('clients').returns(new Promise.resolve(
                                ['clients_slug_unique', 'clients_name_unique', 'clients_secret_unique'])
                            );

                            // Execute
                            dropUnique({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('clients').should.be.true();

                                getIndexesStub.calledOnce.should.be.true();
                                getIndexesStub.calledWith('clients').should.be.true();

                                dropUniqueStub.calledOnce.should.be.true();
                                dropUniqueStub.calledWith('clients', 'secret').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });
                });
            });

            describe('Update to 005', function () {
                it('should call all the 005 database upgrade tasks', function (done) {
                    // Setup
                    // Create a new stub, this will replace sequence, so that db calls don't actually get run
                    var sequenceStub = sandbox.stub(),
                        sequenceReset = update.__set__('sequence', sequenceStub);

                    sequenceStub.returns(Promise.resolve([]));

                    // Execute
                    update.execute({fromVersion: '004', toVersion: '005'}).then(function () {
                        versionsSpy.calledOnce.should.be.true();
                        versionsSpy.calledWith('004', '005').should.be.true();
                        versionsSpy.returned(['004', '005']).should.be.true();

                        tasksSpy.calledOnce.should.be.true();
                        tasksSpy.calledWith('005', loggerStub).should.be.true();
                        tasksSpy.firstCall.returnValue.should.be.an.Array().with.lengthOf(5);

                        sequenceStub.calledOnce.should.be.true();

                        sequenceStub.firstCall.calledWith(sinon.match.array, {
                            transacting: transaction,
                            context: {
                                internal: true
                            }
                        }, loggerStub).should.be.true();

                        sequenceStub.firstCall.args[0].should.be.an.Array().with.lengthOf(5);
                        sequenceStub.firstCall.args[0][0].should.be.a.Function().with.property('name', 'dropHiddenColumnFromTags');
                        sequenceStub.firstCall.args[0][1].should.be.a.Function().with.property('name', 'addVisibilityColumnToKeyTables');
                        sequenceStub.firstCall.args[0][2].should.be.a.Function().with.property('name', 'addMobiledocColumnToPosts');
                        sequenceStub.firstCall.args[0][3].should.be.a.Function().with.property('name', 'addSocialMediaColumnsToUsers');
                        sequenceStub.firstCall.args[0][4].should.be.a.Function().with.property('name', 'addSubscribersTable');

                        // Reset sequence
                        sequenceReset();
                        done();
                    }).catch(done);
                });

                describe('Tasks:', function () {
                    var dropColumnStub, addColumnStub, createTableStub,
                        knexStub, knexMock;

                    beforeEach(function () {
                        knexMock = sandbox.stub().returns({});
                        knexMock.schema = {
                            hasTable: sandbox.stub(),
                            hasColumn: sandbox.stub()
                        };
                        // this MUST use sinon, not sandbox, see sinonjs/sinon#781
                        knexStub = sinon.stub(db, 'knex', {
                            get: function () {
                                return knexMock;
                            }
                        });

                        dropColumnStub = sandbox.stub(schema.commands, 'dropColumn');
                        addColumnStub = sandbox.stub(schema.commands, 'addColumn');
                        createTableStub = sandbox.stub(schema.commands, 'createTable');
                    });

                    afterEach(function () {
                        knexStub.restore();
                    });

                    it('should have tasks for 005', function () {
                        should.exist(updates005);
                        updates005.should.be.an.Array().with.lengthOf(5);
                    });

                    describe('01-drop-hidden-column-from-tags', function () {
                        var dropHiddenColumn = updates005[0];

                        it('does not try to drop column if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(false));

                            // Execute
                            dropHiddenColumn({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.calledWith('tags').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to drop column if the column does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('tags', 'hidden').returns(Promise.resolve(false));

                            // Execute
                            dropHiddenColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('tags').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('tags', 'hidden').should.be.true();

                                dropColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add drop column if table and column are both present', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('tags', 'hidden').returns(Promise.resolve(true));

                            // Execute
                            dropHiddenColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('tags').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('tags', 'hidden').should.be.true();

                                dropColumnStub.calledOnce.should.be.true();
                                dropColumnStub.calledWith('tags', 'hidden').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('02-add-visibility-column-to-key-tables', function () {
                        var addVisibilityColumn = updates005[1];

                        it('does not try to add new column if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(false));
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(false));
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(false));

                            // Execute
                            addVisibilityColumn({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.firstCall.calledWith('posts').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add new columns if the columns already exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));

                            knexMock.schema.hasColumn.withArgs('posts', 'visibility').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('tags', 'visibility').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'visibility').returns(Promise.resolve(true));

                            // Execute
                            addVisibilityColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledThrice.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts').should.be.true();
                                knexMock.schema.hasTable.calledWith('tags').should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledThrice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('tags', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'visibility').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledThrice.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add new columns if table is present but columns are not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));

                            knexMock.schema.hasColumn.withArgs('posts', 'visibility').returns(Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('tags', 'visibility').returns(Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('users', 'visibility').returns(Promise.resolve(false));

                            // Execute
                            addVisibilityColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledThrice.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts').should.be.true();
                                knexMock.schema.hasTable.calledWith('tags').should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledThrice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('tags', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'visibility').should.be.true();

                                addColumnStub.calledThrice.should.be.true();
                                addColumnStub.calledWith('posts', 'visibility').should.be.true();
                                addColumnStub.calledWith('tags', 'visibility').should.be.true();
                                addColumnStub.calledWith('users', 'visibility').should.be.true();

                                loggerStub.info.calledThrice.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });

                        it('will only try to add columns that do not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('tags').returns(Promise.resolve(true));
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));

                            knexMock.schema.hasColumn.withArgs('posts', 'visibility').returns(Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('tags', 'visibility').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'visibility').returns(Promise.resolve(false));
                            // Execute
                            addVisibilityColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledThrice.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts').should.be.true();
                                knexMock.schema.hasTable.calledWith('tags').should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledThrice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('tags', 'visibility').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'visibility').should.be.true();

                                addColumnStub.calledTwice.should.be.true();
                                addColumnStub.calledWith('posts', 'visibility').should.be.true();
                                addColumnStub.calledWith('tags', 'visibility').should.be.false();
                                addColumnStub.calledWith('users', 'visibility').should.be.true();

                                loggerStub.info.calledTwice.should.be.true();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('03-add-mobiledoc-column-to-posts', function () {
                        var addMobiledocColumn = updates005[2];

                        it('does not try to add a new column if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(false));

                            // Execute
                            addMobiledocColumn({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.firstCall.calledWith('posts').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add a new column if the column already exists', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(new Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('posts', 'mobiledoc').returns(Promise.resolve(true));

                            // Execute
                            addMobiledocColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts', 'mobiledoc').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add a new column if table is present but column is not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('posts').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('posts', 'mobiledoc').returns(Promise.resolve(false));

                            // Execute
                            addMobiledocColumn({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('posts').should.be.true();

                                knexMock.schema.hasColumn.calledOnce.should.be.true();
                                knexMock.schema.hasColumn.calledWith('posts', 'mobiledoc').should.be.true();

                                addColumnStub.calledOnce.should.be.true();
                                addColumnStub.calledWith('posts', 'mobiledoc').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('04-add-social-media-columns-to-users', function () {
                        var addSocialMediaColumns = updates005[3];

                        it('does not try to add new columns if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(false));

                            // Execute
                            addSocialMediaColumns({transacting: knexMock}, loggerStub)
                                .then(function () {
                                    done(new Error('expected table not found error'));
                                })
                                .catch(function (err) {
                                    should.exist(err);
                                    err.message.should.eql('Table does not exist!');
                                    knexMock.schema.hasTable.calledOnce.should.be.true();
                                    knexMock.schema.hasTable.firstCall.calledWith('users').should.be.true();

                                    knexMock.schema.hasColumn.called.should.be.false();

                                    addColumnStub.called.should.be.false();

                                    loggerStub.info.called.should.be.false();
                                    loggerStub.warn.calledOnce.should.be.false();
                                    done();
                                });
                        });

                        it('does not try to add new columns if the columns already exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'facebook').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'twitter').returns(Promise.resolve(true));

                            // Execute
                            addSocialMediaColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledTwice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'facebook').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'twitter').should.be.true();

                                addColumnStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledTwice.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add new columns if table is present but columns are not', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'facebook').returns(Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('users', 'twitter').returns(Promise.resolve(false));

                            // Execute
                            addSocialMediaColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledTwice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'facebook').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'twitter').should.be.true();

                                addColumnStub.calledTwice.should.be.true();
                                addColumnStub.calledWith('users', 'facebook').should.be.true();
                                addColumnStub.calledWith('users', 'twitter').should.be.true();

                                loggerStub.info.calledTwice.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });

                        it('will only try to add columns that do not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('users').returns(Promise.resolve(true));
                            knexMock.schema.hasColumn.withArgs('users', 'facebook').returns(Promise.resolve(false));
                            knexMock.schema.hasColumn.withArgs('users', 'twitter').returns(Promise.resolve(true));

                            // Execute
                            addSocialMediaColumns({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('users').should.be.true();

                                knexMock.schema.hasColumn.calledTwice.should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'facebook').should.be.true();
                                knexMock.schema.hasColumn.calledWith('users', 'twitter').should.be.true();

                                addColumnStub.callCount.should.eql(1);
                                addColumnStub.calledWith('users', 'facebook').should.be.true();
                                addColumnStub.calledWith('users', 'twitter').should.be.false();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });
                    });

                    describe('05-add-subscribers-table', function () {
                        var addSubscribers = updates005[4];

                        it('does not try to add a new table if the table already exists', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('subscribers').returns(new Promise.resolve(true));

                            // Execute
                            addSubscribers({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('subscribers').should.be.true();

                                createTableStub.called.should.be.false();

                                loggerStub.info.called.should.be.false();
                                loggerStub.warn.calledOnce.should.be.true();

                                done();
                            }).catch(done);
                        });

                        it('tries to add a new table if the table does not exist', function (done) {
                            // Setup
                            knexMock.schema.hasTable.withArgs('subscribers').returns(new Promise.resolve(false));

                            // Execute
                            addSubscribers({transacting: knexMock}, loggerStub).then(function () {
                                knexMock.schema.hasTable.calledOnce.should.be.true();
                                knexMock.schema.hasTable.calledWith('subscribers').should.be.true();

                                createTableStub.calledOnce.should.be.true();
                                createTableStub.calledWith('subscribers').should.be.true();

                                loggerStub.info.calledOnce.should.be.true();
                                loggerStub.warn.called.should.be.false();

                                done();
                            }).catch(done);
                        });
                    });
                });
            });
        });

        describe('Update Database Schema', function () {
            var updateDatabaseSchema = update.__get__('updateDatabaseSchema');

            it('should not do anything if there are no tasks', function (done) {
                updateDatabaseSchema([], {}, loggerStub).then(function () {
                    loggerStub.info.called.should.be.false();
                    loggerStub.warn.called.should.be.false();
                    done();
                }).catch(done);
            });

            it('should call the tasks if they are provided', function (done) {
                var task1Stub = sandbox.stub().returns(new Promise.resolve()),
                    task2Stub = sandbox.stub().returns(new Promise.resolve());

                updateDatabaseSchema([task1Stub, task2Stub], loggerStub).then(function () {
                    task1Stub.calledOnce.should.be.true();
                    task2Stub.calledOnce.should.be.true();
                    sinon.assert.callOrder(task1Stub, task2Stub);

                    done();
                }).catch(done);
            });
        });
    });
});


text:
<!--
/**-->

# Performance Tips {#performancetips}

## High CPU occupancy

  - Try to reduce actor count ( less actors == less processing)
  - Delete any actors that are not visible, or move them off stage
  - Use TextureAtlases ( reduces OpenGL driver calls to glBindTexture
  - Optimize / reduce any constraints used

## High GPU occupancy

  - Reduce visible actor count ( == less draw calls)
  - For 2D UI graphics which require no z sorting you can use

~~~{.cpp}
// C++
// In this mode depth testing is turned off and order is determined by the hierarchy (depth-first search order).
// Not always recommended if there is going to be a lot of overdraw ( if lots of actors are on top of each other)

Actor::SetDrawMode( DrawMode::OVERLAY_2D ); // C++
~~~

  - Use TextureAtlases (reduces state changes in the GPU)
  - Use compressed textures
  - Use lower quality textures, e.g. smaller, lower number of bits per pixel
  - Avoid using too many textures which contain alpha and require blending
  - Avoid using too many Dali::Layer with depth testing enabled. Otherwise the layer has to clear the depth buffer.
  - Optimize any shaders used. Pixel shaders should be kept as lean as possible.

*/

redpajama_set_name: RedPajamaGithub


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<TITLE>
JcrIdentifier (JCROM 2.1.1 API)
</TITLE>

<META NAME="date" CONTENT="2014-12-29">

<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">

      Overview
      Package
    Class
      Use
      Tree
      Deprecated
      Index
      Help

 PREV CLASS
 NEXT CLASS

  FRAMES
 NO FRAMES

  All Classes

  SUMMARY: REQUIRED | OPTIONAL

DETAIL: ELEMENT

org.jcrom.annotations

Annotation Type JcrIdentifier

@Retention(value=RUNTIME)
@Target(value=FIELD)
public @interface JcrIdentifier

 This annotation is used to mark a field that should store an Identifier
 read from any node. If the node does not have such an Identifier, then the field
 will be left empty.

JcrUUID is deprecated since JCR 2.0.

Since:

2.0.0

Author:

Nicolas Dos Santos

      Overview
      Package
    Class
      Use
      Tree
      Deprecated
      Index
      Help

 PREV CLASS
 NEXT CLASS

  FRAMES
 NO FRAMES

  All Classes

  SUMMARY: REQUIRED | OPTIONAL

DETAIL: ELEMENT

Copyright © 2008-2014. All Rights Reserved.


text:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/Content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/RecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>

    <android.support.v7.widget.CardView
        android:id="@+id/CardView"
        android:layout_width="match_parent"
        android:layout_height="?android:actionBarSize"
        android:layout_gravity="center_vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:background="?attr/colorPrimaryDark">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:padding="6dp"
                android:text="AAA"
                android:textColor="@color/WHITE" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:padding="6dp"
                android:text="BBB"
                android:textColor="@color/WHITE" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:padding="6dp"
                android:text="CCC"
                android:textColor="@color/WHITE" />
        </LinearLayout>
    </android.support.v7.widget.CardView>

</FrameLayout>


text:
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <item name="tv_title_name" type="id"/>
    <item name="tv_left_text" type="id"/>
    <item name="iv_left_image" type="id"/>
    <item name="iv_right_image" type="id"/>
    <item name="iv_right_image_two" type="id"/>
    <item name="tv_right_text" type="id"/>
    <item name="tv_right_text_two" type="id"/>

</resources>


text:
// Copyright (C) 2013, Ennio Barbaro.
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://sbabbi.github.io/tinympl for documentation.
//
// You are welcome to contact the author at:
//  enniobarbaro@gmail.com
//

#ifndef TINYMPL_IS_SEQUENCE_HPP
#define TINYMPL_IS_SEQUENCE_HPP

#include <type_traits>
#include <tinympl/as_sequence.hpp>

namespace tinympl {

/**
 * \ingroup SeqSupport
 * \class is_sequence
 * \brief Metafunction to determine if a given type is a sequence
 */
template<class T,class = void> struct is_sequence : std::false_type {};
template<class T> struct is_sequence<T,
	typename std::conditional<true, void, as_sequence_t<T> >::type> : std::true_type {};

}

#endif // TINYMPL_IS_SEQUENCE_HPP


text:
"use strict";
/*jshint -W079*/
var chai = require("chai"),
    expect = chai.expect,
    ncp = require('ncp').ncp,
    grunt = require("grunt"),
    _ = grunt.util._,
    JsBeautifierTask = require("../lib/jsbeautifier");
chai.use(require('chai-fs'));

/*jshint -W030*/
describe("JsBeautifier: rc file test", function() {
    var createMockTask;
    var mockTask;

    createMockTask = function(taskOptions, files, done) {
        return {
            _taskOptions: taskOptions,
            files: [{
                src: grunt.file.expand(files)
            }],
            options: function(defs) {
                return _.defaults(this._taskOptions, defs);
            },
            async: function() {
                return done || function() {};
            }
        };
    };

    beforeEach(function(done) {
        grunt.file.mkdir("tmp/rcFile");
        ncp("test/fixtures/configFile", "tmp/rcFile", done);
    });

    afterEach(function() {
        mockTask = null;
        grunt.file.delete("tmp");
        grunt.file.delete(".jsbeautifierrc");
    });

    function assertBeautifiedFile(actualFile, expectedFile) {
        var actual = "tmp/rcFile/" + actualFile,
            expected = grunt.file.read("tmp/rcFile/" + expectedFile);

        expect(actual).to.have.content(expected, "should beautify js " + actualFile + " using config file");
    }

    it("beautification of js, css & html file using settings from rc file", function(done) {
        var task;
        ncp("tmp/rcFile/jsbeautifyrc.json", ".jsbeautifierrc", function() {
            /*config: "tmp/configFile/jsbeautifyrc.json"*/
            mockTask = createMockTask({}, ["tmp/rcFile/test.js", "tmp/rcFile/test.css", "tmp/rcFile/test.html"], function() {
                assertBeautifiedFile("test.js", "expected/test_expected.js");
                assertBeautifiedFile("test.css", "expected/test_expected.css");
                assertBeautifiedFile("test.html", "expected/test_expected.html");
                done();
            });

            task = new JsBeautifierTask(mockTask);
            console.log('Running task');
            task.run();
        });
    });

    it("beautification of js, css & html file using settings from flat rc file", function(done) {
        var task;
        ncp("tmp/rcFile/jsbeautifyrc_flat.json", ".jsbeautifierrc", function() {
            /*config: "tmp/configFile/jsbeautifyrc_flat.json"*/
            mockTask = createMockTask({}, ["tmp/rcFile/test.js", "tmp/rcFile/test.css", "tmp/rcFile/test.html"], function() {
                assertBeautifiedFile("test.js", "expected/test_expected.js");
                assertBeautifiedFile("test.css", "expected/test_expected.css");
                assertBeautifiedFile("test.html", "expected/test_expected.html");
                done();
            });

            task = new JsBeautifierTask(mockTask);
            task.run();
        });
    });

    it("beautification of js, css & html file using settings from rc file and gruntfile", function(done) {
        var task;
        ncp("tmp/rcFile/jsbeautifyrc_flat.json", ".jsbeautifierrc", function() {
            /*config: "tmp/configFile/jsbeautifyrc_flat.json",*/
            mockTask = createMockTask({
                css: {
                    indentSize: 5
                },
                html: {
                    indentSize: 7
                }
            }, ["tmp/rcFile/test.js", "tmp/rcFile/test.css", "tmp/rcFile/test.html"], function() {
                assertBeautifiedFile("test.js", "expected/withGruntFileOptions/test_expected.js");
                assertBeautifiedFile("test.css", "expected/withGruntFileOptions/test_expected.css");
                assertBeautifiedFile("test.html", "expected/withGruntFileOptions/test_expected.html");
                done();
            });

            task = new JsBeautifierTask(mockTask);
            task.run();
        });
    });
});


text:
SYNONYM

#### According to
The Catalogue of Life, 3rd January 2011

#### Published in
Mycol. eur. (Erlanga) 1: 117 (1822)

#### Original name
Thelephora sanguinolenta Alb. & Schwein., 1805

### Remarks
null


text:
import datetime
import pytz

from django.core.management.base import BaseCommand

from appy.models import Application
from appy.models import Reminder

class Command(BaseCommand):
    def handle(self, *args, **options):
        utc=pytz.UTC
        reminders = Reminder.objects.all()
        for rem in reminders:
            td = Reminder.duration_to_timedelta(rem.duration)
            date_to_check = datetime.datetime.now() - td
            date_to_check = utc.localize(date_to_check)

            applications = Application.objects.filter(user=rem.user, status=rem.status)
            for app in applications:
                if app.updated_at < date_to_check:
                    rem.send(app)