Merge pull request #5 from chilts/i4-check-patch-level-after-each-patch
I4 check patch level after each patch
This commit is contained in:
Коммит
4330b25016
|
@ -109,6 +109,12 @@ e.g. Reverse patch file : `patch-02-01.sql`
|
|||
UPDATE metadata SET value = '1' WHERE name = 'schema-patch-level';
|
||||
```
|
||||
|
||||
## Changelog ##
|
||||
|
||||
### v0.4.0 - 2014-11-14 ###
|
||||
|
||||
* added a check between patches to make sure the patch level was incremented properly
|
||||
|
||||
## License ##
|
||||
|
||||
[Mozilla Public License v2](https://www.mozilla.org/MPL/2.0/)
|
||||
|
|
46
index.js
46
index.js
|
@ -13,6 +13,8 @@ var clone = require('clone')
|
|||
// globals for this package
|
||||
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
|
||||
|
@ -100,14 +102,14 @@ function changeUser(callback) {
|
|||
}
|
||||
|
||||
function checkDbMetadataExists(callback) {
|
||||
var context = this
|
||||
var ctx = this
|
||||
var query = "SELECT COUNT(*) AS count FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?"
|
||||
this.connection.query(
|
||||
query,
|
||||
[ this.options.database, this.options.metaTable ],
|
||||
[ ctx.options.database, ctx.options.metaTable ],
|
||||
function (err, result) {
|
||||
if (err) { return callback(err) }
|
||||
context.metaTableExists = result[0].count === 0 ? false : true
|
||||
ctx.metaTableExists = result[0].count === 0 ? false : true
|
||||
callback()
|
||||
}
|
||||
)
|
||||
|
@ -229,7 +231,43 @@ function applyPatches(callback) {
|
|||
ctx.patchesToApply,
|
||||
function(patch, donePatch) {
|
||||
// emit : 'Updating DB for patch ' + patch.from + ' to ' + patch.to
|
||||
ctx.connection.query(patch.sql, donePatch)
|
||||
ctx.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(
|
||||
query,
|
||||
[ ctx.options.patchKey ],
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
// this is not an error if we are wanting to patch to level 0
|
||||
// and the problem is that the metaTable is not there
|
||||
if ( patch.to === 0 && err.errno === ERR_NO_SUCH_TABLE ) {
|
||||
return donePatch()
|
||||
}
|
||||
|
||||
// otherwise, return this error since we don't know what it is
|
||||
return donePatch(err)
|
||||
}
|
||||
|
||||
if ( result.length === 0 ) {
|
||||
// nothing in the table yet
|
||||
return donePatch(new Error('The patchKey does not exist in the metaTable'))
|
||||
}
|
||||
|
||||
// convert the patch level from a string to a number
|
||||
result[0].value = +result[0].value
|
||||
|
||||
// check if this value is incorrect
|
||||
if ( result[0].value !== patch.to ) {
|
||||
return donePatch(new Error('Patch level in metaTable (%s) is incorrect after this patch (%s)', result[0].value, patch.to))
|
||||
}
|
||||
|
||||
donePatch()
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
callback
|
||||
)
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
"dependencies": {
|
||||
"async": "^0.9.0",
|
||||
"bluebird": "^2.3.0",
|
||||
"clone": "^0.1.18"
|
||||
"clone": "^0.1.18",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mysql": "^2.4.2",
|
||||
|
|
|
@ -12,7 +12,7 @@ var options = {
|
|||
createDatabase : true,
|
||||
user : 'root',
|
||||
database : 'patcher',
|
||||
// password : '',
|
||||
password : '',
|
||||
dir : path.join(__dirname, 'end-to-end'),
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'schema-patch-level',
|
||||
|
@ -24,14 +24,15 @@ var options = {
|
|||
var connection = mysql.createConnection(options)
|
||||
|
||||
test('run an end to end test, with no error (to patch 0)', function(t) {
|
||||
t.plan(5)
|
||||
|
||||
// change the expected patchLevel to 0
|
||||
options.patchLevel = 0
|
||||
|
||||
patcher.patch(options, function(err, res) {
|
||||
console.log(err, res)
|
||||
t.ok(!err, 'There was no error when patching the database')
|
||||
|
||||
// create a connection and check the metadata key has been updated to 3
|
||||
// check the metadata table does not yet exist
|
||||
connection.query("SELECT value FROM metadata WHERE name = 'schema-patch-level'", function(err, res) {
|
||||
t.ok(err, 'There was an error getting the database patch level')
|
||||
t.equal(err.code, 'ER_NO_SUCH_TABLE', 'No metadata table')
|
||||
|
@ -44,13 +45,15 @@ test('run an end to end test, with no error (to patch 0)', function(t) {
|
|||
})
|
||||
|
||||
test('run an end to end test, with no error(to patch 3)', function(t) {
|
||||
// change the expected patchLevel to 0
|
||||
t.plan(3)
|
||||
|
||||
// change the expected patchLevel to 3
|
||||
options.patchLevel = 3
|
||||
|
||||
patcher.patch(options, function(err, res) {
|
||||
t.ok(!err, 'There was no error when patching the database')
|
||||
|
||||
// create a connection and check the metadata key has been updated to 3
|
||||
// check the metadata key has been updated to 3
|
||||
connection.query("SELECT value FROM metadata WHERE name = 'schema-patch-level'", function(err, res) {
|
||||
t.ok(!err, 'There was no error getting the database patch level')
|
||||
|
||||
|
@ -61,9 +64,26 @@ test('run an end to end test, with no error(to patch 3)', function(t) {
|
|||
})
|
||||
})
|
||||
|
||||
test('run an end to end test, with no error (back to patch 0)', function(t) {
|
||||
t.plan(5)
|
||||
|
||||
// change the expected patchLevel to 0
|
||||
options.patchLevel = 0
|
||||
|
||||
patcher.patch(options, function(err, res) {
|
||||
t.ok(!err, 'There was no error when patching the database')
|
||||
|
||||
// check the metadata table no longer exists
|
||||
connection.query("SELECT value FROM metadata WHERE name = 'schema-patch-level'", function(err, res) {
|
||||
t.ok(err, 'There was an error getting the database patch level')
|
||||
t.equal(err.code, 'ER_NO_SUCH_TABLE', 'No metadata table')
|
||||
t.equal(err.errno, 1146, 'Correct error number')
|
||||
t.equal(err.message, "ER_NO_SUCH_TABLE: Table 'patcher.metadata' doesn't exist", 'Correct message')
|
||||
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('the last test, just to close the connection', function(t) {
|
||||
connection.end()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
-- Do Nothing
|
|
@ -0,0 +1 @@
|
|||
-- Do Nothing
|
|
@ -0,0 +1,10 @@
|
|||
-- Create the 'metadata' table.
|
||||
-- Note: This should be the only thing in this initial patch.
|
||||
|
||||
CREATE TABLE failed_insert (
|
||||
name VARCHAR(255) NOT NULL PRIMARY KEY,
|
||||
value VARCHAR(255) NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- Not doing this, so that the check fails.
|
||||
-- INSERT INTO failed_insert SET name = 'schema-patch-level', value = '1';
|
|
@ -0,0 +1,2 @@
|
|||
-- -- drop the metadata table
|
||||
DROP TABLE failed_insert;
|
|
@ -0,0 +1,75 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var path = require('path')
|
||||
|
||||
var xtend = require('xtend')
|
||||
var mysql = require('mysql')
|
||||
var test = require('tape')
|
||||
|
||||
var patcher = require('../')
|
||||
|
||||
var options = {
|
||||
createDatabase : true,
|
||||
user : 'root',
|
||||
database : 'patcher',
|
||||
password : '',
|
||||
patchKey : 'schema-patch-level',
|
||||
mysql : mysql,
|
||||
|
||||
// dir : ?, // \
|
||||
// metaTable : ?, // > to be set in each test
|
||||
// patchLevel : ?, // /
|
||||
}
|
||||
|
||||
test('run an end to end test, error updating metaTable in patch 1', function(t) {
|
||||
// set expected patch and metaTable
|
||||
var opts = xtend(
|
||||
options,
|
||||
{
|
||||
dir : path.join(__dirname, 'failed-insert'),
|
||||
patchLevel : 1,
|
||||
metaTable : 'failed_insert',
|
||||
}
|
||||
)
|
||||
|
||||
// create a connection for us to use directly
|
||||
var connection = mysql.createConnection(opts)
|
||||
|
||||
t.plan(3)
|
||||
|
||||
patcher.patch(opts, function(err, res) {
|
||||
t.ok(err, 'There was an error when patching the database')
|
||||
t.ok(!res, 'No result was returned')
|
||||
t.equal('' + err, 'Error: The patchKey does not exist in the metaTable', 'Error message is correct')
|
||||
connection.end()
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
|
||||
test('run an end to end test, metaTable not created', function(t) {
|
||||
// set expected patch and metaTable
|
||||
var opts = xtend(
|
||||
options,
|
||||
{
|
||||
dir : path.join(__dirname, 'failed-create-metatable'),
|
||||
patchLevel : 1,
|
||||
metaTable : 'failed_create_metatable',
|
||||
}
|
||||
)
|
||||
|
||||
// create a connection for us to use directly
|
||||
var connection = mysql.createConnection(opts)
|
||||
|
||||
t.plan(4)
|
||||
|
||||
patcher.patch(opts, function(err, res) {
|
||||
t.ok(err, 'There was an error when patching the database')
|
||||
t.ok(!res, 'No result was returned')
|
||||
// t.equal('' + err, 'Error: ER_NO_SUCH_TABLE: Table \'patcher.failed_create_metatable\' doesn\'t exist', 'Error message is correct')
|
||||
t.equal(err.errno, 1146, 'Error number is correct')
|
||||
t.equal(err.code, 'ER_NO_SUCH_TABLE', 'Error code is correct')
|
||||
connection.end()
|
||||
t.end()
|
||||
})
|
||||
})
|
|
@ -145,24 +145,39 @@ 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 : {
|
||||
query : function(sql, callback) {
|
||||
query : function(sql, args, callback) {
|
||||
if ( typeof callback === 'undefined' ) {
|
||||
callback = args
|
||||
args = undefined
|
||||
}
|
||||
// if this query is the patcher trying to get the patch level, ignore it
|
||||
if ( sql.match(/SELECT value FROM metadata WHERE name/) ) {
|
||||
return callback(null, [])
|
||||
}
|
||||
t.equal(sql, ctx.patchesToApply[count].sql, 'SQL is correct')
|
||||
count += 1
|
||||
callback()
|
||||
callback(null, [])
|
||||
},
|
||||
},
|
||||
patchesToApply : [
|
||||
{ sql : '-- 0->1' },
|
||||
{ sql : '-- 1->2' },
|
||||
{ sql : '-- 2->3' },
|
||||
],
|
||||
}
|
||||
|
||||
patcher.applyPatches.call(ctx, function(err) {
|
||||
patcher.readPatchFiles.call(ctx, function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
patcher.checkAllPatchesAvailable.call(ctx, function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
patcher.applyPatches.call(ctx, function(err) {
|
||||
t.ok(!err, 'No error occurred')
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
test('checking that an error comes back if a patch is missing', function(t) {
|
||||
|
@ -170,15 +185,19 @@ test('checking that an error comes back if a patch is missing', function(t) {
|
|||
|
||||
var count = 0
|
||||
var ctx = {
|
||||
options : {
|
||||
metaTable : 'metadata',
|
||||
patchKey : 'level',
|
||||
},
|
||||
patchesToApply : [
|
||||
{ sql : '-- 0->1' },
|
||||
],
|
||||
connection : {
|
||||
query : function(sql, callback) {
|
||||
t.equal(sql, '-- 0->1', 'The sql is what is expected')
|
||||
callback(new Error('Something went wrong'))
|
||||
},
|
||||
},
|
||||
patchesToApply : [
|
||||
{ sql : '-- 0->1' },
|
||||
],
|
||||
}
|
||||
|
||||
patcher.applyPatches.call(ctx, function(err) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче