Refactor to use an explicit "Patcher" class for state management.
This commit is contained in:
Родитель
fd4fd06432
Коммит
08e2dd935b
208
index.js
208
index.js
|
@ -15,50 +15,63 @@ var noop = Function.prototype // a No Op function
|
|||
|
||||
var ERR_NO_SUCH_TABLE = 1146
|
||||
|
||||
// the main export from this package
|
||||
function patch(options, callback) {
|
||||
callback = callback || noop
|
||||
|
||||
// A stateful Patcher class for interacting with the db.
|
||||
// This is the main export form this module.
|
||||
|
||||
function Patcher(options) {
|
||||
this.options = clone(options)
|
||||
|
||||
// check the required options
|
||||
if ( !options.dir ) {
|
||||
return callback(new Error("Option 'dir' is required"))
|
||||
if ( !this.options.dir ) {
|
||||
throw new Error("Option 'dir' is required")
|
||||
}
|
||||
|
||||
if ( !('patchLevel' in options ) ) {
|
||||
return callback(new Error("Option 'patchLevel' is required"))
|
||||
if ( !('patchLevel' in this.options ) ) {
|
||||
throw new Error("Option 'patchLevel' is required")
|
||||
}
|
||||
|
||||
if ( !this.options.mysql || !this.options.mysql.createConnection ) {
|
||||
throw new Error("Option 'mysql' must be a mysql module object")
|
||||
}
|
||||
|
||||
// set some defaults
|
||||
options.metaTable = options.metaTable || 'metadata'
|
||||
options.reversePatchAllowed = options.reversePatchAllowed || false
|
||||
options.patchKey = options.patchKey || 'patch'
|
||||
options.createDatabase = options.createDatabase || false
|
||||
this.options.metaTable = this.options.metaTable || 'metadata'
|
||||
this.options.reversePatchAllowed = this.options.reversePatchAllowed || false
|
||||
this.options.patchKey = this.options.patchKey || 'patch'
|
||||
this.options.createDatabase = this.options.createDatabase || false
|
||||
|
||||
// set this on the connection since we can have multiple statements in every patch
|
||||
options.multipleStatements = true
|
||||
// set this on the connection since we can have multiple statements
|
||||
// in every patch
|
||||
this.options.multipleStatements = true
|
||||
|
||||
// ToDo: fill in once other supporting functions are complete
|
||||
// some stub properties, mostly for documentation purposes.
|
||||
this.connection = null
|
||||
this.metaTableExists = undefined
|
||||
this.currentPatchLevel = undefined
|
||||
this.patches = {}
|
||||
this.patchesToApply = []
|
||||
|
||||
var context = {
|
||||
options : options,
|
||||
}
|
||||
}
|
||||
|
||||
Patcher.prototype.patch = function patch(callback) {
|
||||
async.series(
|
||||
[
|
||||
createConnection.bind(context),
|
||||
createDatabase.bind(context),
|
||||
changeUser.bind(context),
|
||||
checkDbMetadataExists.bind(context),
|
||||
readDbPatchLevel.bind(context),
|
||||
readPatchFiles.bind(context),
|
||||
checkAllPatchesAvailable.bind(context),
|
||||
applyPatches.bind(context),
|
||||
this.createConnection.bind(this),
|
||||
this.createDatabase.bind(this),
|
||||
this.changeUser.bind(this),
|
||||
this.checkDbMetadataExists.bind(this),
|
||||
this.readDbPatchLevel.bind(this),
|
||||
this.readPatchFiles.bind(this),
|
||||
this.checkAllPatchesAvailable.bind(this),
|
||||
this.applyPatches.bind(this),
|
||||
],
|
||||
function(err) {
|
||||
(function(err) {
|
||||
// firstly check for errors
|
||||
if (err) {
|
||||
// close the connection if we have one open
|
||||
if ( context.connection ) {
|
||||
context.connection.end(function(err) {
|
||||
if ( this.connection ) {
|
||||
this.connection.end(function(err) {
|
||||
// ignore any errors here since we already have one
|
||||
})
|
||||
}
|
||||
|
@ -66,15 +79,15 @@ function patch(options, callback) {
|
|||
}
|
||||
|
||||
// all ok, so just close the connection normally
|
||||
context.connection.end(function(err2) {
|
||||
this.connection.end(function(err2) {
|
||||
// ignore this error if there is one, callback with the original error
|
||||
callback(err2)
|
||||
})
|
||||
}
|
||||
}).bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
function createConnection(callback) {
|
||||
Patcher.prototype.createConnection = function createConnection(callback) {
|
||||
// when creating the database, we need to connect without a database name
|
||||
var opts = clone(this.options)
|
||||
delete opts.database
|
||||
|
@ -88,7 +101,7 @@ function createConnection(callback) {
|
|||
})
|
||||
}
|
||||
|
||||
function createDatabase(callback) {
|
||||
Patcher.prototype.createDatabase = function createDatabase(callback) {
|
||||
if ( this.options.createDatabase ) {
|
||||
this.connection.query(
|
||||
'CREATE DATABASE IF NOT EXISTS ' + this.options.database + ' CHARACTER SET utf8 COLLATE utf8_unicode_ci',
|
||||
|
@ -100,7 +113,7 @@ function createDatabase(callback) {
|
|||
}
|
||||
}
|
||||
|
||||
function changeUser(callback) {
|
||||
Patcher.prototype.changeUser = function changeUser(callback) {
|
||||
this.connection.changeUser(
|
||||
{
|
||||
user : this.options.user,
|
||||
|
@ -111,68 +124,64 @@ function changeUser(callback) {
|
|||
)
|
||||
}
|
||||
|
||||
function checkDbMetadataExists(callback) {
|
||||
var ctx = this
|
||||
Patcher.prototype.checkDbMetadataExists = function checkDbMetadataExists(callback) {
|
||||
var query = "SELECT COUNT(*) AS count FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?"
|
||||
this.connection.query(
|
||||
query,
|
||||
[ ctx.options.database, ctx.options.metaTable ],
|
||||
function (err, result) {
|
||||
[ this.options.database, this.options.metaTable ],
|
||||
(function (err, result) {
|
||||
if (err) { return callback(err) }
|
||||
ctx.metaTableExists = result[0].count === 0 ? false : true
|
||||
this.metaTableExists = result[0].count === 0 ? false : true
|
||||
callback()
|
||||
}
|
||||
}).bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
function readDbPatchLevel(callback) {
|
||||
var ctx = this
|
||||
|
||||
if ( ctx.metaTableExists === false ) {
|
||||
Patcher.prototype.readDbPatchLevel = function readDbPatchLevel(callback) {
|
||||
if ( this.metaTableExists === false ) {
|
||||
// the table doesn't exist, so start at patch level 0
|
||||
ctx.currentPatchLevel = 0
|
||||
this.currentPatchLevel = 0
|
||||
process.nextTick(callback)
|
||||
return
|
||||
}
|
||||
|
||||
// find out what patch level the database is currently at
|
||||
var query = "SELECT value FROM " + ctx.options.metaTable + " WHERE name = ?"
|
||||
ctx.connection.query(
|
||||
var query = "SELECT value FROM " + this.options.metaTable + " WHERE name = ?"
|
||||
this.connection.query(
|
||||
query,
|
||||
[ ctx.options.patchKey ],
|
||||
function(err, result) {
|
||||
[ this.options.patchKey ],
|
||||
(function(err, result) {
|
||||
if (err) { return callback(err) }
|
||||
|
||||
if ( result.length === 0 ) {
|
||||
// nothing in the table yet
|
||||
ctx.currentPatchLevel = 0
|
||||
this.currentPatchLevel = 0
|
||||
}
|
||||
else {
|
||||
// convert the patch level from a string to a number
|
||||
ctx.currentPatchLevel = +result[0].value
|
||||
this.currentPatchLevel = +result[0].value
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
}).bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
function readPatchFiles(callback) {
|
||||
var ctx = this
|
||||
Patcher.prototype.readPatchFiles = function readPatchFiles(callback) {
|
||||
|
||||
ctx.patches = {}
|
||||
this.patches = {}
|
||||
|
||||
fs.readdir(ctx.options.dir, function(err, files) {
|
||||
fs.readdir(this.options.dir, (function(err, files) {
|
||||
if (err) return callback(err)
|
||||
|
||||
files = files.map(function(filename) {
|
||||
return path.join(ctx.options.dir, filename)
|
||||
})
|
||||
files = files.map((function(filename) {
|
||||
return path.join(this.options.dir, filename)
|
||||
}).bind(this))
|
||||
|
||||
async.eachLimit(
|
||||
files,
|
||||
10,
|
||||
function(filename, done) {
|
||||
(function(filename, done) {
|
||||
var m = filename.match(/-(\d+)-(\d+)\.sql$/)
|
||||
if ( !m ) {
|
||||
return done(new Error('Unknown file format: ' + filename))
|
||||
|
@ -180,42 +189,41 @@ function readPatchFiles(callback) {
|
|||
|
||||
var from = parseInt(m[1], 10)
|
||||
var to = parseInt(m[2], 10)
|
||||
ctx.patches[from] = ctx.patches[from] || {}
|
||||
this.patches[from] = this.patches[from] || {}
|
||||
|
||||
fs.readFile(filename, { encoding : 'utf8' }, function(err, data) {
|
||||
fs.readFile(filename, { encoding : 'utf8' }, (function(err, data) {
|
||||
if (err) return done(err)
|
||||
ctx.patches[from][to] = data
|
||||
this.patches[from][to] = data
|
||||
done()
|
||||
})
|
||||
},
|
||||
}).bind(this))
|
||||
}).bind(this),
|
||||
function(err) {
|
||||
if (err) return callback(err)
|
||||
callback()
|
||||
}
|
||||
)
|
||||
})
|
||||
}).bind(this))
|
||||
}
|
||||
|
||||
function checkAllPatchesAvailable(callback) {
|
||||
var ctx = this
|
||||
Patcher.prototype.checkAllPatchesAvailable = function checkAllPatchesAvailable(callback) {
|
||||
|
||||
ctx.patchesToApply = []
|
||||
this.patchesToApply = []
|
||||
|
||||
// if we don't need any patches
|
||||
if ( ctx.options.patchLevel === ctx.currentPatchLevel ) {
|
||||
if ( this.options.patchLevel === this.currentPatchLevel ) {
|
||||
process.nextTick(callback)
|
||||
return
|
||||
}
|
||||
|
||||
// First, loop through all the patches we need to apply to make sure they exist.
|
||||
var direction = ctx.currentPatchLevel < ctx.options.patchLevel ? 1 : -1
|
||||
var currentPatchLevel = ctx.currentPatchLevel
|
||||
var direction = this.currentPatchLevel < this.options.patchLevel ? 1 : -1
|
||||
var currentPatchLevel = this.currentPatchLevel
|
||||
var nextPatchLevel
|
||||
while ( currentPatchLevel !== ctx.options.patchLevel ) {
|
||||
while ( currentPatchLevel !== this.options.patchLevel ) {
|
||||
nextPatchLevel = currentPatchLevel + direction
|
||||
|
||||
// check that this patch exists
|
||||
if ( !ctx.patches[currentPatchLevel] || !ctx.patches[currentPatchLevel][nextPatchLevel] ) {
|
||||
if ( !this.patches[currentPatchLevel] || !this.patches[currentPatchLevel][nextPatchLevel] ) {
|
||||
process.nextTick(function() {
|
||||
callback(new Error('Patch from level ' + currentPatchLevel + ' to ' + nextPatchLevel + ' does not exist'))
|
||||
})
|
||||
|
@ -223,8 +231,8 @@ function checkAllPatchesAvailable(callback) {
|
|||
}
|
||||
|
||||
// add this patch onto the patchesToApply
|
||||
ctx.patchesToApply.push({
|
||||
sql : ctx.patches[currentPatchLevel][nextPatchLevel],
|
||||
this.patchesToApply.push({
|
||||
sql : this.patches[currentPatchLevel][nextPatchLevel],
|
||||
from : currentPatchLevel,
|
||||
to : nextPatchLevel,
|
||||
})
|
||||
|
@ -234,21 +242,19 @@ function checkAllPatchesAvailable(callback) {
|
|||
callback()
|
||||
}
|
||||
|
||||
function applyPatches(callback) {
|
||||
var ctx = this
|
||||
|
||||
Patcher.prototype.applyPatches = function applyPatches(callback) {
|
||||
async.eachSeries(
|
||||
ctx.patchesToApply,
|
||||
function(patch, donePatch) {
|
||||
this.patchesToApply,
|
||||
(function(patch, donePatch) {
|
||||
// emit : 'Updating DB for patch ' + patch.from + ' to ' + patch.to
|
||||
ctx.connection.query(patch.sql, function(err, info) {
|
||||
this.connection.query(patch.sql, (function(err, info) {
|
||||
if (err) return donePatch(err)
|
||||
|
||||
// check that the database is now at the (intermediate) patch level
|
||||
var query = "SELECT value FROM " + ctx.options.metaTable + " WHERE name = ?"
|
||||
ctx.connection.query(
|
||||
var query = "SELECT value FROM " + this.options.metaTable + " WHERE name = ?"
|
||||
this.connection.query(
|
||||
query,
|
||||
[ ctx.options.patchKey ],
|
||||
[ this.options.patchKey ],
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
// this is not an error if we are wanting to patch to level 0
|
||||
|
@ -277,23 +283,33 @@ function applyPatches(callback) {
|
|||
donePatch()
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
}).bind(this))
|
||||
}).bind(this),
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function closeConnection(callback) {
|
||||
Patcher.prototype.closeConnection = function closeConnection(callback) {
|
||||
this.connection.end(callback)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// A much simpler, stateless function for just doing a patch.
|
||||
|
||||
Patcher.patch = function patch(options, callback) {
|
||||
callback = callback || noop
|
||||
|
||||
try {
|
||||
var patcher = new Patcher(options);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
patcher.patch(callback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// main export
|
||||
module.exports.patch = patch
|
||||
// and these for testing purposes
|
||||
module.exports.createDatabase = createDatabase
|
||||
module.exports.changeUser = changeUser
|
||||
module.exports.checkDbMetadataExists = checkDbMetadataExists
|
||||
module.exports.readDbPatchLevel = readDbPatchLevel
|
||||
module.exports.readPatchFiles = readPatchFiles
|
||||
module.exports.checkAllPatchesAvailable = checkAllPatchesAvailable
|
||||
module.exports.applyPatches = applyPatches
|
||||
module.exports = Patcher
|
||||
|
|
|
@ -5,27 +5,32 @@ var path = require('path')
|
|||
|
||||
var test = require('tape')
|
||||
var patcher = require('../')
|
||||
var mockMySQL = require('./mock-mysql')
|
||||
|
||||
test('check that the changeUser is doing the right thing', function(t) {
|
||||
t.plan(2)
|
||||
t.plan(5)
|
||||
|
||||
var count = 0
|
||||
var ctx = {
|
||||
connection : {
|
||||
var options = {
|
||||
user : 'user',
|
||||
password : 'password',
|
||||
database : 'database',
|
||||
dir : 'nonexistent',
|
||||
patchLevel: 0,
|
||||
mysql : mockMySQL({
|
||||
changeUser : function(obj, callback) {
|
||||
t.deepEqual(obj, ctx.options, 'The changeUser was passed user, password and database as expected')
|
||||
t.equal(obj.user, options.user, 'changeUser was passed user correctly')
|
||||
t.equal(obj.password, options.password, 'changeUser was passed password correctly')
|
||||
t.equal(obj.database, options.database, 'changeUser was passed database correctly')
|
||||
process.nextTick(callback)
|
||||
},
|
||||
},
|
||||
options : {
|
||||
user : 'user',
|
||||
password : 'password',
|
||||
database : 'database',
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
patcher.changeUser.call(ctx, function(err) {
|
||||
var p = new patcher(options);
|
||||
p.createConnection(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
p.changeUser(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,49 +5,54 @@ var path = require('path')
|
|||
|
||||
var test = require('tape')
|
||||
var patcher = require('../')
|
||||
var mockMySQL = require('./mock-mysql')
|
||||
|
||||
test('check that createDatabase is being called when asked for', function(t) {
|
||||
t.plan(2)
|
||||
t.plan(3)
|
||||
|
||||
var count = 0
|
||||
var ctx = {
|
||||
connection : {
|
||||
var p = new patcher({
|
||||
database : 'database',
|
||||
createDatabase : true,
|
||||
dir: 'nonexistent',
|
||||
patchLevel: 0,
|
||||
mysql : mockMySQL({
|
||||
query : function(sql, callback) {
|
||||
t.equal(sql, 'CREATE DATABASE IF NOT EXISTS database CHARACTER SET utf8 COLLATE utf8_unicode_ci')
|
||||
callback()
|
||||
},
|
||||
},
|
||||
options : {
|
||||
database : 'database',
|
||||
createDatabase : true,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
patcher.createDatabase.call(ctx, function(err) {
|
||||
p.createConnection(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
p.createDatabase(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('check that createDatabase is not being called when false', function(t) {
|
||||
t.plan(1)
|
||||
t.plan(2)
|
||||
|
||||
var count = 0
|
||||
var ctx = {
|
||||
connection : {
|
||||
var p = new patcher({
|
||||
database : 'database',
|
||||
createDatabase : false,
|
||||
dir: 'nonexistent',
|
||||
patchLevel: 0,
|
||||
mysql : mockMySQL({
|
||||
query : function(sql, callback) {
|
||||
t.fail('.query() should not have been called with the create database command')
|
||||
callback()
|
||||
},
|
||||
},
|
||||
options : {
|
||||
database : 'database',
|
||||
createDatabase : false,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
patcher.createDatabase.call(ctx, function(err) {
|
||||
p.createConnection(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
p.createDatabase(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var xtend = require('xtend')
|
||||
|
||||
var defaultConnectionMethods = {
|
||||
connect: function mockConnect(callback) {
|
||||
return callback()
|
||||
},
|
||||
query: function mockQuery() {
|
||||
throw 'query() should not have been called'
|
||||
},
|
||||
changeUser: function mockChangeUser() {
|
||||
throw 'changeUser() should not have been called'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function mockMySQL(mockMethods) {
|
||||
return {
|
||||
createConnection: function(callback) {
|
||||
return xtend(defaultConnectionMethods, mockMethods || {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
189
test/patches.js
189
test/patches.js
|
@ -5,19 +5,20 @@ var path = require('path')
|
|||
|
||||
var test = require('tape')
|
||||
var patcher = require('../')
|
||||
var mockMySQL = require('./mock-mysql')
|
||||
|
||||
test('read patch set (ok)', function (t) {
|
||||
var ctx = {
|
||||
options : {
|
||||
dir : path.join(__dirname, 'patches'),
|
||||
},
|
||||
}
|
||||
|
||||
// call readPatchFiles() with the above context
|
||||
patcher.readPatchFiles.call(ctx, function(err) {
|
||||
var p = new patcher({
|
||||
dir : path.join(__dirname, 'patches'),
|
||||
patchLevel : 0,
|
||||
mysql : mockMySQL()
|
||||
})
|
||||
|
||||
p.readPatchFiles(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
|
||||
var patches = ctx.patches
|
||||
var patches = p.patches
|
||||
|
||||
// check there are 3 patch levels
|
||||
var levels = Object.keys(patches).length
|
||||
|
@ -39,29 +40,30 @@ test('read patch set (ok)', function (t) {
|
|||
})
|
||||
|
||||
test('check all patches are available (forwards)', function(t) {
|
||||
var ctx = {
|
||||
options : {
|
||||
patchLevel : 2,
|
||||
},
|
||||
currentPatchLevel : 0,
|
||||
patches : {
|
||||
'0' : {
|
||||
'1' : '-- 0->1\n',
|
||||
},
|
||||
'1' : {
|
||||
'2' : '-- 1->2\n',
|
||||
},
|
||||
|
||||
var p = new patcher({
|
||||
patchLevel : 2,
|
||||
dir : "nonexistent",
|
||||
mysql : mockMySQL()
|
||||
})
|
||||
p.currentPatchLevel = 0
|
||||
p.patches = {
|
||||
'0' : {
|
||||
'1' : '-- 0->1\n',
|
||||
},
|
||||
'1' : {
|
||||
'2' : '-- 1->2\n',
|
||||
}
|
||||
}
|
||||
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
p.checkAllPatchesAvailable(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
|
||||
var patches = [
|
||||
{ sql : '-- 0->1\n', from : 0, to : 1, },
|
||||
{ sql : '-- 1->2\n', from : 1, to : 2, },
|
||||
]
|
||||
t.deepEqual(ctx.patchesToApply, patches, 'The patches to be applied')
|
||||
t.deepEqual(p.patchesToApply, patches, 'The patches to be applied')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
@ -69,29 +71,29 @@ test('check all patches are available (forwards)', function(t) {
|
|||
})
|
||||
|
||||
test('check all patches are available (backwards)', function(t) {
|
||||
var ctx = {
|
||||
options : {
|
||||
patchLevel : 0,
|
||||
},
|
||||
currentPatchLevel : 2,
|
||||
patches : {
|
||||
'2' : {
|
||||
'1' : '-- 2->1\n',
|
||||
},
|
||||
'1' : {
|
||||
'0' : '-- 1->0\n',
|
||||
},
|
||||
var p = new patcher({
|
||||
patchLevel : 0,
|
||||
dir : "nonexistent",
|
||||
mysql : mockMySQL()
|
||||
})
|
||||
p.currentPatchLevel = 2
|
||||
p.patches = {
|
||||
'2' : {
|
||||
'1' : '-- 2->1\n',
|
||||
},
|
||||
'1' : {
|
||||
'0' : '-- 1->0\n',
|
||||
}
|
||||
}
|
||||
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
p.checkAllPatchesAvailable(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
|
||||
var patches = [
|
||||
{ sql : '-- 2->1\n', from : 2, to : 1, },
|
||||
{ sql : '-- 1->0\n', from : 1, to : 0, },
|
||||
]
|
||||
t.deepEqual(ctx.patchesToApply, patches, 'The patches to be applied')
|
||||
t.deepEqual(p.patchesToApply, patches, 'The patches to be applied')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
@ -99,19 +101,19 @@ test('check all patches are available (backwards)', function(t) {
|
|||
})
|
||||
|
||||
test('check all patches are available (fails, no patch #2)', function(t) {
|
||||
var ctx = {
|
||||
options : {
|
||||
patchLevel : 2,
|
||||
},
|
||||
currentPatchLevel : 0,
|
||||
patches : {
|
||||
'0' : {
|
||||
'1' : '-- 0->1\n',
|
||||
},
|
||||
},
|
||||
var p = new patcher({
|
||||
patchLevel : 2,
|
||||
dir : "nonexistent",
|
||||
mysql : mockMySQL()
|
||||
})
|
||||
p.currentPatchLevel = 0
|
||||
p.patches = {
|
||||
'0' : {
|
||||
'1' : '-- 0->1\n',
|
||||
}
|
||||
}
|
||||
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
p.checkAllPatchesAvailable(function(err) {
|
||||
t.ok(err, 'An error occurred since patch 2 is missing')
|
||||
|
||||
t.equal(err.message, 'Patch from level 1 to 2 does not exist', 'The error message is correct')
|
||||
|
@ -121,19 +123,19 @@ test('check all patches are available (fails, no patch #2)', function(t) {
|
|||
})
|
||||
|
||||
test('check all patches are available (fails, no patch #1)', function(t) {
|
||||
var ctx = {
|
||||
options : {
|
||||
patchLevel : 2,
|
||||
},
|
||||
currentPatchLevel : 0,
|
||||
patches : {
|
||||
'1' : {
|
||||
'2' : '-- 1->2\n',
|
||||
},
|
||||
},
|
||||
var p = new patcher({
|
||||
patchLevel : 2,
|
||||
dir : "nonexistent",
|
||||
mysql : mockMySQL()
|
||||
})
|
||||
p.currentPatchLevel = 0
|
||||
p.patches = {
|
||||
'1' : {
|
||||
'2' : '-- 1->2\n',
|
||||
}
|
||||
}
|
||||
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
p.checkAllPatchesAvailable(function(err) {
|
||||
t.ok(err, 'An error occurred since patch 1 is missing')
|
||||
|
||||
t.equal(err.message, 'Patch from level 0 to 1 does not exist', 'The error message is correct')
|
||||
|
@ -144,13 +146,12 @@ test('check all patches are available (fails, no patch #1)', function(t) {
|
|||
|
||||
test('checking that these patch files are executed', function(t) {
|
||||
var count = 0
|
||||
var ctx = {
|
||||
options : {
|
||||
dir : path.join(__dirname, 'end-to-end'),
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'schema-patch-level',
|
||||
},
|
||||
connection : {
|
||||
var p = new patcher({
|
||||
dir : path.join(__dirname, 'end-to-end'),
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'schema-patch-level',
|
||||
patchLevel : 0,
|
||||
mysql : mockMySQL({
|
||||
query : function(sql, args, callback) {
|
||||
if ( typeof callback === 'undefined' ) {
|
||||
callback = args
|
||||
|
@ -160,20 +161,24 @@ test('checking that these patch files are executed', function(t) {
|
|||
if ( sql.match(/SELECT value FROM metadata WHERE name/) ) {
|
||||
return callback(null, [])
|
||||
}
|
||||
t.equal(sql, ctx.patchesToApply[count].sql, 'SQL is correct')
|
||||
t.equal(sql, p.patchesToApply[count].sql, 'SQL is correct')
|
||||
count += 1
|
||||
callback(null, [])
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
p.currentPatchLevel = 0
|
||||
|
||||
patcher.readPatchFiles.call(ctx, function(err) {
|
||||
p.createConnection(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
p.readPatchFiles(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
patcher.applyPatches.call(ctx, function(err) {
|
||||
p.checkAllPatchesAvailable(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
p.applyPatches(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -181,28 +186,30 @@ test('checking that these patch files are executed', function(t) {
|
|||
})
|
||||
|
||||
test('checking that an error comes back if a patch is missing', function(t) {
|
||||
t.plan(3)
|
||||
t.plan(4)
|
||||
|
||||
var count = 0
|
||||
var ctx = {
|
||||
options : {
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'level',
|
||||
},
|
||||
patchesToApply : [
|
||||
{ sql : '-- 0->1' },
|
||||
],
|
||||
connection : {
|
||||
var p = new patcher({
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'level',
|
||||
patchLevel : 0,
|
||||
dir : "nonexistent",
|
||||
mysql : mockMySQL({
|
||||
query : function(sql, callback) {
|
||||
t.equal(sql, '-- 0->1', 'The sql is what is expected')
|
||||
callback(new Error('Something went wrong'))
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
p.patchesToApply = [
|
||||
{ sql : '-- 0->1' },
|
||||
]
|
||||
|
||||
patcher.applyPatches.call(ctx, function(err) {
|
||||
t.ok(err, 'An error occurred')
|
||||
t.equal(err.message, 'Something went wrong', 'The message is correct')
|
||||
t.end()
|
||||
p.createConnection(function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
p.applyPatches(function(err) {
|
||||
t.ok(err, 'An error occurred')
|
||||
t.equal(err.message, 'Something went wrong', 'The message is correct')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче