From 3dfca41acef08d99e06fd3cf7eec063ab01fefad Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 16:53:19 -0700 Subject: [PATCH 01/24] Starting on bookshelf migration --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 3690b62..59f1098 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ }, "dependencies": { "async": "0.2.9", + "bookshelf": "0.6.12", "commander": "2.1.0", "connect-flash": "0.1.1", "express": "3.4.6", "express-namespace": "0.1.1", "feedparser": "0.16.3", "jade": "0.35.0", + "knex": "0.5.15", "lodash": "2.4.1", "moment": "2.4.0", "murmurhash3": "0.2.3", From 0e36f4191b0f3f362f396b7cfce917455fe74bce Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 17:03:17 -0700 Subject: [PATCH 02/24] Migrate to standard config solution --- .gitignore | 1 - .travis.yml | 3 --- config/config.json.example | 23 ----------------------- config/default.json | 2 ++ config/development.json | 7 +++++++ config/production.json | 7 +++++++ config/runtime.json | 1 + config/test.json | 7 +++++++ models/index.js | 2 +- package.json | 1 + 10 files changed, 26 insertions(+), 28 deletions(-) delete mode 100644 config/config.json.example create mode 100644 config/default.json create mode 100644 config/development.json create mode 100644 config/production.json create mode 100644 config/runtime.json create mode 100644 config/test.json diff --git a/.gitignore b/.gitignore index 5977731..0645fdd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules coverage.html *.sqlite -config/config.json diff --git a/.travis.yml b/.travis.yml index b3b1afc..6e5919d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,3 @@ -# please wake up, travis language: node_js node_js: - "0.10" -before_script: - - cp config/config.json.example config/config.json diff --git a/config/config.json.example b/config/config.json.example deleted file mode 100644 index c309367..0000000 --- a/config/config.json.example +++ /dev/null @@ -1,23 +0,0 @@ -{ - "test": { - "dialect": "sqlite", - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1" - }, - "development": { - "dialect": "postgres", - "username": "rssmtp", - "password": "password_here", - "database": "rssmtp_development", - "host": "127.0.0.1" - }, - "production": { - "dialect": "postgres", - "username": "rssmtp", - "password": "password_here", - "database": "rssmtp_production", - "host": "127.0.0.1" - } -} diff --git a/config/default.json b/config/default.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/config/default.json @@ -0,0 +1,2 @@ +{ +} diff --git a/config/development.json b/config/development.json new file mode 100644 index 0000000..34b930c --- /dev/null +++ b/config/development.json @@ -0,0 +1,7 @@ +{ + "dialect": "sqlite", + "username": "root", + "password": null, + "database": "database_test", + "host": "127.0.0.1" +} diff --git a/config/production.json b/config/production.json new file mode 100644 index 0000000..9bf51e7 --- /dev/null +++ b/config/production.json @@ -0,0 +1,7 @@ +{ + "dialect": "postgres", + "username": "rssmtp", + "password": "password_here", + "database": "rssmtp_production", + "host": "127.0.0.1" +} diff --git a/config/runtime.json b/config/runtime.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/config/runtime.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/config/test.json b/config/test.json new file mode 100644 index 0000000..8991f80 --- /dev/null +++ b/config/test.json @@ -0,0 +1,7 @@ +{ + "dialect": "sqlite", + "username": "root", + "password": null, + "database": "database_test", + "host": "127.0.0.1" +} diff --git a/models/index.js b/models/index.js index 0544142..2f612e6 100644 --- a/models/index.js +++ b/models/index.js @@ -2,7 +2,7 @@ var Sequelize = require('sequelize') , path = require('path') , fs = require('fs') , env = process.env.NODE_ENV || 'development' - , config = require(__dirname + '/../config/config.json')[env] + , config = require('config') , _ = require('lodash') ; diff --git a/package.json b/package.json index 59f1098..4f2ed36 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "async": "0.2.9", "bookshelf": "0.6.12", "commander": "2.1.0", + "config": "^0.4.36", "connect-flash": "0.1.1", "express": "3.4.6", "express-namespace": "0.1.1", From 2545051e64c8e38cb4f8675dbe7d7214ddb04988 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 17:11:29 -0700 Subject: [PATCH 03/24] Placeholder model files --- models/article.js | 0 models/feed.js | 0 models/user.js | 0 test/models/article.js | 0 test/models/feed.js | 0 test/models/user.js | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 models/article.js create mode 100644 models/feed.js create mode 100644 models/user.js create mode 100644 test/models/article.js create mode 100644 test/models/feed.js create mode 100644 test/models/user.js diff --git a/models/article.js b/models/article.js new file mode 100644 index 0000000..e69de29 diff --git a/models/feed.js b/models/feed.js new file mode 100644 index 0000000..e69de29 diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..e69de29 diff --git a/test/models/article.js b/test/models/article.js new file mode 100644 index 0000000..e69de29 diff --git a/test/models/feed.js b/test/models/feed.js new file mode 100644 index 0000000..e69de29 diff --git a/test/models/user.js b/test/models/user.js new file mode 100644 index 0000000..e69de29 From 0054d5f4170378c36e01355d39609f13d7e7be3e Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 17:21:21 -0700 Subject: [PATCH 04/24] Namespace database config Also, use js instead of json, so we can have comments and pull things from ENV --- config/development.js | 9 +++++++++ config/development.json | 7 ------- config/production.js | 9 +++++++++ config/production.json | 7 ------- config/test.js | 9 +++++++++ config/test.json | 7 ------- models/index.js | 2 +- 7 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 config/development.js delete mode 100644 config/development.json create mode 100644 config/production.js delete mode 100644 config/production.json create mode 100644 config/test.js delete mode 100644 config/test.json diff --git a/config/development.js b/config/development.js new file mode 100644 index 0000000..42cfbe8 --- /dev/null +++ b/config/development.js @@ -0,0 +1,9 @@ +module.exports = { + database: { + dialect: "sqlite", + username: "root", + password: null, + database: "database_test", + host: "127.0.0.1" + } +}; diff --git a/config/development.json b/config/development.json deleted file mode 100644 index 34b930c..0000000 --- a/config/development.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dialect": "sqlite", - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1" -} diff --git a/config/production.js b/config/production.js new file mode 100644 index 0000000..f90db5e --- /dev/null +++ b/config/production.js @@ -0,0 +1,9 @@ +module.exports = { + database: { + dialect: "postgres", + username: "rssmtp", + password: "password_here", + database: "rssmtp_production", + host: "127.0.0.1" + } +}; diff --git a/config/production.json b/config/production.json deleted file mode 100644 index 9bf51e7..0000000 --- a/config/production.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dialect": "postgres", - "username": "rssmtp", - "password": "password_here", - "database": "rssmtp_production", - "host": "127.0.0.1" -} diff --git a/config/test.js b/config/test.js new file mode 100644 index 0000000..894ce78 --- /dev/null +++ b/config/test.js @@ -0,0 +1,9 @@ +module.exports = { + database: { + dialect: "sqlite", + username: "root", + password: null, + database: "database_test", + host: "127.0.0.1" + } +}; diff --git a/config/test.json b/config/test.json deleted file mode 100644 index 8991f80..0000000 --- a/config/test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dialect": "sqlite", - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1" -} diff --git a/models/index.js b/models/index.js index 2f612e6..243d5f8 100644 --- a/models/index.js +++ b/models/index.js @@ -6,7 +6,7 @@ var Sequelize = require('sequelize') , _ = require('lodash') ; -var options = _.merge({}, config, { +var options = _.merge({}, config.database, { define: { underscored: true , freezeTableName: true From 41c50ecb45cf5de8740c602d813a5093b6cb8605 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 17:27:51 -0700 Subject: [PATCH 05/24] Prepare configuration for knex --- config/development.js | 9 ++++++++- config/production.js | 12 +++++++++++- config/test.js | 9 ++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/config/development.js b/config/development.js index 42cfbe8..afc609e 100644 --- a/config/development.js +++ b/config/development.js @@ -1,9 +1,16 @@ module.exports = { database: { + // for sequelize dialect: "sqlite", username: "root", password: null, database: "database_test", - host: "127.0.0.1" + host: "127.0.0.1", + + // for bookshelf + client: 'sqlite', + connection: { + filename: ':memory:' + } } }; diff --git a/config/production.js b/config/production.js index f90db5e..b9518af 100644 --- a/config/production.js +++ b/config/production.js @@ -1,9 +1,19 @@ module.exports = { database: { + // for sequelize dialect: "postgres", username: "rssmtp", password: "password_here", database: "rssmtp_production", - host: "127.0.0.1" + host: "127.0.0.1", + + // for bookshelf + client: 'pg', + connection: { + host: "127.0.0.1", + username: "rssmtp", + password: "password_here", + database: "rssmtp_production" + } } }; diff --git a/config/test.js b/config/test.js index 894ce78..98dffa3 100644 --- a/config/test.js +++ b/config/test.js @@ -1,9 +1,16 @@ module.exports = { database: { + // sequelize dialect: "sqlite", username: "root", password: null, database: "database_test", - host: "127.0.0.1" + host: "127.0.0.1", + + // for bookshelf + client: 'sqlite', + connection: { + filename: ':memory:' + } } }; From 9ec0f78217782188328c61e127c283bbe152ccb5 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 21:10:06 -0700 Subject: [PATCH 06/24] Ditch shrinkwrap for now --- npm-shrinkwrap.json | 644 -------------------------------------------- 1 file changed, 644 deletions(-) delete mode 100644 npm-shrinkwrap.json diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json deleted file mode 100644 index 79cf454..0000000 --- a/npm-shrinkwrap.json +++ /dev/null @@ -1,644 +0,0 @@ -{ - "name": "rssmtp", - "version": "0.0.1", - "dependencies": { - "express": { - "version": "3.4.6", - "from": "express@~3", - "dependencies": { - "commander": { - "version": "1.3.2", - "from": "commander@1.3.2", - "dependencies": { - "keypress": { - "version": "0.1.0", - "from": "keypress@0.1.x" - } - } - }, - "range-parser": { - "version": "0.0.4", - "from": "range-parser@0.0.4" - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.5" - }, - "buffer-crc32": { - "version": "0.2.1", - "from": "buffer-crc32@0.2.1" - }, - "fresh": { - "version": "0.2.0", - "from": "fresh@0.2.0" - }, - "methods": { - "version": "0.1.0", - "from": "methods@0.1.0" - }, - "send": { - "version": "0.1.4", - "from": "send@0.1.4", - "dependencies": { - "mime": { - "version": "1.2.11", - "from": "mime@~1.2.9" - } - } - }, - "cookie-signature": { - "version": "1.0.1", - "from": "cookie-signature@1.0.1" - }, - "debug": { - "version": "0.7.4", - "from": "debug@>= 0.7.3 < 1" - } - } - }, - "jade": { - "version": "0.35.0", - "from": "jade@*", - "dependencies": { - "commander": { - "version": "2.0.0", - "from": "commander@~2.0.0" - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.x" - }, - "transformers": { - "version": "2.1.0", - "from": "transformers@2.1.0", - "dependencies": { - "promise": { - "version": "2.0.0", - "from": "promise@~2.0", - "dependencies": { - "is-promise": { - "version": "1.0.0", - "from": "is-promise@~1" - } - } - }, - "css": { - "version": "1.0.8", - "from": "css@~1.0.8", - "dependencies": { - "css-parse": { - "version": "1.0.4", - "from": "css-parse@1.0.4" - }, - "css-stringify": { - "version": "1.0.5", - "from": "css-stringify@1.0.5" - } - } - }, - "uglify-js": { - "version": "2.2.5", - "from": "uglify-js@~2.2.5", - "dependencies": { - "source-map": { - "version": "0.1.31", - "from": "source-map@~0.1.7", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "from": "amdefine@>=0.0.4" - } - } - }, - "optimist": { - "version": "0.3.7", - "from": "optimist@~0.3.5", - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "from": "wordwrap@~0.0.2" - } - } - } - } - } - } - }, - "character-parser": { - "version": "1.2.0", - "from": "character-parser@1.2.0" - }, - "monocle": { - "version": "1.1.50", - "from": "monocle@1.1.50", - "dependencies": { - "readdirp": { - "version": "0.2.5", - "from": "readdirp@~0.2.3", - "dependencies": { - "minimatch": { - "version": "0.2.12", - "from": "minimatch@>=0.2.4", - "dependencies": { - "lru-cache": { - "version": "2.5.0", - "from": "lru-cache@2" - }, - "sigmund": { - "version": "1.0.0", - "from": "sigmund@~1.0.0" - } - } - } - } - } - } - }, - "with": { - "version": "1.1.1", - "from": "with@~1.1.0", - "dependencies": { - "uglify-js": { - "version": "2.4.0", - "from": "uglify-js@2.4.0", - "dependencies": { - "source-map": { - "version": "0.1.31", - "from": "source-map@~0.1.7", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "from": "amdefine@>=0.0.4" - } - } - }, - "optimist": { - "version": "0.3.7", - "from": "optimist@~0.3.5", - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "from": "wordwrap@~0.0.2" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.1", - "from": "uglify-to-browserify@~1.0.0" - } - } - } - } - }, - "constantinople": { - "version": "1.0.2", - "from": "constantinople@~1.0.1", - "dependencies": { - "uglify-js": { - "version": "2.4.6", - "from": "uglify-js@~2.4.0", - "dependencies": { - "source-map": { - "version": "0.1.31", - "from": "source-map@~0.1.7", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "from": "amdefine@>=0.0.4" - } - } - }, - "optimist": { - "version": "0.3.7", - "from": "optimist@~0.3.5", - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "from": "wordwrap@~0.0.2" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.1", - "from": "uglify-to-browserify@~1.0.0" - } - } - } - } - } - } - }, - "feedparser": { - "version": "0.16.3", - "from": "feedparser@~0.16", - "dependencies": { - "sax": { - "version": "0.5.5", - "from": "sax@0.5.x" - }, - "addressparser": { - "version": "0.1.3", - "from": "addressparser@~0.1.3" - }, - "array-indexofobject": { - "version": "0.0.1", - "from": "array-indexofobject@0.0.1" - }, - "readable-stream": { - "version": "1.0.17", - "from": "readable-stream@1.0.x" - }, - "resanitize": { - "version": "0.3.0", - "from": "resanitize@~0.3.0", - "dependencies": { - "validator": { - "version": "1.5.1", - "from": "validator@~1.5.1" - } - } - } - } - }, - "nodemailer": { - "version": "0.5.13", - "from": "nodemailer@~0.5", - "dependencies": { - "mailcomposer": { - "version": "0.2.6", - "from": "mailcomposer@~0.2.5", - "dependencies": { - "mimelib": { - "version": "0.2.14", - "from": "mimelib@~0.2.14", - "dependencies": { - "encoding": { - "version": "0.1.7", - "from": "encoding@~0.1", - "dependencies": { - "iconv-lite": { - "version": "0.2.11", - "from": "iconv-lite@~0.2.11" - } - } - }, - "addressparser": { - "version": "0.2.0", - "from": "addressparser@~0.2.0" - } - } - }, - "mime": { - "version": "1.2.9", - "from": "mime@1.2.9" - }, - "punycode": { - "version": "1.2.3", - "from": "punycode@>=0.2.0" - }, - "dkim-signer": { - "version": "0.1.0", - "from": "dkim-signer@~0.1.0" - } - } - }, - "simplesmtp": { - "version": "0.3.16", - "from": "simplesmtp@~0.2 || ~0.3", - "dependencies": { - "rai": { - "version": "0.1.8", - "from": "rai@~0.1" - }, - "xoauth2": { - "version": "0.1.8", - "from": "xoauth2@~0.1" - } - } - }, - "directmail": { - "version": "0.1.5", - "from": "directmail@~0.1.1" - }, - "he": { - "version": "0.3.6", - "from": "he@~0.3.6" - }, - "readable-stream": { - "version": "1.1.9", - "from": "readable-stream@*", - "dependencies": { - "core-util-is": { - "version": "1.0.0", - "from": "core-util-is@~1.0.0" - }, - "debuglog": { - "version": "0.0.2", - "from": "debuglog@0.0.2" - } - } - } - } - }, - "async": { - "version": "0.2.9", - "from": "async@~0" - }, - "request": { - "version": "2.28.0", - "from": "request@~2", - "dependencies": { - "qs": { - "version": "0.6.6", - "from": "qs@~0.6.0" - }, - "json-stringify-safe": { - "version": "5.0.0", - "from": "json-stringify-safe@~5.0.0" - }, - "forever-agent": { - "version": "0.5.0", - "from": "forever-agent@~0.5.0" - }, - "node-uuid": { - "version": "1.4.1", - "from": "node-uuid@~1.4.0" - }, - "mime": { - "version": "1.2.11", - "from": "mime@~1.2.9" - }, - "tough-cookie": { - "version": "0.9.15", - "from": "tough-cookie@~0.9.15", - "dependencies": { - "punycode": { - "version": "1.2.3", - "from": "punycode@>=0.2.0" - } - } - }, - "form-data": { - "version": "0.1.2", - "from": "form-data@~0.1.0", - "dependencies": { - "combined-stream": { - "version": "0.0.4", - "from": "combined-stream@~0.0.4", - "dependencies": { - "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5" - } - } - } - } - }, - "tunnel-agent": { - "version": "0.3.0", - "from": "tunnel-agent@~0.3.0" - }, - "http-signature": { - "version": "0.10.0", - "from": "http-signature@~0.10.0", - "dependencies": { - "assert-plus": { - "version": "0.1.2", - "from": "assert-plus@0.1.2" - }, - "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11" - }, - "ctype": { - "version": "0.5.2", - "from": "ctype@0.5.2" - } - } - }, - "oauth-sign": { - "version": "0.3.0", - "from": "oauth-sign@~0.3.0" - }, - "hawk": { - "version": "1.0.0", - "from": "hawk@~1.0.0", - "dependencies": { - "hoek": { - "version": "0.9.1", - "from": "hoek@0.9.x" - }, - "boom": { - "version": "0.4.2", - "from": "boom@0.4.x" - }, - "cryptiles": { - "version": "0.2.2", - "from": "cryptiles@0.2.x" - }, - "sntp": { - "version": "0.2.4", - "from": "sntp@0.2.x" - } - } - }, - "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@~0.5.0" - } - } - }, - "passport": { - "version": "0.1.17", - "from": "passport@~0.1.16", - "dependencies": { - "pkginfo": { - "version": "0.2.3", - "from": "pkginfo@0.2.x" - }, - "pause": { - "version": "0.0.1", - "from": "pause@0.0.1" - } - } - }, - "passport-google-oauth": { - "version": "0.1.5", - "from": "passport-google-oauth@~0.1.5", - "dependencies": { - "pkginfo": { - "version": "0.2.3", - "from": "pkginfo@0.2.x" - }, - "passport-oauth": { - "version": "0.1.15", - "from": "passport-oauth@~0.1.4", - "dependencies": { - "oauth": { - "version": "0.9.10", - "from": "oauth@0.9.x" - } - } - } - } - }, - "express-namespace": { - "version": "0.1.1", - "from": "express-namespace@~0.1.1", - "dependencies": { - "methods": { - "version": "0.0.1", - "from": "methods@0.0.1" - } - } - }, - "underscore": { - "version": "1.5.2", - "from": "underscore@~1" - }, - "moment": { - "version": "2.4.0", - "from": "moment@~2" - }, - "murmurhash3": { - "version": "0.2.3", - "from": "murmurhash3@~0.2", - "resolved": "https://registry.npmjs.org/murmurhash3/-/murmurhash3-0.2.3.tgz" - }, - "connect-flash": { - "version": "0.1.1", - "from": "connect-flash@~0.1" - }, - "sequelize": { - "version": "1.7.0-beta.1", - "from": "sequelize@git+https://github.com/elliotf/sequelize.git#elliotf_customized", - "resolved": "git+https://github.com/elliotf/sequelize.git#0d4bdc9874d3d2ac602e28064796f1ca4eb15c6d", - "dependencies": { - "lodash": { - "version": "2.2.1", - "from": "lodash@~2.2.0" - }, - "underscore.string": { - "version": "2.3.3", - "from": "underscore.string@~2.3.0" - }, - "lingo": { - "version": "0.0.5", - "from": "lingo@~0.0.5" - }, - "validator": { - "version": "1.5.1", - "from": "validator@~1.5.1" - }, - "moment": { - "version": "2.2.1", - "from": "moment@~2.2.1" - }, - "commander": { - "version": "2.0.0", - "from": "commander@~2.0.0" - }, - "dottie": { - "version": "0.0.8-0", - "from": "dottie@0.0.8-0" - }, - "toposort-class": { - "version": "0.2.1", - "from": "toposort-class@~0.2.0" - }, - "generic-pool": { - "version": "2.0.4", - "from": "generic-pool@2.0.4" - }, - "promise": { - "version": "3.2.0", - "from": "promise@~3.2.0" - }, - "sql": { - "version": "0.28.0", - "from": "sql@~0.28.0", - "dependencies": { - "sliced": { - "version": "0.0.5", - "from": "sliced@0.0.x" - }, - "lodash": { - "version": "1.3.1", - "from": "lodash@1.3.x" - } - } - } - } - }, - "lodash": { - "version": "2.4.1", - "from": "lodash@~2" - }, - "pg": { - "version": "2.8.3", - "from": "pg@~2", - "dependencies": { - "generic-pool": { - "version": "2.0.3", - "from": "generic-pool@2.0.3" - }, - "buffer-writer": { - "version": "1.0.0", - "from": "buffer-writer@1.0.0", - "dependencies": { - "cloned": { - "version": "0.0.1", - "from": "cloned@0.0.1", - "dependencies": { - "rmdir": { - "version": "1.0.0", - "from": "rmdir@~1.0.0" - } - } - } - } - } - } - }, - "commander": { - "version": "2.1.0", - "from": "commander@~2" - }, - "prettyjson": { - "version": "0.9.0", - "from": "prettyjson@~0", - "dependencies": { - "colors": { - "version": "0.6.0-1", - "from": "colors@0.6.0-1" - } - } - }, - "resumer": { - "version": "0.0.0", - "from": "resumer@0.0.0", - "dependencies": { - "through": { - "version": "2.3.4", - "from": "through@~2.3.4" - } - } - }, - "sinon": { - "version": "1.7.3", - "from": "sinon@>=1.4.0 <2", - "dependencies": { - "buster-format": { - "version": "0.5.6", - "from": "buster-format@~0.5", - "dependencies": { - "buster-core": { - "version": "0.6.4", - "from": "buster-core@=0.6.4" - } - } - } - } - } - } -} From e17a480f7d9e799cad78f46484c0645b2d3f92de Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 7 Jun 2014 22:43:00 -0700 Subject: [PATCH 07/24] Use strict versioning for now --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4f2ed36..f118b36 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "async": "0.2.9", "bookshelf": "0.6.12", "commander": "2.1.0", - "config": "^0.4.36", + "config": "0.4.36", "connect-flash": "0.1.1", "express": "3.4.6", "express-namespace": "0.1.1", @@ -34,7 +34,7 @@ }, "devDependencies": { "blanket": "1.1.5", - "chai": "^1.9.1", + "chai": "1.9.1", "chai-fuzzy": "1.3.0", "cheerio": "0.12.4", "chicken-little": "0.1.2", From 6fa967e7e2e501e4f3efd9c6dfab28caf42647f4 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 14:02:28 -0700 Subject: [PATCH 08/24] Updated dependencies --- config/{development.js => default.js} | 5 +- config/default.json | 2 - config/dev.js | 2 + config/{production.js => prod.js} | 0 config/test.js | 1 + knexfile.js | 3 + .../20140608000221_initial-migration.js | 77 +++++++++++++++++++ models/base.js | 9 +++ models/user.js | 1 + package.json | 15 ++-- test/models/user.js | 11 +++ views/layout.jade | 2 +- 12 files changed, 116 insertions(+), 12 deletions(-) rename config/{development.js => default.js} (64%) delete mode 100644 config/default.json create mode 100644 config/dev.js rename config/{production.js => prod.js} (100%) create mode 100644 knexfile.js create mode 100644 migrations/20140608000221_initial-migration.js create mode 100644 models/base.js diff --git a/config/development.js b/config/default.js similarity index 64% rename from config/development.js rename to config/default.js index afc609e..be635c0 100644 --- a/config/development.js +++ b/config/default.js @@ -4,13 +4,14 @@ module.exports = { dialect: "sqlite", username: "root", password: null, - database: "database_test", + database: "db.sqlite", host: "127.0.0.1", + storage: __dirname + "/../dev-db.sqlite", // for bookshelf client: 'sqlite', connection: { - filename: ':memory:' + filename: __dirname + "/../dev-db.sqlite", } } }; diff --git a/config/default.json b/config/default.json deleted file mode 100644 index 2c63c08..0000000 --- a/config/default.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/config/dev.js b/config/dev.js new file mode 100644 index 0000000..7be35b6 --- /dev/null +++ b/config/dev.js @@ -0,0 +1,2 @@ +module.exports = { +}; diff --git a/config/production.js b/config/prod.js similarity index 100% rename from config/production.js rename to config/prod.js diff --git a/config/test.js b/config/test.js index 98dffa3..164220d 100644 --- a/config/test.js +++ b/config/test.js @@ -6,6 +6,7 @@ module.exports = { password: null, database: "database_test", host: "127.0.0.1", + storage: '', // for bookshelf client: 'sqlite', diff --git a/knexfile.js b/knexfile.js new file mode 100644 index 0000000..4b0c009 --- /dev/null +++ b/knexfile.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports.development = require('config'); diff --git a/migrations/20140608000221_initial-migration.js b/migrations/20140608000221_initial-migration.js new file mode 100644 index 0000000..a98752f --- /dev/null +++ b/migrations/20140608000221_initial-migration.js @@ -0,0 +1,77 @@ + +exports.up = function(knex, Promise) { + var todo = []; + + todo.push(knex.schema.createTable('users', function(table){ + table.increments('id').primary().unsigned(); + + table.string('email',2048).notNullable(); + table.string('oauth_provider',2048).notNullable(); + table.string('oauth_id',2048).notNullable(); + + table.timestamps(); + })); + + todo.push(knex.schema.createTable('feeds', function(table){ + table.increments('id').primary().unsigned(); + + table.string('url',2048).notNullable(); + table.string('name',2048).notNullable(); + + table.timestamps(); + })); + + todo.push(knex.schema.createTable('feedsusers', function(table){ + table.increments('id').primary().unsigned(); + + table.integer('user_id').notNullable().unsigned(); + table.integer('feed_id').notNullable().unsigned(); + + table.timestamps(); + })); + + todo.push(knex.schema.createTable('articles', function(table){ + table.increments('id').primary().unsigned(); + + table.integer('feed_id').notNullable().unsigned(); + + table.timestamps(); + })); + + return Promise.all(todo); +}; + +exports.down = function(knex, Promise) { +}; + +/* + +CREATE TABLE `feeds` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `url` VARCHAR(2048), + `name` VARCHAR(2048) NOT NULL DEFAULT 'unnamed feed', + `last_fetched` DATETIME NOT NULL, + `created_at` DATETIME NOT NULL, + `updated_at` DATETIME NOT NULL +); + +CREATE TABLE `articles` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `link` VARCHAR(2048), + `title` VARCHAR(2048) NOT NULL DEFAULT 'untitled article', + `description` TEXT NOT NULL DEFAULT 'this article does not have content', + `date` DATETIME NOT NULL, + `guid` VARCHAR(255) NOT NULL, + `feed_id` INTEGER NOT NULL REFERENCES `feeds` (`id`), + `created_at` DATETIME NOT NULL, + `updated_at` DATETIME NOT NULL +); + +CREATE TABLE `feedsusers` ( + `created_at` DATETIME NOT NULL, + `updated_at` DATETIME NOT NULL, + `feed_id` INTEGER NOT NULL, + `user_id` INTEGER NOT NULL, + PRIMARY KEY (`feed_id`, `user_id`) +); +*/ diff --git a/models/base.js b/models/base.js new file mode 100644 index 0000000..90c7575 --- /dev/null +++ b/models/base.js @@ -0,0 +1,9 @@ +var Bookshelf = require('bookshelf') + , config = require('config') + , db +; + +//db = Bookshelf.initialize(config.database); +//db.plugin('registry'); + +//module.exports = db; diff --git a/models/user.js b/models/user.js index e69de29..ad9a93a 100644 --- a/models/user.js +++ b/models/user.js @@ -0,0 +1 @@ +'use strict'; diff --git a/package.json b/package.json index f118b36..e720659 100644 --- a/package.json +++ b/package.json @@ -10,33 +10,34 @@ }, "dependencies": { "async": "0.2.9", - "bookshelf": "0.6.12", + "bookshelf": "0.7.9", "commander": "2.1.0", "config": "0.4.36", "connect-flash": "0.1.1", "express": "3.4.6", "express-namespace": "0.1.1", "feedparser": "0.16.3", - "jade": "0.35.0", - "knex": "0.5.15", + "jade": "1.9.2", + "knex": "0.7.6", "lodash": "2.4.1", - "moment": "2.4.0", + "moment": "2.10.2", "murmurhash3": "0.2.3", "nodemailer": "0.5.13", "passport": "0.1.17", "passport-google-oauth": "0.1.5", - "pg": "2.8.3", + "pg": "4.3.0", "prettyjson": "0.9.0", "request": "2.28.0", "resumer": "0.0.0", "sequelize": "1.7.0", + "supertest": "0.15.0", "underscore": "1.5.2" }, "devDependencies": { "blanket": "1.1.5", "chai": "1.9.1", "chai-fuzzy": "1.3.0", - "cheerio": "0.12.4", + "cheerio": "0.19.0", "chicken-little": "0.1.2", "connect": "2.11.2", "cookie": "0.1.0", @@ -44,7 +45,7 @@ "mocha-sinon": "1.0.0", "nodemon": "0.7.2", "sinon-chai": "2.4.0", - "sqlite3": "2.1.19", + "sqlite3": "3.0.5", "supertest": "0.8.2" } } diff --git a/test/models/user.js b/test/models/user.js index e69de29..7722bec 100644 --- a/test/models/user.js +++ b/test/models/user.js @@ -0,0 +1,11 @@ +var helper = require('../../support/spec_helper') + , models = require('../../models') + , User = models.user + , expect = require('chai').expect + , async = require('async') + , _ = require('lodash') +; + +describe("User model (bookshelf)", function() { +}); + diff --git a/views/layout.jade b/views/layout.jade index ff00399..a44fe87 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -1,4 +1,4 @@ -doctype 5 +doctype html html head title RSSMTP From 4abed2fe21595b6066c221714e0a38af62463ced Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 15:34:46 -0700 Subject: [PATCH 09/24] DB is migrated/cleared using knex Oddly enough, it does not seem to be able to migrate the DB correctly in the test. When migrating via the spec helper, it does not record the migration in the `knex_migrations` table, so tries to migrate it again the next time around (which fails.) And oh, past self, you are so cray-cray. Once again, Mr. Frank was right.. Utilizing the context object of the test runner is just a bad idea. --- {models => bookmodels}/article.js | 0 {models => bookmodels}/feed.js | 0 bookmodels/index.js | 17 ++++ bookmodels/user.js | 10 +++ config/default.js | 12 +-- config/test.js | 15 ---- db.js | 16 ++++ knexfile.js | 2 +- lib/promise.js | 11 +++ .../20140608000221_initial-migration.js | 43 +++------ models/base.js | 9 -- models/index.js | 2 +- models/user.js | 1 - package.json | 6 +- support/spec_helper.js | 50 ++++++++--- .../article.js => bookmodels.article.js} | 0 test/{models/feed.js => bookmodels.feed.js} | 0 test/bookmodels.user.js | 41 +++++++++ test/models/Article.js | 87 ++++++++++--------- test/models/user.js | 11 --- 20 files changed, 206 insertions(+), 127 deletions(-) rename {models => bookmodels}/article.js (100%) rename {models => bookmodels}/feed.js (100%) create mode 100644 bookmodels/index.js create mode 100644 bookmodels/user.js create mode 100644 db.js create mode 100644 lib/promise.js delete mode 100644 models/base.js delete mode 100644 models/user.js rename test/{models/article.js => bookmodels.article.js} (100%) rename test/{models/feed.js => bookmodels.feed.js} (100%) create mode 100644 test/bookmodels.user.js delete mode 100644 test/models/user.js diff --git a/models/article.js b/bookmodels/article.js similarity index 100% rename from models/article.js rename to bookmodels/article.js diff --git a/models/feed.js b/bookmodels/feed.js similarity index 100% rename from models/feed.js rename to bookmodels/feed.js diff --git a/bookmodels/index.js b/bookmodels/index.js new file mode 100644 index 0000000..b4f3e97 --- /dev/null +++ b/bookmodels/index.js @@ -0,0 +1,17 @@ +var _str = require('underscore.string'); +var fs = require('fs'); +var path = require('path'); + +var files = fs.readdirSync(__dirname); + +files.forEach(function(file) { + if (file === path.basename(__filename)) { + return; + } + + var class_name = _str.classify(path.basename(file, '.js')); + + var model = require('./' + file); + + module.exports[class_name] = model; +}); diff --git a/bookmodels/user.js b/bookmodels/user.js new file mode 100644 index 0000000..667dccb --- /dev/null +++ b/bookmodels/user.js @@ -0,0 +1,10 @@ +'use strict'; + +var db = require('../db'); + +var User = db.BaseModel.extend({ + tableName: 'users' +}, { +}); + +module.exports = User; diff --git a/config/default.js b/config/default.js index be635c0..21c8a39 100644 --- a/config/default.js +++ b/config/default.js @@ -1,17 +1,19 @@ module.exports = { database: { // for sequelize - dialect: "sqlite", + dialect: "mysql", username: "root", password: null, - database: "db.sqlite", + database: "rssmtp", host: "127.0.0.1", - storage: __dirname + "/../dev-db.sqlite", + + //debug: true, // for bookshelf - client: 'sqlite', + client: 'mysql2', connection: { - filename: __dirname + "/../dev-db.sqlite", + database: 'rssmtp', + user: 'root' } } }; diff --git a/config/test.js b/config/test.js index 164220d..7be35b6 100644 --- a/config/test.js +++ b/config/test.js @@ -1,17 +1,2 @@ module.exports = { - database: { - // sequelize - dialect: "sqlite", - username: "root", - password: null, - database: "database_test", - host: "127.0.0.1", - storage: '', - - // for bookshelf - client: 'sqlite', - connection: { - filename: ':memory:' - } - } }; diff --git a/db.js b/db.js new file mode 100644 index 0000000..e6f13fa --- /dev/null +++ b/db.js @@ -0,0 +1,16 @@ +var Bookshelf = require('bookshelf'); +var config = require('config'); +var Knex = require('knex'); + +var knex = Knex(config.database); +var bookshelf = Bookshelf.initialize(knex); + +bookshelf.plugin('registry'); +var BaseModel = bookshelf.Model.extend({ + hasTimestamps: true +}, { +}); + +module.exports.knex = knex; +module.exports.bookshelf = bookshelf; +module.exports.BaseModel = BaseModel; diff --git a/knexfile.js b/knexfile.js index 4b0c009..ce1ac3f 100644 --- a/knexfile.js +++ b/knexfile.js @@ -1,3 +1,3 @@ 'use strict'; -module.exports.development = require('config'); +module.exports.development = require('config').database; diff --git a/lib/promise.js b/lib/promise.js new file mode 100644 index 0000000..fcb8064 --- /dev/null +++ b/lib/promise.js @@ -0,0 +1,11 @@ +var BookPromise = require('bookshelf/lib/base/promise'); +var KnexPromise = require('knex/lib/promise'); + +function unhandledRejectionHandler(err) { + throw err; +} + +BookPromise.onPossiblyUnhandledRejection(unhandledRejectionHandler); +KnexPromise.onPossiblyUnhandledRejection(unhandledRejectionHandler); + +module.exports = KnexPromise; diff --git a/migrations/20140608000221_initial-migration.js b/migrations/20140608000221_initial-migration.js index a98752f..1b8e3c9 100644 --- a/migrations/20140608000221_initial-migration.js +++ b/migrations/20140608000221_initial-migration.js @@ -18,6 +18,8 @@ exports.up = function(knex, Promise) { table.string('url',2048).notNullable(); table.string('name',2048).notNullable(); + table.datetime('last_fetched').notNullable(); + table.timestamps(); })); @@ -27,12 +29,21 @@ exports.up = function(knex, Promise) { table.integer('user_id').notNullable().unsigned(); table.integer('feed_id').notNullable().unsigned(); + table.index(['user_id','feed_id'], 'ix_user_id_feed_id'); + table.index(['feed_id','user_id'], 'ix_feed_id_user_id'); + table.timestamps(); })); todo.push(knex.schema.createTable('articles', function(table){ table.increments('id').primary().unsigned(); + table.datetime('date').notNullable(); + table.string('guid').notNullable(); + table.string('link',2048); + table.string('title',2048).notNullable().defaultTo('untitled article'); + table.text('description').defaultTo('this article does not have content'); + table.integer('feed_id').notNullable().unsigned(); table.timestamps(); @@ -43,35 +54,3 @@ exports.up = function(knex, Promise) { exports.down = function(knex, Promise) { }; - -/* - -CREATE TABLE `feeds` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `url` VARCHAR(2048), - `name` VARCHAR(2048) NOT NULL DEFAULT 'unnamed feed', - `last_fetched` DATETIME NOT NULL, - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL -); - -CREATE TABLE `articles` ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `link` VARCHAR(2048), - `title` VARCHAR(2048) NOT NULL DEFAULT 'untitled article', - `description` TEXT NOT NULL DEFAULT 'this article does not have content', - `date` DATETIME NOT NULL, - `guid` VARCHAR(255) NOT NULL, - `feed_id` INTEGER NOT NULL REFERENCES `feeds` (`id`), - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL -); - -CREATE TABLE `feedsusers` ( - `created_at` DATETIME NOT NULL, - `updated_at` DATETIME NOT NULL, - `feed_id` INTEGER NOT NULL, - `user_id` INTEGER NOT NULL, - PRIMARY KEY (`feed_id`, `user_id`) -); -*/ diff --git a/models/base.js b/models/base.js deleted file mode 100644 index 90c7575..0000000 --- a/models/base.js +++ /dev/null @@ -1,9 +0,0 @@ -var Bookshelf = require('bookshelf') - , config = require('config') - , db -; - -//db = Bookshelf.initialize(config.database); -//db.plugin('registry'); - -//module.exports = db; diff --git a/models/index.js b/models/index.js index 243d5f8..99e42e9 100644 --- a/models/index.js +++ b/models/index.js @@ -14,7 +14,7 @@ var options = _.merge({}, config.database, { , logging: false }); -var sequelize = new Sequelize(config.database, config.username, config.password, options); +var sequelize = new Sequelize(config.database.database, config.database.username, config.database.password, options); var files = fs.readdirSync(__dirname) .filter(function(filename){ diff --git a/models/user.js b/models/user.js deleted file mode 100644 index ad9a93a..0000000 --- a/models/user.js +++ /dev/null @@ -1 +0,0 @@ -'use strict'; diff --git a/package.json b/package.json index e720659..3893ce5 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "lodash": "2.4.1", "moment": "2.10.2", "murmurhash3": "0.2.3", + "mysql": "^2.6.2", + "mysql2": "^0.15.5", "nodemailer": "0.5.13", "passport": "0.1.17", "passport-google-oauth": "0.1.5", @@ -31,7 +33,8 @@ "resumer": "0.0.0", "sequelize": "1.7.0", "supertest": "0.15.0", - "underscore": "1.5.2" + "underscore": "1.5.2", + "underscore.string": "^3.0.3" }, "devDependencies": { "blanket": "1.1.5", @@ -41,6 +44,7 @@ "chicken-little": "0.1.2", "connect": "2.11.2", "cookie": "0.1.0", + "dirty-chai": "^1.2.0", "mocha": "1.15.1", "mocha-sinon": "1.0.0", "nodemon": "0.7.2", diff --git a/support/spec_helper.js b/support/spec_helper.js index 8d6b72a..583d885 100644 --- a/support/spec_helper.js +++ b/support/spec_helper.js @@ -1,16 +1,20 @@ -var app = require('../app') - , request = require('supertest') - , chai = require('chai') - , cheerio = require('cheerio') - , async = require('async') - , _ = require('lodash') - , models = require('../models') -; +var _ = require('lodash'); +var app = require('../app'); +var async = require('async'); +var chai = require('chai'); +var cheerio = require('cheerio'); +var config = require('config'); +var db = require('../db'); +var models = require('../models'); +var Promise = require('../lib/promise'); +var request = require('supertest'); +var expect = chai.expect; require('mocha-sinon'); // chai setup chai.Assertion.includeStack = true; +//chai.use(require('dirty-chai')); chai.use(require('chai-fuzzy')); chai.use(require('sinon-chai')); @@ -51,10 +55,35 @@ exports.model = function(model) { return require('../models/' + model); }; -before(function(done){ - models._sequelize.sync({force: true}).done(done); +before(function(done) { + this.timeout(30 * 1000); + + db.knex.migrate + .latest(config.database) + .exec(function(err) { + expect(err).to.not.exist; + + setTimeout(done, 20); + }); +}); + +beforeEach(function(done) { + var tables_to_clear = ['articles', 'feeds', 'feedsusers', 'users']; + + Promise + .map(tables_to_clear, function(table_name) { + return db + .knex(table_name) + .del(); + }) + .exec(function(err) { + expect(err).to.not.exist; + + done(); + }); }); +/* beforeEach(function(done) { var todo = []; @@ -68,6 +97,7 @@ beforeEach(function(done) { async.parallel(todo, done); }); +*/ beforeEach(function(done) { var self = this diff --git a/test/models/article.js b/test/bookmodels.article.js similarity index 100% rename from test/models/article.js rename to test/bookmodels.article.js diff --git a/test/models/feed.js b/test/bookmodels.feed.js similarity index 100% rename from test/models/feed.js rename to test/bookmodels.feed.js diff --git a/test/bookmodels.user.js b/test/bookmodels.user.js new file mode 100644 index 0000000..9f543c3 --- /dev/null +++ b/test/bookmodels.user.js @@ -0,0 +1,41 @@ +var helper = require('../support/spec_helper') + , models = require('../bookmodels') + , expect = require('chai').expect + , async = require('async') + , _ = require('lodash') +; + +describe("models.User (bookshelf)", function() { + var minimum_attrs; + + beforeEach(function() { + minimum_attrs = { + }; + }); + + it('can be saved', function(done) { + var now = new Date(); + now.setMilliseconds(0); + var clock = this.sinon.useFakeTimers(now.valueOf()); + models.User + .forge(minimum_attrs) + .save() + .exec(function(err, user) { + clock.restore(); + + expect(err).to.not.exist; + + var actual = user.toJSON(); + expect(actual.id).to.be.a('number').above(0); + delete actual.id; + + expect(actual).to.deep.equal({ + created_at: now, + updated_at: now, + }); + + done(); + }); + }); +}); + diff --git a/test/models/Article.js b/test/models/Article.js index ed02e84..cba0e83 100644 --- a/test/models/Article.js +++ b/test/models/Article.js @@ -11,6 +11,8 @@ var helper = require('../../support/spec_helper') ; describe("Article model (RDBMS)", function() { + var data; + beforeEach(function(done) { Feed.create({ url: "http://example.com/article.rss" @@ -24,23 +26,24 @@ describe("Article model (RDBMS)", function() { }); beforeEach(function() { - this.data = { + data = { title: "article title here: with " , description: "

article content here, with and &'s

" , link: 'http://example.com/an_article' , date: new Date(2010,0) , guid: 'a guid here' , feed_id: this.feed.id - } + }; }); it("can be saved", function(done) { - Article.create(this.data).done(done); + Article.create(data).done(done); }); - it("cannot be created with an invalid feed_id ", function(done) { - this.data.feed_id = 9000000; - Article.create(this.data).done(function(err, article){ + // I don't think we care about this at the moment + it.skip("cannot be created with an invalid feed_id ", function(done) { + data.feed_id = 9000000; + Article.create(data).done(function(err, article){ expect(err).to.exist; expect(err).to.match(/foreign key constraint/i); @@ -49,8 +52,9 @@ describe("Article model (RDBMS)", function() { }); it("cannot be created without a feed_id", function(done) { - delete this.data.feed_id; - Article.create(this.data).done(function(err, article){ + data.feed_id = null; + + Article.create(data).done(function(err, article){ expect(err).to.exist; expect(err).to.match(/null/i); expect(err).to.match(/feed_id/i); @@ -61,19 +65,19 @@ describe("Article model (RDBMS)", function() { describe(".cleanAttrs", function() { it("strips out unsupported attributes", function() { - this.data.id = 'a non numeric key that will be discarded'; - this.data.discarded = 'this will be thrown away'; + data.id = 'a non numeric key that will be discarded'; + data.discarded = 'this will be thrown away'; - var cleaned = Article.cleanAttrs(this.data); + var cleaned = Article.cleanAttrs(data); expect(_.keys(cleaned)).to.include('description'); expect(_.keys(cleaned)).to.not.include('discarded'); expect(_.keys(cleaned)).to.not.include('id'); }); it("removes empty attributes to allow defaults to be set", function() { - this.data.title = ''; + data.title = ''; - var cleaned = Article.cleanAttrs(this.data); + var cleaned = Article.cleanAttrs(data); expect(_.keys(cleaned)).to.include('description'); expect(_.keys(cleaned)).to.not.include('title'); }); @@ -82,16 +86,16 @@ describe("Article model (RDBMS)", function() { describe(".setDefaults", function() { describe("when there is no title", function() { beforeEach(function() { - delete this.data['title']; + delete data['title']; }); describe.skip("but there is content", function() { beforeEach(function() { - this.data.description = "

Article's content here & more will be truncated because it's too long"; + data.description = "

Article's content here & more will be truncated because it's too long"; }); it("sets the title to be the first N char of the content", function() { - var defaulted = Article.setDefaults(this.data); + var defaulted = Article.setDefaults(data); expect(defaulted.title + '').to.equal("Article's content here & more will be truncated because it's(...)"); }); @@ -99,18 +103,18 @@ describe("Article model (RDBMS)", function() { describe("but there is a link", function() { it("sets the title to be the first N char of the content", function() { - var defaulted = Article.setDefaults(this.data); + var defaulted = Article.setDefaults(data); - expect(defaulted.title).to.equal(this.data.link); + expect(defaulted.title).to.equal(data.link); }); }); }); it("does not modify pre-existing values", function() { - var defaulted = Article.setDefaults(this.data); + var defaulted = Article.setDefaults(data); - expect(defaulted === this.data).to.be.false; - expect(defaulted).to.deep.equal(this.data); + expect(defaulted === data).to.be.false; + expect(defaulted).to.deep.equal(data); }); it("cleans attributes before setting defaults", function() { @@ -118,9 +122,9 @@ describe("Article model (RDBMS)", function() { this.sinon.stub(Article, 'cleanAttrs', function() { return fakeCleaned}); - var defaulted = Article.setDefaults(this.data); + var defaulted = Article.setDefaults(data); - expect(Article.cleanAttrs).to.have.been.calledWith(this.data); + expect(Article.cleanAttrs).to.have.been.calledWith(data); expect(defaulted.fake).to.equal('cleaned'); }); @@ -141,14 +145,14 @@ describe("Article model (RDBMS)", function() { it("cleans the input before hashing", function(done) { this.sinon.stub(Article, 'setDefaults', function(){ return { fake: 'defaulted' };}); - Article.setGUID(this.data, function(err, attrs){ + Article.setGUID(data, function(err, attrs){ expect(err).to.not.exist; - expect(Article.setDefaults).to.have.been.calledWith(this.data); + expect(Article.setDefaults).to.have.been.calledWith(data); expect(mmh3.murmur128Hex).to.have.been.calledWith('fake: defaulted'); expect(attrs).to.be.ok; - expect(attrs).to.not.equal(this.data); + expect(attrs).to.not.equal(data); done(); }.bind(this)); @@ -156,7 +160,7 @@ describe("Article model (RDBMS)", function() { describe("when the input already has a guid", function() { it("does not overwrite it", function(done) { - Article.setGUID(this.data, function(err, attrs){ + Article.setGUID(data, function(err, attrs){ expect(err).to.not.exist; expect(attrs.guid).to.equal('a guid here'); @@ -169,11 +173,11 @@ describe("Article model (RDBMS)", function() { describe("when the input does not have a guid", function() { beforeEach(function() { - delete this.data['guid']; + delete data['guid']; }); it("generates a guid", function(done) { - Article.setGUID(this.data, function(err, attrs){ + Article.setGUID(data, function(err, attrs){ expect(err).to.not.exist; expect(attrs.guid).to.match(/[0-9a-f]{32}/); @@ -190,14 +194,14 @@ describe("Article model (RDBMS)", function() { it("calls sequelize's findOrCreate using processed input", function(done) { this.sinon.spy(Article, 'findOrCreate'); - Article.findOrCreateFromData(this.data, function(err, article, created){ + Article.findOrCreateFromData(data, function(err, article, created){ expect(err).to.not.exist; - expect(Article.findOrCreate).to.have.been.calledWith({guid: this.data.guid, feed_id: this.data.feed_id}, this.data); + expect(Article.findOrCreate).to.have.been.calledWith({guid: data.guid, feed_id: data.feed_id}, data); expect(created).to.be.true; expect(article).to.be.ok; - expect(article.title).to.equal(this.data.title); + expect(article.title).to.equal(data.title); done(); }.bind(this)); @@ -206,22 +210,23 @@ describe("Article model (RDBMS)", function() { describe("#asEmailOptions", function() { describe("when there is content", function() { + var article; + var emails; + beforeEach(function(done) { - var self = this - , todo = [] - ; + var todo = []; todo.push(function(done){ Article - .create(self.data) + .create(data) .error(done) - .success(function(article){ - self.article = article; + .success(function(result){ + article = result; done(); }); }); - self.emails = [ + emails = [ 'default_user@example.com' , 'other_user@example.com' ]; @@ -230,7 +235,7 @@ describe("Article model (RDBMS)", function() { }); it("generates a nodemailer-ready message", function() { - var emailData = this.article.asEmailOptions(this.feed, this.emails); + var emailData = article.asEmailOptions(this.feed, emails); expect(emailData).to.exist; @@ -249,7 +254,7 @@ describe("Article model (RDBMS)", function() { , to: "RSS - my_ feed's _name_ plus shotguns " , bcc: ['default_user@example.com', 'other_user@example.com'] , subject: "article title here: with " - , date: this.data.date + , date: data.date , headers: { "List-ID": this.feed.id + '.rssmtp.firetaco.com' , "List-Unsubscribe": 'http://rssmtp.firetaco.com/feed/' + this.feed.id diff --git a/test/models/user.js b/test/models/user.js deleted file mode 100644 index 7722bec..0000000 --- a/test/models/user.js +++ /dev/null @@ -1,11 +0,0 @@ -var helper = require('../../support/spec_helper') - , models = require('../../models') - , User = models.user - , expect = require('chai').expect - , async = require('async') - , _ = require('lodash') -; - -describe("User model (bookshelf)", function() { -}); - From 9eee55f47013f406d63ae5a44aa2c8d1b528d053 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 15:38:55 -0700 Subject: [PATCH 10/24] Flatten model tests --- test/{models/Article.js => models.Article.js} | 4 ++-- test/{models/Feed.js => models.Feed.js} | 6 +++--- test/{models/User.js => models.User.js} | 4 ++-- test/{models/mailer.js => models.mailer.js} | 4 ++-- test/{models/poller.js => models.poller.js} | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) rename test/{models/Article.js => models.Article.js} (98%) rename test/{models/Feed.js => models.Feed.js} (98%) rename test/{models/User.js => models.User.js} (96%) rename test/{models/mailer.js => models.mailer.js} (93%) rename test/{models/poller.js => models.poller.js} (97%) diff --git a/test/models/Article.js b/test/models.Article.js similarity index 98% rename from test/models/Article.js rename to test/models.Article.js index cba0e83..28db16f 100644 --- a/test/models/Article.js +++ b/test/models.Article.js @@ -1,5 +1,5 @@ -var helper = require('../../support/spec_helper') - , models = require('../../models') +var helper = require('../support/spec_helper') + , models = require('../models') , User = models.User , Feed = models.Feed , Article = models.Article diff --git a/test/models/Feed.js b/test/models.Feed.js similarity index 98% rename from test/models/Feed.js rename to test/models.Feed.js index 7993162..90d5242 100644 --- a/test/models/Feed.js +++ b/test/models.Feed.js @@ -1,5 +1,5 @@ -var helper = require('../../support/spec_helper') - , models = require('../../models') +var helper = require('../support/spec_helper') + , models = require('../models') , User = models.User , Feed = models.Feed , Article = models.Article @@ -7,7 +7,7 @@ var helper = require('../../support/spec_helper') , async = require('async') , _ = require('lodash') , request = require('request') - , feedparser = require('../../lib/feedparser') + , feedparser = require('../lib/feedparser') , moment = require('moment') ; diff --git a/test/models/User.js b/test/models.User.js similarity index 96% rename from test/models/User.js rename to test/models.User.js index 516aef1..fc73aba 100644 --- a/test/models/User.js +++ b/test/models.User.js @@ -1,5 +1,5 @@ -var helper = require('../../support/spec_helper') - , models = require('../../models') +var helper = require('../support/spec_helper') + , models = require('../models') , User = models.User , Feed = models.Feed , expect = require('chai').expect diff --git a/test/models/mailer.js b/test/models.mailer.js similarity index 93% rename from test/models/mailer.js rename to test/models.mailer.js index df6f200..c16a319 100644 --- a/test/models/mailer.js +++ b/test/models.mailer.js @@ -1,5 +1,5 @@ -var helper = require('../../support/spec_helper') - , models = require('../../models') +var helper = require('../support/spec_helper') + , models = require('../models') , Mailer = helper.model('mailer') , expect = require('chai').expect , _ = require('lodash') diff --git a/test/models/poller.js b/test/models.poller.js similarity index 97% rename from test/models/poller.js rename to test/models.poller.js index 8c19bd1..b08f946 100644 --- a/test/models/poller.js +++ b/test/models.poller.js @@ -1,7 +1,7 @@ -var helper = require('../../support/spec_helper') +var helper = require('../support/spec_helper') , expect = require('chai').expect , Poller = helper.model('poller') - , models = require('../../models') + , models = require('../models') , Feed = models.Feed ; From 0c901fbdb5ec07fd118e5ebe838c8c0949eb06b4 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 15:44:51 -0700 Subject: [PATCH 11/24] Try to get travis build passing --- .travis.yml | 4 ++++ config/ci.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 config/ci.js diff --git a/.travis.yml b/.travis.yml index 6e5919d..f33990e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ language: node_js node_js: - "0.10" +before_script: + - mysql -e 'create database rssmtp;' + - ./node_modules/.bin/knex migrate:latest +env: NODE_ENV=ci diff --git a/config/ci.js b/config/ci.js new file mode 100644 index 0000000..3f0aeb4 --- /dev/null +++ b/config/ci.js @@ -0,0 +1,17 @@ +module.exports = { + database: { + // for sequelize + dialect: "mysql", + username: "travis", + password: null, + database: "rssmtp", + host: "127.0.0.1", + + // for bookshelf + client: 'mysql2', + connection: { + database: 'rssmtp', + user: 'travis' + } + } +}; From fa9cf068f1dcc07f215104f6c23e9853b60763f5 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 15:47:43 -0700 Subject: [PATCH 12/24] Inform knex about the ci env --- knexfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/knexfile.js b/knexfile.js index ce1ac3f..5cd1f72 100644 --- a/knexfile.js +++ b/knexfile.js @@ -1,3 +1,4 @@ 'use strict'; module.exports.development = require('config').database; +module.exports.ci = require('config').database; From 1bf100f51b695d0e618be394ccf147ca30faa744 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 16:24:43 -0700 Subject: [PATCH 13/24] Start porting models to bookshelf --- bookmodels/user.js | 30 +++++++++ ...0910_users-oauth-provider-id-are-unique.js | 19 ++++++ support/spec_helper.js | 8 ++- test/bookmodels.user.js | 65 +++++++++++++++++++ test/models.User.js | 34 +++++----- 5 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 migrations/20150417160910_users-oauth-provider-id-are-unique.js diff --git a/bookmodels/user.js b/bookmodels/user.js index 667dccb..c29b156 100644 --- a/bookmodels/user.js +++ b/bookmodels/user.js @@ -5,6 +5,36 @@ var db = require('../db'); var User = db.BaseModel.extend({ tableName: 'users' }, { + findOrCreateFromOAUTH: function findOrCreateFromOAUTH(oauth_data, done) { + var attrs = { + oauth_provider: oauth_data.provider, + oauth_id: oauth_data.id + }; + + var created = false; + var user = User + .forge(attrs) + + user + .fetch() + .then(function(result) { + if (result) { + return result; + } + + created = true; + user.set('email', oauth_data.emails[0].value); + + return user.save(); + }) + .exec(function(err, user) { + if (err) { + return done(err); + } + + done(null, user, created); + }); + } }); module.exports = User; diff --git a/migrations/20150417160910_users-oauth-provider-id-are-unique.js b/migrations/20150417160910_users-oauth-provider-id-are-unique.js new file mode 100644 index 0000000..183bae2 --- /dev/null +++ b/migrations/20150417160910_users-oauth-provider-id-are-unique.js @@ -0,0 +1,19 @@ +'use strict'; + +exports.up = function(knex, Promise) { + return knex.schema.raw("ALTER TABLE users MODIFY COLUMN oauth_provider VARCHAR(255) NOT NULL") + .then(function() { + return knex.schema.raw("ALTER TABLE users MODIFY COLUMN oauth_id VARCHAR(255) NOT NULL") + }) + .then(function() { + return knex.schema.table('users', function(table) { + table.unique(['oauth_provider', 'oauth_id'], 'ux_oauth_provider_oauth_id'); + }); + }); +}; + +exports.down = function(knex, Promise) { + return knex.schema.table('users', function(table) { + table.dropUnique(['oauth_provider', 'oauth_id'], 'ux_oauth_provider_oauth_id'); + }); +}; diff --git a/support/spec_helper.js b/support/spec_helper.js index 583d885..cc22515 100644 --- a/support/spec_helper.js +++ b/support/spec_helper.js @@ -107,7 +107,9 @@ beforeEach(function(done) { todo.push(function(done){ models.User .create({ - email: 'default_user@example.com' + email: 'default_user@example.com', + oauth_provider: 'fake_oauth_provider', + oauth_id: 'default user' }) .error(done) .success(function(user){ @@ -119,7 +121,9 @@ beforeEach(function(done) { todo.push(function(done){ models.User .create({ - email: 'other_user@example.com' + email: 'other_user@example.com', + oauth_provider: 'fake_oauth_provider', + oauth_id: 'other user' }) .error(done) .success(function(user){ diff --git a/test/bookmodels.user.js b/test/bookmodels.user.js index 9f543c3..c7832ff 100644 --- a/test/bookmodels.user.js +++ b/test/bookmodels.user.js @@ -1,3 +1,5 @@ +'use strict'; + var helper = require('../support/spec_helper') , models = require('../bookmodels') , expect = require('chai').expect @@ -37,5 +39,68 @@ describe("models.User (bookshelf)", function() { done(); }); }); + + describe("findOrCreateFromOAUTH", function() { + var dummyProfileData; + var user; + + beforeEach(function() { + dummyProfileData = { + provider: 'oauth_provider_here' + , id: 'oauth_id_here' + , displayName: 'Bob Foster' + , name: { + givenName: 'Bob' + , familyName: 'Foster' + } + , emails: [ + { value: 'bob.foster@example.com' } + ] + }; + }); + + context("when the specified user *does not* exist", function() { + it("creates the user", function(done) { + models.User.findOrCreateFromOAUTH(dummyProfileData, function(err, user, created) { + expect(err).to.not.exist; + + expect(created).to.be.true; + expect(user.pick('email', 'oauth_provider', 'oauth_id')).to.deep.equal({ + email: 'bob.foster@example.com', + oauth_provider: 'oauth_provider_here', + oauth_id: 'oauth_id_here' + }); + + done(); + }); + }); + }); + + context("when the specified user *does* exist", function() { + beforeEach(function(done) { + models.User.forge({ + email: 'bob@example.com' + , oauth_provider: 'oauth_provider_here' + , oauth_id: 'oauth_id_here' + }) + .save() + .exec(function(err, result){ + expect(err).to.not.exist; + user = result; + done(); + }); + }); + + it("returns the existing user", function(done) { + models.User.findOrCreateFromOAUTH(dummyProfileData, function(err, user, created) { + expect(err).to.not.exist; + + expect(created).to.be.false; + expect(user.get('email')).to.equal("bob@example.com"); + done(); + }); + }); + }); + }); }); diff --git a/test/models.User.js b/test/models.User.js index fc73aba..863e842 100644 --- a/test/models.User.js +++ b/test/models.User.js @@ -15,21 +15,10 @@ describe("User model (RDBMS)", function() { }); describe("findOrCreateFromOAUTH", function() { - beforeEach(function(done) { - User.create({ - email: 'bob@example.com' - , oauth_provider: 'oauth_provider_here' - , oauth_id: 'oauth_id_here' - }) - .error(done) - .success(function(model){ - this.user = model; - done(); - }.bind(this)); - + beforeEach(function() { this.dummyProfileData = { - provider: 'dummy_oauth_provider' - , id: '3.14159' + provider: 'oauth_provider_here' + , id: 'oauth_id_here' , displayName: 'Bob Foster' , name: { givenName: 'Bob' @@ -48,15 +37,26 @@ describe("User model (RDBMS)", function() { expect(created).to.be.true; expect(user.email).to.equal("bob.foster@example.com"); + expect(user.oauth_id).to.equal('oauth_id_here'); + expect(user.oauth_provider).to.equal('oauth_provider_here'); + done(); }); }); }); describe("when the specified user EXISTS", function() { - beforeEach(function() { - this.dummyProfileData['provider'] = 'oauth_provider_here'; - this.dummyProfileData['id'] = 'oauth_id_here'; + beforeEach(function(done) { + User.create({ + email: 'bob@example.com' + , oauth_provider: this.dummyProfileData.provider + , oauth_id: this.dummyProfileData.id + }) + .error(done) + .success(function(model){ + this.user = model; + done(); + }.bind(this)); }); it("returns the existing user", function(done) { From b211dd21e402115211dff35f10a0e5bdbcf8a753 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 16:31:56 -0700 Subject: [PATCH 14/24] beginning of bookshelf version of Feed model --- bookmodels/feed.js | 10 ++++++++++ support/spec_helper.js | 16 ---------------- test/bookmodels.feed.js | 39 +++++++++++++++++++++++++++++++++++++++ test/bookmodels.user.js | 10 +++------- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/bookmodels/feed.js b/bookmodels/feed.js index e69de29..d2fae4a 100644 --- a/bookmodels/feed.js +++ b/bookmodels/feed.js @@ -0,0 +1,10 @@ +'use strict'; + +var db = require('../db'); + +var Feed = db.BaseModel.extend({ + tableName: 'feeds' +}, { +}); + +module.exports = Feed; diff --git a/support/spec_helper.js b/support/spec_helper.js index cc22515..ff0454b 100644 --- a/support/spec_helper.js +++ b/support/spec_helper.js @@ -83,22 +83,6 @@ beforeEach(function(done) { }); }); -/* -beforeEach(function(done) { - var todo = []; - - _.forEach(models, function(model, name){ - if (model && 'function' === typeof model['destroy']) { - todo.push(function(done){ - model.destroy().done(done); - }); - } - }); - - async.parallel(todo, done); -}); -*/ - beforeEach(function(done) { var self = this , todo = [] diff --git a/test/bookmodels.feed.js b/test/bookmodels.feed.js index e69de29..8626d12 100644 --- a/test/bookmodels.feed.js +++ b/test/bookmodels.feed.js @@ -0,0 +1,39 @@ +'use strict'; + +var expect = require('chai').expect; +var models = require('../bookmodels'); +var helper = require('../support/spec_helper'); + +describe.only("models.Feed (bookshelf)", function() { + var minimum_attrs; + + beforeEach(function() { + minimum_attrs = { + }; + }); + + it('can be saved', function(done) { + var now = new Date(); + now.setMilliseconds(0); + var clock = this.sinon.useFakeTimers(now.valueOf()); + models.Feed + .forge(minimum_attrs) + .save() + .exec(function(err, user) { + clock.restore(); + + expect(err).to.not.exist; + + var actual = user.toJSON(); + expect(actual.id).to.be.a('number').above(0); + delete actual.id; + + expect(actual).to.deep.equal({ + created_at: now, + updated_at: now, + }); + + done(); + }); + }); +}); diff --git a/test/bookmodels.user.js b/test/bookmodels.user.js index c7832ff..a30a4d8 100644 --- a/test/bookmodels.user.js +++ b/test/bookmodels.user.js @@ -1,11 +1,8 @@ 'use strict'; -var helper = require('../support/spec_helper') - , models = require('../bookmodels') - , expect = require('chai').expect - , async = require('async') - , _ = require('lodash') -; +var expect = require('chai').expect; +var models = require('../bookmodels'); +var helper = require('../support/spec_helper'); describe("models.User (bookshelf)", function() { var minimum_attrs; @@ -103,4 +100,3 @@ describe("models.User (bookshelf)", function() { }); }); }); - From d2ac119739d879f33a55107ce7dc963184d615de Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 20:51:46 -0700 Subject: [PATCH 15/24] A poller is not really a model --- agents/index.js | 17 +++++++++++++++ {models => agents}/poller.js | 0 server.js | 10 ++++----- test/{models.poller.js => agents.poller.js} | 24 ++++++++++----------- test/bookmodels.feed.js | 2 +- test/server.js | 23 ++++++++++---------- 6 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 agents/index.js rename {models => agents}/poller.js (100%) rename test/{models.poller.js => agents.poller.js} (86%) diff --git a/agents/index.js b/agents/index.js new file mode 100644 index 0000000..b4f3e97 --- /dev/null +++ b/agents/index.js @@ -0,0 +1,17 @@ +var _str = require('underscore.string'); +var fs = require('fs'); +var path = require('path'); + +var files = fs.readdirSync(__dirname); + +files.forEach(function(file) { + if (file === path.basename(__filename)) { + return; + } + + var class_name = _str.classify(path.basename(file, '.js')); + + var model = require('./' + file); + + module.exports[class_name] = model; +}); diff --git a/models/poller.js b/agents/poller.js similarity index 100% rename from models/poller.js rename to agents/poller.js diff --git a/server.js b/server.js index 8a0f03d..405c0df 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,7 @@ -var http = require('http') - , app = require('./app') - , models = require('./models') -; +var http = require('http'); +var app = require('./app'); +var models = require('./models'); +var agents = require('./agents'); function start(done) { http.createServer(app).listen(app.get('port'), app.get('bindip'), function(){ @@ -15,7 +15,7 @@ function start(done) { models._sequelize.sync(syncArgs).done(function(err){ if (err) throw err; - var poller = new models.poller({ + var poller = new agents.Poller({ FeedClass: models.Feed , mailer: new models.mailer() }); diff --git a/test/models.poller.js b/test/agents.poller.js similarity index 86% rename from test/models.poller.js rename to test/agents.poller.js index b08f946..711d204 100644 --- a/test/models.poller.js +++ b/test/agents.poller.js @@ -1,19 +1,17 @@ -var helper = require('../support/spec_helper') - , expect = require('chai').expect - , Poller = helper.model('poller') - , models = require('../models') - , Feed = models.Feed -; - -describe("Poller model", function() { +var helper = require('../support/spec_helper'); +var expect = require('chai').expect; +var models = require('../models'); +var agents = require('../agents'); + +describe("agents.Poller", function() { beforeEach(function() { this.fakeMailer = { sendMail: this.sinon.stub() }; this.fakeMailer.sendMail.callsArg(1) - this.poller = new Poller({ - FeedClass: Feed + this.poller = new agents.Poller({ + FeedClass: models.Feed , mailer: this.fakeMailer }); this.sinon.stub(this.poller, 'requeue', function(){}); @@ -23,7 +21,7 @@ describe("Poller model", function() { beforeEach(function(done) { var self = this; - Feed + models.Feed .create({ url: 'http://example.com/#updateOneFeed' }) @@ -35,7 +33,7 @@ describe("Poller model", function() { done(); }); - this.sinon.stub(Feed, 'getOutdated', function(done){ + this.sinon.stub(models.Feed, 'getOutdated', function(done){ done(null, this.feed); }.bind(this)); }); @@ -44,7 +42,7 @@ describe("Poller model", function() { this.poller.updateOneFeed(function(err, feed){ expect(err).to.not.exist; - expect(Feed.getOutdated).to.have.been.called; + expect(models.Feed.getOutdated).to.have.been.called; expect(this.feed.publish).to.have.been.called; expect(this.feed.publish).to.have.been.calledWith(this.fakeMailer); diff --git a/test/bookmodels.feed.js b/test/bookmodels.feed.js index 8626d12..68f90fe 100644 --- a/test/bookmodels.feed.js +++ b/test/bookmodels.feed.js @@ -4,7 +4,7 @@ var expect = require('chai').expect; var models = require('../bookmodels'); var helper = require('../support/spec_helper'); -describe.only("models.Feed (bookshelf)", function() { +describe("models.Feed (bookshelf)", function() { var minimum_attrs; beforeEach(function() { diff --git a/test/server.js b/test/server.js index bfe028b..52ce7af 100644 --- a/test/server.js +++ b/test/server.js @@ -1,11 +1,10 @@ -var helper = require('../support/spec_helper') - , http = require('http') - , models = require('../models') - , Poller = models.poller - , app = require('../app') - , expect = require('chai').expect - , server = require('../server') -; +var helper = require('../support/spec_helper'); +var http = require('http'); +var models = require('../models'); +var agents = require('../agents'); +var app = require('../app'); +var expect = require('chai').expect; +var server = require('../server'); describe("Server", function() { beforeEach(function() { @@ -22,8 +21,8 @@ describe("Server", function() { return fakeMailer; }); - this.sinon.stub(Poller.prototype, 'start', function(){}); - this.sinon.spy(models, 'poller'); + this.sinon.stub(agents.Poller.prototype, 'start', function(){}); + this.sinon.spy(agents, 'Poller'); }); it("starts up services", function(done) { @@ -33,11 +32,11 @@ describe("Server", function() { expect(http.createServer).to.have.been.calledWith(app); expect(this.fakeServer.listen).to.have.been.calledWith(3000, '127.0.0.1'); - expect(models.poller).to.have.been.calledWith({ + expect(agents.Poller).to.have.been.calledWith({ FeedClass: models.Feed , mailer: this.fakeMailer }); - expect(Poller.prototype.start).to.have.been.called; + expect(agents.Poller.prototype.start).to.have.been.called; done(); }.bind(this)); From 0f1948d319fcfd17c5a4ea4f0acf49900916e7d7 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 20:59:16 -0700 Subject: [PATCH 16/24] A mailer is not a model, either --- {models => agents}/mailer.js | 3 +-- server.js | 2 +- test/{models.mailer.js => agents.mailer.js} | 20 +++++++++----------- test/server.js | 4 ++-- 4 files changed, 13 insertions(+), 16 deletions(-) rename {models => agents}/mailer.js (93%) rename test/{models.mailer.js => agents.mailer.js} (74%) diff --git a/models/mailer.js b/agents/mailer.js similarity index 93% rename from models/mailer.js rename to agents/mailer.js index b434b8c..83ef2b6 100644 --- a/models/mailer.js +++ b/agents/mailer.js @@ -1,5 +1,4 @@ -var nodemailer = require('nodemailer') -; +var nodemailer = require('nodemailer'); function Mailer() { var settings = { diff --git a/server.js b/server.js index 405c0df..5d1b345 100644 --- a/server.js +++ b/server.js @@ -17,7 +17,7 @@ function start(done) { var poller = new agents.Poller({ FeedClass: models.Feed - , mailer: new models.mailer() + , mailer: new agents.Mailer() }); poller.start(); diff --git a/test/models.mailer.js b/test/agents.mailer.js similarity index 74% rename from test/models.mailer.js rename to test/agents.mailer.js index c16a319..6ce98a8 100644 --- a/test/models.mailer.js +++ b/test/agents.mailer.js @@ -1,12 +1,10 @@ -var helper = require('../support/spec_helper') - , models = require('../models') - , Mailer = helper.model('mailer') - , expect = require('chai').expect - , _ = require('lodash') - , nodemailer = require('nodemailer') -; - -describe("Mailer model", function() { +var helper = require('../support/spec_helper'); +var agents = require('../agents'); +var expect = require('chai').expect; +var _ = require('lodash'); +var nodemailer = require('nodemailer'); + +describe("agents.Mailer", function() { beforeEach(function() { var mockedMailer = this.mockedMailer = nodemailer.createTransport(); @@ -16,7 +14,7 @@ describe("Mailer model", function() { }); it("is a wrapper around nodemailer", function() { - var mailer = new Mailer(); + var mailer = new agents.Mailer(); expect(nodemailer.createTransport).to.have.been.calledWith("SMTP", { host: "smtp.example.com" @@ -31,7 +29,7 @@ describe("Mailer model", function() { describe("#sendMail", function() { beforeEach(function() { - this.mailer = new Mailer(); + this.mailer = new agents.Mailer(); }); it("sends the given email via nodemailer", function(done) { diff --git a/test/server.js b/test/server.js index 52ce7af..dbc431e 100644 --- a/test/server.js +++ b/test/server.js @@ -16,8 +16,8 @@ describe("Server", function() { return fakeServer; }); - var fakeMailer = this.fakeMailer = new models.mailer(); - this.sinon.stub(models, 'mailer', function() { + var fakeMailer = this.fakeMailer = new agents.Mailer(); + this.sinon.stub(agents, 'Mailer', function() { return fakeMailer; }); From b4a6a5e1cdda6f9ef7ce6a9f2c1adc5b2bd9b7aa Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 21:04:33 -0700 Subject: [PATCH 17/24] More inline with how I do code these days --- models/Article.js | 6 ++---- models/Feed.js | 11 +++++------ test/models.Article.js | 20 +++++++++----------- test/models.Feed.js | 23 +++++++++++------------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/models/Article.js b/models/Article.js index 9600a70..3310900 100644 --- a/models/Article.js +++ b/models/Article.js @@ -1,7 +1,5 @@ -var mmh3 = require('murmurhash3') - , _ = require('lodash') - , nodemailer = require('nodemailer') -; +var mmh3 = require('murmurhash3'); +var _ = require('lodash'); function init(Sequelize, sequelize, name, models) { var statics = {} diff --git a/models/Feed.js b/models/Feed.js index 85ccfee..5c859f0 100644 --- a/models/Feed.js +++ b/models/Feed.js @@ -1,9 +1,8 @@ -var request = require('request') - , feedparser = require('../lib/feedparser') - , moment = require('moment') - , async = require('async') - , _ = require('lodash') -; +var request = require('request'); +var feedparser = require('../lib/feedparser'); +var moment = require('moment'); +var async = require('async'); +var _ = require('lodash'); function init(Sequelize, sequelize, name, models) { var statics = {} diff --git a/test/models.Article.js b/test/models.Article.js index 28db16f..d2152ee 100644 --- a/test/models.Article.js +++ b/test/models.Article.js @@ -1,14 +1,12 @@ -var helper = require('../support/spec_helper') - , models = require('../models') - , User = models.User - , Feed = models.Feed - , Article = models.Article - , expect = require('chai').expect - , async = require('async') - , _ = require('lodash') - , mmh3 = require('murmurhash3') - , nodemailer = require('nodemailer') -; +var helper = require('../support/spec_helper'); +var models = require('../models'); +var User = models.User; +var Feed = models.Feed; +var Article = models.Article; +var expect = require('chai').expect; +var async = require('async'); +var _ = require('lodash'); +var mmh3 = require('murmurhash3'); describe("Article model (RDBMS)", function() { var data; diff --git a/test/models.Feed.js b/test/models.Feed.js index 90d5242..1ef487d 100644 --- a/test/models.Feed.js +++ b/test/models.Feed.js @@ -1,15 +1,14 @@ -var helper = require('../support/spec_helper') - , models = require('../models') - , User = models.User - , Feed = models.Feed - , Article = models.Article - , expect = require('chai').expect - , async = require('async') - , _ = require('lodash') - , request = require('request') - , feedparser = require('../lib/feedparser') - , moment = require('moment') -; +var helper = require('../support/spec_helper'); +var models = require('../models'); +var User = models.User; +var Feed = models.Feed; +var Article = models.Article; +var expect = require('chai').expect; +var async = require('async'); +var _ = require('lodash'); +var request = require('request'); +var feedparser = require('../lib/feedparser'); +var moment = require('moment'); describe("Feed model (RDBMS)", function() { beforeEach(function() { From e973caee991140a3bd4c4235b65d6c0a52b19065 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 22:14:02 -0700 Subject: [PATCH 18/24] Beginning of agents.Fetcher, updated dep's --- agents/fetcher.js | 17 +++++++ db.js | 37 +++++++++++++- models/Article.js | 6 ++- package.json | 18 +++---- support/spec_helper.js | 2 +- test/agents.fetcher.js | 113 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 agents/fetcher.js create mode 100644 test/agents.fetcher.js diff --git a/agents/fetcher.js b/agents/fetcher.js new file mode 100644 index 0000000..b0bbb7c --- /dev/null +++ b/agents/fetcher.js @@ -0,0 +1,17 @@ +function Fetcher() { +} + +Fetcher.fetchFeed = function fetchFeed(feed, done) { + feed.set('last_fetched', new Date()); + feed + .save() + .exec(function(err) { + if (err) { + return done(err); + } + + done(); + }); +}; + +module.exports = Fetcher; diff --git a/db.js b/db.js index e6f13fa..a9da3af 100644 --- a/db.js +++ b/db.js @@ -6,8 +6,41 @@ var knex = Knex(config.database); var bookshelf = Bookshelf.initialize(knex); bookshelf.plugin('registry'); -var BaseModel = bookshelf.Model.extend({ - hasTimestamps: true +var Parent = bookshelf.Model; +var BaseModel = Parent.extend({ + initialize: function initialize(attrs, options) { + Parent.prototype.initialize.call(this, attrs, options); + + this.on('saving', this.onSaving); + }, + parse: function parse(input, options) { + var attrs = Parent.prototype.parse.call(this, input, options); + + (this.dateColumns || []).forEach(function(column) { + if (null != attrs[column]) { + attrs[column] = new Date(attrs[column]); + } + }); + + return attrs; + }, + onSaving: function onSaving(model, attrs, options) { + trim_milliseconds = this.trimMilliseconds; + + (this.dateColumns || []).forEach(function(column) { + if (null != attrs[column]) { + attrs[column] = new Date(attrs[column]); + + if (trim_milliseconds) { + attrs[column].setMilliseconds(0); + model.set(column, attrs[column]); + } + } + }); + }, + dateColumns: ['created_at', 'updated_at', 'last_fetched'], + trimMilliseconds: true, + hasTimestamps: true, }, { }); diff --git a/models/Article.js b/models/Article.js index 3310900..0f1490f 100644 --- a/models/Article.js +++ b/models/Article.js @@ -73,8 +73,10 @@ function init(Sequelize, sequelize, name, models) { var cleaned = _.pick(input, _.keys(attrs)); delete cleaned['id']; - _(input).each(function(v,k){ - if (!v) delete cleaned[k]; + _.each(input, function(v,k){ + if (!v) { + delete cleaned[k]; + } }); return cleaned; diff --git a/package.json b/package.json index 3893ce5..c67606b 100644 --- a/package.json +++ b/package.json @@ -19,36 +19,36 @@ "feedparser": "0.16.3", "jade": "1.9.2", "knex": "0.7.6", - "lodash": "2.4.1", + "lodash": "3.7.0", "moment": "2.10.2", "murmurhash3": "0.2.3", - "mysql": "^2.6.2", - "mysql2": "^0.15.5", + "mysql": "2.6.2", + "mysql2": "0.15.5", "nodemailer": "0.5.13", "passport": "0.1.17", "passport-google-oauth": "0.1.5", "pg": "4.3.0", "prettyjson": "0.9.0", - "request": "2.28.0", + "request": "2.55.0", "resumer": "0.0.0", "sequelize": "1.7.0", "supertest": "0.15.0", - "underscore": "1.5.2", - "underscore.string": "^3.0.3" + "underscore": "1.8.3", + "underscore.string": "3.0.3" }, "devDependencies": { "blanket": "1.1.5", - "chai": "1.9.1", + "chai": "2.2.0", "chai-fuzzy": "1.3.0", "cheerio": "0.19.0", "chicken-little": "0.1.2", "connect": "2.11.2", "cookie": "0.1.0", - "dirty-chai": "^1.2.0", + "dirty-chai": "1.2.0", "mocha": "1.15.1", "mocha-sinon": "1.0.0", "nodemon": "0.7.2", - "sinon-chai": "2.4.0", + "sinon-chai": "2.7.0", "sqlite3": "3.0.5", "supertest": "0.8.2" } diff --git a/support/spec_helper.js b/support/spec_helper.js index ff0454b..eb57b49 100644 --- a/support/spec_helper.js +++ b/support/spec_helper.js @@ -13,7 +13,7 @@ var expect = chai.expect; require('mocha-sinon'); // chai setup -chai.Assertion.includeStack = true; +chai.config.includeStack = true; //chai.use(require('dirty-chai')); chai.use(require('chai-fuzzy')); chai.use(require('sinon-chai')); diff --git a/test/agents.fetcher.js b/test/agents.fetcher.js new file mode 100644 index 0000000..eadf6ff --- /dev/null +++ b/test/agents.fetcher.js @@ -0,0 +1,113 @@ +var _ = require('lodash'); +var request = require('request'); +var expect = require('chai').expect; +var helper = require('../support/spec_helper'); +var agents = require('../agents'); +var feedparser = require('../lib/feedparser'); +var models = require('../bookmodels'); + +describe("agents.Fetcher", function() { + var fetcher; + var feed_title; + var fake_http_err; + var fake_http_body; + + beforeEach(function() { + fetcher = new agents.Fetcher(); + + feed_title = 'a title'; + + fake_http_err = null; //{fake: 'HttpErr'}; + fake_http_response = {fake: 'HttpResponse'}; + fake_http_body = [ + '', + '', + 'a title', + '' + ].join(''); + + this.sinon.stub(request, 'get') + .yields(function(args, done){ + done(fakeHttpErr, fakeHttpResponse, fakeHttpBody); + }); + + this.sinon.spy(feedparser, 'parseString'); + }); + + describe('.fetchFeed', function() { + var feed; + + beforeEach(function(done) { + var attrs = { + url: 'http://example.com/rss.xml', + name: 'an fake feed', + last_fetched: new Date('2010-01-01T00:00:00.000Z') + }; + + models.Feed + .forge(attrs) + .save() + .exec(function(err, result) { + expect(err).to.not.exist; + + feed = result; + + done(); + }); + }); + + it('updates the `last_fetched` timestamp', function(done) { + var now = new Date(); + now.setMilliseconds(0); + var clock = this.sinon.useFakeTimers(now.valueOf()); + agents.Fetcher.fetchFeed(feed, function(err, updated, articles) { + clock.restore(); + expect(err).to.not.exist; + + models.Feed.fetchAll().exec(function(err, feeds) { + expect(err).to.not.exist; + + expect(feed.get('last_fetched')).to.deep.equal(now); + + var expected = feed.toJSON(); + expected.last_fetched = now; + expect(feeds.toJSON()).to.deep.equal([expected]); + + done(); + }); + }); + }); + + context.skip('when there are articles', function() { + context('and some are new', function() { + it('publishes them', function(done) { + }); + }); + + context('and none are new', function() { + }); + }); + + context.skip('when the feed\'s url has changed', function() { + it('updates the feed\'s url', function(done) { + }); + }); + }); + + describe.skip('#getAvailableFeeds', function() { + context('when the url contains pointers to feeds', function() { + it('returns an array of available feeds', function(done) { + }); + }); + + context('when the url contains no feeds', function() { + }); + + context('when the url is not HTML', function() { + context('but is a feed', function() { + it('returns the feed data', function(done) { + }); + }); + }); + }); +}); From 32600d7b2d7cdfbf43215205e286aef0bd1a22b4 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Fri, 17 Apr 2015 22:24:29 -0700 Subject: [PATCH 19/24] More agents.Fetcher WIP --- agents/fetcher.js | 14 +++++++++++++- test/agents.fetcher.js | 18 +++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/agents/fetcher.js b/agents/fetcher.js index b0bbb7c..1a1a8dd 100644 --- a/agents/fetcher.js +++ b/agents/fetcher.js @@ -1,3 +1,7 @@ +'use strict'; + +var request = require('request'); + function Fetcher() { } @@ -10,7 +14,15 @@ Fetcher.fetchFeed = function fetchFeed(feed, done) { return done(err); } - done(); + var url = feed.get('url'); + + request.get(url, function(err, response, body) { + if (err) { + return done(err); + } + + done(); + }); }); }; diff --git a/test/agents.fetcher.js b/test/agents.fetcher.js index eadf6ff..dbc52c3 100644 --- a/test/agents.fetcher.js +++ b/test/agents.fetcher.js @@ -1,3 +1,5 @@ +'use strict'; + var _ = require('lodash'); var request = require('request'); var expect = require('chai').expect; @@ -10,6 +12,7 @@ describe("agents.Fetcher", function() { var fetcher; var feed_title; var fake_http_err; + var fake_http_response; var fake_http_body; beforeEach(function() { @@ -27,9 +30,7 @@ describe("agents.Fetcher", function() { ].join(''); this.sinon.stub(request, 'get') - .yields(function(args, done){ - done(fakeHttpErr, fakeHttpResponse, fakeHttpBody); - }); + .yields(fake_http_err, fake_http_response, fake_http_body); this.sinon.spy(feedparser, 'parseString'); }); @@ -78,6 +79,17 @@ describe("agents.Fetcher", function() { }); }); + it('fetches the feed\'s url', function(done) { + agents.Fetcher.fetchFeed(feed, function(err, updated, articles) { + expect(err).to.not.exist; + + expect(request.get).to.have.been.calledOnce; + expect(request.get).to.have.been.calledWith('http://example.com/rss.xml'); + + done(); + }); + }); + context.skip('when there are articles', function() { context('and some are new', function() { it('publishes them', function(done) { From 518f7c6b0220be6eaaf9b15045f373687e3bd1a6 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 18 Apr 2015 10:53:17 -0700 Subject: [PATCH 20/24] Make it easy to reset everything --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d1d98e..91a2112 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +clean: + rm -rf node_modules + coverage: NODE_ENV=test \ DB_TOKEN="coverage" \ @@ -44,4 +47,4 @@ testwatch: install: npm install -.PHONY: cov coverage dev supper test testwatch +.PHONY: clean cov coverage dev supper test testwatch From 04798fe0d02ca56ed8af27d2884303f80607872d Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 18 Apr 2015 11:01:57 -0700 Subject: [PATCH 21/24] bookmodels.Article --- bookmodels/article.js | 10 ++++++++++ test/bookmodels.article.js | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/bookmodels/article.js b/bookmodels/article.js index e69de29..a7fe785 100644 --- a/bookmodels/article.js +++ b/bookmodels/article.js @@ -0,0 +1,10 @@ +'use strict'; + +var db = require('../db'); + +var Article = db.BaseModel.extend({ + tableName: 'articles' +}, { +}); + +module.exports = Article; diff --git a/test/bookmodels.article.js b/test/bookmodels.article.js index e69de29..0d69971 100644 --- a/test/bookmodels.article.js +++ b/test/bookmodels.article.js @@ -0,0 +1,39 @@ +'use strict'; + +var expect = require('chai').expect; +var models = require('../bookmodels'); +var helper = require('../support/spec_helper'); + +describe("models.Article (bookshelf)", function() { + var minimum_attrs; + + beforeEach(function() { + minimum_attrs = { + }; + }); + + it('can be saved', function(done) { + var now = new Date(); + now.setMilliseconds(0); + var clock = this.sinon.useFakeTimers(now.valueOf()); + models.Article + .forge(minimum_attrs) + .save() + .exec(function(err, user) { + clock.restore(); + + expect(err).to.not.exist; + + var actual = user.toJSON(); + expect(actual.id).to.be.a('number').above(0); + delete actual.id; + + expect(actual).to.deep.equal({ + created_at: now, + updated_at: now, + }); + + done(); + }); + }); +}); From 9cefa00890f25cbbfbe5c9c84a9a701f297fd952 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 18 Apr 2015 11:22:11 -0700 Subject: [PATCH 22/24] Mysql, please pay attention to schema --- bookmodels/article.js | 9 ++++- db.js | 15 ++++++- test/bookmodels.article.js | 83 +++++++++++++++++++++++++++++--------- test/bookmodels.feed.js | 16 ++++++-- test/bookmodels.user.js | 10 ++++- 5 files changed, 107 insertions(+), 26 deletions(-) diff --git a/bookmodels/article.js b/bookmodels/article.js index a7fe785..0ccb617 100644 --- a/bookmodels/article.js +++ b/bookmodels/article.js @@ -3,7 +3,14 @@ var db = require('../db'); var Article = db.BaseModel.extend({ - tableName: 'articles' + tableName: 'articles', + defaults: function defaults() { + return { + link: null, + description: null, + title: 'untitled article' + } + } }, { }); diff --git a/db.js b/db.js index a9da3af..734f197 100644 --- a/db.js +++ b/db.js @@ -1,8 +1,21 @@ +var _ = require('lodash'); var Bookshelf = require('bookshelf'); var config = require('config'); var Knex = require('knex'); -var knex = Knex(config.database); +var db_config = _.cloneDeep(config.database); +if (db_config.client.match(/^mysql/)) { + // make mysql pay attention to schema and not "just go with it" + db_config.pool = { + afterCreate: function(connection, callback) { + connection.query("SET sql_MODE='STRICT_ALL_TABLES';", function(err) { + callback(err, connection); + }); + } + }; +} + +var knex = Knex(db_config); var bookshelf = Bookshelf.initialize(knex); bookshelf.plugin('registry'); diff --git a/test/bookmodels.article.js b/test/bookmodels.article.js index 0d69971..bbc9927 100644 --- a/test/bookmodels.article.js +++ b/test/bookmodels.article.js @@ -9,31 +9,78 @@ describe("models.Article (bookshelf)", function() { beforeEach(function() { minimum_attrs = { + feed_id: 0, + date: new Date('2010-01-01T00:00:00.000Z'), + guid: 'a fake guid' }; }); - it('can be saved', function(done) { - var now = new Date(); - now.setMilliseconds(0); - var clock = this.sinon.useFakeTimers(now.valueOf()); - models.Article - .forge(minimum_attrs) - .save() - .exec(function(err, user) { - clock.restore(); + describe('#save', function() { + context('when a date is not provided', function() { + it('yields an error', function(done) { + delete minimum_attrs.date; - expect(err).to.not.exist; + models.Article + .forge(minimum_attrs) + .save() + .exec(function(err, article) { + expect(err).to.exist; - var actual = user.toJSON(); - expect(actual.id).to.be.a('number').above(0); - delete actual.id; + done(); + }); + }); + }); + }); + + describe('#defaults', function() { + context('when some attributes are not provided', function() { + it('generates attribute values', function(done) { + var now = new Date(); + now.setMilliseconds(0); + var clock = this.sinon.useFakeTimers(now.valueOf()); + models.Article + .forge(minimum_attrs) + .save() + .exec(function(err, article) { + clock.restore(); + + expect(err).to.not.exist; + + var actual = article.toJSON(); + expect(actual.id).to.be.a('number').above(0); + delete actual.id; - expect(actual).to.deep.equal({ - created_at: now, - updated_at: now, - }); + expect(actual).to.deep.equal({ + feed_id: 0, + date: new Date('2010-01-01T00:00:00.000Z'), + guid: 'a fake guid', + link: null, + title: 'untitled article', + description: null, + created_at: now, + updated_at: now, + }); + + var actual = article.toJSON(); + expect(actual.id).to.be.a('number').above(0); + delete actual.id; + + models.Article + .fetchAll() + .exec(function(err, articles) { + expect(err).to.not.exist; + + expect(articles.toJSON()).to.deep.equal([article.toJSON()]); + + done(); + }); + }); + }); + }); - done(); + context.skip('when there is no guid', function() { + it('generates one via hashing the article attributes', function(done) { }); + }); }); }); diff --git a/test/bookmodels.feed.js b/test/bookmodels.feed.js index 68f90fe..9de7227 100644 --- a/test/bookmodels.feed.js +++ b/test/bookmodels.feed.js @@ -6,15 +6,20 @@ var helper = require('../support/spec_helper'); describe("models.Feed (bookshelf)", function() { var minimum_attrs; + var now; beforeEach(function() { + now = new Date(); + now.setMilliseconds(0); + minimum_attrs = { + url: 'http://example.com/rss.xml', + name: 'fake feed name', + last_fetched: now }; }); it('can be saved', function(done) { - var now = new Date(); - now.setMilliseconds(0); var clock = this.sinon.useFakeTimers(now.valueOf()); models.Feed .forge(minimum_attrs) @@ -29,8 +34,11 @@ describe("models.Feed (bookshelf)", function() { delete actual.id; expect(actual).to.deep.equal({ - created_at: now, - updated_at: now, + url: 'http://example.com/rss.xml', + name: 'fake feed name', + last_fetched: now, + created_at: now, + updated_at: now, }); done(); diff --git a/test/bookmodels.user.js b/test/bookmodels.user.js index a30a4d8..afc17d7 100644 --- a/test/bookmodels.user.js +++ b/test/bookmodels.user.js @@ -9,6 +9,9 @@ describe("models.User (bookshelf)", function() { beforeEach(function() { minimum_attrs = { + email: 'fake@example.com', + oauth_provider: 'fake oauth_provider', + oauth_id: 'fake oauth_id' }; }); @@ -29,8 +32,11 @@ describe("models.User (bookshelf)", function() { delete actual.id; expect(actual).to.deep.equal({ - created_at: now, - updated_at: now, + email: 'fake@example.com', + oauth_provider: 'fake oauth_provider', + oauth_id: 'fake oauth_id', + created_at: now, + updated_at: now, }); done(); From 72893053dc66484cf46897c873829c3e67ce3fd3 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 18 Apr 2015 11:26:38 -0700 Subject: [PATCH 23/24] Inject dependencies --- agents/fetcher.js | 11 ++++++----- test/agents.fetcher.js | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/agents/fetcher.js b/agents/fetcher.js index 1a1a8dd..91b92de 100644 --- a/agents/fetcher.js +++ b/agents/fetcher.js @@ -1,11 +1,12 @@ 'use strict'; -var request = require('request'); - -function Fetcher() { +function Fetcher(getter) { + this._getter = getter; } -Fetcher.fetchFeed = function fetchFeed(feed, done) { +Fetcher.prototype.fetchFeed = function fetchFeed(feed, done) { + var getter = this._getter; + feed.set('last_fetched', new Date()); feed .save() @@ -16,7 +17,7 @@ Fetcher.fetchFeed = function fetchFeed(feed, done) { var url = feed.get('url'); - request.get(url, function(err, response, body) { + getter(url, function(err, response, body) { if (err) { return done(err); } diff --git a/test/agents.fetcher.js b/test/agents.fetcher.js index dbc52c3..17a780b 100644 --- a/test/agents.fetcher.js +++ b/test/agents.fetcher.js @@ -16,8 +16,6 @@ describe("agents.Fetcher", function() { var fake_http_body; beforeEach(function() { - fetcher = new agents.Fetcher(); - feed_title = 'a title'; fake_http_err = null; //{fake: 'HttpErr'}; @@ -33,9 +31,11 @@ describe("agents.Fetcher", function() { .yields(fake_http_err, fake_http_response, fake_http_body); this.sinon.spy(feedparser, 'parseString'); + + fetcher = new agents.Fetcher(request.get); }); - describe('.fetchFeed', function() { + describe('#fetchFeed', function() { var feed; beforeEach(function(done) { @@ -61,7 +61,7 @@ describe("agents.Fetcher", function() { var now = new Date(); now.setMilliseconds(0); var clock = this.sinon.useFakeTimers(now.valueOf()); - agents.Fetcher.fetchFeed(feed, function(err, updated, articles) { + fetcher.fetchFeed(feed, function(err, updated, articles) { clock.restore(); expect(err).to.not.exist; @@ -80,7 +80,7 @@ describe("agents.Fetcher", function() { }); it('fetches the feed\'s url', function(done) { - agents.Fetcher.fetchFeed(feed, function(err, updated, articles) { + fetcher.fetchFeed(feed, function(err, updated, articles) { expect(err).to.not.exist; expect(request.get).to.have.been.calledOnce; From 236cbb2699c9d6872887e8b4b844a43dc0c572b1 Mon Sep 17 00:00:00 2001 From: Elliot Foster Date: Sat, 15 Aug 2015 07:48:06 -0700 Subject: [PATCH 24/24] Faster test display --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d1d98e..b7ff07c 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ test: APP_SMTP_SSL="true" \ APP_SMTP_FROM="no-reply@example.com" \ APP_SMTP_PASS="dummy password" \ - ./node_modules/.bin/mocha --recursive test -R list + ./node_modules/.bin/mocha --recursive test -R dot testwatch: DB_TOKEN="testwatch" ./node_modules/.bin/chicken -c 'clear; time make test' .