diff --git a/lib/configuration.js b/lib/configuration.js index 7682c5c0b713a8a400292af9c316d5cc20232519..b6523801135ed7dc8d4538efe43ed4cc5d83fc18 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -114,16 +114,13 @@ if (undefined !== process.env['NODE_EXTRA_CONFIG']) { // test environments are variations on local g_configs.test_json = JSON.parse(JSON.stringify(g_configs.local)); g_configs.test_json.database = { - driver: "json", - // use a temporary path for testing - path: temp.path({suffix: '.db'}) + driver: "json" }; g_configs.test_mysql = JSON.parse(JSON.stringify(g_configs.local)); g_configs.test_mysql.database = { driver: "mysql", user: "test", - database: "browserid_" + secrets.generate(6), create_schema: true }; diff --git a/lib/db.js b/lib/db.js index fa05e1b640e59185816399a17f855f849ce31692..b71dcf8894660df2447c43bb07ac5e48d40ec21b 100644 --- a/lib/db.js +++ b/lib/db.js @@ -77,12 +77,21 @@ exports.open = function(cfg, cb) { exports.close = function(cb) { + checkReady(); driver.close(function(err) { ready = false; if (cb) cb(err); }); }; +exports.closeAndRemove = function(cb) { + checkReady(); + driver.closeAndRemove(function(err) { + ready = false; + if (cb) cb(err); + }); +}; + // accepts a function that will be invoked once the database is ready for transactions. // this hook is important to pause the rest of application startup until async database // connection establishment is complete. diff --git a/lib/db/json.js b/lib/db/json.js index 9b393950ed17884c76fcdda1bdf750bcc2e08538..d26202992c7689a4ee84883e2f7aa5a1daad5bb8 100644 --- a/lib/db/json.js +++ b/lib/db/json.js @@ -52,8 +52,6 @@ const ESC = JSON.stringify; var dbPath = path.join(configuration.get('var_path'), "authdb.json"); -var drop_on_close = undefined; - /* The JSON database. The structure is thus: * [ * { @@ -102,25 +100,19 @@ exports.open = function(cfg, cb) { } logger.debug("opening JSON database: " + dbPath); - if (cfg && cfg.drop_on_close) { - logger.debug("will remove database upon close"); - drop_on_close = true; - } - sync(); setTimeout(cb, 0); }; -exports.close = function(cb) { +exports.closeAndRemove = function(cb) { flush(); + fs.unlink(dbPath, function(err) { cb(err === null ? undefined : err); }); +}; - if (drop_on_close) { - drop_on_close = undefined; - fs.unlink(dbPath, function(err) { cb(err === null ? undefined : err); }); - } else { - setTimeout(cb, 0); - } +exports.close = function(cb) { + flush(); + setTimeout(function() { cb(undefined) }, 0); }; exports.emailKnown = function(email, cb) { diff --git a/lib/db/mysql.js b/lib/db/mysql.js index 08295f7f4c562a4c805018e2e559762d4102da17..97070a51cdd84d5bf83aa23a21616761c57ed6a0 100644 --- a/lib/db/mysql.js +++ b/lib/db/mysql.js @@ -65,9 +65,6 @@ logger = require('../logging.js').logger; var client = undefined; -// may get defined at open() time causing a database to be dropped upon connection closing. -var drop_on_close = undefined; - // If you change these schemas, please notify <services-ops@mozilla.com> const schemas = [ "CREATE TABLE IF NOT EXISTS user (" + @@ -122,13 +119,9 @@ exports.open = function(cfg, cb) { var database = cfg.database; if (!database) database = "browserid"; - // if the client specifies a name other than 'browserid', and specifies - // that we should drop the database on close, do it - if (database !== 'browserid' && cfg.drop_on_close) drop_on_close = database; - // create the client function doConnect() { - logger.debug("connecting to database"); + logger.debug("connecting to database: " + database); options.database = database; client = mysql.createClient(options); client.ping(function(err) { @@ -185,22 +178,25 @@ exports.open = function(cfg, cb) { }; exports.close = function(cb) { - function endConn() { - client.end(function(err) { - client = undefined; - if (err) logUnexpectedError(err); - if (cb) cb(err); - }); - } - // when unit_test is specified at open time, we use a temporary database, - // and clean it up upon close. - if (drop_on_close) { - client.query("DROP DATABASE " + drop_on_close, function() { - endConn(); - }); - } else { - endConn(); + client.end(function(err) { + client = undefined; + if (err) logUnexpectedError(err); + if (cb) cb(err); + }); +}; + +exports.closeAndRemove = function(cb) { + var db_to_remove = client.database; + + // don't let this happen if the name of the database is 'browserid', + // as a sanity check + if (db_to_remove === 'browserid') { + throw "dropping a database named 'browserid' is not allowed"; } + + client.query("DROP DATABASE " + db_to_remove, function(err) { + exports.close(cb); + }); }; exports.emailKnown = function(email, cb) { @@ -258,11 +254,9 @@ exports.emailForVerificationSecret = function(secret, cb) { }; exports.verificationSecretForEmail = function(email, cb) { - console.log("looking for " + email); client.query( "SELECT secret FROM staged WHERE email = ?", [ email ], function(err, rows) { - console.log("got", err, rows); if (err) logUnexpectedError(err); cb((rows && rows.length > 0) ? rows[0].secret : undefined); }); diff --git a/scripts/run_locally.js b/scripts/run_locally.js index 344e09a1133cbfd1c61eba19e89af6fae49e4d7a..979a85aeca31bde6d1b5b92857df2efaaa758bd4 100755 --- a/scripts/run_locally.js +++ b/scripts/run_locally.js @@ -2,7 +2,10 @@ const spawn = require('child_process').spawn, -path = require('path'); +path = require('path'), +config = require('../lib/configuration.js'), +temp = require('temp'), +secrets = require('../lib/secrets.js'); exports.daemons = daemons = {}; @@ -41,6 +44,19 @@ process.env['BROWSERID_URL'] = 'http://' + HOST + ":10002"; process.env['VERIFIER_URL'] = 'http://' + HOST + ":10000/verify"; process.env['KEYSIGNER_URL'] = 'http://' + HOST + ":10003"; +// if the environment is a 'test_' environment, then we'll use an +// ephemeral database +if (config.get('env').substr(0,5) === 'test_') { + if (config.get('database').driver === 'mysql') { + process.env['MYSQL_DATABASE_NAME'] = + process.env['MYSQL_DATABASE_NAME'] ||"browserid_tmp_" + secrets.generate(6); + console.log("temp mysql database:", process.env['MYSQL_DATABASE_NAME']); + } else if (config.get('database').driver === 'json') { + process.env['JSON_DATABASE_PATH'] = process.env['JSON_DATABASE_PATH'] || temp.path({suffix: '.db'}); + console.log("temp json database:", process.env['JSON_DATABASE_PATH']); + } +} + function runDaemon(daemon, cb) { Object.keys(daemonsToRun[daemon]).forEach(function(ek) { process.env[ek] = daemonsToRun[daemon][ek]; @@ -53,8 +69,8 @@ function runDaemon(daemon, cb) { if (d.length === 0) return; console.log(daemon, '(' + p.pid + '):', d); - // when we find a line that looks like 'running on <url>' then we've fully - // started up and can run the next daemon. see issue #556 + // when we find a line that looks like 'running on <url>' then we've + // fully started up and can run the next daemon. see issue #556 if (cb && /^.*running on http:\/\/.*:[0-9]+$/.test(d)) { cb(); cb = undefined; diff --git a/tests/db-test.js b/tests/db-test.js index 892319b46390218f6fb8651e063feed3c9fe9720..3fdba7a344b6b1bf1a15bbed3322c5aa18cc7ff5 100755 --- a/tests/db-test.js +++ b/tests/db-test.js @@ -290,6 +290,22 @@ suite.addBatch({ }, "should work": function(err) { assert.isUndefined(err); + }, + "re-opening the database": { + topic: function() { + db.open(dbCfg, this.callback); + }, + "works": function(r) { + assert.isUndefined(r); + }, + "and then purging": { + topic: function() { + db.closeAndRemove(this.callback); + }, + "works": function(r) { + assert.isUndefined(r); + } + } } } }); diff --git a/tests/lib/start-stop.js b/tests/lib/start-stop.js index 7432041dd78601ebf0945b55899cde37d858785a..b9115f510f6a4acc6b6c88f842aa8f8679f8f51a 100644 --- a/tests/lib/start-stop.js +++ b/tests/lib/start-stop.js @@ -209,9 +209,9 @@ exports.addShutdownBatches = function(suite) { // clean up suite.addBatch({ - "closing the database": { + "closing (and removing) the database": { topic: function() { - db.close(this.callback); + db.closeAndRemove(this.callback); }, "should work": function(err) { assert.isUndefined(err); diff --git a/tests/lib/test_env.js b/tests/lib/test_env.js index 40f12e4c835347f7383c4e92e5d181752da33a10..664fe4c32f4638567ca219620fd03f5abfbdb473 100644 --- a/tests/lib/test_env.js +++ b/tests/lib/test_env.js @@ -44,5 +44,14 @@ if (undefined === process.env['NODE_ENV']) { process.env['NODE_ENV'] = 'test_json'; } else if (process.env['NODE_ENV'].substr(0,5) !== 'test_') { console.log("(Woah. Running tests without a test_ configuration. Is this *really* what you want?)"); + process.exit(1); } +// if the environment is a 'test_' environment, then we'll use an +// ephemeral database +if (process.env['NODE_ENV'] === 'test_mysql') { + process.env['MYSQL_DATABASE_NAME'] = "browserid_tmp_" + + require('../../lib/secrets.js').generate(6); +} else if (process.env['NODE_ENV'] === 'test_json') { + process.env['JSON_DATABASE_PATH'] = require('temp').path({suffix: '.db'}); +}