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:
Andrew Chilton 2014-11-14 11:47:20 +13:00
Родитель ceac40049d b2570309c0
Коммит 4330b25016
10 изменённых файлов: 195 добавлений и 22 удалений

Просмотреть файл

@ -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/)

Просмотреть файл

@ -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;

75
test/failed.js Normal file
Просмотреть файл

@ -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) {