Skip to content

Commit

Permalink
migration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
timgit committed May 19, 2016
1 parent 1459da8 commit bc566d2
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 91 deletions.
68 changes: 32 additions & 36 deletions src/contractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const assert = require('assert');
const EventEmitter = require('events').EventEmitter; //node 0.10 compatibility
const Db = require('./db');
const plans = require('./plans');
const migrations = require('./migrations');
const schemaVersion = require('../version.json').schema;
const Promise = require("bluebird");

Expand Down Expand Up @@ -38,52 +39,47 @@ class Contractor extends EventEmitter {
}

static migrationPlans(schema, version, uninstall){
let migration = plans.getMigration(schema, version, uninstall);
let migration = migrations.get(schema, version, uninstall);
assert(migration, 'migration not found for this version');
return migration.commands.join(';\n\n');
}

start(){
let config = this.config;
let db = this.db;
let self = this;

this.isInstalled()
.then(installed => {
if(!installed)
return create();

self.version()
.then(version => {
if(schemaVersion === version)
return this.emit('go');

migrate(version);
});
if(!installed) {
this.create()
.then(() => this.emit('go'))
.catch(error => this.emit('error', error));
} else {
this.version()
.then(version => {
if (schemaVersion !== version) {
this.update(version)
.then(() => this.emit('go'));
} else {
this.emit('go');
}
});
}
});
}

function migrate(version) {
if(version == '0.0.2')
version = '0.0.1';

let migration = plans.getMigration(config.schema, version);
update(current) {
// temp workaround for bad 0.0.2 schema update
if(current == '0.0.2')
current = '0.0.1';

Promise.each(migration.commands, command => db.executeSql(command))
.then(() => {
if(migration.version === schemaVersion)
self.emit('go');
else
migrate(migration.version);
})
.catch(error => self.emit('error', error));
}

function create(){
Promise.each(plans.createAll(config.schema), command => db.executeSql(command))
.then(() => db.executeSql(plans.insertVersion(config.schema), schemaVersion))
.then(() => self.emit('go'))
.catch(error => self.emit('error', error));
}
return this.db.migrate(current)
.then(version => {
if(version !== schemaVersion)
return this.update(version);
});
}

create(){
return Promise.each(plans.createAll(this.config.schema), command => this.db.executeSql(command))
.then(() => this.db.executeSql(plans.insertVersion(this.config.schema), schemaVersion));
}

connect(){
Expand Down
8 changes: 7 additions & 1 deletion src/db.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const pg = require('pg');
const Promise = require("bluebird");
const migrations = require('./migrations');

class Db {
constructor(config){
Expand All @@ -15,7 +16,6 @@ class Db {
return this.execute({text,values});
}


execute(query) {
if(query.values && !Array.isArray(query.values))
query.values = [query.values];
Expand Down Expand Up @@ -45,6 +45,12 @@ class Db {
}
}

migrate(version, uninstall) {
let migration = migrations.get(this.config.schema, version, uninstall);

return Promise.each(migration.commands, command => this.executeSql(command))
.then(() => migration.version);
}
}

module.exports = Db;
46 changes: 46 additions & 0 deletions src/migrations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module.exports = {
get: get
};

function get(schema, version, uninstall) {
let migrations = getMigrations(schema);

for(var m=0; m<migrations.length; m++){
let migration = migrations[m];

let targetVersion = uninstall ? 'previous' : 'version';
let sourceVersion = uninstall ? 'version' : 'previous';

let targetCommands = uninstall ? 'uninstall' : 'install';

if(migration[sourceVersion] === version){
let commands = migration[targetCommands].concat();
commands.push(`UPDATE ${schema}.version SET version = '${migration[targetVersion]}'`);

return {
version: migration[targetVersion],
commands
};
}
}
}

function getMigrations(schema) {
return [
{
version: '0.1.0',
previous: '0.0.1',
install: [
`ALTER TABLE ${schema}.job ADD singletonOn timestamp without time zone`,
`ALTER TABLE ${schema}.job ADD CONSTRAINT job_singleton UNIQUE(name, singletonOn)`,
// one time truncate because previous schema was inserting each version
`TRUNCATE TABLE ${schema}.version`,
`INSERT INTO ${schema}.version(version) values('0.0.1')`
],
uninstall: [
`ALTER TABLE ${schema}.job DROP CONSTRAINT job_singleton`,
`ALTER TABLE ${schema}.job DROP COLUMN singletonOn`
]
}
];
}
42 changes: 1 addition & 41 deletions src/plans.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ module.exports = {
expireJob: expireJob,
completeJob: completeJob,
insertJob: insertJob,
archive: archive,
getMigration: getMigration
archive: archive
};

function createAll(schema) {
Expand Down Expand Up @@ -119,42 +118,3 @@ function archive(schema) {
WHERE state = 'completed'
AND completedOn + CAST($1 as interval) < now()`;
}

function getMigration(schema, version, uninstall) {
let migrations = [
{
version: '0.1.0',
previous: '0.0.1',
install: [
`ALTER TABLE ${schema}.job ADD singletonOn timestamp without time zone`,
`ALTER TABLE ${schema}.job ADD CONSTRAINT job_singleton UNIQUE(name, singletonOn)`,
// one time truncate because previous schema was inserting each version
`TRUNCATE TABLE ${schema}.version`,
`INSERT INTO ${schema}.version(version) values('0.0.1')`
],
uninstall: [
`ALTER TABLE ${schema}.job DROP CONSTRAINT job_singleton`,
`ALTER TABLE ${schema}.job DROP COLUMN singletonOn`
]
}
];

for(var m=0; m<migrations.length; m++){
let migration = migrations[m];

let targetVersion = uninstall ? 'previous' : 'version';
let sourceVersion = uninstall ? 'version' : 'previous';

let targetCommands = uninstall ? 'uninstall' : 'install';

if(migration[sourceVersion] === version){
let commands = migration[targetCommands].concat();
commands.push(`UPDATE ${schema}.version SET version = '${migration[targetVersion]}'`);

return {
version: migration[targetVersion],
commands
};
}
}
}
4 changes: 0 additions & 4 deletions test/connectTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ describe('connect', function() {
});
});

after(function(finished){
helper.getDb().executeSql(`DROP SCHEMA IF EXISTS ${helper.config.schema} CASCADE`).then(() => finished());
});

it('should fail if connecting to an older schema version', function (finished) {

helper.getDb().executeSql(`UPDATE ${helper.config.schema}.version SET VERSION = '0.0.0'`)
Expand Down
3 changes: 2 additions & 1 deletion test/initTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ var helper = require('./testHelper');
describe('initialization', function(){

beforeEach(function(finished) {
helper.getDb().executeSql(`DROP SCHEMA IF EXISTS ${helper.config.schema} CASCADE`).then(() => finished());
helper.init()
.then(() => finished());
});

it('should fail if connecting to an uninitialized instance', function(finished) {
Expand Down
53 changes: 53 additions & 0 deletions test/migrationTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var assert = require('chai').assert;
var PgBoss = require('../src/index');
var helper = require('./testHelper');
var Contractor = require('../src/contractor');
var currentSchemaVersion = require('../version.json').schema;

describe('migration', function() {

var db = helper.getDb();
var contractor = new Contractor(helper.config);

beforeEach(function(finished){
helper.init()
.then(() => finished());
});

it('should migrate to previous version and back again', function (finished) {
contractor.create()
.then(() => db.migrate(currentSchemaVersion, 'remove'))
.then(() => contractor.version())
.then(version => {
assert(version);
assert.notEqual(version, currentSchemaVersion);

return db.migrate(version);
})
.then(() => contractor.version())
.then(version => {
assert.equal(version, currentSchemaVersion);
finished();
});
});

it('should migrate to latest during start if on previous schema version', function(finished){

contractor.create()
.then(() => db.migrate(currentSchemaVersion, 'remove'))
.then(() => {
var boss = new PgBoss(helper.config);
boss.on('ready', () => {
contractor.version()
.then(version => {
assert.equal(version, currentSchemaVersion);
finished();
})
});

boss.start();
});

});

});
9 changes: 1 addition & 8 deletions test/testHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ var Db = require('../src/db');
var config = require('./config.json');
var PgBoss = require('../src/index');
var Promise = require('bluebird');
var Contractor = require('../src/contractor');

if(process.env.TRAVIS) {
config.port = 5433;
Expand All @@ -24,13 +23,7 @@ function getDb() {
}

function init() {
var truncateJobTable = `truncate table ${config.schema}.job`;
var db = getDb();
var contractor = new Contractor(config);

return contractor.isInstalled()
.then(installed => installed ? db.execute(truncateJobTable) : null)
.catch(error => console.error(error));
return getDb().executeSql(`DROP SCHEMA IF EXISTS ${config.schema} CASCADE`);
}

function start(options) {
Expand Down

0 comments on commit bc566d2

Please sign in to comment.