From 371ce944f5b2d3a0e0d23002ffd938deadb99fd9 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 21 Aug 2017 18:07:38 +0300 Subject: [PATCH 01/98] add Promise support for connect, now connectAsync use promises --- lib/LazyLoad.js | 14 ++ lib/ORM.js | 99 +++++++++----- package-lock.json | 5 + package.json | 7 +- test/config.example.js | 29 ---- test/integration/db.js | 4 + test/integration/orm-exports.js | 228 +++++++++++++++++++++++++++++++- 7 files changed, 319 insertions(+), 67 deletions(-) delete mode 100644 test/config.example.js diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 48af3780..f6322862 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -22,6 +22,20 @@ function addLazyLoadProperty(name, Instance, Model, property) { }, enumerable: false }); + + Object.defineProperty(Instance, "getAsync" + method, { + value: function () { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + return cb(err, item ? item[property] : null); + }); + + return this; + }, + enumerable: false + }); Object.defineProperty(Instance, "remove" + method, { value: function (cb) { var conditions = {}; diff --git a/lib/ORM.js b/lib/ORM.js index 4683f2ea..8515becd 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -3,9 +3,12 @@ var async = require("async"); var enforce = require("enforce"); var events = require("events"); var hat = require("hat"); +var Promise = require('bluebird'); var Query = require("sql-query"); var url = require("url"); var util = require("util"); +var enforce = require("enforce"); +var _ = require("lodash"); var adapters = require("./Adapters"); var DriverAliases = require("./Drivers/aliases"); @@ -32,39 +35,7 @@ exports.Property = require("./Property"); exports.Settings = Settings; exports.ErrorCodes = ORMError.codes; -exports.Text = Query.Text; -for (var k in Query.Comparators) { - exports[Query.Comparators[k]] = Query[Query.Comparators[k]]; -} - -exports.express = function () { - return require("./Express").apply(this, arguments); -}; - -exports.use = function (connection, proto, opts, cb) { - if (DriverAliases[proto]) { - proto = DriverAliases[proto]; - } - if (typeof opts === "function") { - cb = opts; - opts = {}; - } - - try { - var Driver = adapters.get(proto); - var settings = new Settings.Container(exports.settings.get('*')); - var driver = new Driver(null, connection, { - debug : (opts.query && opts.query.debug === 'true'), - settings : settings - }); - - return cb(null, new ORM(proto, driver, settings)); - } catch (ex) { - return cb(ex); - } -}; - -exports.connect = function (opts, cb) { +var connect = function (opts, cb) { if (arguments.length === 0 || !opts) { return ORM_Error(new ORMError("CONNECTION_URL_EMPTY", 'PARAM_MISMATCH'), cb); } @@ -148,6 +119,56 @@ exports.connect = function (opts, cb) { return db; }; +exports.Text = Query.Text; +for (var k in Query.Comparators) { + exports[Query.Comparators[k]] = Query[Query.Comparators[k]]; +} + +exports.express = function () { + return require("./Express").apply(this, arguments); +}; + +exports.use = function (connection, proto, opts, cb) { + if (DriverAliases[proto]) { + proto = DriverAliases[proto]; + } + if (typeof opts === "function") { + cb = opts; + opts = {}; + } + + try { + var Driver = adapters.get(proto); + var settings = new Settings.Container(exports.settings.get('*')); + var driver = new Driver(null, connection, { + debug : (opts.query && opts.query.debug === 'true'), + settings : settings + }); + + return cb(null, new ORM(proto, driver, settings)); + } catch (ex) { + return cb(ex); + } +}; + +/** + * + * @param opts + */ +exports.connectAsync = function (opts) { + return new Promise(function (resolve, reject) { + var cb = function (resolve, reject) { + return function (err, data) { + if (err !== null) { + reject(err); + } + resolve(data); + } + }; + connect(opts, cb(resolve, reject)); + }); +}; + exports.addAdapter = adapters.add; function ORM(driver_name, driver, settings) { @@ -296,6 +317,9 @@ ORM.prototype.load = function () { async.eachSeries(filesWithPath, iterator, cb); }; + + + ORM.prototype.sync = function (cb) { var modelIds = Object.keys(this.models); var syncNext = function () { @@ -324,6 +348,13 @@ ORM.prototype.sync = function (cb) { return this; }; + +ORM.prototype.dropAsync = function () { + return Promise.each(this.models, function (model) { + console.log(model.drop); + }) +}; + ORM.prototype.drop = function (cb) { var modelIds = Object.keys(this.models); var dropNext = function () { @@ -411,3 +442,5 @@ function queryParamCast (val) { } return val; } + +exports.connect = connect; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c0832151..79fccedc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,11 @@ "integrity": "sha1-8725mtUmihX8HwvtL7AY4mk/4jY=", "dev": true }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", diff --git a/package.json b/package.json index 424d140a..481cfe63 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ ], "main": "./lib/ORM", "scripts": { - "test": "make test" + "test": "ORM_PROTOCOL=postgres make test" }, "engines": { "node": "*" @@ -62,12 +62,13 @@ "analyse": false, "dependencies": { "async": "2.5.0", + "bluebird": "^3.5.0", "enforce": "0.1.7", "hat": "0.0.3", "lodash": "4.17.4", "path-is-absolute": "1.0.1", - "sql-query": "0.1.26", - "sql-ddl-sync": "0.3.13" + "sql-ddl-sync": "0.3.13", + "sql-query": "0.1.26" }, "devDependencies": { "chalk": "2.0.1", diff --git a/test/config.example.js b/test/config.example.js deleted file mode 100644 index bb9c2d73..00000000 --- a/test/config.example.js +++ /dev/null @@ -1,29 +0,0 @@ -// To test, rename this file to config.js and update -// the following configuration -// -// To run a single driver, go to root folder and do (mysql example): -// ORM_PROTOCOL=mysql node test/run -// -// To run all drivers: -// make test - -exports.mysql = { - user : "root", - password : "", - database : "test" -}; -exports.postgres = { - user : "root", - password : "", - database : "test" -}; -exports.redshift = { - user : "root", - password : "", - database : "test" -}; -exports.mongodb = { - host: "localhost", - database: "test" -}; -exports.sqlite = { }; // uses in-memory database diff --git a/test/integration/db.js b/test/integration/db.js index d0f4c793..da72e113 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -18,6 +18,10 @@ describe("db.use()", function () { return db.close(); }); + it.only('~!_!-!!_!~-~_~_-_-', function () { + console.log(db.dropAsync); + }); + it("should be able to register a plugin", function (done) { var MyPlugin = require("../support/my_plugin"); var opts = { diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index 9b66b1c1..fb7be6c7 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -50,6 +50,229 @@ describe("ORM", function() { }); }); +describe('ORM.connectAsync()', function () { + it('should be a function', function () { + ORM.connectAsync.should.be.a.Function() + }); + + it('should throw error with correct message when protocol not supported', function () { + ORM.connectAsync("pg://127.0.0.6") + .then(function () { + done('Fail.'); + }) + .catch(function () { + should.exist(err); + err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); + done(); + }); + }); + + it('should throw error with correct message when connection URL doesn\'t exist', function (done) { + ORM.connectAsync() + .then(function () { + done('Fail') + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done(); + }); + }); + + it("should throw error when passed empty string like connection URL", function (done) { + ORM.connectAsync("") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); + + it("should throw error when passed string with spaces only", function (done) { + ORM.connectAsync(" ") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); + + it("should throw error when passed invalid protocol", function (done) { + ORM.connectAsync("user@db") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); + done() + }); + }); + + it("should throw error when passed unknown protocol", function (done) { + ORM.connectAsync("unknown://db") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + should.equal(err.literalCode, 'NO_SUPPORT'); + should.equal( + err.message, + "Connection protocol not supported - have you installed the database driver for unknown?" + ); + done() + }); + }); + + it("should throw error when passed invalid connection db link", function (done) { + ORM.connectAsync("mysql://fakeuser:nopassword@127.0.0.1/unknowndb") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + should.exist(err); + should.equal(err.message.indexOf("Connection protocol not supported"), -1); + err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); + err.message.should.not.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); + + it("should do not mutate opts", function (done) { + var opts = { + protocol : 'mysql', + user : 'notauser', + password : "wrong password", + query : { pool: true, debug: true } + }; + + var expected = JSON.stringify(opts); + + ORM.connectAsync(opts) + .then(function () { + done('Fail'); + }) + .catch(function () { + should.equal( + JSON.stringify(opts), + expected + ); + done(); + }); + }); + + it("should pass successful when opts is OK!", function (done) { + ORM.connectAsync(common.getConnectionString()) + .then(function (db) { + should.exist(db); + + db.use.should.be.a.Function(); + db.define.should.be.a.Function(); + db.sync.should.be.a.Function(); + db.load.should.be.a.Function(); + + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + describe('POOL via connectAsync', function () { + if (protocol !== 'mongodb') { + it("should understand pool `'false'` from query string", function (done) { + var connString = common.getConnectionString() + "debug=false&pool=false"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should understand pool `'0'` from query string", function (done) { + var connString = common.getConnectionString() + "debug=0&pool=0"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should understand pool `'true'` from query string", function (done) { + var connString = common.getConnectionString() + "debug=true&pool=true"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should understand pool `'true'` from query string", function (done) { + var connString = common.getConnectionString() + "debug=1&pool=1"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should understand pool `'true'` from query string", function (done) { + var connOpts = _.extend(common.getConfig(), { + protocol: common.protocol(), + query: { + pool: true, debug: true + } + }); + ORM.connectAsync(connOpts) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should understand pool `false` from query options", function (done) { + var connOpts = _.extend(common.getConfig(), { + protocol: common.protocol(), + query: { + pool: false, debug: false + } + }); + ORM.connectAsync(connOpts) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + } + }); +}); + describe("ORM.connect()", function () { it("should expose .use(), .define(), .sync() and .load()", function (done) { var db = ORM.connect(); @@ -72,8 +295,9 @@ describe("ORM.connect()", function () { }); }); - it("should allow protocol alias", function (done) { - var db = ORM.connect("pg://127.0.0.2"); + it.skip("should allow protocol alias", function (done) { + this.timeout(60000); + var db = ORM.connect("pg://127.0.0.6"); db.once("connect", function (err) { should.exist(err); From 5d358a02bbca221a5ea39f7280b736824b9bf440 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 12:54:29 +0300 Subject: [PATCH 02/98] add dropAsync and syncPromise, add test coverage for this methods --- lib/ORM.js | 31 +- package-lock.json | 90 +++- package.json | 1 + test/integration/db.js | 392 ++++++++------- test/integration/orm-exports.js | 844 ++++++++++++++++---------------- 5 files changed, 754 insertions(+), 604 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index 8515becd..8d37bb24 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -49,7 +49,6 @@ var connect = function (opts, cb) { } opts.query = opts.query || {}; - for(var k in opts.query) { opts.query[k] = queryParamCast(opts.query[k]); opts[k] = opts.query[k]; @@ -156,6 +155,7 @@ exports.use = function (connection, proto, opts, cb) { * @param opts */ exports.connectAsync = function (opts) { + var options = _.cloneDeep(opts); return new Promise(function (resolve, reject) { var cb = function (resolve, reject) { return function (err, data) { @@ -165,7 +165,7 @@ exports.connectAsync = function (opts) { resolve(data); } }; - connect(opts, cb(resolve, reject)); + connect(options, cb(resolve, reject)); }); }; @@ -318,7 +318,21 @@ ORM.prototype.load = function () { async.eachSeries(filesWithPath, iterator, cb); }; - +ORM.prototype.syncPromise = function () { + var self = this; + var modelIds = Object.keys(this.models); + return Promise.each(modelIds, function (id) { + return new Promise(function (resolve, reject) { + self.models[id].sync(function (err) { + if (err) { + err.model = id; + reject(err); + } + resolve(id); + }); + }); + }) +}; ORM.prototype.sync = function (cb) { var modelIds = Object.keys(this.models); @@ -350,8 +364,15 @@ ORM.prototype.sync = function (cb) { }; ORM.prototype.dropAsync = function () { - return Promise.each(this.models, function (model) { - console.log(model.drop); + var self = this; + var modelIds = Object.keys(this.models); + return Promise.each(modelIds, function (id) { + return new Promise(function (resolve, reject) { + self.models[id].drop(function (err, data) { + if (err) reject(err); + resolve(data); + }); + }); }) }; diff --git a/package-lock.json b/package-lock.json index 79fccedc..b1760b04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,8 +134,7 @@ "diff": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", - "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", - "dev": true + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" }, "enforce": { "version": "0.1.7", @@ -148,6 +147,14 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "formatio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "requires": { + "samsam": "1.2.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -226,6 +233,11 @@ "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, + "just-extend": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz", + "integrity": "sha1-MzCvdWyralQnAMZLLk5KoGLVL/8=" + }, "kerberos": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.4.tgz", @@ -306,6 +318,11 @@ "lodash.isarray": "3.0.4" } }, + "lolex": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz", + "integrity": "sha1-JpS5U8nqTQE+W4v7qJHJkQJbJik=" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -440,6 +457,29 @@ "integrity": "sha1-PHa1OC6rM+RLdY0oE8qdkuk0LzQ=", "dev": true }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" + }, + "nise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.0.1.tgz", + "integrity": "sha1-DakrEKhU6XwPSW9sKEWjASgLPu8=", + "requires": { + "formatio": "1.2.0", + "just-extend": "1.1.22", + "lolex": "1.6.0", + "path-to-regexp": "1.7.0" + }, + "dependencies": { + "lolex": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", + "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=" + } + } + }, "object-assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", @@ -466,6 +506,21 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, "pg": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/pg/-/pg-6.4.1.tgz", @@ -583,6 +638,11 @@ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, + "samsam": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz", + "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=" + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -643,6 +703,22 @@ "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", "dev": true }, + "sinon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", + "integrity": "sha512-KY3OLOWpek/I4NGAMHetuutVgS2aRgMR5g5/1LSYvPJ3qo2BopIvk3esFztPxF40RWf/NNNJzdFPriSkXUVK3A==", + "requires": { + "diff": "3.2.0", + "formatio": "1.2.0", + "lolex": "2.1.2", + "native-promise-only": "0.8.1", + "nise": "1.0.1", + "path-to-regexp": "1.7.0", + "samsam": "1.2.1", + "text-encoding": "0.6.4", + "type-detect": "4.0.3" + } + }, "split": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", @@ -1784,12 +1860,22 @@ "has-flag": "2.0.0" } }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "type-detect": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", + "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 481cfe63..faae57a6 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "hat": "0.0.3", "lodash": "4.17.4", "path-is-absolute": "1.0.1", + "sinon": "^3.2.1", "sql-ddl-sync": "0.3.13", "sql-query": "0.1.26" }, diff --git a/test/integration/db.js b/test/integration/db.js index da72e113..8546e3c6 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -1,12 +1,12 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); +var sinon = require('sinon'); var common = require('../common'); +var _ = require('lodash'); -describe("db.use()", function () { +describe('DB', function () { var db = null; - - before(function (done) { + beforeEach(function (done) { helper.connect(function (connection) { db = connection; @@ -14,256 +14,276 @@ describe("db.use()", function () { }); }); - after(function () { + afterEach(function () { return db.close(); }); - it.only('~!_!-!!_!~-~_~_-_-', function () { - console.log(db.dropAsync); - }); - - it("should be able to register a plugin", function (done) { - var MyPlugin = require("../support/my_plugin"); - var opts = { - option : true, - calledDefine : false - }; - - db.use(MyPlugin, opts); - - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + describe('db.syncPromise()', function () { + it('should call sync method from model, and return array with model id', function (done) { + db.define("my_model", { + property: String + }); + var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { cb(null, {}) }); + db.dropAsync() + .then(function (data) { + syncStub.calledOnce.should.be.true; + data.should.be.Array; + should.equal(data[0], 'my_model'); + done(); + }) + .catch(function (err) { + done(err) + }); + syncStub.restore(); }); - opts.calledDefine.should.be.true; - - return done(); - }); - - it("a plugin should be able to catch models before defining them", function (done) { - var MyPlugin = require("../support/my_plugin"); - var opts = { - option : true, - calledDefine : false, - beforeDefine : function (name, props, opts) { - props.otherprop = Number; - } - }; - - db.use(MyPlugin, opts); - - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + it('should return an empty array when no model created', function (done) { + db.dropAsync() + .then(function (data) { + data.should.be.Array; + should.equal(_.isEmpty(data), true); + done() + }) + .catch(function (err) { + done(err) + }) }); - - opts.calledDefine.should.be.true; - MyModel.properties.should.have.property("otherprop"); - - return done(); }); - it("should be able to register a plugin as string", function (done) { - var opts = { - option : true, - calledDefine : false - }; - - db.use("../support/my_plugin", opts); + describe("db.dropAsync()", function () { + it('should call drop method from model, and return array with model id', function (done) { + db.define("my_model", { + property: String + }); + var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { cb(null, {}) }); + db.dropAsync() + .then(function (data) { + dropStub.calledOnce.should.be.true; + data.should.be.Array; + should.equal(data[0], 'my_model'); + done(); + }) + .catch(function (err) { + done(err) + }); + dropStub.restore(); + }); - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + it('should return an empty array when no model created', function (done) { + db.dropAsync() + .then(function (data) { + data.should.be.Array; + should.equal(_.isEmpty(data), true); + done() + }) + .catch(function (err) { + done(err) + }) }); + }); - opts.calledDefine.should.be.true; + describe("db.use()", function () { + it("should be able to register a plugin", function (done) { + var MyPlugin = require("../support/my_plugin"); + var opts = { + option : true, + calledDefine : false + }; - return done(); - }); -}); + db.use(MyPlugin, opts); -describe("db.define()", function() { - var db = null; + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); - before(function (done) { - helper.connect(function (connection) { - db = connection; + opts.calledDefine.should.be.true; return done(); }); - }); - after(function () { - return db.close(); - }); + it("a plugin should be able to catch models before defining them", function (done) { + var MyPlugin = require("../support/my_plugin"); + var opts = { + option : true, + calledDefine : false, + beforeDefine : function (name, props, opts) { + props.otherprop = Number; + } + }; + + db.use(MyPlugin, opts); + + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); - it("should use setting model.namePrefix as table prefix if defined", function (done) { - db.settings.set("model.namePrefix", "orm_"); + opts.calledDefine.should.be.true; + MyModel.properties.should.have.property("otherprop"); - var Person = db.define("person", { - name: String + done(); }); - Person.table.should.equal("orm_person"); + it("should be able to register a plugin as string", function (done) { + var opts = { + option : true, + calledDefine : false + }; - return done(); - }); -}); + db.use("../support/my_plugin", opts); -describe("db.load()", function () { - var db = null; + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); - beforeEach(function (done) { - helper.connect(function (connection) { - db = connection; + opts.calledDefine.should.be.true; - done(); + return done(); }); }); - afterEach(function () { - db.close(); - }); + describe("db.define()", function() { + it("should use setting model.namePrefix as table prefix if defined", function (done) { + db.settings.set("model.namePrefix", "orm_"); - it("should require a file based on relative path", function (done) { - db.load("../support/spec_load", function (err) { - should.not.exist(err); + var Person = db.define("person", { + name: String + }); - db.models.should.have.property("person"); - db.models.should.have.property("pet"); + Person.table.should.equal("orm_person"); - done(); + return done(); }); }); - it("should be able to load more than one file", function (done) { - db.load("../support/spec_load_second", "../support/spec_load_third", function (err) { - should.not.exist(err); + describe("db.load()", function () { + it("should require a file based on relative path", function (done) { + db.load("../support/spec_load", function (err) { + should.not.exist(err); - db.models.should.have.property("person"); - db.models.should.have.property("pet"); + db.models.should.have.property("person"); + db.models.should.have.property("pet"); - done(); + return done(); + }); }); - }); - it("should be able to load more than one file passed as Array", function (done) { - db.load([ "../support/spec_load_second", "../support/spec_load_third" ], function (err) { - should.not.exist(err); + it("should be able to load more than one file", function (done) { + db.load("../support/spec_load_second", "../support/spec_load_third", function (err) { + should.not.exist(err); - db.models.should.have.property("person"); - db.models.should.have.property("pet"); + db.models.should.have.property("person"); + db.models.should.have.property("pet"); - done(); + return done(); + }); }); - }); -}); -describe("db.serial()", function () { - var db = null; + it("should be able to load more than one file passed as Array", function (done) { + db.load([ "../support/spec_load_second", "../support/spec_load_third" ], function (err) { + should.not.exist(err); - before(function (done) { - helper.connect(function (connection) { - db = connection; + db.models.should.have.property("person"); + db.models.should.have.property("pet"); - return done(); + return done(); + }); }); }); - after(function () { - return db.close(); - }); - - it("should be able to execute chains in serial", function (done) { - var Person = db.define("person", { - name : String, - surname : String - }); - helper.dropSync(Person, function () { - Person.create([ - { name : "John", surname : "Doe" }, - { name : "Jane", surname : "Doe" } - ], function () { - db.serial( - Person.find({ surname : "Doe" }), - Person.find({ name : "John" }) - ).get(function (err, DoeFamily, JohnDoe) { - should.equal(err, null); - - should(Array.isArray(DoeFamily)); - should(Array.isArray(JohnDoe)); - - DoeFamily.length.should.equal(2); - JohnDoe.length.should.equal(1); - - DoeFamily[0].surname.should.equal("Doe"); - DoeFamily[1].surname.should.equal("Doe"); - - JohnDoe[0].name.should.equal("John"); - - return done(); + describe("db.serial()", function () { + it("should be able to execute chains in serial", function (done) { + var Person = db.define("person", { + name : String, + surname : String + }); + helper.dropSync(Person, function () { + Person.create([ + { name : "John", surname : "Doe" }, + { name : "Jane", surname : "Doe" } + ], function () { + db.serial( + Person.find({ surname : "Doe" }), + Person.find({ name : "John" }) + ).get(function (err, DoeFamily, JohnDoe) { + should.equal(err, null); + + should(Array.isArray(DoeFamily)); + should(Array.isArray(JohnDoe)); + + DoeFamily.length.should.equal(2); + JohnDoe.length.should.equal(1); + + DoeFamily[0].surname.should.equal("Doe"); + DoeFamily[1].surname.should.equal("Doe"); + + JohnDoe[0].name.should.equal("John"); + + return done(); + }); }); }); }); }); -}); -describe("db.driver", function () { - var db = null; + describe("db.driver", function () { + var db = null; - before(function (done) { - helper.connect(function (connection) { - db = connection; + before(function (done) { + helper.connect(function (connection) { + db = connection; - var Log = db.define('log', { - what : { type: 'text' }, - when : { type: 'date', time: true }, - who : { type: 'text' } - }); + var Log = db.define('log', { + what : { type: 'text' }, + when : { type: 'date', time: true }, + who : { type: 'text' } + }); - helper.dropSync(Log, function (err) { - if (err) return done(err); + helper.dropSync(Log, function (err) { + if (err) return done(err); - Log.create([ - { what: "password reset", when: new Date('2013/04/07 12:33:05'), who: "jane" }, - { what: "user login", when: new Date('2013/04/07 13:01:44'), who: "jane" }, - { what: "user logout", when: new Date('2013/05/12 04:09:31'), who: "john" } - ], done); + Log.create([ + { what: "password reset", when: new Date('2013/04/07 12:33:05'), who: "jane" }, + { what: "user login", when: new Date('2013/04/07 13:01:44'), who: "jane" }, + { what: "user logout", when: new Date('2013/05/12 04:09:31'), who: "john" } + ], done); + }); }); }); - }); - after(function () { - return db.close(); - }); - - it("should be available", function () { - should.exist(db.driver); - }); - - if (common.protocol() == "mongodb") return; + after(function () { + return db.close(); + }); - describe("query", function () { it("should be available", function () { - should.exist(db.driver.query); + should.exist(db.driver); }); - describe("#execQuery", function () { - it("should execute sql queries", function (done) { - db.driver.execQuery("SELECT id FROM log", function (err, data) { - should.not.exist(err); + if (common.protocol() == "mongodb") return; - should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); - done(); - }); + describe("query", function () { + it("should be available", function () { + should.exist(db.driver.query); }); - it("should escape sql queries", function (done) { - var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; - var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; - db.driver.execQuery(query, args, function (err, data) { - should.not.exist(err); + describe("#execQuery", function () { + it("should execute sql queries", function (done) { + db.driver.execQuery("SELECT id FROM log", function (err, data) { + should.not.exist(err); - should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); - done(); + should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); + done(); + }); + }); + + it("should escape sql queries", function (done) { + var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; + var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; + db.driver.execQuery(query, args, function (err, data) { + should.not.exist(err); + + should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); + done(); + }); }); }); }); diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index fb7be6c7..852967cc 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -48,526 +48,548 @@ describe("ORM", function() { return done(); }); }); -}); - -describe('ORM.connectAsync()', function () { - it('should be a function', function () { - ORM.connectAsync.should.be.a.Function() - }); + describe('ORM.connectAsync()', function () { + it('should be a function', function () { + ORM.connectAsync.should.be.a.Function() + }); - it('should throw error with correct message when protocol not supported', function () { - ORM.connectAsync("pg://127.0.0.6") - .then(function () { - done('Fail.'); - }) - .catch(function () { - should.exist(err); - err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); - done(); - }); - }); + it('should throw error with correct message when protocol not supported', function () { + ORM.connectAsync("pg://127.0.0.6") + .then(function () { + done('Fail.'); + }) + .catch(function () { + should.exist(err); + err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); + done(); + }); + }); - it('should throw error with correct message when connection URL doesn\'t exist', function (done) { - ORM.connectAsync() - .then(function () { - done('Fail') - }) - .catch(function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); - done(); - }); - }); + it('should throw error with correct message when connection URL doesn\'t exist', function (done) { + ORM.connectAsync() + .then(function () { + done('Fail') + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done(); + }); + }); - it("should throw error when passed empty string like connection URL", function (done) { - ORM.connectAsync("") - .then(function () { - done('Fail'); - }) - .catch(function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); - done() - }); - }); + it("should throw error when passed empty string like connection URL", function (done) { + ORM.connectAsync("") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); - it("should throw error when passed string with spaces only", function (done) { - ORM.connectAsync(" ") - .then(function () { - done('Fail'); - }) - .catch(function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); - done() - }); - }); + it("should throw error when passed string with spaces only", function (done) { + ORM.connectAsync(" ") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); - it("should throw error when passed invalid protocol", function (done) { - ORM.connectAsync("user@db") - .then(function () { - done('Fail'); - }) - .catch(function (err) { - err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); - done() - }); - }); + it("should throw error when passed invalid protocol", function (done) { + ORM.connectAsync("user@db") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); + done() + }); + }); - it("should throw error when passed unknown protocol", function (done) { - ORM.connectAsync("unknown://db") - .then(function () { - done('Fail'); - }) - .catch(function (err) { - should.equal(err.literalCode, 'NO_SUPPORT'); - should.equal( - err.message, - "Connection protocol not supported - have you installed the database driver for unknown?" - ); - done() - }); - }); + it("should throw error when passed unknown protocol", function (done) { + ORM.connectAsync("unknown://db") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + should.equal(err.literalCode, 'NO_SUPPORT'); + should.equal( + err.message, + "Connection protocol not supported - have you installed the database driver for unknown?" + ); + done() + }); + }); - it("should throw error when passed invalid connection db link", function (done) { - ORM.connectAsync("mysql://fakeuser:nopassword@127.0.0.1/unknowndb") - .then(function () { - done('Fail'); - }) - .catch(function (err) { - should.exist(err); - should.equal(err.message.indexOf("Connection protocol not supported"), -1); - err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); - err.message.should.not.equal("CONNECTION_URL_EMPTY"); - done() - }); - }); + it("should throw error when passed invalid connection db link", function (done) { + ORM.connectAsync("mysql://fakeuser:nopassword@127.0.0.1/unknowndb") + .then(function () { + done('Fail'); + }) + .catch(function (err) { + should.exist(err); + should.equal(err.message.indexOf("Connection protocol not supported"), -1); + err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); + err.message.should.not.equal("CONNECTION_URL_EMPTY"); + done() + }); + }); - it("should do not mutate opts", function (done) { - var opts = { - protocol : 'mysql', - user : 'notauser', - password : "wrong password", - query : { pool: true, debug: true } - }; - - var expected = JSON.stringify(opts); - - ORM.connectAsync(opts) - .then(function () { - done('Fail'); - }) - .catch(function () { - should.equal( - JSON.stringify(opts), - expected - ); - done(); - }); - }); + it("should do not mutate opts", function (done) { + var opts = { + protocol : 'mysql', + user : 'notauser', + password : "wrong password", + query : { pool: true, debug: true } + }; + + var expected = JSON.stringify(opts); + + ORM.connectAsync(opts) + .then(function () { + done('Fail'); + }) + .catch(function () { + should.equal( + JSON.stringify(opts), + expected + ); + done(); + }); + }); - it("should pass successful when opts is OK!", function (done) { - ORM.connectAsync(common.getConnectionString()) - .then(function (db) { - should.exist(db); + it("should pass successful when opts is OK!", function (done) { + ORM.connectAsync(common.getConnectionString()) + .then(function (db) { + should.exist(db); - db.use.should.be.a.Function(); - db.define.should.be.a.Function(); - db.sync.should.be.a.Function(); - db.load.should.be.a.Function(); + db.use.should.be.a.Function(); + db.define.should.be.a.Function(); + db.sync.should.be.a.Function(); + db.load.should.be.a.Function(); - done(); - }) - .catch(function (err) { - done(err); - }); - }); + done(); + }) + .catch(function (err) { + done(err); + }); + }); - describe('POOL via connectAsync', function () { - if (protocol !== 'mongodb') { - it("should understand pool `'false'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=false&pool=false"; - ORM.connectAsync(connString) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); - }) - .catch(function (err) { - done(err); - }); - }); + describe('POOL via connectAsync', function () { + var connStr = null; - it("should understand pool `'0'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=0&pool=0"; - ORM.connectAsync(connString) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); - }) - .catch(function (err) { - done(err); - }); + beforeEach(function () { + connStr = common.getConnectionString(); }); - it("should understand pool `'true'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=true&pool=true"; - ORM.connectAsync(connString) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); - }); + afterEach(function () { + connStr = null }); - it("should understand pool `'true'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=1&pool=1"; - ORM.connectAsync(connString) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); - }); - }); + if (protocol !== 'mongodb') { + it("should understand pool `'false'` from query string", function (done) { + var connString = connStr + "debug=false&pool=false"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); + }); - it("should understand pool `'true'` from query string", function (done) { - var connOpts = _.extend(common.getConfig(), { - protocol: common.protocol(), - query: { - pool: true, debug: true - } + it("should understand pool `'0'` from query string", function (done) { + var connString = connStr + "debug=0&pool=0"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); }); - ORM.connectAsync(connOpts) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); - }); - }); - it("should understand pool `false` from query options", function (done) { - var connOpts = _.extend(common.getConfig(), { - protocol: common.protocol(), - query: { - pool: false, debug: false - } + it("should understand pool `'true'` from query string", function (done) { + var connString = connStr + "debug=true&pool=true"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); }); - ORM.connectAsync(connOpts) - .then(function (db) { - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); - }) - .catch(function (err) { - done(err); - }); - }); - } - }); -}); -describe("ORM.connect()", function () { - it("should expose .use(), .define(), .sync() and .load()", function (done) { - var db = ORM.connect(); + it("should understand pool `'true'` from query string", function (done) { + var connString = connStr + "debug=1&pool=1"; + ORM.connectAsync(connString) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); + }); - db.use.should.be.a.Function(); - db.define.should.be.a.Function(); - db.sync.should.be.a.Function(); - db.load.should.be.a.Function(); + it("should understand pool `'true'` from query string", function (done) { + var connCopy = _.cloneDeep(common.getConfig()); + var connOpts = _.extend(connCopy, { + protocol: common.protocol(), + query: { + pool: true, debug: true + } + }); + ORM.connectAsync(connOpts) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }) + .catch(function (err) { + done(err); + }); + }); - return done(); + it("should understand pool `false` from query options", function (done) { + var connCopy = _.cloneDeep(common.getConfig()); + var connOpts = _.extend(connCopy, { + protocol: common.protocol(), + query: { + pool: false, debug: false + } + }); + ORM.connectAsync(connOpts) + .then(function (db) { + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + } + }); }); - it("should emit an error if no url is passed", function (done) { - var db = ORM.connect(); + describe("ORM.connect()", function () { + it("should expose .use(), .define(), .sync() and .load()", function (done) { + var db = ORM.connect(); - db.on("connect", function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); + db.use.should.be.a.Function(); + db.define.should.be.a.Function(); + db.sync.should.be.a.Function(); + db.load.should.be.a.Function(); return done(); }); - }); - it.skip("should allow protocol alias", function (done) { - this.timeout(60000); - var db = ORM.connect("pg://127.0.0.6"); + it("should emit an error if no url is passed", function (done) { + var db = ORM.connect(); - db.once("connect", function (err) { - should.exist(err); - err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); + db.on("connect", function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); - return done(); + return done(); + }); }); - }); - it("should emit an error if empty url is passed", function (done) { - var db = ORM.connect(""); + it.skip("should allow protocol alias", function (done) { + this.timeout(60000); + var db = ORM.connect("pg://127.0.0.6"); - db.on("connect", function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); + db.once("connect", function (err) { + should.exist(err); + err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); - return done(); + return done(); + }); }); - }); - it("should emit an error if empty url (with only spaces) is passed", function (done) { - var db = ORM.connect(" "); + it("should emit an error if empty url is passed", function (done) { + var db = ORM.connect(""); - db.on("connect", function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); + db.on("connect", function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); - return done(); + return done(); + }); }); - }); - it("should emit an error if no protocol is passed", function (done) { - var db = ORM.connect("user@db"); + it("should emit an error if empty url (with only spaces) is passed", function (done) { + var db = ORM.connect(" "); - db.on("connect", function (err) { - err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); + db.on("connect", function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); - return done(); + return done(); + }); }); - }); - it("should emit an error if unknown protocol is passed", function (done) { - var db = ORM.connect("unknown://db"); + it("should emit an error if no protocol is passed", function (done) { + var db = ORM.connect("user@db"); - db.on("connect", function (err) { - should.equal(err.literalCode, 'NO_SUPPORT'); - should.equal( - err.message, - "Connection protocol not supported - have you installed the database driver for unknown?" - ); + db.on("connect", function (err) { + err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); - return done(); + return done(); + }); }); - }); - it("should emit an error if cannot connect", function (done) { - var db = ORM.connect("mysql://fakeuser:nopassword@127.0.0.1/unknowndb"); + it("should emit an error if unknown protocol is passed", function (done) { + var db = ORM.connect("unknown://db"); - db.on("connect", function (err) { - should.exist(err); - should.equal(err.message.indexOf("Connection protocol not supported"), -1); - err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); - err.message.should.not.equal("CONNECTION_URL_EMPTY"); + db.on("connect", function (err) { + should.equal(err.literalCode, 'NO_SUPPORT'); + should.equal( + err.message, + "Connection protocol not supported - have you installed the database driver for unknown?" + ); - return done(); + return done(); + }); }); - }); - it("should emit valid error if exception being thrown during connection try", function (done) { - var testConfig = { - protocol : 'mongodb', - href : 'unknownhost', - database : 'unknowndb', - user : '', - password : '' - }, - db = ORM.connect(testConfig); - - db.on("connect", function (err) { - should.exist(err); - should.equal(err.message.indexOf("Connection protocol not supported"), -1); - err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); - err.message.should.not.equal("CONNECTION_URL_EMPTY"); + it("should emit an error if cannot connect", function (done) { + var db = ORM.connect("mysql://fakeuser:nopassword@127.0.0.1/unknowndb"); - return done(); - }); - }); + db.on("connect", function (err) { + should.exist(err); + should.equal(err.message.indexOf("Connection protocol not supported"), -1); + err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); + err.message.should.not.equal("CONNECTION_URL_EMPTY"); - it("should not modify connection opts", function (done) { - var opts = { - protocol : 'mysql', - user : 'notauser', - password : "wrong password", - query : { pool: true, debug: true } - }; + return done(); + }); + }); - var expected = JSON.stringify(opts); + it("should emit valid error if exception being thrown during connection try", function (done) { + var testConfig = { + protocol : 'mongodb', + href : 'unknownhost', + database : 'unknowndb', + user : '', + password : '' + }, + db = ORM.connect(testConfig); + + db.on("connect", function (err) { + should.exist(err); + should.equal(err.message.indexOf("Connection protocol not supported"), -1); + err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); + err.message.should.not.equal("CONNECTION_URL_EMPTY"); - ORM.connect(opts, function (err, db) { - should.equal( - JSON.stringify(opts), - expected - ); - done(); + return done(); + }); }); - }); - it("should emit no error if ok", function (done) { - var db = ORM.connect(common.getConnectionString()); + it("should not modify connection opts", function (done) { + var opts = { + protocol : 'mysql', + user : 'notauser', + password : "wrong password", + query : { pool: true, debug: true } + }; - db.on("connect", function (err) { - should.not.exist(err); + var expected = JSON.stringify(opts); - return done(); + ORM.connect(opts, function (err, db) { + should.equal( + JSON.stringify(opts), + expected + ); + done(); + }); }); - }); - describe("if no connection error", function () { - var db = null; + it("should emit no error if ok", function (done) { + var db = ORM.connect(common.getConnectionString()); - before(function (done) { - helper.connect(function (connection) { - db = connection; + db.on("connect", function (err) { + should.not.exist(err); return done(); }); }); - after(function () { - return db.close(); - }); + describe("if no connection error", function () { + var db = null; - it("should be able to ping the server", function (done) { - db.ping(function () { - return done(); + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); }); - }); - }); - describe("if callback is passed", function (done) { - it("should return an error if empty url is passed", function (done) { - ORM.connect("", function (err) { - err.message.should.equal("CONNECTION_URL_EMPTY"); + after(function () { + return db.close(); + }); - return done(); + it("should be able to ping the server", function (done) { + db.ping(function () { + return done(); + }); }); }); - it("should return an error if no protocol is passed", function (done) { - ORM.connect("user@db", function (err) { - err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); + describe("if callback is passed", function (done) { + it("should return an error if empty url is passed", function (done) { + ORM.connect("", function (err) { + err.message.should.equal("CONNECTION_URL_EMPTY"); - return done(); + return done(); + }); }); - }); - it("should return an error if unknown protocol is passed", function (done) { - ORM.connect("unknown://db", function (err) { - should.equal(err.literalCode, 'NO_SUPPORT'); - should.equal( - err.message, - "Connection protocol not supported - have you installed the database driver for unknown?" - ); + it("should return an error if no protocol is passed", function (done) { + ORM.connect("user@db", function (err) { + err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); - return done(); + return done(); + }); }); - }); - }); - if (protocol != 'mongodb') { - describe("query options", function () { - it("should understand pool `'false'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=false&pool=false"; - ORM.connect(connString, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); + it("should return an error if unknown protocol is passed", function (done) { + ORM.connect("unknown://db", function (err) { + should.equal(err.literalCode, 'NO_SUPPORT'); + should.equal( + err.message, + "Connection protocol not supported - have you installed the database driver for unknown?" + ); + + return done(); }); }); + }); - it("should understand pool `'0'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=0&pool=0"; - ORM.connect(connString, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); + if (protocol !== 'mongodb') { + describe("query options", function () { + var connStr = null; + + beforeEach(function () { + connStr = common.getConnectionString(); }); - }); - it("should understand pool `'true'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=true&pool=true"; - ORM.connect(connString, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); + afterEach(function () { + connStr = null + }); + it("should understand pool `'false'` from query string", function (done) { + var connString = connStr + "debug=false&pool=false"; + ORM.connect(connString, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }); }); - }); - it("should understand pool `'1'` from query string", function (done) { - var connString = common.getConnectionString() + "debug=1&pool=1"; - ORM.connect(connString, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); + it("should understand pool `'0'` from query string", function (done) { + var connString = connStr + "debug=0&pool=0"; + ORM.connect(connString, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }); }); - }); - it("should understand pool `true` from query options", function (done) { - var connOpts = _.extend(common.getConfig(), { - protocol: common.protocol(), - query: { - pool: true, debug: true - } + it("should understand pool `'true'` from query string", function (done) { + var connString = connStr + "debug=true&pool=true"; + ORM.connect(connString, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }); }); - ORM.connect(connOpts, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, true); - should.strictEqual(db.driver.opts.debug, true); - done(); + + it("should understand pool `'1'` from query string", function (done) { + var connString = connStr + "debug=1&pool=1"; + ORM.connect(connString, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }); }); - }); - it("should understand pool `false` from query options", function (done) { - var connOpts = _.extend(common.getConfig(), { - protocol: common.protocol(), - query: { - pool: false, debug: false - } + it("should understand pool `true` from query options", function (done) { + var connCopy = _.cloneDeep(common.getConfig()); + var connOpts = _.extend(connCopy, { + protocol: common.protocol(), + query: { + pool: true, debug: true + } + }); + ORM.connect(connOpts, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, true); + should.strictEqual(db.driver.opts.debug, true); + done(); + }); }); - ORM.connect(connOpts, function (err, db) { - should.not.exist(err); - should.strictEqual(db.driver.opts.pool, false); - should.strictEqual(db.driver.opts.debug, false); - done(); + + it("should understand pool `false` from query options", function (done) { + var connCopy = _.cloneDeep(common.getConfig()); + var connOpts = _.extend(connCopy, { + protocol: common.protocol(), + query: { + pool: false, debug: false + } + }); + ORM.connect(connOpts, function (err, db) { + should.not.exist(err); + should.strictEqual(db.driver.opts.pool, false); + should.strictEqual(db.driver.opts.debug, false); + done(); + }); }); }); - }); - } -}); + } + }); -describe("ORM.use()", function () { - it("should be able to use an established connection", function (done) { - var db = new sqlite.Database(':memory:'); + describe("ORM.use()", function () { + it("should be able to use an established connection", function (done) { + var db = new sqlite.Database(':memory:'); - ORM.use(db, "sqlite", function (err) { - should.not.exist(err); + ORM.use(db, "sqlite", function (err) { + should.not.exist(err); - return done(); + return done(); + }); }); - }); - it("should be accept protocol alias", function (done) { - var db = new pg.Client(); + it("should be accept protocol alias", function (done) { + var db = new pg.Client(); - ORM.use(db, "pg", function (err) { - should.equal(err, null); + ORM.use(db, "pg", function (err) { + should.equal(err, null); - return done(); + return done(); + }); }); - }); - it("should return an error in callback if protocol not supported", function (done) { - var db = new pg.Client(); + it("should return an error in callback if protocol not supported", function (done) { + var db = new pg.Client(); - ORM.use(db, "unknowndriver", function (err) { - should.exist(err); + ORM.use(db, "unknowndriver", function (err) { + should.exist(err); - return done(); + return done(); + }); }); }); -}); +}); \ No newline at end of file From 7c01df480530aa4f5eaaf3800dc4ce8916460e09 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 13:03:08 +0300 Subject: [PATCH 03/98] fix code dublication error --- lib/LazyLoad.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index f6322862..48af3780 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -22,20 +22,6 @@ function addLazyLoadProperty(name, Instance, Model, property) { }, enumerable: false }); - - Object.defineProperty(Instance, "getAsync" + method, { - value: function () { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - return cb(err, item ? item[property] : null); - }); - - return this; - }, - enumerable: false - }); Object.defineProperty(Instance, "remove" + method, { value: function (cb) { var conditions = {}; From 29d7ff1c93f90d55a6c9485ac9324c81051b6f6c Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 13:13:18 +0300 Subject: [PATCH 04/98] remove env var from pkg json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index faae57a6..511c8787 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ ], "main": "./lib/ORM", "scripts": { - "test": "ORM_PROTOCOL=postgres make test" + "test": "make test" }, "engines": { "node": "*" From 959faed243bc3b4f6c69165ab6bec03779d3966c Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 13:50:07 +0300 Subject: [PATCH 05/98] fix after review --- lib/ORM.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ORM.js b/lib/ORM.js index 8d37bb24..410fb0de 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -159,7 +159,7 @@ exports.connectAsync = function (opts) { return new Promise(function (resolve, reject) { var cb = function (resolve, reject) { return function (err, data) { - if (err !== null) { + if (err) { reject(err); } resolve(data); From 9f2f2989a5962963d37a7039a6dfd2a9a8c85b42 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 17:54:29 +0300 Subject: [PATCH 06/98] restore example file, fix after review --- lib/ORM.js | 4 +--- test/config.example.js | 29 +++++++++++++++++++++++++++++ test/integration/db.js | 8 ++++---- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 test/config.example.js diff --git a/lib/ORM.js b/lib/ORM.js index 410fb0de..08ef4352 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -1,14 +1,12 @@ var _ = require("lodash"); var async = require("async"); +var Promise = require('bluebird'); var enforce = require("enforce"); var events = require("events"); var hat = require("hat"); -var Promise = require('bluebird'); var Query = require("sql-query"); var url = require("url"); var util = require("util"); -var enforce = require("enforce"); -var _ = require("lodash"); var adapters = require("./Adapters"); var DriverAliases = require("./Drivers/aliases"); diff --git a/test/config.example.js b/test/config.example.js new file mode 100644 index 00000000..bb9c2d73 --- /dev/null +++ b/test/config.example.js @@ -0,0 +1,29 @@ +// To test, rename this file to config.js and update +// the following configuration +// +// To run a single driver, go to root folder and do (mysql example): +// ORM_PROTOCOL=mysql node test/run +// +// To run all drivers: +// make test + +exports.mysql = { + user : "root", + password : "", + database : "test" +}; +exports.postgres = { + user : "root", + password : "", + database : "test" +}; +exports.redshift = { + user : "root", + password : "", + database : "test" +}; +exports.mongodb = { + host: "localhost", + database: "test" +}; +exports.sqlite = { }; // uses in-memory database diff --git a/test/integration/db.js b/test/integration/db.js index 8546e3c6..ed5e3e78 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -1,8 +1,8 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var sinon = require('sinon'); +var sinon = require('sinon'); var common = require('../common'); -var _ = require('lodash'); +var _ = require('lodash'); describe('DB', function () { var db = null; @@ -37,7 +37,7 @@ describe('DB', function () { syncStub.restore(); }); - it('should return an empty array when no model created', function (done) { + it('should return an empty array when no model is created', function (done) { db.dropAsync() .then(function (data) { data.should.be.Array; @@ -51,7 +51,7 @@ describe('DB', function () { }); describe("db.dropAsync()", function () { - it('should call drop method from model, and return array with model id', function (done) { + it('should call the model drop method and return an array with a model id', function (done) { db.define("my_model", { property: String }); From 59dc1af430afcb36797c3a37e644d061ba1b2f45 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 22 Aug 2017 18:04:24 +0300 Subject: [PATCH 07/98] LazyLoad should return Promise --- lib/LazyLoad.js | 199 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 141 insertions(+), 58 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 48af3780..cd3663d5 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,73 +1,156 @@ +var Promise = require("bluebird"); + exports.extend = function (Instance, Model, properties) { - for (var k in properties) { - if (properties[k].lazyload === true) { - addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); + for (var k in properties) { + if (properties[k].lazyload === true) { + addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); + } } - } }; -function addLazyLoadProperty(name, Instance, Model, property) { - var method = ucfirst(name); - - Object.defineProperty(Instance, "get" + method, { - value: function (cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - return cb(err, item ? item[property] : null); - }); - - return this; - }, - enumerable: false - }); - Object.defineProperty(Instance, "remove" + method, { - value: function (cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); +exports.extendAsync = function (Instance, Model, properties) { + // TODO: + for (var k in properties) { + if (properties[k].lazyload) { + addLazyLoadPropertyAsync(properties[k].lazyname || k, Instance, Model, k); } + } +}; - item[property] = null; +function addLazyLoadPropertyAsync(name, Instance, Model, property) { + var method = ucfirst(name); + Object.defineProperty(Instance, "get" + method, { + value: function () { + return new Promise (function (resolve, reject) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; - return item.save(cb); - }); + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return reject (err); + } + resolve(item ? item[property] : null); + }); - return this; - }, - enumerable: false - }); - Object.defineProperty(Instance, "set" + method, { - value: function (data, cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; + }); + // return this; + }, + enumerable: false + }); - Model.find(conditions, { identityCache: false }).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); - } + Object.defineProperty(Instance, "remove" + method, { + value: function () { + return new Promise (function (resolve, reject) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; - item[property] = data; + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return reject(err); + } + if (!item) { + return reject(null); + } - return item.save(cb); - }); + item[property] = null; + + return item.save(resolve); + }); + + // return this; + }); + }, + enumerable: false + }); + Object.defineProperty(Instance, "set" + method, { + value: function (data) { + return new Promise (function (resolve, reject) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Model.find(conditions, {identityCache: false}).first(function (err, item) { + if (err) { + return reject(err); + } + if (!item) { + return reject(null); + } + + item[property] = data; + + return item.save(resolve); + }); + + // return this; + }); + }, + enumerable: false + }); +}; - return this; - }, - enumerable: false - }); +function addLazyLoadProperty(name, Instance, Model, property) { + var method = ucfirst(name); + + Object.defineProperty(Instance, "get" + method, { + value: function (cb) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + return cb(err, item ? item[property] : null); + }); + + // return this; + }, + enumerable: false + }); + + Object.defineProperty(Instance, "remove" + method, { + value: function (cb) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } + + item[property] = null; + + return item.save(cb); + }); + + return this; + }, + enumerable: false + }); + Object.defineProperty(Instance, "set" + method, { + value: function (data, cb) { + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Model.find(conditions, { identityCache: false }).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } + + item[property] = data; + + return item.save(cb); + }); + + return this; + }, + enumerable: false + }); } function ucfirst(text) { - return text[0].toUpperCase() + text.substr(1).toLowerCase(); -} + return text[0].toUpperCase() + text.substr(1).toLowerCase(); +} \ No newline at end of file From 5dddc625b8b39d8077260760b346188196fe2f8d Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 23 Aug 2017 12:39:55 +0300 Subject: [PATCH 08/98] addLazyLoadProperty function could add async methods to the Instance --- lib/LazyLoad.js | 149 ++++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 87 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index cd3663d5..77f0daf0 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -8,107 +8,26 @@ exports.extend = function (Instance, Model, properties) { } }; -exports.extendAsync = function (Instance, Model, properties) { - // TODO: - for (var k in properties) { - if (properties[k].lazyload) { - addLazyLoadPropertyAsync(properties[k].lazyname || k, Instance, Model, k); - } - } -}; - -function addLazyLoadPropertyAsync(name, Instance, Model, property) { - var method = ucfirst(name); - Object.defineProperty(Instance, "get" + method, { - value: function () { - return new Promise (function (resolve, reject) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return reject (err); - } - resolve(item ? item[property] : null); - }); - - }); - // return this; - }, - enumerable: false - }); - - Object.defineProperty(Instance, "remove" + method, { - value: function () { - return new Promise (function (resolve, reject) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return reject(err); - } - if (!item) { - return reject(null); - } - - item[property] = null; - - return item.save(resolve); - }); - - // return this; - }); - }, - enumerable: false - }); - Object.defineProperty(Instance, "set" + method, { - value: function (data) { - return new Promise (function (resolve, reject) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Model.find(conditions, {identityCache: false}).first(function (err, item) { - if (err) { - return reject(err); - } - if (!item) { - return reject(null); - } - - item[property] = data; - - return item.save(resolve); - }); - - // return this; - }); - }, - enumerable: false - }); -}; - function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + Object.defineProperty(Instance, "get" + method, { value: function (cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { return cb(err, item ? item[property] : null); }); - // return this; + return this; }, enumerable: false }); Object.defineProperty(Instance, "remove" + method, { value: function (cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { if (err) { @@ -127,10 +46,9 @@ function addLazyLoadProperty(name, Instance, Model, property) { }, enumerable: false }); + Object.defineProperty(Instance, "set" + method, { value: function (data, cb) { - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; Model.find(conditions, { identityCache: false }).first(function (err, item) { if (err) { @@ -149,6 +67,63 @@ function addLazyLoadProperty(name, Instance, Model, property) { }, enumerable: false }); + + Object.defineProperty(Instance, "get" + method + 'Async', { + value: function () { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return reject (err); + } + resolve(item ? item[property] : null); + }); + }); + }, + enumerable: false + }); + + Object.defineProperty(Instance, "remove" + method + 'Async', { + value: function () { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return reject(err); + } + + if (!item) { + return reject(null); + } + + item[property] = null; + return item.save(function (err, data) { //TODO: change save() to async + if (err) return reject(err); + return resolve(data); + }); + }); + }); + }, + enumerable: false + }); + + Object.defineProperty(Instance, "set" + method + 'Async', { + value: function (data) { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).first(function (err, item) { + if (err) return reject(err); + + if (!item) return resolve(null); + + item[property] = data; + + return item.save(function (err, data) { //TODO: change save() to async + if (err) return reject(err); + return resolve(data); + }); + }); + }); + }, + enumerable: false + }); } function ucfirst(text) { From ef15df3160c571c7dba813b3fa01582c81efc9a0 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 23 Aug 2017 14:52:24 +0300 Subject: [PATCH 09/98] integraion tests updated --- test/integration/property-lazyload.js | 87 +++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/test/integration/property-lazyload.js b/test/integration/property-lazyload.js index 3149eaa0..6ce3398c 100644 --- a/test/integration/property-lazyload.js +++ b/test/integration/property-lazyload.js @@ -59,9 +59,13 @@ describe("LazyLoad properties", function() { should.equal(err, null); John.should.be.a.Object(); + John.getPhoto.should.be.a.Function(); John.setPhoto.should.be.a.Function(); John.removePhoto.should.be.a.Function(); + John.getPhotoAsync.should.be.a.Function(); + John.setPhotoAsync.should.be.a.Function(); + John.removePhotoAsync.should.be.a.Function(); return done(); }); @@ -82,10 +86,23 @@ describe("LazyLoad properties", function() { }); }); - it("setAccessor should change property", function (done) { + it("promise-based getAccessor should return property", function (done) { Person.find().first(function (err, John) { should.equal(err, null); + John.should.be.a.Object(); + John.getPhotoAsync() + .then(function (photo) { + photo.toString().should.equal(PersonPhoto.toString()); + done(); + }).catch(function(err) { + if (err) done(err); + }); + }); + }); + it("setAccessor should change property", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); John.should.be.a.Object(); John.setPhoto(OtherPersonPhoto, function (err) { @@ -93,13 +110,11 @@ describe("LazyLoad properties", function() { Person.find().first(function (err, John) { should.equal(err, null); - John.should.be.a.Object(); John.getPhoto(function (err, photo) { should.equal(err, null); photo.toString().should.equal(OtherPersonPhoto.toString()); - return done(); }); }); @@ -107,26 +122,74 @@ describe("LazyLoad properties", function() { }); }); + it("promise-based setAccessor should change property", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + John.should.be.a.Object(); + + John.setPhotoAsync(OtherPersonPhoto) + .then(function (johnPhotoUpdated) { + johnPhotoUpdated.should.be.a.Object(); + + Person.find().first(function (err, John) { + should.equal(err, null); + John.should.be.a.Object(); + + John.getPhotoAsync() + .then(function (photo) { + should.equal(err, null); + photo.toString().should.equal(OtherPersonPhoto.toString()); + done(); + }).catch(function (err) { + if (err) done(err); + }); + }); + }); + }); + }); + it("removeAccessor should change property", function (done) { Person.find().first(function (err, John) { should.equal(err, null); + John.should.be.a.Object(); + + John.removePhoto(function (err) { + should.equal(err, null); + + Person.get(John[Person.id], function (err, John) { + should.equal(err, null); + John.should.be.a.Object(); + + John.getPhoto(function (err, photo) { + should.equal(err, null); + should.equal(photo, null); + + return done(); + }); + }); + }); + }); + }); + it("promise-based removeAccessor should change property", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); John.should.be.a.Object(); - John.removePhoto(function (err) { + John.removePhotoAsync().then(function () { should.equal(err, null); - Person.get(John[Person.id], function (err, John) { should.equal(err, null); - John.should.be.a.Object(); - John.getPhoto(function (err, photo) { - should.equal(err, null); - should.equal(photo, null); - - return done(); - }); + John.getPhotoAsync() + .then(function (photo) { + should.equal(err, null); + should.equal(photo, null); + done(); + }).catch(function (err) { + if (err) done(err); + }); }); }); }); From 62dcd08e7a2947aea8937ea839513fb5e957b780 Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 23 Aug 2017 15:09:33 +0300 Subject: [PATCH 10/98] add pingAsync and connectAsync methods, add test for it, update README.md --- Readme.md | 5 +++++ lib/ORM.js | 34 +++++++++++++++++++++++++++------ test/integration/orm-exports.js | 7 +++++++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 4a68bbfc..1360fc2e 100755 --- a/Readme.md +++ b/Readme.md @@ -98,6 +98,11 @@ orm.connect("mysql://username:password@host/database", function (err, db) { ## Promises +The methods what has Async like a postfix, like example `connectAsync` +can apply the same arguments as a original method but return a promise. +Exclusion method `sync` they called `syncPromise`. +More details [wiki](linkToWikiHere). // TODO add link to wiki where described async methods. + You can use the [promise enabled wrapper library](https://github.com/rafaelkaufmann/q-orm). diff --git a/lib/ORM.js b/lib/ORM.js index 08ef4352..85a230ee 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -158,9 +158,9 @@ exports.connectAsync = function (opts) { var cb = function (resolve, reject) { return function (err, data) { if (err) { - reject(err); + return reject(err); } - resolve(data); + return resolve(data); } }; connect(options, cb(resolve, reject)); @@ -271,11 +271,33 @@ ORM.prototype.define = function (name, properties, opts) { return this.models[name]; }; + ORM.prototype.defineType = function (name, opts) { this.customTypes[name] = opts; this.driver.customTypes[name] = opts; return this; }; + +ORM.prototype.pingAsync = function () { + const self = this; + return new Promise(function (resolve, reject) { + self.driver.ping(function (err, data) { + if (err) return reject(err); + return resolve(data); + }); + }); +}; + +ORM.prototype.closeAsync = function () { + const self = this; + return new Promise(function (resolve, reject) { + self.driver.close(function (err, data) { + if (err) return reject(err); + return resolve(data); + }); + }); +}; + ORM.prototype.ping = function (cb) { this.driver.ping(cb); @@ -324,9 +346,9 @@ ORM.prototype.syncPromise = function () { self.models[id].sync(function (err) { if (err) { err.model = id; - reject(err); + return reject(err); } - resolve(id); + return resolve(id); }); }); }) @@ -367,8 +389,8 @@ ORM.prototype.dropAsync = function () { return Promise.each(modelIds, function (id) { return new Promise(function (resolve, reject) { self.models[id].drop(function (err, data) { - if (err) reject(err); - resolve(data); + if (err) return reject(err); + return resolve(data); }); }); }) diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index 852967cc..dd5ba615 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -443,6 +443,13 @@ describe("ORM", function() { return done(); }); }); + + it("should be able to pingAsync the server", function (done) { + db.pingAsync() + .then(function () { + return done(); + }); + }); }); describe("if callback is passed", function (done) { From 97f22810a1ac19bcdee3861224a5ad1d8a43e7fe Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 23 Aug 2017 15:40:13 +0300 Subject: [PATCH 11/98] Fixes for PR comments --- lib/LazyLoad.js | 234 +++++++++++++------------- test/integration/property-lazyload.js | 6 +- 2 files changed, 117 insertions(+), 123 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 77f0daf0..95881d46 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,131 +1,125 @@ var Promise = require("bluebird"); exports.extend = function (Instance, Model, properties) { - for (var k in properties) { - if (properties[k].lazyload === true) { - addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); - } + for (var k in properties) { + if (properties[k].lazyload === true) { + addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); } + } }; function addLazyLoadProperty(name, Instance, Model, property) { - var method = ucfirst(name); - - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - - Object.defineProperty(Instance, "get" + method, { - value: function (cb) { - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - return cb(err, item ? item[property] : null); - }); - - return this; - }, - enumerable: false - }); - - Object.defineProperty(Instance, "remove" + method, { - value: function (cb) { - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); - } - - item[property] = null; - - return item.save(cb); - }); - - return this; - }, - enumerable: false - }); - - Object.defineProperty(Instance, "set" + method, { - value: function (data, cb) { - - Model.find(conditions, { identityCache: false }).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); - } - - item[property] = data; - - return item.save(cb); - }); - - return this; - }, - enumerable: false - }); - - Object.defineProperty(Instance, "get" + method + 'Async', { - value: function () { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return reject (err); - } - resolve(item ? item[property] : null); - }); - }); - }, - enumerable: false - }); - - Object.defineProperty(Instance, "remove" + method + 'Async', { - value: function () { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return reject(err); - } - - if (!item) { - return reject(null); - } - - item[property] = null; - return item.save(function (err, data) { //TODO: change save() to async - if (err) return reject(err); - return resolve(data); - }); - }); - }); - }, - enumerable: false - }); - - Object.defineProperty(Instance, "set" + method + 'Async', { - value: function (data) { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).first(function (err, item) { - if (err) return reject(err); - - if (!item) return resolve(null); - - item[property] = data; - - return item.save(function (err, data) { //TODO: change save() to async - if (err) return reject(err); - return resolve(data); - }); - }); - }); - }, - enumerable: false - }); + var method = ucfirst(name); + + var conditions = {}; + conditions[Model.id] = Instance[Model.id]; + + Object.defineProperty(Instance, "get" + method, { + value: function (cb) { + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + return cb(err, item ? item[property] : null); + }); + + return this; + }, + enumerable: false + }); + + Object.defineProperty(Instance, "remove" + method, { + value: function (cb) { + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } + + item[property] = null; + + return item.save(cb); + }); + + return this; + }, + enumerable: false + }); + + Object.defineProperty(Instance, "set" + method, { + value: function (data, cb) { + + Model.find(conditions, { identityCache: false }).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } + + item[property] = data; + + return item.save(cb); + }); + + return this; + }, + enumerable: false + }); + + Object.defineProperty(Instance, "get" + method + 'Async', { + value: function () { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) return reject (err); + return resolve(item ? item[property] : null); + }); + }); + }, + enumerable: false + }); + + Object.defineProperty(Instance, "remove" + method + 'Async', { + value: function () { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { + if (err) return reject(err); + + if (!item) return resolve(null); + + item[property] = null; + return item.save(function (err, data) { //TODO: change save() to async + if (err) return reject(err); + return resolve(data); + }); + }); + }); + }, + enumerable: false + }); + + Object.defineProperty(Instance, "set" + method + 'Async', { + value: function (data) { + return new Promise (function (resolve, reject) { + Model.find(conditions, {identityCache: false}).first(function (err, item) { + if (err) return reject(err); + + if (!item) return resolve(null); + + item[property] = data; + + return item.save(function (err, data) { //TODO: change save() to async + if (err) return reject(err); + return resolve(data); + }); + }); + }); + }, + enumerable: false + }); } function ucfirst(text) { - return text[0].toUpperCase() + text.substr(1).toLowerCase(); + return text[0].toUpperCase() + text.substr(1).toLowerCase(); } \ No newline at end of file diff --git a/test/integration/property-lazyload.js b/test/integration/property-lazyload.js index 6ce3398c..0b146be0 100644 --- a/test/integration/property-lazyload.js +++ b/test/integration/property-lazyload.js @@ -95,7 +95,7 @@ describe("LazyLoad properties", function() { photo.toString().should.equal(PersonPhoto.toString()); done(); }).catch(function(err) { - if (err) done(err); + done(err); }); }); }); @@ -141,7 +141,7 @@ describe("LazyLoad properties", function() { photo.toString().should.equal(OtherPersonPhoto.toString()); done(); }).catch(function (err) { - if (err) done(err); + done(err); }); }); }); @@ -188,7 +188,7 @@ describe("LazyLoad properties", function() { should.equal(photo, null); done(); }).catch(function (err) { - if (err) done(err); + done(err); }); }); }); From 6d95a05bc3c2cfab19793183692d33eefb790e28 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 29 Aug 2017 15:33:42 +0300 Subject: [PATCH 12/98] Async methods will use promisify instead new Promise --- lib/LazyLoad.js | 74 ++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 95881d46..9c07110d 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,6 +1,6 @@ var Promise = require("bluebird"); -exports.extend = function (Instance, Model, properties) { +var extend = function (Instance, Model, properties) { for (var k in properties) { if (properties[k].lazyload === true) { addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); @@ -69,57 +69,61 @@ function addLazyLoadProperty(name, Instance, Model, property) { }); Object.defineProperty(Instance, "get" + method + 'Async', { - value: function () { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) return reject (err); - return resolve(item ? item[property] : null); - }); + value: Promise.promisify(function (cb) { + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + return cb(err, item ? item[property] : null); }); - }, + + return this; + }), enumerable: false }); Object.defineProperty(Instance, "remove" + method + 'Async', { - value: function () { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).only(Model.id.concat(property)).first(function (err, item) { - if (err) return reject(err); - - if (!item) return resolve(null); - - item[property] = null; - return item.save(function (err, data) { //TODO: change save() to async - if (err) return reject(err); - return resolve(data); - }); - }); + value: Promise.promisify(function (cb) { + + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } + + item[property] = null; + + return item.save(cb); }); - }, + + return this; + }), enumerable: false }); Object.defineProperty(Instance, "set" + method + 'Async', { - value: function (data) { - return new Promise (function (resolve, reject) { - Model.find(conditions, {identityCache: false}).first(function (err, item) { - if (err) return reject(err); + value: Promise.promisify(function (data, cb) { - if (!item) return resolve(null); + Model.find(conditions, { identityCache: false }).first(function (err, item) { + if (err) { + return cb(err); + } + if (!item) { + return cb(null); + } - item[property] = data; + item[property] = data; - return item.save(function (err, data) { //TODO: change save() to async - if (err) return reject(err); - return resolve(data); - }); - }); + return item.save(cb); }); - }, + + return this; + }), enumerable: false }); } function ucfirst(text) { return text[0].toUpperCase() + text.substr(1).toLowerCase(); -} \ No newline at end of file +} + +exports.extend = extend; \ No newline at end of file From 5861ad7c6b199592e764b2028566c4a21f9eb485 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 30 Aug 2017 12:04:53 +0300 Subject: [PATCH 13/98] Remove code duplicates in promisify methods --- lib/LazyLoad.js | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 9c07110d..c5d4ca96 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -69,55 +69,17 @@ function addLazyLoadProperty(name, Instance, Model, property) { }); Object.defineProperty(Instance, "get" + method + 'Async', { - value: Promise.promisify(function (cb) { - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - return cb(err, item ? item[property] : null); - }); - - return this; - }), + value: Promise.promisify(Instance['get'+ method]), enumerable: false }); Object.defineProperty(Instance, "remove" + method + 'Async', { - value: Promise.promisify(function (cb) { - - Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); - } - - item[property] = null; - - return item.save(cb); - }); - - return this; - }), + value: Promise.promisify(Instance['remove'+ method]), enumerable: false }); Object.defineProperty(Instance, "set" + method + 'Async', { - value: Promise.promisify(function (data, cb) { - - Model.find(conditions, { identityCache: false }).first(function (err, item) { - if (err) { - return cb(err); - } - if (!item) { - return cb(null); - } - - item[property] = data; - - return item.save(cb); - }); - - return this; - }), + value: Promise.promisify(Instance['set'+ method]), enumerable: false }); } From c05863d79dc0d695a4c3c7b62132de7aa371ec93 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 30 Aug 2017 14:08:06 +0300 Subject: [PATCH 14/98] Optimized functions names. Added Async prefix in Settings file --- lib/LazyLoad.js | 36 ++++++++++++++++++++++++++---------- lib/Settings.js | 3 ++- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index c5d4ca96..88cdff67 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,4 +1,5 @@ -var Promise = require("bluebird"); +var Promise = require("bluebird"); +var Settings = require("./Settings"); var extend = function (Instance, Model, properties) { for (var k in properties) { @@ -10,11 +11,26 @@ var extend = function (Instance, Model, properties) { function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); + var promiseFunctionPrefix = Settings.defaults().promiseFunctionPrefix; + var funcNamesObj = { + get: { + callbackFuncName: "get" + method, + promiseFuncName: "get" + method + promiseFunctionPrefix + }, + remove: { + callbackFuncName: "remove" + method, + promiseFuncName: "remove" + method + promiseFunctionPrefix + }, + set: { + callbackFuncName: "set" + method, + promiseFuncName: "set" + method + promiseFunctionPrefix + } + } var conditions = {}; conditions[Model.id] = Instance[Model.id]; - Object.defineProperty(Instance, "get" + method, { + Object.defineProperty(Instance, funcNamesObj.get['callbackFuncName'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -26,7 +42,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, "remove" + method, { + Object.defineProperty(Instance, funcNamesObj.remove['callbackFuncName'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -47,7 +63,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, "set" + method, { + Object.defineProperty(Instance, funcNamesObj.set['callbackFuncName'], { value: function (data, cb) { Model.find(conditions, { identityCache: false }).first(function (err, item) { @@ -68,18 +84,18 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, "get" + method + 'Async', { - value: Promise.promisify(Instance['get'+ method]), + Object.defineProperty(Instance, funcNamesObj.get['promiseFuncName'], { + value: Promise.promisify(Instance[funcNamesObj.get['callbackFuncName']]), enumerable: false }); - Object.defineProperty(Instance, "remove" + method + 'Async', { - value: Promise.promisify(Instance['remove'+ method]), + Object.defineProperty(Instance, funcNamesObj.remove['promiseFuncName'], { + value: Promise.promisify(Instance[funcNamesObj.remove['callbackFuncName']]), enumerable: false }); - Object.defineProperty(Instance, "set" + method + 'Async', { - value: Promise.promisify(Instance['set'+ method]), + Object.defineProperty(Instance, funcNamesObj.set['promiseFuncName'], { + value: Promise.promisify(Instance[funcNamesObj.set['callbackFuncName']]), enumerable: false }); } diff --git a/lib/Settings.js b/lib/Settings.js index fc5893df..7af076cf 100644 --- a/lib/Settings.js +++ b/lib/Settings.js @@ -23,7 +23,8 @@ var default_settings = { reconnect : true, pool : false, debug : false - } + }, + promiseFunctionPrefix : 'Async' }; exports.Container = Settings; From c146c455950b4cb02d6ddf31cfec6bf659de44ca Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 30 Aug 2017 14:38:13 +0300 Subject: [PATCH 15/98] Changed properties names in funcNamesObj and Settings.defaults --- lib/LazyLoad.js | 32 ++++++++++++++++---------------- lib/Settings.js | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 88cdff67..4ac66e00 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -11,26 +11,26 @@ var extend = function (Instance, Model, properties) { function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); - var promiseFunctionPrefix = Settings.defaults().promiseFunctionPrefix; + var promiseFunctionPostfix = Settings.defaults().promiseFunctionPostfix; var funcNamesObj = { get: { - callbackFuncName: "get" + method, - promiseFuncName: "get" + method + promiseFunctionPrefix + callback : "get" + method, + promise : "get" + method + promiseFunctionPostfix }, remove: { - callbackFuncName: "remove" + method, - promiseFuncName: "remove" + method + promiseFunctionPrefix + callback : "remove" + method, + promise : "remove" + method + promiseFunctionPostfix }, set: { - callbackFuncName: "set" + method, - promiseFuncName: "set" + method + promiseFunctionPrefix + callback : "set" + method, + promise : "set" + method + promiseFunctionPostfix } } var conditions = {}; conditions[Model.id] = Instance[Model.id]; - Object.defineProperty(Instance, funcNamesObj.get['callbackFuncName'], { + Object.defineProperty(Instance, funcNamesObj.get['callback'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -42,7 +42,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.remove['callbackFuncName'], { + Object.defineProperty(Instance, funcNamesObj.remove['callback'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -63,7 +63,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.set['callbackFuncName'], { + Object.defineProperty(Instance, funcNamesObj.set['callback'], { value: function (data, cb) { Model.find(conditions, { identityCache: false }).first(function (err, item) { @@ -84,18 +84,18 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.get['promiseFuncName'], { - value: Promise.promisify(Instance[funcNamesObj.get['callbackFuncName']]), + Object.defineProperty(Instance, funcNamesObj.get['promise'], { + value: Promise.promisify(Instance[funcNamesObj.get['callback']]), enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.remove['promiseFuncName'], { - value: Promise.promisify(Instance[funcNamesObj.remove['callbackFuncName']]), + Object.defineProperty(Instance, funcNamesObj.remove['promise'], { + value: Promise.promisify(Instance[funcNamesObj.remove['callback']]), enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.set['promiseFuncName'], { - value: Promise.promisify(Instance[funcNamesObj.set['callbackFuncName']]), + Object.defineProperty(Instance, funcNamesObj.set['promise'], { + value: Promise.promisify(Instance[funcNamesObj.set['callback']]), enumerable: false }); } diff --git a/lib/Settings.js b/lib/Settings.js index 7af076cf..500cfc5a 100644 --- a/lib/Settings.js +++ b/lib/Settings.js @@ -24,7 +24,7 @@ var default_settings = { pool : false, debug : false }, - promiseFunctionPrefix : 'Async' + promiseFunctionPostfix : 'Async' }; exports.Container = Settings; From 40b431504ac975f70231eb38d44d072c0288b29a Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 30 Aug 2017 14:43:28 +0300 Subject: [PATCH 16/98] Renamed funcNamesObj on functionNames --- lib/LazyLoad.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 4ac66e00..7a888c04 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -12,7 +12,7 @@ var extend = function (Instance, Model, properties) { function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); var promiseFunctionPostfix = Settings.defaults().promiseFunctionPostfix; - var funcNamesObj = { + var functionNames = { get: { callback : "get" + method, promise : "get" + method + promiseFunctionPostfix @@ -30,7 +30,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { var conditions = {}; conditions[Model.id] = Instance[Model.id]; - Object.defineProperty(Instance, funcNamesObj.get['callback'], { + Object.defineProperty(Instance, functionNames.get['callback'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -42,7 +42,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.remove['callback'], { + Object.defineProperty(Instance, functionNames.remove['callback'], { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -63,7 +63,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.set['callback'], { + Object.defineProperty(Instance, functionNames.set['callback'], { value: function (data, cb) { Model.find(conditions, { identityCache: false }).first(function (err, item) { @@ -84,18 +84,18 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.get['promise'], { - value: Promise.promisify(Instance[funcNamesObj.get['callback']]), + Object.defineProperty(Instance, functionNames.get['promise'], { + value: Promise.promisify(Instance[functionNames.get['callback']]), enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.remove['promise'], { - value: Promise.promisify(Instance[funcNamesObj.remove['callback']]), + Object.defineProperty(Instance, functionNames.remove['promise'], { + value: Promise.promisify(Instance[functionNames.remove['callback']]), enumerable: false }); - Object.defineProperty(Instance, funcNamesObj.set['promise'], { - value: Promise.promisify(Instance[funcNamesObj.set['callback']]), + Object.defineProperty(Instance, functionNames.set['promise'], { + value: Promise.promisify(Instance[functionNames.set['callback']]), enumerable: false }); } From ae5519db6f2bdfe6f73cff12a77e912989017e14 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Wed, 30 Aug 2017 15:00:54 +0300 Subject: [PATCH 17/98] Changed promiseFunctionPostfix value. Also small fixes --- lib/LazyLoad.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 7a888c04..e6a39a1c 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,5 +1,4 @@ var Promise = require("bluebird"); -var Settings = require("./Settings"); var extend = function (Instance, Model, properties) { for (var k in properties) { @@ -11,7 +10,7 @@ var extend = function (Instance, Model, properties) { function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); - var promiseFunctionPostfix = Settings.defaults().promiseFunctionPostfix; + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); var functionNames = { get: { callback : "get" + method, @@ -30,7 +29,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { var conditions = {}; conditions[Model.id] = Instance[Model.id]; - Object.defineProperty(Instance, functionNames.get['callback'], { + Object.defineProperty(Instance, functionNames.get.callback, { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -42,7 +41,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, functionNames.remove['callback'], { + Object.defineProperty(Instance, functionNames.remove.callback, { value: function (cb) { Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { @@ -63,7 +62,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, functionNames.set['callback'], { + Object.defineProperty(Instance, functionNames.set.callback, { value: function (data, cb) { Model.find(conditions, { identityCache: false }).first(function (err, item) { @@ -84,18 +83,18 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, functionNames.get['promise'], { - value: Promise.promisify(Instance[functionNames.get['callback']]), + Object.defineProperty(Instance, functionNames.get.promise, { + value: Promise.promisify(Instance[functionNames.get.callback]), enumerable: false }); - Object.defineProperty(Instance, functionNames.remove['promise'], { - value: Promise.promisify(Instance[functionNames.remove['callback']]), + Object.defineProperty(Instance, functionNames.remove.promise, { + value: Promise.promisify(Instance[functionNames.remove.callback]), enumerable: false }); - Object.defineProperty(Instance, functionNames.set['promise'], { - value: Promise.promisify(Instance[functionNames.set['callback']]), + Object.defineProperty(Instance, functionNames.set.promise, { + value: Promise.promisify(Instance[functionNames.set.callback]), enumerable: false }); } From 5c4b67bc78cccc147a2dffa91b48840ab52ade22 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 28 Aug 2017 18:16:57 +0300 Subject: [PATCH 18/98] Add promise support to association extend --- lib/Associations/Extend.js | 154 ++++++++++++++++++++++- test/integration/association-extend.js | 164 ++++++++++++++++++++++++- 2 files changed, 314 insertions(+), 4 deletions(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index 3aaed679..e17b65db 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -3,9 +3,10 @@ var ORMError = require("../Error"); var Settings = require("../Settings"); var Singleton = require("../Singleton"); var util = require("../Utilities"); +var Promise = require("bluebird"); exports.prepare = function (db, Model, associations) { - Model.extendsTo = function (name, properties, opts) { + Model.extendsTo = function (name, properties, opts) { // TODO: extendsTo and find methods should be changed to Async opts = opts || {}; var assocName = opts.name || ucfirst(name); @@ -107,11 +108,33 @@ exports.autoFetch = function (Instance, associations, opts, cb) { } }; +exports.autoFetchAsync = function (Instance, associations, opts) { + return new Promise (function (resolve, reject) { + + if (associations.length === 0) { + return reject(); + } + + var pending = associations.length; + var autoFetchDone = function autoFetchDone() { + pending -= 1; + + if (pending === 0) { + return resolve(); + } + }; + + associations.forEach(function (association) { + autoFetchInstanceAsync(Instance, association, opts, autoFetchDone); + }); + }); +}; + function extendInstance(Model, Instance, Driver, association, opts) { Object.defineProperty(Instance, association.hasAccessor, { value : function (cb) { if (!Instance[Model.id]) { - cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); } else { association.model.get(util.values(Instance, Model.id), function (err, extension) { return cb(err, !err && extension ? true : false); @@ -129,7 +152,7 @@ function extendInstance(Model, Instance, Driver, association, opts) { } if (!Instance[Model.id]) { - cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); } else { association.model.get(util.values(Instance, Model.id), opts, cb); } @@ -203,6 +226,106 @@ function extendInstance(Model, Instance, Driver, association, opts) { }, enumerable : false }); + Object.defineProperty(Instance, association.hasAccessor + 'Async', { + value : function () { + return new Promise (function(resolve, reject) { + if (!Instance[Model.id]) { + return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + association.model.get(util.values(Instance, Model.id), function (err, extension) { + if (err) return reject(err); + return resolve(!err && extension ? true : false); + }); + } + }); + }, + enumerable : false + }); + Object.defineProperty(Instance, association.getAccessor + 'Async', { + value: function (opts) { + return new Promise (function(resolve, reject) { + + if (typeof opts == "function") { + var callbackData = opts; + opts = {}; + } + + if (!Instance[Model.id]) { + return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + association.model.get(util.values(Instance, Model.id), opts, function(err, result) { + if (err) return reject(err); + if (callbackData) return resolve(callbackData); + return resolve(result); + }); + } + }); + }, + enumerable : false + }); + Object.defineProperty(Instance, association.setAccessor + 'Async', { + value : function (Extension) { + return new Promise (function(resolve, reject){ + Instance.save(function (err) { //TODO: change save() to async + if (err) return reject(err); + + Instance[association.delAccessor](function (err) { + if (err) return reject(err); + + var fields = Object.keys(association.field); + + if (!Extension.isInstance) { + Extension = new association.model(Extension); + } + + for (var i = 0; i < Model.id.length; i++) { + Extension[fields[i]] = Instance[Model.id[i]]; + } + + Extension.save(function(err, result) { + if (err) return reject(err); + return resolve(result); + }); + }); + }); + }); + }, + enumerable : false + }); + Object.defineProperty(Instance, association.delAccessor + 'Async', { + value : function () { + return new Promise (function(resolve, reject) { + if (!Instance[Model.id]) { + return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + var conditions = {}; + var fields = Object.keys(association.field); + + for (var i = 0; i < Model.id.length; i++) { + conditions[fields[i]] = Instance[Model.id[i]]; + } + + association.model.find(conditions, function (err, extensions) { + if (err) return reject(err); + + var pending = extensions.length; + + for (var i = 0; i < extensions.length; i++) { + Singleton.clear(extensions[i].__singleton_uid()); + extensions[i].remove(function () { + if (--pending === 0) { + return resolve(); + } + }); + } + + if (pending === 0) return resolve(); + }); + } + }); + }, + enumerable : false + }); } function autoFetchInstance(Instance, association, opts, cb) { @@ -231,6 +354,31 @@ function autoFetchInstance(Instance, association, opts, cb) { } } +function autoFetchInstanceAsync(Instance, association, opts) { + return new Promise (function(resolve, reject) { + if (!Instance.saved()) return resolve(); + + if (!opts.hasOwnProperty("autoFetchLimit") || !opts.autoFetchLimit) { + opts.autoFetchLimit = association.autoFetchLimit; + } + + if (opts.autoFetchLimit === 0 || (!opts.autoFetch && !association.autoFetch)) return resolve(); + + if (Instance.isPersisted()) { + Instance[association.getAccessor]({ autoFetchLimit: opts.autoFetchLimit - 1 }, function (err, Assoc) { + if (!err) { + Instance[association.name] = Assoc; + return resolve(); + } + + return reject(); + }); + } else { + return resolve(); + } + }); +} + function ucfirst(text) { return text[0].toUpperCase() + text.substr(1); } diff --git a/test/integration/association-extend.js b/test/integration/association-extend.js index ea7a7e93..7bf0dd22 100644 --- a/test/integration/association-extend.js +++ b/test/integration/association-extend.js @@ -215,7 +215,169 @@ describe("Model.extendsTo()", function() { }); }); - describe("findBy()", function () { + describe("when calling hasAccessor + Async", function () { + before(setup()); + + it("should return true if found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + John.hasAddressAsync().then(function (hasAddress) { + should.equal(err, null); + hasAddress.should.equal(true); + + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + + it("should return false if not found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + John.removeAddressAsync().then(function(){ + John.hasAddressAsync().then(function (hasAddress) { + }).catch(function(err) { + err.should.be.a.Object(); + done(); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.hasAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("when calling getAccessor + Async", function () { + before(setup()); + + it("should return extension if found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + John.getAddressAsync(John).then(function (Address) { + Address.should.be.a.Object(); + Address.should.have.property("street", "Liberty"); + done(); + }).catch(function (err) { + done(err); + }); + }); + }); + + it("should return error if not found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + John.removeAddressAsync().then(function () { + John.getAddressAsync(John).catch(function(err){ + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_FOUND); + done(); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.getAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("when calling setAccessor + Async", function () { + before(setup()); + + it("should remove any previous extension", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(1); + + var addr = new PersonAddress({ + street : "4th Ave", + number : 4 + }); + + John.setAddressAsync(addr).then(function () { + John.getAddressAsync(addr).then(function (Address) { + Address.should.be.a.Object(); + Address.should.have.property("street", addr.street); + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(0); + done(); + }); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("when calling delAccessor + Async", function () { + before(setup()); + + it("should remove any extension", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(1); + + var addr = new PersonAddress({ + street : "4th Ave", + number : 4 + }); + + John.removeAddressAsync(addr).then(function () { + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(0); + + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.removeAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("findBy()", function () { // TODO: make async after Models method include async support before(setup()); it("should throw if no conditions passed", function (done) { From 1eab7e0f4204b0b4d97c4d503775007c22f75c4b Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 29 Aug 2017 14:09:06 +0300 Subject: [PATCH 19/98] Part of Async methods for Association many --- lib/Associations/Many.js | 157 +++++++++++++++++++++++- test/integration/association-hasmany.js | 40 +++++- 2 files changed, 194 insertions(+), 3 deletions(-) diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index cd5359c7..f1c26d15 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -145,6 +145,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan Object.defineProperty(Instance, association.hasAccessor, { value: function () { + console.log(arguments) var Instances = Array.prototype.slice.apply(arguments); var cb = Instances.pop(); var conditions = {}, options = {}; @@ -181,6 +182,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan util.populateConditions(association.model, Object.keys(association.mergeAssocId), Instances[i], options.__merge.where[1], false); } + console.log('hasAccessor Instances: ' + Instances) association.model.find(conditions, options, function (err, foundItems) { if (err) return cb(err); if (_.isEmpty(Instances)) return cb(null, false); @@ -204,6 +206,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: false, writable: true }); + Object.defineProperty(Instance, association.getAccessor, { value: function () { var options = {}; @@ -263,7 +266,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan options.extra = association.props; options.extra_info = { - table: association.mergeTable, + table: association.mergeTable, id: util.values(Instance, Model.id), id_prop: Object.keys(association.mergeId), assoc_prop: Object.keys(association.mergeAssocId) @@ -282,6 +285,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: false, writable: true }); + Object.defineProperty(Instance, association.setAccessor, { value: function () { var items = _.flatten(arguments); @@ -476,6 +480,157 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan }, enumerable: true }); + + Object.defineProperty(Instance, association.hasAccessor + 'Async', { + value: function () { + var Instances = Array.prototype.slice.apply(arguments); + var conditions = {}, options = {}; + return new Promise (function(resolve, reject) { + if (Instances.length) { + if (Array.isArray(Instances[0])) { + Instances = Instances[0]; + } + } + if (Driver.hasMany) { + return Driver.hasMany(Model, association).has(Instance, Instances, conditions, function(err, result) { + if (err) return reject (err); + return resolve(result); + }); + } + + options.autoFetchLimit = 0; + options.__merge = { + from: { table: association.mergeTable, field: Object.keys(association.mergeAssocId) }, + to: { table: association.model.table, field: association.model.id.slice(0) }, // clone model id + where: [ association.mergeTable, {} ] + }; + + adjustForMapsTo(options); + + options.extra = association.props; + options.extra_info = { + table: association.mergeTable, + id: util.values(Instance, Model.id), + id_prop: Object.keys(association.mergeId), + assoc_prop: Object.keys(association.mergeAssocId) + }; + + util.populateConditions(Model, Object.keys(association.mergeId), Instance, options.__merge.where[1]); + + for (var i = 0; i < Instances.length; i++) { + util.populateConditions(association.model, Object.keys(association.mergeAssocId), Instances[i], options.__merge.where[1], false); + } + + association.model.find(conditions, options, function (err, foundItems) { + if (err) return reject(err); + if (_.isEmpty(Instances)) return resolve(false); + + var mapKeysToString = function (item) { + return _.map(association.model.keys, function (k) { + return item[k]; + }).join(',') + } + + var foundItemsIDs = _(foundItems).map(mapKeysToString).uniq().value(); + var InstancesIDs = _(Instances ).map(mapKeysToString).uniq().value(); + + var sameLength = foundItemsIDs.length == InstancesIDs.length; + var sameContents = sameLength && _.isEmpty(_.difference(foundItemsIDs, InstancesIDs)); + + return resolve(sameContents); + }); + }); + }, + enumerable: false, + writable: true + }); + + Object.defineProperty(Instance, association.getAccessor + 'Async', { + value: function () { + var options = {}; + var conditions = null; + var order = null; + var cb = null; + + return new Promise (function (resolve, reject) { + for (var i = 0; i < arguments.length; i++) { + switch (typeof arguments[i]) { + case "function": + cb = arguments[i]; + break; + case "object": + if (Array.isArray(arguments[i])) { + order = arguments[i]; + order[0] = [association.model.table, order[0]]; + } else { + if (conditions === null) { + conditions = arguments[i]; + } else { + options = arguments[i]; + } + } + break; + case "string": + if (arguments[i][0] == "-") { + order = [[association.model.table, arguments[i].substr(1)], "Z"]; + } else { + order = [[association.model.table, arguments[i]]]; + } + break; + case "number": + options.limit = arguments[i]; + break; + } + } + + if (order !== null) { + options.order = order; + } + + if (conditions === null) { + conditions = {}; + } + + if (Driver.hasMany) { + return Driver.hasMany(Model, association).get(Instance, conditions, options, createInstance, function (err, result){ + if (err) return reject(err); + return resolve(result); + }); + } + + options.__merge = { + from: {table: association.mergeTable, field: Object.keys(association.mergeAssocId)}, + to: {table: association.model.table, field: association.model.id.slice(0)}, // clone model id + where: [association.mergeTable, {}] + }; + + adjustForMapsTo(options); + + options.extra = association.props; + options.extra_info = { + table: association.mergeTable, + id: util.values(Instance, Model.id), + id_prop: Object.keys(association.mergeId), + assoc_prop: Object.keys(association.mergeAssocId) + }; + + util.populateConditions(Model, Object.keys(association.mergeId), Instance, options.__merge.where[1]); + cb = null; + if (cb === null) { + console.log(association.model.find(conditions, options)) + return association.model.find(conditions, options); + } + + association.model.find(conditions, options, function(err, result) { + if (err) return reject(err); + return resolve (result); + }); + }); + }, + enumerable: false, + writable: true + }); + } function autoFetchInstance(Instance, association, opts, cb) { diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index f4bc3fad..da64ccd1 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -224,7 +224,8 @@ describe("hasMany", function () { Jane.hasPets(pets[0], function (err, has_pets) { should.equal(err, null); - has_pets.should.be.true; + console.log(has_pets) + has_pets.should.equal(true); return done(); }); @@ -232,19 +233,54 @@ describe("hasMany", function () { }); }); - it("should return true if not passing any instance and has associated items", function (done) { + it("should return true if instance has associated item", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + should.equal(err, null); + + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets[0]).then(function (has_pets) { + // should.equal(err, null); + console.log(has_pets) + has_pets.should.equal(true); + // console.log(has_pets) + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it.only("should return true if not passing any instance and has associated items", function (done) { Person.find({ name: "Jane" }).first(function (err, Jane) { should.equal(err, null); Jane.hasPets(function (err, has_pets) { should.equal(err, null); + console.log('has_pets: ' + has_pets) has_pets.should.be.true; + // has_pets.should.equal(true); return done(); }); }); }); + it("should return true if not passing any instance and has associated items (promise-based)", function (done) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync().then(function (has_pets) { + has_pets.should.equal(true); + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + it("should return true if all passed instances are associated", function (done) { Pet.find(function (err, pets) { Person.find({ name: "John" }).first(function (err, John) { From dee47043121ae0e20c8aa2985f2a0e20425535e2 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 29 Aug 2017 16:43:32 +0300 Subject: [PATCH 20/98] Added promisify instead new Promises for Extend Associations. Revert some changes from tests --- lib/Associations/Extend.js | 191 +++++++++--------------- lib/Associations/Many.js | 3 - test/integration/association-extend.js | 2 +- test/integration/association-hasmany.js | 14 +- 4 files changed, 74 insertions(+), 136 deletions(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index e17b65db..b6c006f0 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -108,28 +108,6 @@ exports.autoFetch = function (Instance, associations, opts, cb) { } }; -exports.autoFetchAsync = function (Instance, associations, opts) { - return new Promise (function (resolve, reject) { - - if (associations.length === 0) { - return reject(); - } - - var pending = associations.length; - var autoFetchDone = function autoFetchDone() { - pending -= 1; - - if (pending === 0) { - return resolve(); - } - }; - - associations.forEach(function (association) { - autoFetchInstanceAsync(Instance, association, opts, autoFetchDone); - }); - }); -}; - function extendInstance(Model, Instance, Driver, association, opts) { Object.defineProperty(Instance, association.hasAccessor, { value : function (cb) { @@ -227,103 +205,97 @@ function extendInstance(Model, Instance, Driver, association, opts) { enumerable : false }); Object.defineProperty(Instance, association.hasAccessor + 'Async', { - value : function () { - return new Promise (function(resolve, reject) { - if (!Instance[Model.id]) { - return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - association.model.get(util.values(Instance, Model.id), function (err, extension) { - if (err) return reject(err); - return resolve(!err && extension ? true : false); - }); - } - }); - }, + value : Promise.promisify(function (cb) { + if (!Instance[Model.id]) { + cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + association.model.get(util.values(Instance, Model.id), function (err, extension) { + return cb(err, !err && extension ? true : false); + }); + } + return this; + }), enumerable : false }); Object.defineProperty(Instance, association.getAccessor + 'Async', { - value: function (opts) { - return new Promise (function(resolve, reject) { - - if (typeof opts == "function") { - var callbackData = opts; - opts = {}; - } + value: Promise.promisify(function (opts, cb) { + if (typeof opts == "function") { + cb = opts; + opts = {}; + } - if (!Instance[Model.id]) { - return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - association.model.get(util.values(Instance, Model.id), opts, function(err, result) { - if (err) return reject(err); - if (callbackData) return resolve(callbackData); - return resolve(result); - }); - } - }); - }, + if (!Instance[Model.id]) { + cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + association.model.get(util.values(Instance, Model.id), opts, cb); + } + return this; + }), enumerable : false }); Object.defineProperty(Instance, association.setAccessor + 'Async', { - value : function (Extension) { - return new Promise (function(resolve, reject){ - Instance.save(function (err) { //TODO: change save() to async - if (err) return reject(err); + value : Promise.promisify(function (Extension, cb) { + Instance.save(function (err) { + if (err) { + return cb(err); + } - Instance[association.delAccessor](function (err) { - if (err) return reject(err); + Instance[association.delAccessor](function (err) { + if (err) { + return cb(err); + } - var fields = Object.keys(association.field); + var fields = Object.keys(association.field); - if (!Extension.isInstance) { - Extension = new association.model(Extension); - } + if (!Extension.isInstance) { + Extension = new association.model(Extension); + } - for (var i = 0; i < Model.id.length; i++) { - Extension[fields[i]] = Instance[Model.id[i]]; - } + for (var i = 0; i < Model.id.length; i++) { + Extension[fields[i]] = Instance[Model.id[i]]; + } - Extension.save(function(err, result) { - if (err) return reject(err); - return resolve(result); - }); - }); + Extension.save(cb); }); }); - }, + return this; + }), enumerable : false }); Object.defineProperty(Instance, association.delAccessor + 'Async', { - value : function () { - return new Promise (function(resolve, reject) { - if (!Instance[Model.id]) { - return reject(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - var conditions = {}; - var fields = Object.keys(association.field); + value : Promise.promisify(function (cb) { + if (!Instance[Model.id]) { + cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); + } else { + var conditions = {}; + var fields = Object.keys(association.field); - for (var i = 0; i < Model.id.length; i++) { - conditions[fields[i]] = Instance[Model.id[i]]; - } + for (var i = 0; i < Model.id.length; i++) { + conditions[fields[i]] = Instance[Model.id[i]]; + } - association.model.find(conditions, function (err, extensions) { - if (err) return reject(err); + association.model.find(conditions, function (err, extensions) { + if (err) { + return cb(err); + } - var pending = extensions.length; + var pending = extensions.length; - for (var i = 0; i < extensions.length; i++) { - Singleton.clear(extensions[i].__singleton_uid()); - extensions[i].remove(function () { - if (--pending === 0) { - return resolve(); - } - }); - } + for (var i = 0; i < extensions.length; i++) { + Singleton.clear(extensions[i].__singleton_uid()); + extensions[i].remove(function () { + if (--pending === 0) { + return cb(); + } + }); + } - if (pending === 0) return resolve(); - }); - } - }); - }, + if (pending === 0) { + return cb(); + } + }); + } + }), enumerable : false }); } @@ -354,31 +326,6 @@ function autoFetchInstance(Instance, association, opts, cb) { } } -function autoFetchInstanceAsync(Instance, association, opts) { - return new Promise (function(resolve, reject) { - if (!Instance.saved()) return resolve(); - - if (!opts.hasOwnProperty("autoFetchLimit") || !opts.autoFetchLimit) { - opts.autoFetchLimit = association.autoFetchLimit; - } - - if (opts.autoFetchLimit === 0 || (!opts.autoFetch && !association.autoFetch)) return resolve(); - - if (Instance.isPersisted()) { - Instance[association.getAccessor]({ autoFetchLimit: opts.autoFetchLimit - 1 }, function (err, Assoc) { - if (!err) { - Instance[association.name] = Assoc; - return resolve(); - } - - return reject(); - }); - } else { - return resolve(); - } - }); -} - function ucfirst(text) { return text[0].toUpperCase() + text.substr(1); } diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index f1c26d15..e455809a 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -145,7 +145,6 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan Object.defineProperty(Instance, association.hasAccessor, { value: function () { - console.log(arguments) var Instances = Array.prototype.slice.apply(arguments); var cb = Instances.pop(); var conditions = {}, options = {}; @@ -182,7 +181,6 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan util.populateConditions(association.model, Object.keys(association.mergeAssocId), Instances[i], options.__merge.where[1], false); } - console.log('hasAccessor Instances: ' + Instances) association.model.find(conditions, options, function (err, foundItems) { if (err) return cb(err); if (_.isEmpty(Instances)) return cb(null, false); @@ -617,7 +615,6 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan util.populateConditions(Model, Object.keys(association.mergeId), Instance, options.__merge.where[1]); cb = null; if (cb === null) { - console.log(association.model.find(conditions, options)) return association.model.find(conditions, options); } diff --git a/test/integration/association-extend.js b/test/integration/association-extend.js index 7bf0dd22..d5c43da6 100644 --- a/test/integration/association-extend.js +++ b/test/integration/association-extend.js @@ -350,7 +350,7 @@ describe("Model.extendsTo()", function() { number : 4 }); - John.removeAddressAsync(addr).then(function () { + John.removeAddressAsync().then(function () { PersonAddress.find({ number: 123 }).count(function (err, c) { should.equal(err, null); diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index da64ccd1..ddeedde1 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -224,8 +224,7 @@ describe("hasMany", function () { Jane.hasPets(pets[0], function (err, has_pets) { should.equal(err, null); - console.log(has_pets) - has_pets.should.equal(true); + has_pets.should.be.true; return done(); }); @@ -241,10 +240,7 @@ describe("hasMany", function () { should.equal(err, null); Jane.hasPetsAsync(pets[0]).then(function (has_pets) { - // should.equal(err, null); - console.log(has_pets) - has_pets.should.equal(true); - // console.log(has_pets) + has_pets.should.be.true; done(); }).catch(function(err){ done(err); @@ -253,15 +249,13 @@ describe("hasMany", function () { }); }); - it.only("should return true if not passing any instance and has associated items", function (done) { + it("should return true if not passing any instance and has associated items", function (done) { Person.find({ name: "Jane" }).first(function (err, Jane) { should.equal(err, null); Jane.hasPets(function (err, has_pets) { should.equal(err, null); - console.log('has_pets: ' + has_pets) has_pets.should.be.true; - // has_pets.should.equal(true); return done(); }); @@ -273,7 +267,7 @@ describe("hasMany", function () { should.equal(err, null); Jane.hasPetsAsync().then(function (has_pets) { - has_pets.should.equal(true); + has_pets.should.be.true; done(); }).catch(function(err){ done(err); From 0a08c6f1b48aacbe94928bc8532e2fb6ed886a78 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 31 Aug 2017 11:04:26 +0300 Subject: [PATCH 21/98] Added promisify to Manu and One Associations --- lib/Associations/Extend.js | 120 +++--------- lib/Associations/Many.js | 165 +++------------- lib/Associations/One.js | 26 +++ test/integration/association-hasmany.js | 242 ++++++++++++++++++++++++ 4 files changed, 319 insertions(+), 234 deletions(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index b6c006f0..e80b425c 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -6,23 +6,23 @@ var util = require("../Utilities"); var Promise = require("bluebird"); exports.prepare = function (db, Model, associations) { - Model.extendsTo = function (name, properties, opts) { // TODO: extendsTo and find methods should be changed to Async + Model.extendsTo = function (name, properties, opts) { opts = opts || {}; var assocName = opts.name || ucfirst(name); var association = { - name : name, - table : opts.table || (Model.table + '_' + name), - reversed : opts.reversed, - autoFetch : opts.autoFetch || false, - autoFetchLimit : opts.autoFetchLimit || 2, - field : util.wrapFieldObject({ - field: opts.field, model: Model, altName: Model.table - }) || util.formatField(Model, Model.table, false, false), - getAccessor : opts.getAccessor || ("get" + assocName), - setAccessor : opts.setAccessor || ("set" + assocName), - hasAccessor : opts.hasAccessor || ("has" + assocName), - delAccessor : opts.delAccessor || ("remove" + assocName) + name : name, + table : opts.table || (Model.table + '_' + name), + reversed : opts.reversed, + autoFetch : opts.autoFetch || false, + autoFetchLimit : opts.autoFetchLimit || 2, + field : util.wrapFieldObject({ + field: opts.field, model: Model, altName: Model.table + }) || util.formatField(Model, Model.table, false, false), + getAccessor : opts.getAccessor || ("get" + assocName), + setAccessor : opts.setAccessor || ("set" + assocName), + hasAccessor : opts.hasAccessor || ("has" + assocName), + delAccessor : opts.delAccessor || ("remove" + assocName) }; var newproperties = _.cloneDeep(properties); @@ -109,6 +109,8 @@ exports.autoFetch = function (Instance, associations, opts, cb) { }; function extendInstance(Model, Instance, Driver, association, opts) { + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); + Object.defineProperty(Instance, association.hasAccessor, { value : function (cb) { if (!Instance[Model.id]) { @@ -204,98 +206,24 @@ function extendInstance(Model, Instance, Driver, association, opts) { }, enumerable : false }); - Object.defineProperty(Instance, association.hasAccessor + 'Async', { - value : Promise.promisify(function (cb) { - if (!Instance[Model.id]) { - cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - association.model.get(util.values(Instance, Model.id), function (err, extension) { - return cb(err, !err && extension ? true : false); - }); - } - return this; - }), + + Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { + value : Promise.promisify(Instance[association.hasAccessor]), enumerable : false }); - Object.defineProperty(Instance, association.getAccessor + 'Async', { - value: Promise.promisify(function (opts, cb) { - if (typeof opts == "function") { - cb = opts; - opts = {}; - } - if (!Instance[Model.id]) { - cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - association.model.get(util.values(Instance, Model.id), opts, cb); - } - return this; - }), + Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.getAccessor]), enumerable : false }); - Object.defineProperty(Instance, association.setAccessor + 'Async', { - value : Promise.promisify(function (Extension, cb) { - Instance.save(function (err) { - if (err) { - return cb(err); - } - - Instance[association.delAccessor](function (err) { - if (err) { - return cb(err); - } - - var fields = Object.keys(association.field); - - if (!Extension.isInstance) { - Extension = new association.model(Extension); - } - - for (var i = 0; i < Model.id.length; i++) { - Extension[fields[i]] = Instance[Model.id[i]]; - } - Extension.save(cb); - }); - }); - return this; - }), + Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { + value : Promise.promisify(Instance[association.setAccessor]), enumerable : false }); - Object.defineProperty(Instance, association.delAccessor + 'Async', { - value : Promise.promisify(function (cb) { - if (!Instance[Model.id]) { - cb(new ORMError("Instance not saved, cannot get extension", 'NOT_DEFINED', { model: Model.table })); - } else { - var conditions = {}; - var fields = Object.keys(association.field); - - for (var i = 0; i < Model.id.length; i++) { - conditions[fields[i]] = Instance[Model.id[i]]; - } - - association.model.find(conditions, function (err, extensions) { - if (err) { - return cb(err); - } - - var pending = extensions.length; - - for (var i = 0; i < extensions.length; i++) { - Singleton.clear(extensions[i].__singleton_uid()); - extensions[i].remove(function () { - if (--pending === 0) { - return cb(); - } - }); - } - if (pending === 0) { - return cb(); - } - }); - } - }), + Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { + value : Promise.promisify(Instance[association.delAccessor]), enumerable : false }); } diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index e455809a..e7529254 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -5,6 +5,7 @@ var Settings = require("../Settings"); var Property = require("../Property"); var ORMError = require("../Error"); var util = require("../Utilities"); +var Promise = require("bluebird"); exports.prepare = function (db, Model, associations) { Model.hasMany = function () { @@ -61,6 +62,7 @@ exports.prepare = function (db, Model, associations) { var assocName = opts.name || ucfirst(name); var assocTemplateName = opts.accessor || assocName; + var association = { name : name, model : OtherModel || Model, @@ -80,7 +82,8 @@ exports.prepare = function (db, Model, associations) { setAccessor : opts.setAccessor || ("set" + assocTemplateName), hasAccessor : opts.hasAccessor || ("has" + assocTemplateName), delAccessor : opts.delAccessor || ("remove" + assocTemplateName), - addAccessor : opts.addAccessor || ("add" + assocTemplateName) + addAccessor : opts.addAccessor || ("add" + assocTemplateName), + hasAccessorAsync : opts.hasAccessorAsync || ("has" + assocTemplateName + 'Async') }; associations.push(association); @@ -126,6 +129,8 @@ exports.autoFetch = function (Instance, associations, opts, cb) { }; function extendInstance(Model, Instance, Driver, association, opts, createInstance) { + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); + if (Model.settings.get("instance.cascadeRemove")) { Instance.on("beforeRemove", function () { Instance[association.delAccessor](); @@ -358,6 +363,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: false, writable: true }); + Object.defineProperty(Instance, association.addAccessor, { value: function () { var Associations = []; @@ -479,151 +485,34 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: true }); - Object.defineProperty(Instance, association.hasAccessor + 'Async', { - value: function () { - var Instances = Array.prototype.slice.apply(arguments); - var conditions = {}, options = {}; - return new Promise (function(resolve, reject) { - if (Instances.length) { - if (Array.isArray(Instances[0])) { - Instances = Instances[0]; - } - } - if (Driver.hasMany) { - return Driver.hasMany(Model, association).has(Instance, Instances, conditions, function(err, result) { - if (err) return reject (err); - return resolve(result); - }); - } - - options.autoFetchLimit = 0; - options.__merge = { - from: { table: association.mergeTable, field: Object.keys(association.mergeAssocId) }, - to: { table: association.model.table, field: association.model.id.slice(0) }, // clone model id - where: [ association.mergeTable, {} ] - }; - - adjustForMapsTo(options); - - options.extra = association.props; - options.extra_info = { - table: association.mergeTable, - id: util.values(Instance, Model.id), - id_prop: Object.keys(association.mergeId), - assoc_prop: Object.keys(association.mergeAssocId) - }; - - util.populateConditions(Model, Object.keys(association.mergeId), Instance, options.__merge.where[1]); - - for (var i = 0; i < Instances.length; i++) { - util.populateConditions(association.model, Object.keys(association.mergeAssocId), Instances[i], options.__merge.where[1], false); - } - - association.model.find(conditions, options, function (err, foundItems) { - if (err) return reject(err); - if (_.isEmpty(Instances)) return resolve(false); - - var mapKeysToString = function (item) { - return _.map(association.model.keys, function (k) { - return item[k]; - }).join(',') - } - - var foundItemsIDs = _(foundItems).map(mapKeysToString).uniq().value(); - var InstancesIDs = _(Instances ).map(mapKeysToString).uniq().value(); - - var sameLength = foundItemsIDs.length == InstancesIDs.length; - var sameContents = sameLength && _.isEmpty(_.difference(foundItemsIDs, InstancesIDs)); + console.log(association.hasAccessor + promiseFunctionPostfix) - return resolve(sameContents); - }); - }); - }, + Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.hasAccessor]), enumerable: false, writable: true }); - Object.defineProperty(Instance, association.getAccessor + 'Async', { - value: function () { - var options = {}; - var conditions = null; - var order = null; - var cb = null; - - return new Promise (function (resolve, reject) { - for (var i = 0; i < arguments.length; i++) { - switch (typeof arguments[i]) { - case "function": - cb = arguments[i]; - break; - case "object": - if (Array.isArray(arguments[i])) { - order = arguments[i]; - order[0] = [association.model.table, order[0]]; - } else { - if (conditions === null) { - conditions = arguments[i]; - } else { - options = arguments[i]; - } - } - break; - case "string": - if (arguments[i][0] == "-") { - order = [[association.model.table, arguments[i].substr(1)], "Z"]; - } else { - order = [[association.model.table, arguments[i]]]; - } - break; - case "number": - options.limit = arguments[i]; - break; - } - } - - if (order !== null) { - options.order = order; - } - - if (conditions === null) { - conditions = {}; - } - - if (Driver.hasMany) { - return Driver.hasMany(Model, association).get(Instance, conditions, options, createInstance, function (err, result){ - if (err) return reject(err); - return resolve(result); - }); - } - - options.__merge = { - from: {table: association.mergeTable, field: Object.keys(association.mergeAssocId)}, - to: {table: association.model.table, field: association.model.id.slice(0)}, // clone model id - where: [association.mergeTable, {}] - }; - - adjustForMapsTo(options); + Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.getAccessor]), + enumerable: false, + writable: true + }); - options.extra = association.props; - options.extra_info = { - table: association.mergeTable, - id: util.values(Instance, Model.id), - id_prop: Object.keys(association.mergeId), - assoc_prop: Object.keys(association.mergeAssocId) - }; + Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.setAccessor]), + enumerable: false, + writable: true + }); - util.populateConditions(Model, Object.keys(association.mergeId), Instance, options.__merge.where[1]); - cb = null; - if (cb === null) { - return association.model.find(conditions, options); - } + Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.delAccessor]), + enumerable: false, + writable: true + }); - association.model.find(conditions, options, function(err, result) { - if (err) return reject(err); - return resolve (result); - }); - }); - }, + Object.defineProperty(Instance, association.addAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.addAccessor]), enumerable: false, writable: true }); diff --git a/lib/Associations/One.js b/lib/Associations/One.js index 74098dfe..48a48872 100644 --- a/lib/Associations/One.js +++ b/lib/Associations/One.js @@ -2,6 +2,7 @@ var _ = require("lodash"); var util = require("../Utilities"); var ORMError = require("../Error"); var Accessors = { "get": "get", "set": "set", "has": "has", "del": "remove" }; +var Promise = require("bluebird"); exports.prepare = function (Model, associations) { Model.hasOne = function () { @@ -145,6 +146,7 @@ exports.autoFetch = function (Instance, associations, opts, cb) { }; function extendInstance(Model, Instance, Driver, association) { + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); Object.defineProperty(Instance, association.hasAccessor, { value: function (opts, cb) { if (typeof opts === "function") { @@ -285,7 +287,31 @@ function extendInstance(Model, Instance, Driver, association) { enumerable: false, writable: true }); + + Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.delAccessor]), + enumerable: false, + writable: true + }); } + + Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.hasAccessor]), + enumerable: false, + writable: true + }); + + Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.getAccessor]), + enumerable: false, + writable: true + }); + + Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association.setAccessor]), + enumerable: false, + writable: true + }); } function autoFetchInstance(Instance, association, opts, cb) { diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index ddeedde1..6dcd7282 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -290,6 +290,22 @@ describe("hasMany", function () { }); }); + it("should return true if all passed instances are associated (promise-based)", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + it("should return false if any passed instances are not associated", function (done) { Pet.find(function (err, pets) { Person.find({ name: "Jane" }).first(function (err, Jane) { @@ -305,6 +321,23 @@ describe("hasMany", function () { }); }); + it("should return false if any passed instances are not associated (promise-based)", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.false; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + if (common.protocol() != "mongodb") { it("should return true if join table has duplicate entries", function (done) { Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { @@ -336,6 +369,37 @@ describe("hasMany", function () { }); }); }); + it("should return true if join table has duplicate entries (promise-based)", function (done) { + Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.find({ name: "John" }).first(function (err, John) { + should.not.exist(err); + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + + db.driver.execQuery( + "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", + [John.id, pets[0].id, John.id, pets[1].id], + function (err) { + should.not.exist(err); + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + done(); + }).catch(function(err){ + done(err); + }); + } + ); + }).catch(function(err){ + done(err); + }); + }); + }); + }); } }); @@ -363,6 +427,29 @@ describe("hasMany", function () { }); }); }); + + it("should accept arguments in different orders (promise-based)", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + + people[0].getPetsAsync().then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); }); describe("delAccessor", function () { @@ -390,6 +477,29 @@ describe("hasMany", function () { }); }); + it("should remove specific associations if passed (promise-based)", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + people[0].getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + it("should remove all associations if none passed", function (done) { Person.find({ name: "John" }).first(function (err, John) { should.equal(err, null); @@ -408,6 +518,26 @@ describe("hasMany", function () { }); }); }); + + it("should remove all associations if none passed (promise-based)", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.removePetsAsync().then(function () { + John.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); }); describe("addAccessor", function () { @@ -529,6 +659,118 @@ describe("hasMany", function () { }); }); + describe("addAccessorAsync", function () { + before(setup()); + + if (common.protocol() != "mongodb") { + + it("might add duplicates (promise-based)", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "Jane" }, function (err, people) { + should.equal(err, null); + + people[0].addPetsAsync(pets[0]).then(function () { + people[0].getPetsAsync("name").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Mutt"); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + } + + it("should keep associations and add new ones (promise-based)", function (done) { + Pet.find({ name: "Deco" }).first(function (err, Deco) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPets(function (err, janesPets) { + should.not.exist(err); + + var petsAtStart = janesPets.length; + + Jane.addPets(Deco, function (err) { + should.equal(err, null); + + Jane.getPets("name", function (err, pets) { + should.equal(err, null); + + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].name.should.equal("Deco"); + pets[1].name.should.equal("Mutt"); + + return done(); + }); + }); + }); + }); + }); + }); + + it("should accept several arguments as associations (promise-based)", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.addPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should accept array as list of associations (promise-based)", function (done) { + Pet.create([{ name: 'Ruff' }, { name: 'Spotty' }],function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.getPetsAsync().then(function (justinsPets) { + + var petCount = justinsPets.length; + + Justin.addPetsAsync(pets).then(function () { + + Justin.getPets(function (err, justinsPets) { + should.equal(err, null); + + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); + + done(); + }); + }).catch(function(err){ + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + describe("setAccessor", function () { before(setup()); From 607675e7e15e1441418b153e138d30a6131f1e87 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 31 Aug 2017 16:14:34 +0300 Subject: [PATCH 22/98] Added changes to Associations Many. Created tests for Associations Many and One --- lib/Associations/Many.js | 10 +- test/integration/association-hasmany-extra.js | 43 + test/integration/association-hasmany-hooks.js | 94 +++ .../integration/association-hasmany-mapsto.js | 534 ++++++++++++ test/integration/association-hasmany.js | 778 ++++++++++++------ .../integration/association-hasone-reverse.js | 165 ++++ test/integration/association-hasone-zeroid.js | 42 +- test/integration/association-hasone.js | 183 ++++ 8 files changed, 1596 insertions(+), 253 deletions(-) diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index e7529254..c6472ae5 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -9,6 +9,7 @@ var Promise = require("bluebird"); exports.prepare = function (db, Model, associations) { Model.hasMany = function () { + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); var name, makeKey, mergeId, mergeAssocId; var OtherModel = Model; var props = null; @@ -83,7 +84,12 @@ exports.prepare = function (db, Model, associations) { hasAccessor : opts.hasAccessor || ("has" + assocTemplateName), delAccessor : opts.delAccessor || ("remove" + assocTemplateName), addAccessor : opts.addAccessor || ("add" + assocTemplateName), - hasAccessorAsync : opts.hasAccessorAsync || ("has" + assocTemplateName + 'Async') + + getAccessorAsync : opts.getAccessor + promiseFunctionPostfix || ("get" + assocTemplateName + promiseFunctionPostfix), + setAccessorAsync : opts.setAccessor + promiseFunctionPostfix || ("set" + assocTemplateName + promiseFunctionPostfix), + hasAccessorAsync : opts.hasAccessor + promiseFunctionPostfix || ("has" + assocTemplateName + promiseFunctionPostfix), + delAccessorAsync : opts.delAccessor + promiseFunctionPostfix || ("remove" + assocTemplateName + promiseFunctionPostfix), + addAccessorAsync : opts.addAccessor + promiseFunctionPostfix || ("add" + assocTemplateName + promiseFunctionPostfix) }; associations.push(association); @@ -485,8 +491,6 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: true }); - console.log(association.hasAccessor + promiseFunctionPostfix) - Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { value: Promise.promisify(Instance[association.hasAccessor]), enumerable: false, diff --git a/test/integration/association-hasmany-extra.js b/test/integration/association-hasmany-extra.js index 5b8606d9..3d04bb25 100644 --- a/test/integration/association-hasmany-extra.js +++ b/test/integration/association-hasmany-extra.js @@ -75,4 +75,47 @@ describe("hasMany extra properties", function() { }); }); }); + + describe("if passed to addAccessorAsync", function () { + before(setup()); + + it("should be added to association", function (done) { + Person.create([{ + name : "John" + }], function (err, people) { + Pet.create([{ + name : "Deco" + }, { + name : "Mutt" + }], function (err, pets) { + var data = { adopted: true }; + + people[0].addPetsAsync(pets, { since : new Date(), data: data }).then(function () { + + Person.find({ name: "John" }, { autoFetch : true }).first(function (err, John) { + should.equal(err, null); + + John.should.have.property("pets"); + should(Array.isArray(pets)); + + John.pets.length.should.equal(2); + + John.pets[0].should.have.property("name"); + John.pets[0].should.have.property("extra"); + John.pets[0].extra.should.be.a.Object(); + John.pets[0].extra.should.have.property("since"); + should(John.pets[0].extra.since instanceof Date); + + should.equal(typeof John.pets[0].extra.data, 'object'); + should.equal(JSON.stringify(data), JSON.stringify(John.pets[0].extra.data)); + + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); }); diff --git a/test/integration/association-hasmany-hooks.js b/test/integration/association-hasmany-hooks.js index 9f640f04..54bbf504 100644 --- a/test/integration/association-hasmany-hooks.js +++ b/test/integration/association-hasmany-hooks.js @@ -122,4 +122,98 @@ describe("hasMany hooks", function() { }); }); }); + + describe("beforeSaveAsync", function () { + var had_extra = false; + + before(setup({ + born : Date + }, { + hooks : { + beforeSave: function (extra, next) { + had_extra = (typeof extra == "object"); + return next(); + } + } + })); + + it("should pass extra data to hook if extra defined", function (done) { + Person.create({ + name : "John" + }, function (err, John) { + Pet.create({ + name : "Deco" + }, function (err, Deco) { + John.addPetsAsync(Deco).then(function () { + should.not.exist(err); + + had_extra.should.equal(true); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("beforeSaveAsync", function () { + var had_extra = false; + + before(setup({}, { + hooks : { + beforeSave: function (next) { + next.should.be.a.Function(); + return next(); + } + } + })); + + it("should not pass extra data to hook if extra defined", function (done) { + Person.create({ + name : "John" + }, function (err, John) { + Pet.create({ + name : "Deco" + }, function (err, Deco) { + John.addPetsAsync(Deco).then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("beforeSaveAsync", function () { + var had_extra = false; + + before(setup({}, { + hooks : { + beforeSave: function (next) { + setTimeout(function () { + return next(new Error('blocked')); + }, 100); + } + } + })); + + it("should block if error returned", function (done) { + Person.create({ + name : "John" + }, function (err, John) { + Pet.create({ + name : "Deco" + }, function (err, Deco) { + John.addPetsAsync(Deco).catch(function(err) { + should.exist(err); + err.message.should.equal('blocked'); + done() + }); + }); + }); + }); + }); }); diff --git a/test/integration/association-hasmany-mapsto.js b/test/integration/association-hasmany-mapsto.js index 1502d32b..5e265fe5 100644 --- a/test/integration/association-hasmany-mapsto.js +++ b/test/integration/association-hasmany-mapsto.js @@ -663,5 +663,539 @@ describe("hasMany with mapsTo", function () { }); }); + + describe("getAccessorAsync", function () { + before(setup()); + + it("should allow to specify order as string", function (done) { + Person.find({ firstName: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync("-petName").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].model().should.equal(Pet); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it ("should return proper instance model", function(done){ + Person.find({ firstName: "John" }, function (err, people) { + people[0].getPetsAsync("-petName").then(function (pets) { + pets[0].model().should.equal(Pet); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify order as Array", function (done) { + Person.find({ firstName: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync([ "petName", "Z" ]).then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify a limit", function (done) { + Person.find({ firstName: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync(1).then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify conditions", function (done) { + Person.find({ firstName: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync({ petName: "Mutt" }).then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + if (common.protocol() == "mongodb") return; + + it("should allow chaining count()", function (done) { + Person.find({}, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 2); + + people[1].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 1); + + people[2].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + describe("hasAccessorAsync", function () { + before(setup()); + + it("should return true if instance has associated item", function (done) { + Pet.find({ petName: "Mutt" }, function (err, pets) { + should.equal(err, null); + + Person.find({ firstName: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets[0]).then(function (has_pets) { + has_pets.should.equal(true); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should return true if not passing any instance and has associated items", function (done) { + Person.find({ firstName: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync().then(function (has_pets) { + has_pets.should.be.true; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should return true if all passed instances are associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ firstName: "John" }).first(function (err, John) { + should.equal(err, null); + + John.hasPetsAsync(pets).then(function (has_pets) { + has_pets.should.be.true; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should return false if any passed instances are not associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ firstName: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets).then(function (has_pets) { + has_pets.should.be.false; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should accept arguments in different orders", function (done) { + Pet.find({ petName: "Mutt" }, function (err, pets) { + Person.find({ firstName: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + + people[0].getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should remove specific associations if passed", function (done) { + Pet.find({ petName: "Mutt" }, function (err, pets) { + Person.find({ firstName: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + + people[0].getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if none passed", function (done) { + Person.find({ firstName: "John" }).first(function (err, John) { + should.equal(err, null); + + John.removePetsAsync().then(function () { + + John.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + describe("addAccessorAsync", function () { + before(setup()); + + if (common.protocol() != "mongodb") { + it("might add duplicates", function (done) { + Pet.find({ petName: "Mutt" }, function (err, pets) { + Person.find({ firstName: "Jane" }, function (err, people) { + should.equal(err, null); + + people[0].addPetsAsync(pets[0]).then(function () { + + people[0].getPetsAsync("petName").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + } + + it("should keep associations and add new ones", function (done) { + Pet.find({ petName: "Deco" }).first(function (err, Deco) { + Person.find({ firstName: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (janesPets) { + + var petsAtStart = janesPets.length; + + Jane.addPetsAsync(Deco).then(function () { + Jane.getPetsAsync("petName").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].petName.should.equal("Deco"); + pets[1].petName.should.equal("Mutt"); + + done(); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should accept several arguments as associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ firstName: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.addPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should accept array as list of associations", function (done) { + Pet.create([{ petName: 'Ruff' }, { petName: 'Spotty' }],function (err, pets) { + Person.find({ firstName: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.getPetsAsync().then(function (justinsPets) { + + var petCount = justinsPets.length; + + Justin.addPetsAsync(pets).then(function () { + Justin.getPetsAsync().then(function (justinsPets) { + + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should throw if no items passed", function (done) { + Person.one(function (err, person) { + should.equal(err, null); + person.addPetsAsync().catch(function(err) { + should.exists(err); + done(); + }); + }); + }); + }); + + describe("setAccessorAsync", function () { + before(setup()); + + it("should accept several arguments as associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ firstName: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets[0], pets[1]).then(function () { + + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should accept an array of associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ firstName: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets).then(function () { + Justin.getPetsAsync().then(function (all_pets) { + + should(Array.isArray(all_pets)); + all_pets.length.should.equal(pets.length); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if an empty array is passed", function (done) { + Person.find({ firstName: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 2); + + Justin.setPetsAsync([]).then(function () { + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("clears current associations", function (done) { + Pet.find({ petName: "Deco" }, function (err, pets) { + var Deco = pets[0]; + + Person.find({ firstName: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Mutt"); + + Jane.setPetsAsync(Deco).then(function () { + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal(Deco.petName); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("with autoFetch turned on (promised-based test)", function () { + before(setup({ + autoFetchPets : true + })); + + it("should not auto save associations which were autofetched", function (done) { + Pet.all(function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.create({ firstName: 'Paul' }, function (err, paul) { + should.not.exist(err); + + Person.one({ firstName: 'Paul' }, function (err, paul2) { + should.not.exist(err); + should.equal(paul2.pets.length, 0); + + paul.setPetsAsync(pets).then(function () { + should.not.exist(err); + + // reload paul to make sure we have 2 pets + Person.one({ firstName: 'Paul' }, function (err, paul) { + should.not.exist(err); + should.equal(paul.pets.length, 2); + + // Saving paul2 should NOT auto save associations and hence delete + // the associations we just created. + paul2.save(function (err) { + should.not.exist(err); + + // let's check paul - pets should still be associated + Person.one({ firstName: 'Paul' }, function (err, paul) { + should.not.exist(err); + should.equal(paul.pets.length, 2); + + done(); + }); + }); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + }); }); }); diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index 6dcd7282..226f00ec 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -232,23 +232,6 @@ describe("hasMany", function () { }); }); - it("should return true if instance has associated item", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - should.equal(err, null); - - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets[0]).then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - it("should return true if not passing any instance and has associated items", function (done) { Person.find({ name: "Jane" }).first(function (err, Jane) { should.equal(err, null); @@ -262,19 +245,6 @@ describe("hasMany", function () { }); }); - it("should return true if not passing any instance and has associated items (promise-based)", function (done) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync().then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - it("should return true if all passed instances are associated", function (done) { Pet.find(function (err, pets) { Person.find({ name: "John" }).first(function (err, John) { @@ -290,22 +260,6 @@ describe("hasMany", function () { }); }); - it("should return true if all passed instances are associated (promise-based)", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - it("should return false if any passed instances are not associated", function (done) { Pet.find(function (err, pets) { Person.find({ name: "Jane" }).first(function (err, Jane) { @@ -321,23 +275,6 @@ describe("hasMany", function () { }); }); - it("should return false if any passed instances are not associated (promise-based)", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.false; - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - if (common.protocol() != "mongodb") { it("should return true if join table has duplicate entries", function (done) { Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { @@ -428,33 +365,6 @@ describe("hasMany", function () { }); }); - it("should accept arguments in different orders (promise-based)", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - - people[0].getPetsAsync().then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - }); - - describe("delAccessor", function () { - before(setup()); - it("should remove specific associations if passed", function (done) { Pet.find({ name: "Mutt" }, function (err, pets) { Person.find({ name: "John" }, function (err, people) { @@ -477,29 +387,6 @@ describe("hasMany", function () { }); }); - it("should remove specific associations if passed (promise-based)", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - people[0].getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - it("should remove all associations if none passed", function (done) { Person.find({ name: "John" }).first(function (err, John) { should.equal(err, null); @@ -518,26 +405,6 @@ describe("hasMany", function () { }); }); }); - - it("should remove all associations if none passed (promise-based)", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.removePetsAsync().then(function () { - John.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); }); describe("addAccessor", function () { @@ -659,118 +526,6 @@ describe("hasMany", function () { }); }); - describe("addAccessorAsync", function () { - before(setup()); - - if (common.protocol() != "mongodb") { - - it("might add duplicates (promise-based)", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "Jane" }, function (err, people) { - should.equal(err, null); - - people[0].addPetsAsync(pets[0]).then(function () { - people[0].getPetsAsync("name").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].name.should.equal("Mutt"); - pets[1].name.should.equal("Mutt"); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - } - - it("should keep associations and add new ones (promise-based)", function (done) { - Pet.find({ name: "Deco" }).first(function (err, Deco) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPets(function (err, janesPets) { - should.not.exist(err); - - var petsAtStart = janesPets.length; - - Jane.addPets(Deco, function (err) { - should.equal(err, null); - - Jane.getPets("name", function (err, pets) { - should.equal(err, null); - - should(Array.isArray(pets)); - pets.length.should.equal(petsAtStart + 1); - pets[0].name.should.equal("Deco"); - pets[1].name.should.equal("Mutt"); - - return done(); - }); - }); - }); - }); - }); - }); - - it("should accept several arguments as associations (promise-based)", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.addPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should accept array as list of associations (promise-based)", function (done) { - Pet.create([{ name: 'Ruff' }, { name: 'Spotty' }],function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.getPetsAsync().then(function (justinsPets) { - - var petCount = justinsPets.length; - - Justin.addPetsAsync(pets).then(function () { - - Justin.getPets(function (err, justinsPets) { - should.equal(err, null); - - should(Array.isArray(justinsPets)); - // Mongo doesn't like adding duplicates here, so we add new ones. - should.equal(justinsPets.length, petCount + 2); - - done(); - }); - }).catch(function(err){ - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - describe("setAccessor", function () { before(setup()); @@ -870,6 +625,539 @@ describe("hasMany", function () { }); }); + describe("getAccessorAsync", function () { + before(setup()); + + it("should allow to specify order as string", function (done) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync("-name").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].model().should.equal(Pet); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it ("should return proper instance model", function(done){ + Person.find({ name: "John" }, function (err, people) { + people[0].getPetsAsync("-name").then(function (pets) { + pets[0].model().should.equal(Pet); + done(); + }).catch(function(err) { + done(err); + }) + }); + }); + + it("should allow to specify order as Array", function (done) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync([ "name", "Z" ]).then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify a limit", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync(1).then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify conditions", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync({ name: "Mutt" }).then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + if (common.protocol() == "mongodb") return; + + it("should allow chaining count()", function (done) { + Person.find({}, function (err, people) { + should.equal(err, null); + + people[1].getPetsAsync().then(function (count) { + should.strictEqual(count.length, 2); + + people[2].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 1); + + people[3].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + describe("hasAccessorAsync", function () { + before(setup()); + + it("should return true if instance has associated item", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + should.equal(err, null); + + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets[0]).then(function (has_pets) { + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should return true if not passing any instance and has associated items", function (done) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync().then(function (has_pets) { + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + + it("should return true if all passed instances are associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should return false if any passed instances are not associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.false; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + if (common.protocol() != "mongodb") { + it("should return true if join table has duplicate entries", function (done) { + Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.find({ name: "John" }).first(function (err, John) { + should.not.exist(err); + + John.hasPets(pets, function (err, hasPets) { + should.equal(err, null); + should.equal(hasPets, true); + + db.driver.execQuery( + "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", + [John.id, pets[0].id, John.id, pets[1].id], + function (err) { + should.not.exist(err); + + John.hasPets(pets, function (err, hasPets) { + should.equal(err, null); + should.equal(hasPets, true); + + done() + }); + } + ); + }); + }); + }); + }); + it("should return true if join table has duplicate entries (promise-based)", function (done) { + Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.find({ name: "John" }).first(function (err, John) { + should.not.exist(err); + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + + db.driver.execQuery( + "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", + [John.id, pets[0].id, John.id, pets[1].id], + function (err) { + should.not.exist(err); + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + done(); + }).catch(function(err){ + done(err); + }); + } + ); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + } + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should accept arguments in different orders", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + people[0].getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + return done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove specific associations if passed", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + people[0].getPetsAsync().then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if none passed", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.removePetsAsync().then(function () { + John.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + describe("addAccessorAsync", function () { + before(setup()); + + if (common.protocol() != "mongodb") { + + it("might add duplicates (promise-based)", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "Jane" }, function (err, people) { + should.equal(err, null); + + people[0].addPetsAsync(pets[0]).then(function () { + people[0].getPetsAsync("name").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Mutt"); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + } + + it("should keep associations and add new ones", function (done) { + Pet.find({ name: "Deco" }).first(function (err, Deco) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (janesPets) { + should.not.exist(err); + + var petsAtStart = janesPets.length; + + Jane.addPetsAsync(Deco).then(function () { + Jane.getPetsAsync("name").then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].name.should.equal("Deco"); + pets[1].name.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should accept several arguments as associations (promise-based)", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.addPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should accept array as list of associations (promise-based)", function (done) { + Pet.create([{ name: 'Ruff' }, { name: 'Spotty' }],function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.getPetsAsync().then(function (justinsPets) { + + var petCount = justinsPets.length; + + Justin.addPetsAsync(pets).then(function () { + + Justin.getPetsAsync().then(function (justinsPets) { + + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); + + done(); + }); + }).catch(function(err){ + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("setAccessorAsync", function () { + before(setup()); + + it("should accept several arguments as associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function (err) { + done(err); + }); + }); + }); + }); + + it("should accept an array of associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets).then(function () { + Justin.getPetsAsync().then(function (all_pets) { + should(Array.isArray(all_pets)); + all_pets.length.should.equal(pets.length); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if an empty array is passed", function (done) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 4); + + Justin.setPetsAsync([]).then(function () { + + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("clears current associations", function (done) { + Pet.find({ name: "Deco" }, function (err, pets) { + var Deco = pets[0]; + + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Mutt"); + + Jane.setPetsAsync(Deco).then(function () { + + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal(Deco.name); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + describe("with autoFetch turned on", function () { before(setup({ autoFetchPets : true diff --git a/test/integration/association-hasone-reverse.js b/test/integration/association-hasone-reverse.js index 8c637924..dd0b8362 100644 --- a/test/integration/association-hasone-reverse.js +++ b/test/integration/association-hasone-reverse.js @@ -93,6 +93,32 @@ describe("hasOne", function () { }); }); + it("should work (promise-based)", function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + Pet.find({ name: "Deco" }).first(function (err, Deco) { + Deco.hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + Deco.setOwnersAsync(John).then(function () { + Deco.getOwnersAsync().then(function (JohnCopy) { + + should(Array.isArray(JohnCopy)); + John.should.eql(JohnCopy[0]); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + describe("Chain", function () { before(function (done) { var petParams = [ @@ -189,6 +215,47 @@ describe("hasOne", function () { }); }); + it("should be able to set an array of people as the owner (promise-based test)", function (done) { + Person.find({ name: ["John Doe", "Jane Doe"] }, function (err, owners) { + should.not.exist(err); + + Pet.find({ name: "Fido" }).first(function (err, Fido) { + should.not.exist(err); + + Fido.hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + Fido.setOwnersAsync(owners).then(function () { + + Fido.getOwnersAsync().then(function (ownersCopy) { + should(Array.isArray(owners)); + owners.length.should.equal(2); + + // Don't know which order they'll be in. + var idProp = common.protocol() == 'mongodb' ? '_id' : 'id' + + if (owners[0][idProp] == ownersCopy[0][idProp]) { + owners[0].should.eql(ownersCopy[0]); + owners[1].should.eql(ownersCopy[1]); + } else { + owners[0].should.eql(ownersCopy[1]); + owners[1].should.eql(ownersCopy[0]); + } + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + // broken in mongo if (common.protocol() != "mongodb") { describe("findBy()", function () { @@ -333,4 +400,102 @@ describe("hasOne", function () { }, 3, done); }); }); + + describe("reverse find (promise-based)", function () { + it("should be able to find given an association id", function (done) { + common.retry(setup(), function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + should.not.exist(err); + should.exist(John); + Pet.find({ name: "Deco" }).first(function (err, Deco) { + should.not.exist(err); + should.exist(Deco); + Deco.hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + Deco.setOwnersAsync(John).then(function () { + + Person.find({ pet_id: Deco[Pet.id[0]] }).first(function (err, owner) { + should.not.exist(err); + should.exist(owner); + should.equal(owner.name, John.name); + done(); + }); + + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }, 3, done); + }); + + it("should be able to find given an association instance", function (done) { + common.retry(setup(), function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + should.not.exist(err); + should.exist(John); + Pet.find({ name: "Deco" }).first(function (err, Deco) { + should.not.exist(err); + should.exist(Deco); + Deco.hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + Deco.setOwnersAsync(John).then(function () { + + Person.find({ pet: Deco }).first(function (err, owner) { + should.not.exist(err); + should.exist(owner); + should.equal(owner.name, John.name); + done(); + }); + + }).catch(function(err) { + done(err); + }); + }).done(function(err) { + done(err); + }); + }); + }); + }, 3, done); + }); + + it("should be able to find given a number of association instances with a single primary key", function (done) { + common.retry(setup(), function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + should.not.exist(err); + should.exist(John); + Pet.all(function (err, pets) { + should.not.exist(err); + should.exist(pets); + should.equal(pets.length, 2); + + pets[0].hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + pets[0].setOwnersAsync(John).then(function () { + + Person.find({ pet: pets }, function (err, owners) { + should.not.exist(err); + should.exist(owners); + owners.length.should.equal(1); + + should.equal(owners[0].name, John.name); + done(); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }, 3, done); + }); + }); }); diff --git a/test/integration/association-hasone-zeroid.js b/test/integration/association-hasone-zeroid.js index 6248e5f0..17296866 100644 --- a/test/integration/association-hasone-zeroid.js +++ b/test/integration/association-hasone-zeroid.js @@ -126,7 +126,36 @@ describe("hasOne", function() { }); }); - it("should work for zero ownerID ", function (done) { + it("should work for non-zero ownerID (promise-based)", function (done) { + Pet.find({petName: "Muttley"}, function(err, pets) { + should.not.exist(err); + + pets[0].petName.should.equal("Muttley"); + pets[0].should.have.property("id"); + pets[0].id.should.equal(10); + pets[0].ownerID.should.equal(12); + + pets[0].should.not.have.property("owner"); + + // But we should be able to see if its there + pets[0].hasOwnerAsync().then(function(result) { + should.equal(result, true); + + // ...and then get it + pets[0].getOwnerAsync().then(function(result) { + result.firstName.should.equal("Stuey"); + + done() + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should work for zero ownerID (promise-based)", function (done) { Pet.find({petName: "Snagglepuss"}, function(err, pets) { should.not.exist(err); @@ -138,17 +167,20 @@ describe("hasOne", function() { pets[0].should.not.have.property("owner"); // But we should be able to see if its there - pets[0].hasOwner(function(err, result) { - should.not.exist(err); + pets[0].hasOwnerAsync().then(function(result) { should.equal(result, true); // ...and then get it - pets[0].getOwner(function(err, result) { + pets[0].getOwnerAsync().then(function(result) { should.not.exist(err); result.firstName.should.equal("John"); - return done() + done() + }).catch(function(err) { + done(err); }); + }).catch(function(err) { + done(err); }); }); }); diff --git a/test/integration/association-hasone.js b/test/integration/association-hasone.js index bc8a5a91..75455bf1 100644 --- a/test/integration/association-hasone.js +++ b/test/integration/association-hasone.js @@ -468,4 +468,187 @@ describe("hasOne", function() { }); }); }; + + describe("accessors (promise-based)", function () { + before(setup()); + + it("get should get the association", function (done) { + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.exist(leaf); + leaf.getTreeAsync().then(function (tree) { + should.not.exist(err); + should.exist(tree); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should return proper instance model", function (done) { + Leaf.one({ size: 14 }, function (err, leaf) { + leaf.getTreeAsync().then(function (tree) { + tree.model().should.equal(Tree); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("get should get the association with a shell model", function (done) { + Leaf(leafId).getTreeAsync().then(function (tree) { + should.exist(tree); + should.equal(tree[Tree.id], treeId); + done(); + }).catch(function(err) { + done(err); + }); + }); + + it("has should indicate if there is an association present", function (done) { + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.exist(leaf); + + leaf.hasTreeAsync().then(function (has) { + should.equal(has, true); + + leaf.hasStalkAsync().then(function (has) { + should.equal(has, false); + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("set should associate another instance", function (done) { + Stalk.one({ length: 20 }, function (err, stalk) { + should.not.exist(err); + should.exist(stalk); + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.exist(leaf); + should.not.exist(leaf.stalkId); + leaf.setStalkAsync(stalk).then(function () { + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.equal(leaf.stalkId, stalk[Stalk.id]); + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("remove should unassociation another instance", function (done) { + Stalk.one({ length: 20 }, function (err, stalk) { + should.not.exist(err); + should.exist(stalk); + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.exist(leaf); + should.exist(leaf.stalkId); + leaf.removeStalkAsync(function () { + Leaf.one({ size: 14 }, function (err, leaf) { + should.not.exist(err); + should.equal(leaf.stalkId, null); + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("if not passing another Model (promise-based test)", function () { + it("should use same model", function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + + var Person = db.define("person", { + name : String + }); + Person.hasOne("parent", { + autoFetch : true + }); + + helper.dropSync(Person, function () { + var child = new Person({ + name : "Child" + }); + child.setParentAsync(new Person({ name: "Parent" })).then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + if (protocol != "mongodb") { + describe("mapsTo (promise-based tests)", function () { + describe("with `mapsTo` set via `hasOne`", function () { + var leaf = null; + + before(setup()); + + before(function (done) { + Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) { + should.not.exist(err); + leaf = lf; + done(); + }); + }); + + it("should get parent", function (done) { + leaf.getStalkAsync().then(function (stalk) { + + should.exist(stalk); + should.equal(stalk.id, stalkId); + should.equal(stalk.length, 20); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + describe("with `mapsTo` set via property definition", function () { + var leaf = null; + + before(setup()); + + before(function (done) { + Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) { + should.not.exist(err); + leaf = lf; + done(); + }); + }); + + it("should get parent", function (done) { + leaf.getHoleAsync().then(function (hole) { + + should.exist(hole); + should.equal(hole.id, stalkId); + should.equal(hole.width, 3); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }; + }); From 8d4958241371778dd816880a59ee03b0a902895d Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Fri, 1 Sep 2017 11:04:08 +0300 Subject: [PATCH 23/98] Fixed indentation in Extend.js file --- lib/Associations/Extend.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index e80b425c..51eae7ac 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -11,18 +11,18 @@ exports.prepare = function (db, Model, associations) { var assocName = opts.name || ucfirst(name); var association = { - name : name, - table : opts.table || (Model.table + '_' + name), - reversed : opts.reversed, - autoFetch : opts.autoFetch || false, - autoFetchLimit : opts.autoFetchLimit || 2, - field : util.wrapFieldObject({ - field: opts.field, model: Model, altName: Model.table - }) || util.formatField(Model, Model.table, false, false), - getAccessor : opts.getAccessor || ("get" + assocName), - setAccessor : opts.setAccessor || ("set" + assocName), - hasAccessor : opts.hasAccessor || ("has" + assocName), - delAccessor : opts.delAccessor || ("remove" + assocName) + name : name, + table : opts.table || (Model.table + '_' + name), + reversed : opts.reversed, + autoFetch : opts.autoFetch || false, + autoFetchLimit : opts.autoFetchLimit || 2, + field : util.wrapFieldObject({ + field: opts.field, model: Model, altName: Model.table + }) || util.formatField(Model, Model.table, false, false), + getAccessor : opts.getAccessor || ("get" + assocName), + setAccessor : opts.setAccessor || ("set" + assocName), + hasAccessor : opts.hasAccessor || ("has" + assocName), + delAccessor : opts.delAccessor || ("remove" + assocName) }; var newproperties = _.cloneDeep(properties); From 60ff735ffe0747a419a1463a61a048718e380878 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 11:44:24 +0300 Subject: [PATCH 24/98] apply Promise.promisify for callbacks transformation, replace tests for this. --- lib/ORM.js | 46 +++++------------------------------------- test/integration/db.js | 14 ++++--------- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index 85a230ee..d7dc5e84 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -153,18 +153,7 @@ exports.use = function (connection, proto, opts, cb) { * @param opts */ exports.connectAsync = function (opts) { - var options = _.cloneDeep(opts); - return new Promise(function (resolve, reject) { - var cb = function (resolve, reject) { - return function (err, data) { - if (err) { - return reject(err); - } - return resolve(data); - } - }; - connect(options, cb(resolve, reject)); - }); + return Promise.promisify(connect)(opts); }; exports.addAdapter = adapters.add; @@ -279,23 +268,11 @@ ORM.prototype.defineType = function (name, opts) { }; ORM.prototype.pingAsync = function () { - const self = this; - return new Promise(function (resolve, reject) { - self.driver.ping(function (err, data) { - if (err) return reject(err); - return resolve(data); - }); - }); + return Promise.promisify(this.ping, { context: this })(); }; ORM.prototype.closeAsync = function () { - const self = this; - return new Promise(function (resolve, reject) { - self.driver.close(function (err, data) { - if (err) return reject(err); - return resolve(data); - }); - }); + return Promise.promisify(this.driver.close, { context: this })(); }; ORM.prototype.ping = function (cb) { @@ -342,15 +319,7 @@ ORM.prototype.syncPromise = function () { var self = this; var modelIds = Object.keys(this.models); return Promise.each(modelIds, function (id) { - return new Promise(function (resolve, reject) { - self.models[id].sync(function (err) { - if (err) { - err.model = id; - return reject(err); - } - return resolve(id); - }); - }); + return Promise.promisify(self.models[id].sync, { context: self })(); }) }; @@ -387,12 +356,7 @@ ORM.prototype.dropAsync = function () { var self = this; var modelIds = Object.keys(this.models); return Promise.each(modelIds, function (id) { - return new Promise(function (resolve, reject) { - self.models[id].drop(function (err, data) { - if (err) return reject(err); - return resolve(data); - }); - }); + return Promise.promisify(self.models[id].drop, { context: self })(); }) }; diff --git a/test/integration/db.js b/test/integration/db.js index ed5e3e78..14bbb8cf 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -19,14 +19,12 @@ describe('DB', function () { }); describe('db.syncPromise()', function () { - it('should call sync method from model, and return array with model id', function (done) { + it('should return array with model id', function (done) { db.define("my_model", { property: String }); - var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { cb(null, {}) }); - db.dropAsync() + db.syncPromise() .then(function (data) { - syncStub.calledOnce.should.be.true; data.should.be.Array; should.equal(data[0], 'my_model'); done(); @@ -34,11 +32,10 @@ describe('DB', function () { .catch(function (err) { done(err) }); - syncStub.restore(); }); it('should return an empty array when no model is created', function (done) { - db.dropAsync() + db.syncPromise() .then(function (data) { data.should.be.Array; should.equal(_.isEmpty(data), true); @@ -51,14 +48,12 @@ describe('DB', function () { }); describe("db.dropAsync()", function () { - it('should call the model drop method and return an array with a model id', function (done) { + it('should return an array with a model id', function (done) { db.define("my_model", { property: String }); - var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { cb(null, {}) }); db.dropAsync() .then(function (data) { - dropStub.calledOnce.should.be.true; data.should.be.Array; should.equal(data[0], 'my_model'); done(); @@ -66,7 +61,6 @@ describe('DB', function () { .catch(function (err) { done(err) }); - dropStub.restore(); }); it('should return an empty array when no model created', function (done) { From d03fe82caa647a3435c5ff574d9b6bf5ac5b54ff Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 13:03:34 +0300 Subject: [PATCH 25/98] closeAsync and pingAsync generates directly from ORM.prototype --- lib/ORM.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index d7dc5e84..bee24dca 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -267,24 +267,22 @@ ORM.prototype.defineType = function (name, opts) { return this; }; -ORM.prototype.pingAsync = function () { - return Promise.promisify(this.ping, { context: this })(); -}; - -ORM.prototype.closeAsync = function () { - return Promise.promisify(this.driver.close, { context: this })(); -}; - ORM.prototype.ping = function (cb) { this.driver.ping(cb); return this; }; + +ORM.prototype.pingAsync = Promise.promisify(ORM.prototype.ping); + ORM.prototype.close = function (cb) { this.driver.close(cb); return this; }; + +ORM.prototype.closeAsync = Promise.promisify(ORM.prototype.close); + ORM.prototype.load = function () { var files = _.flatten(Array.prototype.slice.apply(arguments)); var self = this; From b8a7f9bcdf86f04900846d3e7c1a764f476d1cf8 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 14:34:26 +0300 Subject: [PATCH 26/98] fix after review --- lib/ORM.js | 36 ++++++++++++----------------- test/integration/db.js | 52 ++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index bee24dca..5cee3f1c 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -16,6 +16,8 @@ var Settings = require("./Settings"); var Singleton = require("./Singleton"); var Utilities = require("./Utilities"); +var OPTS_TYPE_STRING = 'string'; +var OPTS_TYPE_OBJ = 'object'; // Deprecated, use enforce exports.validators = require("./Validators"); @@ -33,16 +35,21 @@ exports.Property = require("./Property"); exports.Settings = Settings; exports.ErrorCodes = ORMError.codes; +var optsChecker = function (opts) { + return [OPTS_TYPE_STRING, OPTS_TYPE_OBJ].some(function (element) { return typeof(opts) === element }) +}; + var connect = function (opts, cb) { - if (arguments.length === 0 || !opts) { + if (arguments.length === 0 || !opts || !optsChecker(opts)) { + cb = typeof(cb) !== 'function' ? opts : cb; return ORM_Error(new ORMError("CONNECTION_URL_EMPTY", 'PARAM_MISMATCH'), cb); } - if (typeof opts == 'string') { + if (typeof opts === 'string') { if (opts.trim().length === 0) { return ORM_Error(new ORMError("CONNECTION_URL_EMPTY", 'PARAM_MISMATCH'), cb); } opts = url.parse(opts, true); - } else if (typeof opts == 'object') { + } else if (typeof opts === 'object') { opts = _.cloneDeep(opts); } @@ -152,9 +159,7 @@ exports.use = function (connection, proto, opts, cb) { * * @param opts */ -exports.connectAsync = function (opts) { - return Promise.promisify(connect)(opts); -}; +exports.connectAsync = Promise.promisify(connect); exports.addAdapter = adapters.add; @@ -313,14 +318,6 @@ ORM.prototype.load = function () { async.eachSeries(filesWithPath, iterator, cb); }; -ORM.prototype.syncPromise = function () { - var self = this; - var modelIds = Object.keys(this.models); - return Promise.each(modelIds, function (id) { - return Promise.promisify(self.models[id].sync, { context: self })(); - }) -}; - ORM.prototype.sync = function (cb) { var modelIds = Object.keys(this.models); var syncNext = function () { @@ -350,13 +347,7 @@ ORM.prototype.sync = function (cb) { return this; }; -ORM.prototype.dropAsync = function () { - var self = this; - var modelIds = Object.keys(this.models); - return Promise.each(modelIds, function (id) { - return Promise.promisify(self.models[id].drop, { context: self })(); - }) -}; +ORM.prototype.syncPromise = Promise.promisify(ORM.prototype.sync); ORM.prototype.drop = function (cb) { var modelIds = Object.keys(this.models); @@ -386,6 +377,9 @@ ORM.prototype.drop = function (cb) { return this; }; + +ORM.prototype.dropAsync = Promise.promisify(ORM.prototype.drop); + ORM.prototype.serial = function () { var chains = Array.prototype.slice.apply(arguments); diff --git a/test/integration/db.js b/test/integration/db.js index 14bbb8cf..597907d8 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -19,61 +19,49 @@ describe('DB', function () { }); describe('db.syncPromise()', function () { - it('should return array with model id', function (done) { + it('should call sync for each model', function (done) { db.define("my_model", { property: String }); + db.define("my_model2", { + property: String + }); + var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { cb(null, {}) }); + var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { cb(null, {}) }); db.syncPromise() - .then(function (data) { - data.should.be.Array; - should.equal(data[0], 'my_model'); + .then(function () { + should.equal(syncStub.calledOnce, true); + should.equal(syncStub2.calledOnce, true); done(); }) .catch(function (err) { done(err) }); }); - - it('should return an empty array when no model is created', function (done) { - db.syncPromise() - .then(function (data) { - data.should.be.Array; - should.equal(_.isEmpty(data), true); - done() - }) - .catch(function (err) { - done(err) - }) - }); }); describe("db.dropAsync()", function () { - it('should return an array with a model id', function (done) { + it('should should call drop for each model', function (done) { db.define("my_model", { property: String }); + + db.define("my_model2", { + property: String + }); + + var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { cb(null, {}) }); + var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { cb(null, {}) }); db.dropAsync() - .then(function (data) { - data.should.be.Array; - should.equal(data[0], 'my_model'); + .then(function () { + should.equal(dropStub.calledOnce, true); + should.equal(dropStub2.calledOnce, true); done(); }) .catch(function (err) { done(err) }); }); - - it('should return an empty array when no model created', function (done) { - db.dropAsync() - .then(function (data) { - data.should.be.Array; - should.equal(_.isEmpty(data), true); - done() - }) - .catch(function (err) { - done(err) - }) - }); }); describe("db.use()", function () { From 9bfd7c8074511e86637fb5a075e4c1224ab2a65b Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 17:18:26 +0300 Subject: [PATCH 27/98] create eagerQueryAsync, execQueryAsync -> return a Promise --- lib/Drivers/DML/_shared.js | 47 ++++++++++++++++++++++++++++++++++++- lib/Drivers/DML/mysql.js | 21 +++++++++++++++++ lib/Drivers/DML/postgres.js | 18 ++++++++++++++ lib/Drivers/DML/sqlite.js | 14 +++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/lib/Drivers/DML/_shared.js b/lib/Drivers/DML/_shared.js index 9875597e..1c508e7e 100644 --- a/lib/Drivers/DML/_shared.js +++ b/lib/Drivers/DML/_shared.js @@ -1,3 +1,21 @@ +var Promise = require('bluebird'); + +var build = function (association, opts, keys) { + var desiredKey = Object.keys(association.field); + var assocKey = Object.keys(association.mergeAssocId); + + var where = {}; + where[desiredKey] = keys; + + var query = this.query.select() + .from(association.model.table) + .select(opts.only) + .from(association.mergeTable, assocKey, opts.keys) + .select(desiredKey).as("$p") + .where(association.mergeTable, where) + .build(); + return query; +}; module.exports = { execQuery: function () { @@ -10,7 +28,18 @@ module.exports = { } return this.execSimpleQuery(query, cb); }, - eagerQuery: function (association, opts, keys, cb) { + + execQueryAsync: function () { + var args = arguments; + if (args.length === 1) { + var query = args[0]; + } else if (args.length === 2) { + var query = this.query.escape(args[0], args[1]); + } + return this.execSimpleQueryAsync(query); + }, + + eagerQueryAsync: function (association, opts, keys) { var desiredKey = Object.keys(association.field); var assocKey = Object.keys(association.mergeAssocId); @@ -24,7 +53,23 @@ module.exports = { .select(desiredKey).as("$p") .where(association.mergeTable, where) .build(); + return this.execSimpleQueryAsync(query); + }, + + eagerQuery: function (association, opts, keys, cb) { + var desiredKey = Object.keys(association.field); + var assocKey = Object.keys(association.mergeAssocId); + + var where = {}; + where[desiredKey] = keys; + var query = this.query.select() + .from(association.model.table) + .select(opts.only) + .from(association.mergeTable, assocKey, opts.keys) + .select(desiredKey).as("$p") + .where(association.mergeTable, where) + .build(); this.execSimpleQuery(query, cb); } }; diff --git a/lib/Drivers/DML/mysql.js b/lib/Drivers/DML/mysql.js index ff4c801b..8719f345 100644 --- a/lib/Drivers/DML/mysql.js +++ b/lib/Drivers/DML/mysql.js @@ -3,6 +3,7 @@ var mysql = require("mysql"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); +var Promise = require("bluebird"); exports.Driver = Driver; @@ -92,7 +93,27 @@ Driver.prototype.getQuery = function () { return this.query; }; +Driver.prototype.execSimpleQueryAsync = function (query) { + const cb = function (resolve, reject) { + return function (err, data) { + if (err) reject(err); + resolve(data); + } + }; + return new Promise(function (resolve, reject) { + if (this.opts.debug) { + require("../../Debug").sql('mysql', query); + } + if (this.opts.pool) { + this.poolQuery(query, cb(resolve, reject)); + } else { + this.db.query(query, cb(resolve, reject)); + } + }); +}; + Driver.prototype.execSimpleQuery = function (query, cb) { + if (this.opts.debug) { require("../../Debug").sql('mysql', query); } diff --git a/lib/Drivers/DML/postgres.js b/lib/Drivers/DML/postgres.js index 26bd07fc..ad0b26cb 100644 --- a/lib/Drivers/DML/postgres.js +++ b/lib/Drivers/DML/postgres.js @@ -3,6 +3,7 @@ var pg = require("pg"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); +var Promise = require("bluebird"); exports.Driver = Driver; @@ -16,6 +17,23 @@ var switchableFunctions = { cb(err); }); }, + execSimpleQueryAsync: function (query) { + return new Promise(function (resolve, reject) { + if (this.opts.debug) { + require("../../Debug").sql('postgres', query); + } + this.db.connect(this.config, function (err, client, done) { + if (err) return reject(err); + + client.query(query, function (err, result) { + done(); + + if (err) reject(err); + resolve(result.rows); + }); + }); + }); + }, execSimpleQuery: function (query, cb) { if (this.opts.debug) { require("../../Debug").sql('postgres', query); diff --git a/lib/Drivers/DML/sqlite.js b/lib/Drivers/DML/sqlite.js index 67191439..8569fa28 100644 --- a/lib/Drivers/DML/sqlite.js +++ b/lib/Drivers/DML/sqlite.js @@ -4,6 +4,7 @@ var sqlite3 = require("sqlite3"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); +var Promise = require("bluebird"); exports.Driver = Driver; @@ -67,6 +68,19 @@ Driver.prototype.getQuery = function () { return this.query; }; +Driver.prototype.execSimpleQueryAsync = function (query) { + return new Promise(function (resolve, reject) { + if (this.opts.debug) { + require("../../Debug").sql('sqlite', query); + } + this.db.all(query, function (err, data) { + if (err) reject(err); + + resolve(data); + }); + }); +}; + Driver.prototype.execSimpleQuery = function (query, cb) { if (this.opts.debug) { require("../../Debug").sql('sqlite', query); From 9a9d771391244951d9297df46665710b41185bdb Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 23 Aug 2017 15:55:31 +0300 Subject: [PATCH 28/98] remove code , change if statement in execQueryAsync --- lib/Drivers/DML/_shared.js | 41 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/lib/Drivers/DML/_shared.js b/lib/Drivers/DML/_shared.js index 1c508e7e..bf8526a9 100644 --- a/lib/Drivers/DML/_shared.js +++ b/lib/Drivers/DML/_shared.js @@ -30,46 +30,25 @@ module.exports = { }, execQueryAsync: function () { - var args = arguments; - if (args.length === 1) { - var query = args[0]; - } else if (args.length === 2) { - var query = this.query.escape(args[0], args[1]); + var query = null; + if (arguments.length === 1) { + query = arguments[0]; + } else if (arguments.length === 2) { + query = this.query.escape(arguments[0], arguments[1]); } + + if (!query) throw new Error('No query provided.'); + return this.execSimpleQueryAsync(query); }, eagerQueryAsync: function (association, opts, keys) { - var desiredKey = Object.keys(association.field); - var assocKey = Object.keys(association.mergeAssocId); - - var where = {}; - where[desiredKey] = keys; - - var query = this.query.select() - .from(association.model.table) - .select(opts.only) - .from(association.mergeTable, assocKey, opts.keys) - .select(desiredKey).as("$p") - .where(association.mergeTable, where) - .build(); + var query = build.apply(this, [association, opts, keys]); return this.execSimpleQueryAsync(query); }, eagerQuery: function (association, opts, keys, cb) { - var desiredKey = Object.keys(association.field); - var assocKey = Object.keys(association.mergeAssocId); - - var where = {}; - where[desiredKey] = keys; - - var query = this.query.select() - .from(association.model.table) - .select(opts.only) - .from(association.mergeTable, assocKey, opts.keys) - .select(desiredKey).as("$p") - .where(association.mergeTable, where) - .build(); + var query = build.apply(this, [association, opts, keys]); this.execSimpleQuery(query, cb); } }; From 2547a14bb9da2a769a7e9e62cfde7b72718fefa5 Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 23 Aug 2017 15:59:09 +0300 Subject: [PATCH 29/98] remove Promise variable, align Promise declaration --- lib/Drivers/DML/_shared.js | 2 -- lib/Drivers/DML/sqlite.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Drivers/DML/_shared.js b/lib/Drivers/DML/_shared.js index bf8526a9..1dea9bb0 100644 --- a/lib/Drivers/DML/_shared.js +++ b/lib/Drivers/DML/_shared.js @@ -1,5 +1,3 @@ -var Promise = require('bluebird'); - var build = function (association, opts, keys) { var desiredKey = Object.keys(association.field); var assocKey = Object.keys(association.mergeAssocId); diff --git a/lib/Drivers/DML/sqlite.js b/lib/Drivers/DML/sqlite.js index 8569fa28..8de6d6c5 100644 --- a/lib/Drivers/DML/sqlite.js +++ b/lib/Drivers/DML/sqlite.js @@ -4,7 +4,7 @@ var sqlite3 = require("sqlite3"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); -var Promise = require("bluebird"); +var Promise = require("bluebird"); exports.Driver = Driver; From 6ced67033690dd0b7fe04d5c3eb3e7f2fd10f9fb Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 14:57:21 +0300 Subject: [PATCH 30/98] transform callbacks use promisify --- lib/Drivers/DML/_shared.js | 58 ++++++++++++++++--------------------- lib/Drivers/DML/mysql.js | 19 ------------ lib/Drivers/DML/postgres.js | 17 ----------- lib/Drivers/DML/sqlite.js | 13 --------- 4 files changed, 25 insertions(+), 82 deletions(-) diff --git a/lib/Drivers/DML/_shared.js b/lib/Drivers/DML/_shared.js index 1dea9bb0..3fdb4d3b 100644 --- a/lib/Drivers/DML/_shared.js +++ b/lib/Drivers/DML/_shared.js @@ -1,3 +1,5 @@ +var Promise = require('bluebird'); + var build = function (association, opts, keys) { var desiredKey = Object.keys(association.field); var assocKey = Object.keys(association.mergeAssocId); @@ -15,38 +17,28 @@ var build = function (association, opts, keys) { return query; }; -module.exports = { - execQuery: function () { - if (arguments.length == 2) { - var query = arguments[0]; - var cb = arguments[1]; - } else if (arguments.length == 3) { - var query = this.query.escape(arguments[0], arguments[1]); - var cb = arguments[2]; - } - return this.execSimpleQuery(query, cb); - }, - - execQueryAsync: function () { - var query = null; - if (arguments.length === 1) { - query = arguments[0]; - } else if (arguments.length === 2) { - query = this.query.escape(arguments[0], arguments[1]); - } - - if (!query) throw new Error('No query provided.'); - - return this.execSimpleQueryAsync(query); - }, - - eagerQueryAsync: function (association, opts, keys) { - var query = build.apply(this, [association, opts, keys]); - return this.execSimpleQueryAsync(query); - }, - - eagerQuery: function (association, opts, keys, cb) { - var query = build.apply(this, [association, opts, keys]); - this.execSimpleQuery(query, cb); +var execQuery = function () { + if (arguments.length == 2) { + var query = arguments[0]; + var cb = arguments[1]; + } else if (arguments.length == 3) { + var query = this.query.escape(arguments[0], arguments[1]); + var cb = arguments[2]; } + return this.execSimpleQuery(query, cb); +}; + +var eagerQuery = function (association, opts, keys, cb) { + var query = build.apply(this, [association, opts, keys]); + this.execSimpleQuery(query, cb); +}; + +module.exports = { + execQuery: execQuery, + + eagerQuery: eagerQuery, + + execQueryAsync: Promise.promisify(execQuery), + + eagerQueryAsync: Promise.promisify(eagerQuery) }; diff --git a/lib/Drivers/DML/mysql.js b/lib/Drivers/DML/mysql.js index 8719f345..fbde3671 100644 --- a/lib/Drivers/DML/mysql.js +++ b/lib/Drivers/DML/mysql.js @@ -93,25 +93,6 @@ Driver.prototype.getQuery = function () { return this.query; }; -Driver.prototype.execSimpleQueryAsync = function (query) { - const cb = function (resolve, reject) { - return function (err, data) { - if (err) reject(err); - resolve(data); - } - }; - return new Promise(function (resolve, reject) { - if (this.opts.debug) { - require("../../Debug").sql('mysql', query); - } - if (this.opts.pool) { - this.poolQuery(query, cb(resolve, reject)); - } else { - this.db.query(query, cb(resolve, reject)); - } - }); -}; - Driver.prototype.execSimpleQuery = function (query, cb) { if (this.opts.debug) { diff --git a/lib/Drivers/DML/postgres.js b/lib/Drivers/DML/postgres.js index ad0b26cb..5df316ef 100644 --- a/lib/Drivers/DML/postgres.js +++ b/lib/Drivers/DML/postgres.js @@ -17,23 +17,6 @@ var switchableFunctions = { cb(err); }); }, - execSimpleQueryAsync: function (query) { - return new Promise(function (resolve, reject) { - if (this.opts.debug) { - require("../../Debug").sql('postgres', query); - } - this.db.connect(this.config, function (err, client, done) { - if (err) return reject(err); - - client.query(query, function (err, result) { - done(); - - if (err) reject(err); - resolve(result.rows); - }); - }); - }); - }, execSimpleQuery: function (query, cb) { if (this.opts.debug) { require("../../Debug").sql('postgres', query); diff --git a/lib/Drivers/DML/sqlite.js b/lib/Drivers/DML/sqlite.js index 8de6d6c5..f36220de 100644 --- a/lib/Drivers/DML/sqlite.js +++ b/lib/Drivers/DML/sqlite.js @@ -68,19 +68,6 @@ Driver.prototype.getQuery = function () { return this.query; }; -Driver.prototype.execSimpleQueryAsync = function (query) { - return new Promise(function (resolve, reject) { - if (this.opts.debug) { - require("../../Debug").sql('sqlite', query); - } - this.db.all(query, function (err, data) { - if (err) reject(err); - - resolve(data); - }); - }); -}; - Driver.prototype.execSimpleQuery = function (query, cb) { if (this.opts.debug) { require("../../Debug").sql('sqlite', query); From ad485a2ed8ddf07eb78f629c3e6779415932bca0 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 14:58:47 +0300 Subject: [PATCH 31/98] remove unnecessary variables --- lib/Drivers/DML/mysql.js | 1 - lib/Drivers/DML/postgres.js | 1 - test/integration/db.js | 1 - 3 files changed, 3 deletions(-) diff --git a/lib/Drivers/DML/mysql.js b/lib/Drivers/DML/mysql.js index fbde3671..095b3f40 100644 --- a/lib/Drivers/DML/mysql.js +++ b/lib/Drivers/DML/mysql.js @@ -3,7 +3,6 @@ var mysql = require("mysql"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); -var Promise = require("bluebird"); exports.Driver = Driver; diff --git a/lib/Drivers/DML/postgres.js b/lib/Drivers/DML/postgres.js index 5df316ef..26bd07fc 100644 --- a/lib/Drivers/DML/postgres.js +++ b/lib/Drivers/DML/postgres.js @@ -3,7 +3,6 @@ var pg = require("pg"); var Query = require("sql-query").Query; var shared = require("./_shared"); var DDL = require("../DDL/SQL"); -var Promise = require("bluebird"); exports.Driver = Driver; diff --git a/test/integration/db.js b/test/integration/db.js index 597907d8..a29b5bff 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -2,7 +2,6 @@ var should = require('should'); var helper = require('../support/spec_helper'); var sinon = require('sinon'); var common = require('../common'); -var _ = require('lodash'); describe('DB', function () { var db = null; From 377b1cf0d104f85be41b65b248b9f69c2c48165e Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 4 Sep 2017 15:44:38 +0300 Subject: [PATCH 32/98] Promisify support in save, validate, remove --- lib/Instance.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/Instance.js b/lib/Instance.js index 0858f34b..42983a1a 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -15,6 +15,22 @@ function Instance(Model, opts) { opts.associations = {}; opts.originalKeyValues = {}; + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); + var instanceFuncNames = { + save: { + callback: 'save', + promise: 'save' + promiseFunctionPostfix + }, + remove: { + callback: 'save', + promise: 'save' + promiseFunctionPostfix + }, + validate: { + callback: 'save', + promise: 'save' + promiseFunctionPostfix + } + } + var instance_saving = false; var events = {}; var instance = {}; @@ -701,6 +717,22 @@ function Instance(Model, opts) { enumerable: false }); + Object.defineProperty(instance, instanceFuncNames.save.promise, { + value: Promise.promisify(instanceFuncNames.save.callback), + enumerable: false, + writable: true + }); + Object.defineProperty(instance, instanceFuncNames.remove.promise, { + value: Promise.promisify(instanceFuncNames.remove.callback), + enumerable: false, + writable: true + }); + Object.defineProperty(instance, instanceFuncNames.validate.promise, { + value: Promise.promisify(instanceFuncNames.validate.callback), + enumerable: false, + writable: true + }); + for (i = 0; i < opts.keyProperties.length; i++) { var prop = opts.keyProperties[i]; From f050b08c4966e48766819918adcdb8c045e1f681 Mon Sep 17 00:00:00 2001 From: Yevheniy Miropolets Date: Mon, 4 Sep 2017 16:04:57 +0300 Subject: [PATCH 33/98] Add Promise support for get aggregation. Add tests for getAsync. --- lib/AggregateFunctions.js | 3 + test/config.example.js | 4 +- test/integration/model-aggregate.js | 208 ++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 2 deletions(-) diff --git a/lib/AggregateFunctions.js b/lib/AggregateFunctions.js index 6a54d880..664945c2 100644 --- a/lib/AggregateFunctions.js +++ b/lib/AggregateFunctions.js @@ -1,5 +1,6 @@ var ORMError = require("./Error"); var Utilities = require("./Utilities"); +var Promise = require('bluebird'); module.exports = AggregateFunctions; @@ -155,6 +156,8 @@ function AggregateFunctions(opts) { } }; + proto.getAsync = Promise.promisify(proto.get); + for (var i = 0; i < opts.driver.aggregate_functions.length; i++) { addAggregate(proto, opts.driver.aggregate_functions[i], appendFunction); } diff --git a/test/config.example.js b/test/config.example.js index bb9c2d73..0d9c232b 100644 --- a/test/config.example.js +++ b/test/config.example.js @@ -13,8 +13,8 @@ exports.mysql = { database : "test" }; exports.postgres = { - user : "root", - password : "", + user : "postgres", + password : "1111", database : "test" }; exports.redshift = { diff --git a/test/integration/model-aggregate.js b/test/integration/model-aggregate.js index 2f45d912..5a10fc61 100644 --- a/test/integration/model-aggregate.js +++ b/test/integration/model-aggregate.js @@ -95,6 +95,49 @@ describe("Model.aggregate()", function() { }); }); + describe("with call() (using getAsync)", function () { + before(setup()); + + it("should accept a function", function (done) { + Person.aggregate().call('COUNT').getAsync() + .then(function (count) { + count.should.equal(3); + return done(); + }) + .catch(function(err) { + done(err); + }) + }); + + it("should accept arguments to the function as an Array", function (done) { + Person.aggregate().call('COUNT', [ 'id' ]).getAsync() + .then(function (count) { + count.should.equal(3); + return done(); + }) + .catch(function(err) { + done(err); + }); + }); + + describe("if function is DISTINCT", function () { + it("should work as calling .distinct() directly", function (done) { + Person.aggregate().call('DISTINCT', [ 'name' ]).as('name').order('name').getAsync() + .then(function (rows) { + should(Array.isArray(rows)); + rows.length.should.equal(2); + + rows[0].should.equal('Jane Doe'); + rows[1].should.equal('John Doe'); + + return done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + describe("with as() without previous aggregates", function () { before(setup()); @@ -149,6 +192,46 @@ describe("Model.aggregate()", function() { }); }); + describe("with select() with arguments (using getAsync)", function () { + before(setup()); + + it("should use them as properties if 1st argument is Array", function (done) { + Person.aggregate().select([ 'id' ]).count('id').groupBy('id').getAsync() + .then(function (people) { + + should(Array.isArray(people)); + people.length.should.be.above(0); + + people[0].should.be.a.Object(); + people[0].should.have.property("id"); + people[0].should.not.have.property("name"); + + return done(); + }) + .catch(function(err) { + done(err); + }); + }); + + it("should use them as properties", function (done) { + Person.aggregate().select('id').count().groupBy('id').getAsync() + .then(function (people) { + + should(Array.isArray(people)); + people.length.should.be.above(0); + + people[0].should.be.a.Object(); + people[0].should.have.property("id"); + people[0].should.not.have.property("name"); + + return done(); + }) + .catch(function(err) { + done(err); + }); + }); + }); + describe("with get() without callback", function () { before(setup()); @@ -171,6 +254,17 @@ describe("Model.aggregate()", function() { }); }); + describe("with getAsync() without aggregates", function () { + before(setup()); + + it("should reject", function (done) { + Person.aggregate().getAsync().catch(function(err) { + should.ok(err); + done(); + }); + }); + }); + describe("with distinct()", function () { before(setup()); @@ -214,6 +308,56 @@ describe("Model.aggregate()", function() { }); }); + describe("with distinct() (using getAsync)", function () { + before(setup()); + + it("should return a list of distinct properties", function (done) { + Person.aggregate().distinct('name').getAsync() + .then(function (names) { + + names.should.be.a.Object(); + names.should.have.property("length", 2); + + return done(); + }) + .catch(function(err) { + done(err); + }); + }); + + describe("with limit(1)", function () { + it("should return only one value", function (done) { + Person.aggregate().distinct('name').limit(1).order("name").getAsync() + .then(function (names) { + names.should.be.a.Object(); + names.should.have.property("length", 1); + names[0].should.equal("Jane Doe"); + + return done(); + }) + .catch(function(err) { + done(err); + }) + }); + }); + + describe("with limit(1, 1)", function () { + it("should return only one value", function (done) { + Person.aggregate().distinct('name').limit(1, 1).order("name").getAsync() + .then(function (names) { + names.should.be.a.Object(); + names.should.have.property("length", 1); + names[0].should.equal("John Doe"); + + return done(); + }) + .catch(function(err) { + done(err); + }) + }); + }); + }); + describe("with groupBy()", function () { before(setup()); @@ -249,6 +393,47 @@ describe("Model.aggregate()", function() { }); }); + describe("with groupBy() (using getAsync)", function () { + before(setup()); + + it("should return items grouped by property", function (done) { + Person.aggregate().count().groupBy('name').getAsync() + .then(function (rows) { + + rows.should.be.a.Object(); + rows.should.have.property("length", 2); + + (rows[0].count + rows[1].count).should.equal(3); // 1 + 2 + + return done(); + }) + .catch(function(err) { + done(err); + }); + }); + + describe("with order()", function () { + before(setup()); + + it("should order items", function (done) { + Person.aggregate().count().groupBy('name').order('-count').getAsync() + .then(function (rows) { + + rows.should.be.a.Object(); + rows.should.have.property("length", 2); + + rows[0].count.should.equal(2); + rows[1].count.should.equal(1); + + return done(); + }) + .catch(function(err) { + done(err); + }) + }); + }); + }); + describe("using as()", function () { before(setup()); @@ -274,4 +459,27 @@ describe("Model.aggregate()", function() { return done(); }); }); + + describe("using as() (with getAsync)", function () { + before(setup()); + + it("should use as an alias", function (done) { + Person.aggregate().count().as('total').groupBy('name').getAsync() + .then(function (people) { + + should(Array.isArray(people)); + people.length.should.be.above(0); + + people[0].should.be.a.Object(); + people[0].should.have.property("total"); + + return done(); + }) + .catch(function (err) { + done(err); + }) + }); + + }); + }); From d1237feb42a1a3c2e29b28ce7d754298f70895de Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 4 Sep 2017 16:06:39 +0300 Subject: [PATCH 34/98] add tests --- lib/Drivers/DML/_shared.js | 28 ++++----- test/integration/db.js | 119 +++++++++++++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 20 deletions(-) diff --git a/lib/Drivers/DML/_shared.js b/lib/Drivers/DML/_shared.js index 3fdb4d3b..e12a0055 100644 --- a/lib/Drivers/DML/_shared.js +++ b/lib/Drivers/DML/_shared.js @@ -1,6 +1,17 @@ var Promise = require('bluebird'); -var build = function (association, opts, keys) { +var execQuery = function () { + if (arguments.length == 2) { + var query = arguments[0]; + var cb = arguments[1]; + } else if (arguments.length == 3) { + var query = this.query.escape(arguments[0], arguments[1]); + var cb = arguments[2]; + } + return this.execSimpleQuery(query, cb); +}; + +var eagerQuery = function (association, opts, keys, cb) { var desiredKey = Object.keys(association.field); var assocKey = Object.keys(association.mergeAssocId); @@ -14,22 +25,7 @@ var build = function (association, opts, keys) { .select(desiredKey).as("$p") .where(association.mergeTable, where) .build(); - return query; -}; -var execQuery = function () { - if (arguments.length == 2) { - var query = arguments[0]; - var cb = arguments[1]; - } else if (arguments.length == 3) { - var query = this.query.escape(arguments[0], arguments[1]); - var cb = arguments[2]; - } - return this.execSimpleQuery(query, cb); -}; - -var eagerQuery = function (association, opts, keys, cb) { - var query = build.apply(this, [association, opts, keys]); this.execSimpleQuery(query, cb); }; diff --git a/test/integration/db.js b/test/integration/db.js index a29b5bff..3b5284cd 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -25,8 +25,12 @@ describe('DB', function () { db.define("my_model2", { property: String }); - var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { cb(null, {}) }); - var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { cb(null, {}) }); + var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { + cb(null, {}) + }); + var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { + cb(null, {}) + }); db.syncPromise() .then(function () { should.equal(syncStub.calledOnce, true); @@ -49,8 +53,12 @@ describe('DB', function () { property: String }); - var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { cb(null, {}) }); - var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { cb(null, {}) }); + var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { + cb(null, {}) + }); + var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { + cb(null, {}) + }); db.dropAsync() .then(function () { should.equal(dropStub.calledOnce, true); @@ -246,6 +254,109 @@ describe('DB', function () { should.exist(db.driver.query); }); + describe('#execQueryAsync', function () { + it('should execute sql queries', function (done) { + db.driver.execQueryAsync('SELECT id FROM log') + .then(function (data) { + should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); + done() + }) + .catch(function (err) { + done(err); + }); + }); + + it("should escape sql queries", function (done) { + var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; + var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; + db.driver.execQueryAsync(query, args) + .then(function (data) { + should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe('#eagerQuery', function () { + var fixture = { + association: { + model: { + table: 'dog' + }, + field: { + dog_id: { + type: 'serial', + key: true, + required: false, + klass: 'primary', + enumerable: true, + mapsTo: 'dog_id', + name: 'dog_id' + } + }, + mergeAssocId: { + family_id: { + type: 'integer', + required: true, + klass: 'primary', + enumerable: true, + mapsTo: 'family_id', + name: 'family_id' + } + }, + mergeTable: 'dog_family', + }, + opts: { + only: ['name', 'id'], + keys: ['id'], + }, + expectedQuery: 'SELECT "t1"."name", "t1"."id", "t2"."dog_id" AS "$p" FROM "dog" "t1" JOIN "dog_family" "t2" ON "t2"."family_id" = "t1"."id" WHERE "t2"."dog_id" IN (1, 5)' + }; + + describe('cb', function () { + it('should build correct query', function (done) { + var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') + .callsFake(function (q, cb) { + cb(); + }); + + db.driver.eagerQuery(fixture.association, fixture.opts, [ 1, 5 ], function (err, data) { + if (err) { + execSimpleQueryStub.restore(); + done(err); + } + should.equal(execSimpleQueryStub.calledOnce, true); + should.equal(execSimpleQueryStub.calledWith(fixture.expectedQuery), true); + execSimpleQueryStub.restore(); + done(); + }); + }); + }); + + describe('async', function () { + it('should build correct query', function (done) { + var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') + .callsFake(function (q, cb) { + cb(); + }); + db.driver.eagerQueryAsync(fixture.association, fixture.opts, [ 1, 5 ]) + .then(function () { + should.equal(execSimpleQueryStub.calledOnce, true); + should.equal(execSimpleQueryStub.calledWith(fixture.expectedQuery), true); + execSimpleQueryStub.restore(); + done(); + }) + .catch(function (err) { + execSimpleQueryStub.restore(); + done(err); + }); + }); + }); + }); + describe("#execQuery", function () { it("should execute sql queries", function (done) { db.driver.execQuery("SELECT id FROM log", function (err, data) { From 3511ad702e760c25065fe15701affd44f91cfeb8 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 4 Sep 2017 17:08:41 +0300 Subject: [PATCH 35/98] Optimized function names. Tests created --- lib/Instance.js | 82 ++++++++++----- test/integration/instance.js | 188 ++++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 25 deletions(-) diff --git a/lib/Instance.js b/lib/Instance.js index 42983a1a..a1046fb0 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -2,6 +2,7 @@ var Utilities = require("./Utilities"); var Property = require("./Property"); var Hook = require("./Hook"); var enforce = require("enforce"); +var Promise = require("bluebird"); exports.Instance = Instance; @@ -18,18 +19,51 @@ function Instance(Model, opts) { var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); var instanceFuncNames = { save: { - callback: 'save', - promise: 'save' + promiseFunctionPostfix + callback : 'save', + promise : 'save' + promiseFunctionPostfix }, remove: { - callback: 'save', - promise: 'save' + promiseFunctionPostfix + callback : 'remove', + promise : 'remove' + promiseFunctionPostfix }, validate: { - callback: 'save', - promise: 'save' + promiseFunctionPostfix + callback : 'validate', + promise : 'validate' + promiseFunctionPostfix + }, + on: { + callback: 'on' + }, + saved: { + callback: 'saved' + }, + set: { + callback: 'set' + }, + markAsDirty: { + callback: 'markAsDirty' + }, + dirtyProperties: { + callback: 'dirtyProperties' + }, + isInstance: { + callback: 'isInstance' + }, + isPersisted: { + callback: 'isPersisted' + }, + isShell: { + callback: 'isShell' + }, + __singleton_uid: { + callback: '__singleton_uid' + }, + __opts: { + callback: '__opts' + }, + model: { + callback: 'model' } - } + }; var instance_saving = false; var events = {}; @@ -580,7 +614,7 @@ function Instance(Model, opts) { addInstanceExtraProperty(k); } - Object.defineProperty(instance, "on", { + Object.defineProperty(instance, instanceFuncNames.on.callback, { value: function (event, cb) { if (!events.hasOwnProperty(event)) { events[event] = []; @@ -592,7 +626,7 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, "save", { + Object.defineProperty(instance, instanceFuncNames.save.callback, { value: function () { var arg = null, objCount = 0; var data = {}, saveOptions = {}, cb = null; @@ -640,14 +674,14 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, "saved", { + Object.defineProperty(instance, instanceFuncNames.saved.callback, { value: function () { return opts.changes.length === 0; }, enumerable: false, writable: true }); - Object.defineProperty(instance, "remove", { + Object.defineProperty(instance, instanceFuncNames.remove.callback, { value: function (cb) { removeInstance(cb); @@ -656,12 +690,12 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, "set", { + Object.defineProperty(instance, instanceFuncNames.set.callback, { value: setPropertyByPath, enumerable: false, writable: true }); - Object.defineProperty(instance, "markAsDirty", { + Object.defineProperty(instance, instanceFuncNames.markAsDirty.callback, { value: function (propName) { if (propName != undefined) { opts.changes.push(propName); @@ -670,28 +704,28 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, "dirtyProperties", { + Object.defineProperty(instance, instanceFuncNames.dirtyProperties.callback, { get: function () { return opts.changes; }, enumerable: false }); - Object.defineProperty(instance, "isInstance", { + Object.defineProperty(instance, instanceFuncNames.isInstance.callback, { value: true, enumerable: false }); - Object.defineProperty(instance, "isPersisted", { + Object.defineProperty(instance, instanceFuncNames.isPersisted.callback, { value: function () { return !opts.is_new; }, enumerable: false, writable: true }); - Object.defineProperty(instance, "isShell", { + Object.defineProperty(instance, instanceFuncNames.isShell.callback, { value: function () { return opts.isShell; }, enumerable: false }); - Object.defineProperty(instance, "validate", { + Object.defineProperty(instance, instanceFuncNames.validate.callback, { value: function (cb) { handleValidations(function (errors) { cb(null, errors || false); @@ -700,17 +734,17 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, "__singleton_uid", { + Object.defineProperty(instance, instanceFuncNames.__singleton_uid.callback, { value: function (cb) { return opts.uid; }, enumerable: false }); - Object.defineProperty(instance, "__opts", { + Object.defineProperty(instance, instanceFuncNames.__opts.callback, { value: opts, enumerable: false }); - Object.defineProperty(instance, "model", { + Object.defineProperty(instance, instanceFuncNames.model.callback, { value: function (cb) { return Model; }, @@ -718,17 +752,17 @@ function Instance(Model, opts) { }); Object.defineProperty(instance, instanceFuncNames.save.promise, { - value: Promise.promisify(instanceFuncNames.save.callback), + value: Promise.promisify(instance[instanceFuncNames.save.callback]), enumerable: false, writable: true }); Object.defineProperty(instance, instanceFuncNames.remove.promise, { - value: Promise.promisify(instanceFuncNames.remove.callback), + value: Promise.promisify(instance[instanceFuncNames.remove.callback]), enumerable: false, writable: true }); Object.defineProperty(instance, instanceFuncNames.validate.promise, { - value: Promise.promisify(instanceFuncNames.validate.callback), + value: Promise.promisify(instance[instanceFuncNames.validate.callback]), enumerable: false, writable: true }); diff --git a/test/integration/instance.js b/test/integration/instance.js index ae9cacf1..4450c045 100644 --- a/test/integration/instance.js +++ b/test/integration/instance.js @@ -96,6 +96,16 @@ describe("Model instance", function() { }); }); }); + + it("should have a saving state to avoid loops (promise-based)", function (done) { + main_item.find({ name : "new name" }).first(function (err, mainItem) { + mainItem.saveAsync({ name : "new name test" }).then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); }); describe("#isInstance", function () { @@ -336,16 +346,62 @@ describe("Model instance", function() { return done(); }); }); + + it("should return validation errors if invalid (promise-based)", function (done) { + var person = new Person({ age: -1 }); + + person.validateAsync().then(function (validationErrors) { + should.equal(Array.isArray(validationErrors), true); + + done(); + }).catch(function(err) { + done(err); + }); + }); + + it("should return false if valid (promise-based)", function (done) { + var person = new Person({ name: 'Janette' }); + + person.validateAsync().then(function (validationErrors) { + should.equal(validationErrors, false); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe("properties", function () { describe("Number", function () { it("should be saved for valid numbers, using both save & create", function (done) { - var person1 = new Person({ height: 190 }); + var person1 = new Person({height: 190}); person1.save(function (err) { should.not.exist(err); + Person.create({height: 170}, function (err, person2) { + should.not.exist(err); + + Person.get(person1[Person.id], function (err, item) { + should.not.exist(err); + should.equal(item.height, 190); + + Person.get(person2[Person.id], function (err, item) { + should.not.exist(err); + should.equal(item.height, 170); + done(); + }); + }); + }); + }); + }); + + it("should be saved for valid numbers, using both save & create (promise-based)", function (done) { + var person1 = new Person({ height: 190 }); + + person1.saveAsync().then(function () { + Person.create({ height: 170 }, function (err, person2) { should.not.exist(err); @@ -356,10 +412,13 @@ describe("Model instance", function() { Person.get(person2[Person.id], function (err, item) { should.not.exist(err); should.equal(item.height, 170); + done(); }); }); }); + }).catch(function(err) { + done(err); }); }); @@ -415,6 +474,55 @@ describe("Model instance", function() { }); }); }); + + it("should raise an error for NaN integers (promise-based)", function (done) { + var person = new Person({ height: NaN }); + + person.saveAsync().catch(function(err) { + var msg = { + postgres : 'invalid input syntax for integer: "NaN"' + }[protocol]; + + should.equal(err.message, msg); + + done(); + }); + }); + + it("should raise an error for Infinity integers (promise-based)", function (done) { + var person = new Person({ height: Infinity }); + + person.saveAsync().catch(function (err) { + should.exist(err); + var msg = { + postgres : 'invalid input syntax for integer: "Infinity"' + }[protocol]; + + should.equal(err.message, msg); + + done(); + }); + }); + + it("should raise an error for nonsensical integers, for both save & create (promise-based)", function (done) { + var person = new Person({ height: 'bugz' }); + + person.saveAsync().catch(function (err) { + should.exist(err); + var msg = { + postgres : 'invalid input syntax for integer: "bugz"' + }[protocol]; + + should.equal(err.message, msg); + + Person.create({ height: 'bugz' }, function (err, instance) { + should.exist(err); + should.equal(err.message, msg); + + done(); + }); + }); + }); } if (protocol != 'mysql') { @@ -442,6 +550,31 @@ describe("Model instance", function() { }); }); }); + + it("should store NaN & Infinite floats (promise-based)", function (done) { + var person = new Person({ weight: NaN }); + + person.saveAsync().then(function () { + + Person.get(person[Person.id], function (err, person) { + should.not.exist(err); + should(isNaN(person.weight)); + + person.saveAsync({ weight: Infinity, name: 'black hole' }).then(function () { + Person.get(person[Person.id], function (err, person) { + should.not.exist(err); + should.strictEqual(person.weight, Infinity); + + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }).catch(function(err) { + done(err); + }); + }); } }); @@ -460,5 +593,58 @@ describe("Model instance", function() { }); }); }); + + describe("#removeAsync", function () { + var main_item, item; + + before(function (done) { + main_item = db.define("main_item", { + name : String + }, { + auteFetch : true + }); + item = db.define("item", { + name : String + }, { + identityCache : false + }); + item.hasOne("main_item", main_item, { + reverse : "items", + autoFetch : true + }); + + return helper.dropSync([ main_item, item ], function () { + main_item.create({ + name : "Main Item" + }, function (err, mainItem) { + item.create({ + name : "Item" + }, function (err, Item) { + mainItem.setItems(Item, function (err) { + should.not.exist(err); + + return done(); + }); + }); + }); + }); + }); + + it("should delete an item and send an error", function (done) { + main_item.find({ name : "Main Item" }).first(function (err, mainItem) { + mainItem.removeAsync().then(function () { + main_item.find({ name : "Main Item" }).first(function (err, itemFound) { + if (err && !itemFound) { + done(); + } + + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); }); }); From 03c47ce7a70f946fb8d44670207b1a25fdf968b4 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 4 Sep 2017 17:25:34 +0300 Subject: [PATCH 36/98] Resolved PR comments --- lib/Instance.js | 89 ++++++++++++------------------------------------- 1 file changed, 21 insertions(+), 68 deletions(-) diff --git a/lib/Instance.js b/lib/Instance.js index a1046fb0..376cbc61 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -17,54 +17,6 @@ function Instance(Model, opts) { opts.originalKeyValues = {}; var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); - var instanceFuncNames = { - save: { - callback : 'save', - promise : 'save' + promiseFunctionPostfix - }, - remove: { - callback : 'remove', - promise : 'remove' + promiseFunctionPostfix - }, - validate: { - callback : 'validate', - promise : 'validate' + promiseFunctionPostfix - }, - on: { - callback: 'on' - }, - saved: { - callback: 'saved' - }, - set: { - callback: 'set' - }, - markAsDirty: { - callback: 'markAsDirty' - }, - dirtyProperties: { - callback: 'dirtyProperties' - }, - isInstance: { - callback: 'isInstance' - }, - isPersisted: { - callback: 'isPersisted' - }, - isShell: { - callback: 'isShell' - }, - __singleton_uid: { - callback: '__singleton_uid' - }, - __opts: { - callback: '__opts' - }, - model: { - callback: 'model' - } - }; - var instance_saving = false; var events = {}; var instance = {}; @@ -614,7 +566,7 @@ function Instance(Model, opts) { addInstanceExtraProperty(k); } - Object.defineProperty(instance, instanceFuncNames.on.callback, { + Object.defineProperty(instance, "on", { value: function (event, cb) { if (!events.hasOwnProperty(event)) { events[event] = []; @@ -626,7 +578,7 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.save.callback, { + Object.defineProperty(instance, "save", { value: function () { var arg = null, objCount = 0; var data = {}, saveOptions = {}, cb = null; @@ -674,14 +626,14 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.saved.callback, { + Object.defineProperty(instance, "saved", { value: function () { return opts.changes.length === 0; }, enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.remove.callback, { + Object.defineProperty(instance, "remove", { value: function (cb) { removeInstance(cb); @@ -690,12 +642,12 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.set.callback, { + Object.defineProperty(instance, "set", { value: setPropertyByPath, enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.markAsDirty.callback, { + Object.defineProperty(instance, "markAsDirty", { value: function (propName) { if (propName != undefined) { opts.changes.push(propName); @@ -704,28 +656,28 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.dirtyProperties.callback, { + Object.defineProperty(instance, "dirtyProperties", { get: function () { return opts.changes; }, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.isInstance.callback, { + Object.defineProperty(instance, "isInstance", { value: true, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.isPersisted.callback, { + Object.defineProperty(instance, "isPersisted", { value: function () { return !opts.is_new; }, enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.isShell.callback, { + Object.defineProperty(instance, "isShell", { value: function () { return opts.isShell; }, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.validate.callback, { + Object.defineProperty(instance, "validate", { value: function (cb) { handleValidations(function (errors) { cb(null, errors || false); @@ -734,35 +686,36 @@ function Instance(Model, opts) { enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.__singleton_uid.callback, { + Object.defineProperty(instance, "__singleton_uid", { value: function (cb) { return opts.uid; }, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.__opts.callback, { + Object.defineProperty(instance, "__opts", { value: opts, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.model.callback, { + Object.defineProperty(instance, "model", { value: function (cb) { return Model; }, enumerable: false }); - Object.defineProperty(instance, instanceFuncNames.save.promise, { - value: Promise.promisify(instance[instanceFuncNames.save.callback]), + Object.defineProperty(instance, 'save' + promiseFunctionPostfix, { + value: Promise.promisify(instance['save']), enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.remove.promise, { - value: Promise.promisify(instance[instanceFuncNames.remove.callback]), + Object.defineProperty(instance, 'remove' + promiseFunctionPostfix, { + value: Promise.promisify(instance['remove']), enumerable: false, writable: true }); - Object.defineProperty(instance, instanceFuncNames.validate.promise, { - value: Promise.promisify(instance[instanceFuncNames.validate.callback]), + + Object.defineProperty(instance, 'validate' + promiseFunctionPostfix, { + value: Promise.promisify(instance['validate']), enumerable: false, writable: true }); From 33fd97cb2c6488a6ed48fadfe801803162471a15 Mon Sep 17 00:00:00 2001 From: Yevheniy Miropolets Date: Mon, 4 Sep 2017 17:28:45 +0300 Subject: [PATCH 37/98] redo getAsync function to resolve array of arguments fix tests fot getAsync function --- lib/AggregateFunctions.js | 14 +++++- test/integration/model-aggregate.js | 67 +++++++++++++++++++---------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/AggregateFunctions.js b/lib/AggregateFunctions.js index 664945c2..560aca6a 100644 --- a/lib/AggregateFunctions.js +++ b/lib/AggregateFunctions.js @@ -1,6 +1,6 @@ var ORMError = require("./Error"); var Utilities = require("./Utilities"); -var Promise = require('bluebird'); +var Promise = require('bluebird'); module.exports = AggregateFunctions; @@ -156,7 +156,17 @@ function AggregateFunctions(opts) { } }; - proto.getAsync = Promise.promisify(proto.get); + proto.getAsync = function () { + return new Promise(function(resolve, reject) { + proto.get(function () { + if (arguments[0]!== null) { + return reject(arguments[0]); + } else { + resolve(Array.from(arguments).slice(1)); + } + }) + }); + }; for (var i = 0; i < opts.driver.aggregate_functions.length; i++) { addAggregate(proto, opts.driver.aggregate_functions[i], appendFunction); diff --git a/test/integration/model-aggregate.js b/test/integration/model-aggregate.js index 5a10fc61..1a4a40bb 100644 --- a/test/integration/model-aggregate.js +++ b/test/integration/model-aggregate.js @@ -55,7 +55,25 @@ describe("Model.aggregate()", function() { }); }); - describe("with call()", function () { + + + describe("with multiple methods using getAsync", function () { + before(setup()); + + it("should return value for everyone of them with getAsync", function (done) { + Person.aggregate().count('id').min('id').max('id').getAsync() + .then(function(results) { + + results[0].should.equal(3); + results[1].should.equal(1); + results[2].should.equal(3); + + return done(); + }); + }); + }); + + describe("with call()", function () { before(setup()); it("should accept a function", function (done) { @@ -100,7 +118,8 @@ describe("Model.aggregate()", function() { it("should accept a function", function (done) { Person.aggregate().call('COUNT').getAsync() - .then(function (count) { + .then(function (results) { + var count = results[0]; count.should.equal(3); return done(); }) @@ -111,7 +130,8 @@ describe("Model.aggregate()", function() { it("should accept arguments to the function as an Array", function (done) { Person.aggregate().call('COUNT', [ 'id' ]).getAsync() - .then(function (count) { + .then(function (results) { + var count = results[0]; count.should.equal(3); return done(); }) @@ -123,7 +143,8 @@ describe("Model.aggregate()", function() { describe("if function is DISTINCT", function () { it("should work as calling .distinct() directly", function (done) { Person.aggregate().call('DISTINCT', [ 'name' ]).as('name').order('name').getAsync() - .then(function (rows) { + .then(function (results) { + var rows = results[0]; should(Array.isArray(rows)); rows.length.should.equal(2); @@ -197,8 +218,8 @@ describe("Model.aggregate()", function() { it("should use them as properties if 1st argument is Array", function (done) { Person.aggregate().select([ 'id' ]).count('id').groupBy('id').getAsync() - .then(function (people) { - + .then(function (results) { + var people = results[0]; should(Array.isArray(people)); people.length.should.be.above(0); @@ -215,8 +236,8 @@ describe("Model.aggregate()", function() { it("should use them as properties", function (done) { Person.aggregate().select('id').count().groupBy('id').getAsync() - .then(function (people) { - + .then(function (results) { + var people = results[0]; should(Array.isArray(people)); people.length.should.be.above(0); @@ -313,8 +334,8 @@ describe("Model.aggregate()", function() { it("should return a list of distinct properties", function (done) { Person.aggregate().distinct('name').getAsync() - .then(function (names) { - + .then(function (results) { + var names = results[0]; names.should.be.a.Object(); names.should.have.property("length", 2); @@ -328,7 +349,8 @@ describe("Model.aggregate()", function() { describe("with limit(1)", function () { it("should return only one value", function (done) { Person.aggregate().distinct('name').limit(1).order("name").getAsync() - .then(function (names) { + .then(function (results) { + var names = results[0]; names.should.be.a.Object(); names.should.have.property("length", 1); names[0].should.equal("Jane Doe"); @@ -337,14 +359,15 @@ describe("Model.aggregate()", function() { }) .catch(function(err) { done(err); - }) + }); }); }); describe("with limit(1, 1)", function () { it("should return only one value", function (done) { Person.aggregate().distinct('name').limit(1, 1).order("name").getAsync() - .then(function (names) { + .then(function (results) { + var names = results[0]; names.should.be.a.Object(); names.should.have.property("length", 1); names[0].should.equal("John Doe"); @@ -353,7 +376,7 @@ describe("Model.aggregate()", function() { }) .catch(function(err) { done(err); - }) + }); }); }); }); @@ -398,8 +421,8 @@ describe("Model.aggregate()", function() { it("should return items grouped by property", function (done) { Person.aggregate().count().groupBy('name').getAsync() - .then(function (rows) { - + .then(function (results) { + var rows = results[0]; rows.should.be.a.Object(); rows.should.have.property("length", 2); @@ -417,8 +440,8 @@ describe("Model.aggregate()", function() { it("should order items", function (done) { Person.aggregate().count().groupBy('name').order('-count').getAsync() - .then(function (rows) { - + .then(function (results) { + var rows = results[0]; rows.should.be.a.Object(); rows.should.have.property("length", 2); @@ -429,7 +452,7 @@ describe("Model.aggregate()", function() { }) .catch(function(err) { done(err); - }) + }); }); }); }); @@ -465,8 +488,8 @@ describe("Model.aggregate()", function() { it("should use as an alias", function (done) { Person.aggregate().count().as('total').groupBy('name').getAsync() - .then(function (people) { - + .then(function (results) { + var people = results[0]; should(Array.isArray(people)); people.length.should.be.above(0); @@ -477,7 +500,7 @@ describe("Model.aggregate()", function() { }) .catch(function (err) { done(err); - }) + }); }); }); From 9e335a17f0bd3ad6372aa46f9d9f8abdfac6a14f Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 4 Sep 2017 17:34:11 +0300 Subject: [PATCH 38/98] Resolve PR comments: added spaces after brackets --- test/integration/instance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/instance.js b/test/integration/instance.js index 4450c045..876f0d6b 100644 --- a/test/integration/instance.js +++ b/test/integration/instance.js @@ -375,12 +375,12 @@ describe("Model instance", function() { describe("properties", function () { describe("Number", function () { it("should be saved for valid numbers, using both save & create", function (done) { - var person1 = new Person({height: 190}); + var person1 = new Person({ height: 190 }); person1.save(function (err) { should.not.exist(err); - Person.create({height: 170}, function (err, person2) { + Person.create({ height: 170 }, function (err, person2) { should.not.exist(err); Person.get(person1[Person.id], function (err, item) { From 35895c05b968c0c35eed774d4eb8db505522e309 Mon Sep 17 00:00:00 2001 From: Yevheniy Miropolets Date: Mon, 4 Sep 2017 17:48:28 +0300 Subject: [PATCH 39/98] revert unneeded changes --- test/config.example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.example.js b/test/config.example.js index 0d9c232b..bb9c2d73 100644 --- a/test/config.example.js +++ b/test/config.example.js @@ -13,8 +13,8 @@ exports.mysql = { database : "test" }; exports.postgres = { - user : "postgres", - password : "1111", + user : "root", + password : "", database : "test" }; exports.redshift = { From 0c325868ab6a8a9d1d3c62fb7360649d8bc863a2 Mon Sep 17 00:00:00 2001 From: Yevheniy Miropolets Date: Tue, 5 Sep 2017 10:40:30 +0300 Subject: [PATCH 40/98] fix getAsync function --- lib/AggregateFunctions.js | 6 +++--- test/integration/model-aggregate.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/AggregateFunctions.js b/lib/AggregateFunctions.js index 560aca6a..f2842dfc 100644 --- a/lib/AggregateFunctions.js +++ b/lib/AggregateFunctions.js @@ -1,6 +1,6 @@ var ORMError = require("./Error"); var Utilities = require("./Utilities"); -var Promise = require('bluebird'); +var Promise = require("bluebird"); module.exports = AggregateFunctions; @@ -159,12 +159,12 @@ function AggregateFunctions(opts) { proto.getAsync = function () { return new Promise(function(resolve, reject) { proto.get(function () { - if (arguments[0]!== null) { + if (arguments[0]) { return reject(arguments[0]); } else { resolve(Array.from(arguments).slice(1)); } - }) + }); }); }; diff --git a/test/integration/model-aggregate.js b/test/integration/model-aggregate.js index 1a4a40bb..2302e52a 100644 --- a/test/integration/model-aggregate.js +++ b/test/integration/model-aggregate.js @@ -73,7 +73,7 @@ describe("Model.aggregate()", function() { }); }); - describe("with call()", function () { + describe("with call()", function () { before(setup()); it("should accept a function", function (done) { From c6aceb0c9a58898fc05ca1c1f12eaf9060ee1fba Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 5 Sep 2017 12:22:20 +0300 Subject: [PATCH 41/98] Add runAsync method for promise support. Create async tests --- lib/ChainFind.js | 4 + test/integration/model-find-chain.js | 146 ++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 7959ceed..230679c7 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -3,6 +3,7 @@ var async = require("async"); var Utilities = require("./Utilities"); var ChainInstance = require("./ChainInstance"); var Promise = require("./Promise").Promise; +var Bluebird = require("bluebird"); module.exports = ChainFind; @@ -89,6 +90,8 @@ function ChainFind(Model, opts) { }); } + var chainRunAsync = Bluebird.promisify(chainRun); + var promise = null; var chain = { find: function () { @@ -223,6 +226,7 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, + runAsync: chainRunAsync, success: function (cb) { if (!promise) { promise = new Promise(); diff --git a/test/integration/model-find-chain.js b/test/integration/model-find-chain.js index 438d1d3a..6c5f7498 100644 --- a/test/integration/model-find-chain.js +++ b/test/integration/model-find-chain.js @@ -91,6 +91,15 @@ describe("Model.find() chaining", function() { return done(); }); }); + it("should limit results to N items (promise-based)", function (done) { + Person.find().limit(2).runAsync().then(function (instances) { + instances.should.have.property("length", 2); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe(".skip(N)", function () { @@ -105,6 +114,16 @@ describe("Model.find() chaining", function() { return done(); }); }); + it("should skip the first N results (promise-based)", function (done) { + Person.find().skip(2).order("age").runAsync().then(function (instances) { + instances.should.have.property("length", 1); + instances[0].age.should.equal(20); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe(".offset(N)", function () { @@ -119,6 +138,16 @@ describe("Model.find() chaining", function() { return done(); }); }); + it("should skip the first N results (promise-based)", function (done) { + Person.find().offset(2).order("age").runAsync().then(function (instances) { + instances.should.have.property("length", 1); + instances[0].age.should.equal(20); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe("order", function () { @@ -135,6 +164,18 @@ describe("Model.find() chaining", function() { }); }); + it("('property') should order by that property ascending (promise-based)", function (done) { + Person.find().order("age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].age.should.equal(18); + instances[2].age.should.equal(20); + + done(); + }).catch(function(err) { + done(err); + }); + }); + it("('-property') should order by that property descending", function (done) { Person.find().order("-age").run(function (err, instances) { should.equal(err, null); @@ -146,6 +187,18 @@ describe("Model.find() chaining", function() { }); }); + it("('-property') should order by that property descending (promise-based)", function (done) { + Person.find().order("-age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].age.should.equal(20); + instances[2].age.should.equal(18); + + done(); + }).catch(function(err) { + done(err); + }); + }); + it("('property', 'Z') should order by that property descending", function (done) { Person.find().order("age", "Z").run(function (err, instances) { should.equal(err, null); @@ -156,6 +209,18 @@ describe("Model.find() chaining", function() { return done(); }); }); + + it("('property', 'Z') should order by that property descending (promise-based)", function (done) { + Person.find().order("age", "Z").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].age.should.equal(20); + instances[2].age.should.equal(18); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe("orderRaw", function () { @@ -174,6 +239,18 @@ describe("Model.find() chaining", function() { }); }); + it("should allow ordering by SQL (promise-based)", function (done) { + Person.find().orderRaw("age DESC").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].age.should.equal(20); + instances[2].age.should.equal(18); + + done(); + }).catch(function(err) { + done(err); + }); + }); + it("should allow ordering by SQL with escaping", function (done) { Person.find().orderRaw("?? DESC", ['age']).run(function (err, instances) { should.equal(err, null); @@ -184,6 +261,18 @@ describe("Model.find() chaining", function() { return done(); }); }); + + it("should allow ordering by SQL with escaping (promise-based)", function (done) { + Person.find().orderRaw("?? DESC", ['age']).runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].age.should.equal(20); + instances[2].age.should.equal(18); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe("only", function () { @@ -201,8 +290,21 @@ describe("Model.find() chaining", function() { }); }); + it("('property', ...) should return only those properties, others null (promise-based)", function (done) { + Person.find().only("age", "surname").order("-age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].should.have.property("age"); + instances[0].should.have.property("surname", "Doe"); + instances[0].should.have.property("name", null); + + done(); + }).catch(function(err){ + done(err); + }); + }); + // This works if cache is disabled. I suspect a cache bug. - xit("(['property', ...]) should return only those properties, others null", function (done) { + it("(['property', ...]) should return only those properties, others null", function (done) { Person.find().only([ "age", "surname" ]).order("-age").run(function (err, instances) { should.equal(err, null); instances.should.have.property("length", 3); @@ -213,6 +315,18 @@ describe("Model.find() chaining", function() { return done(); }); }); + it("(['property', ...]) should return only those properties, others null (promise-based)", function (done) { + Person.find().only([ "age", "surname" ]).order("-age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].should.have.property("age"); + instances[0].should.have.property("surname", "Doe"); + instances[0].should.have.property("name", null); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe("omit", function () { @@ -234,6 +348,23 @@ describe("Model.find() chaining", function() { }); }); + it("('property', ...) should not get these properties (promise-based)", function (done) { + Person.find().omit("age", "surname").order("-age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + if (common.protocol() != "mongodb") { + should.exist(instances[0].id); + } + should.exist(instances[0].friend_id); + instances[0].should.have.property("age", null); + instances[0].should.have.property("surname", null); + instances[0].should.have.property("name", "Jane"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + it("(['property', ...]) should not get these properties", function (done) { Person.find().omit(["age", "surname"]).order("-age").run(function (err, instances) { should.equal(err, null); @@ -245,6 +376,19 @@ describe("Model.find() chaining", function() { return done(); }); }); + + it("(['property', ...]) should not get these properties (promise-based)", function (done) { + Person.find().omit(["age", "surname"]).order("-age").runAsync().then(function (instances) { + instances.should.have.property("length", 3); + instances[0].should.have.property("age", null); + instances[0].should.have.property("surname", null); + instances[0].should.have.property("name", "Jane"); + + done(); + }).catch(function(err) { + done(err); + }); + }); }); describe(".count()", function () { From 6f7d531e890ed682ca6d1d107f324c2ce4ac1cfb Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 5 Sep 2017 13:23:07 +0300 Subject: [PATCH 42/98] add clearAsync, createAsync, getAsync and test for it --- lib/Model.js | 9 +- test/integration/model-clearAsync.js | 69 +++++++ test/integration/model-createAsync.js | 133 +++++++++++++ test/integration/model-get.js | 22 +-- test/integration/model-getAsync.js | 266 ++++++++++++++++++++++++++ 5 files changed, 487 insertions(+), 12 deletions(-) create mode 100644 test/integration/model-clearAsync.js create mode 100644 test/integration/model-createAsync.js create mode 100644 test/integration/model-getAsync.js diff --git a/lib/Model.js b/lib/Model.js index 698d93a6..ab34f9dc 100644 --- a/lib/Model.js +++ b/lib/Model.js @@ -12,6 +12,7 @@ var Utilities = require("./Utilities"); var Validators = require("./Validators"); var ORMError = require("./Error"); var Hook = require("./Hook"); +var Promise = require("bluebird"); var AvailableHooks = [ "beforeCreate", "afterCreate", "beforeSave", "afterSave", @@ -315,6 +316,8 @@ function Model(opts) { return this; }; + model.getAsync = Promise.promisify(model.get); + model.find = function () { var options = {}; var conditions = null; @@ -561,7 +564,7 @@ function Model(opts) { }; model.create = function () { - var itemsParams = [] + var itemsParams = []; var items = []; var options = {}; var done = null; @@ -619,6 +622,8 @@ function Model(opts) { return this; }; + model.createAsync = Promise.promisify(model.create); + model.clear = function (cb) { opts.driver.clear(opts.table, function (err) { if (typeof cb === "function") cb(err); @@ -627,6 +632,8 @@ function Model(opts) { return this; }; + model.clearAsync = Promise.promisify(model.clear); + model.prependValidation = function (key, validation) { if(opts.validations.hasOwnProperty(key)) { opts.validations[key].splice(0, 0, validation); diff --git a/test/integration/model-clearAsync.js b/test/integration/model-clearAsync.js new file mode 100644 index 00000000..5a6c34a4 --- /dev/null +++ b/test/integration/model-clearAsync.js @@ -0,0 +1,69 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("Model.clearAsync()", function() { + var db = null; + var Person = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + + ORM.singleton.clear(); + + return helper.dropSync(Person, function () { + Person.create([{ + name: "John Doe" + }, { + name: "Jane Doe" + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("with callback", function () { + before(setup()); + + it("should call when done", function (done) { + Person.clearAsync() + .then(function () { + Person.find().count(function (err, count) { + count.should.equal(0); + + return done(); + }); + }); + }); + }); + + describe("without callback", function () { + before(setup()); + + it("should still remove", function (done) { + Person.clearAsync(); + + setTimeout(function () { + Person.find().count(function (err, count) { + count.should.equal(0); + + return done(); + }); + }, 200); + }); + }); +}); diff --git a/test/integration/model-createAsync.js b/test/integration/model-createAsync.js new file mode 100644 index 00000000..5e3371d8 --- /dev/null +++ b/test/integration/model-createAsync.js @@ -0,0 +1,133 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); + +describe("Model.createAsync()", function() { + var db = null; + var Pet = null; + var Person = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + Pet = db.define("pet", { + name : { type: "text", defaultValue: "Mutt" } + }); + Person.hasMany("pets", Pet); + + return helper.dropSync([ Person, Pet ], done); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("if passing an object", function () { + before(setup()); + + it("should accept it as the only item to create", function (done) { + Person.createAsync({ + name : "John Doe" + }) + .then(function (John) { + John.should.have.property("name", "John Doe"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("if passing an array", function () { + before(setup()); + + it("should accept it as a list of items to create", function (done) { + Person.createAsync([{ + name : "John Doe" + }, { + name : "Jane Doe" + }]) + .then(function (people) { + should(Array.isArray(people)); + + people.should.have.property("length", 2); + people[0].should.have.property("name", "John Doe"); + people[1].should.have.property("name", "Jane Doe"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("if element has an association", function () { + before(setup()); + + it("should also create it or save it", function (done) { + Person.createAsync({ + name : "John Doe", + pets : [ new Pet({ name: "Deco" }) ] + }) + .then(function (John) { + John.should.have.property("name", "John Doe"); + + should(Array.isArray(John.pets)); + + John.pets[0].should.have.property("name", "Deco"); + John.pets[0].should.have.property(Pet.id); + John.pets[0].saved().should.be.true; + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should also create it or save it even if it's an object and not an instance", function (done) { + Person.createAsync({ + name : "John Doe", + pets : [ { name: "Deco" } ] + }) + .then(function (John) { + John.should.have.property("name", "John Doe"); + + should(Array.isArray(John.pets)); + + John.pets[0].should.have.property("name", "Deco"); + John.pets[0].should.have.property(Pet.id); + John.pets[0].saved().should.be.true; + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("when not passing a property", function () { + before(setup()); + + it("should use defaultValue if defined", function (done) { + Pet.createAsync({}) + .then(function (Mutt) { + Mutt.should.have.property("name", "Mutt"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); +}); diff --git a/test/integration/model-get.js b/test/integration/model-get.js index 3bcffc78..1759cf64 100644 --- a/test/integration/model-get.js +++ b/test/integration/model-get.js @@ -124,23 +124,23 @@ describe("Model.get()", function() { }); describe("changing instance.identityCacheSaveCheck = false", function () { - before(function (done) { + before(function () { Person.settings.set("instance.identityCacheSaveCheck", false); + }); - it("should return the same object with the changed name", function (done) { - Person.get(John[Person.id], function (err, John1) { - should.equal(err, null); + it("should return the same object with the changed name", function (done) { + Person.get(John[Person.id], function (err, John1) { + should.equal(err, null); - John1.name = "James"; + John1.name = "James"; - Person.get(John[Person.id], function (err, John2) { - should.equal(err, null); + Person.get(John[Person.id], function (err, John2) { + should.equal(err, null); - John1[Person.id].should.equal(John2[Person.id]); - John2.name.should.equal("James"); + John1[Person.id].should.equal(John2[Person.id]); + John2.name.should.equal("James"); - return done(); - }); + return done(); }); }); }); diff --git a/test/integration/model-getAsync.js b/test/integration/model-getAsync.js new file mode 100644 index 00000000..2504cad1 --- /dev/null +++ b/test/integration/model-getAsync.js @@ -0,0 +1,266 @@ +var should = require('should'); +var Promise = require('bluebird'); +var helper = require('../support/spec_helper'); +var common = require('../common'); +var ORM = require('../../'); + +describe("Model.getAsync()", function () { + var db = null; + var Person = null; + var John; + + var setup = function (identityCache) { + return function (done) { + Person = db.define("person", { + name : { type: 'text', mapsTo: 'fullname' } + }, { + identityCache : identityCache, + methods : { + UID: function () { + return this[Person.id]; + } + } + }); + + ORM.singleton.clear(); // clear identityCache cache + + return helper.dropSync(Person, function () { + Person.create([{ + name: "John Doe" + }, { + name: "Jane Doe" + }], function (err, people) { + if (err) done(err); + John = people[0]; + + return done(); + }); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe('with identityCache cache', function () { + before(setup(true)); + + it("should throw if passed a wrong number of ids", function (done) { + Person.getAsync(1, 2) + .then(function () { + done(new Error('Fail')); + }) + .catch(function () { + done(); + }); + }); + + it("should accept and try to fetch if passed an Array with ids", function (done) { + Person.getAsync([ John[Person.id] ]) + .then(function (John) { + John.should.be.a.Object(); + John.should.have.property(Person.id, John[Person.id]); + John.should.have.property("name", "John Doe"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should throw err", function (done) { + Person.getAsync(999) + .then(function () { + done(new Error('Fail!')); + }) + .catch(function (err) { + err.should.be.a.Object(); + err.message.should.equal("Not found"); + done(); + }); + }); + + it("should return item with id 1", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John) { + John.should.be.a.Object(); + John.should.have.property(Person.id, John[Person.id]); + John.should.have.property("name", "John Doe"); + + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should have an UID method", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John) { + John.UID.should.be.a.Function(); + John.UID().should.equal(John[Person.id]); + + done(); + }) + .catch(function (err) { + done(err) + }); + }); + + it("should return the original object with unchanged name", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John1) { + John1.name = "James"; + return Person.getAsync(John[Person.id]); + }) + .then(function (John2) { + should.equal(John2.name, "John Doe"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + describe("changing instance.identityCacheSaveCheck = false", function () { + before(function () { + Person.settings.set("instance.identityCacheSaveCheck", false); + }); + + it("should return the same object with the changed name", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John1) { + John1.name = "James"; + return [John1, Person.getAsync(John[Person.id])]; + }) + .spread(function (John1, John2) { + John1[Person.id].should.equal(John2[Person.id]); + John2.name.should.equal("James"); + + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + }); + + describe("with no identityCache cache", function () { + before(setup(false)); + + it("should return different objects", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John1) { + return [John1, Person.getAsync(John[Person.id])]; + }) + .spread(function (John1, John2) { + John1[Person.id].should.equal(John2[Person.id]); + John1.should.not.equal(John2); + + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("with identityCache cache = 0.5 secs", function () { + before(setup(0.5)); + + it("should return same objects after 0.2 sec", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John1) { + return [John1, Promise.delay(200)]; + }) + .spread(function (John1) { + return [John1, Person.getAsync(John[Person.id])]; + }) + .spread(function (John1, John2) { + John1[Person.id].should.equal(John2[Person.id]); + John1.should.equal(John2); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should return different objects after 0.7 sec", function (done) { + Person.getAsync(John[Person.id]) + .then(function (John1) { + return [John1, Promise.delay(700)]; + }) + .spread(function (John1) { + return [John1, Person.getAsync(John[Person.id])]; + }) + .spread(function (John1, John2) { + John1.should.not.equal(John2); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("if primary key name is changed", function () { + before(function (done) { + Person = db.define("person", { + name : String + }); + + ORM.singleton.clear(); + + return helper.dropSync(Person, function () { + Person.create([{ + name : "John Doe" + }, { + name : "Jane Doe" + }], done); + }); + }); + + it("should search by key name and not 'id'", function (done) { + db.settings.set('properties.primary_key', 'name'); + + var OtherPerson = db.define("person", { + id : Number + }); + + OtherPerson.getAsync("Jane Doe") + .then(function (person) { + person.name.should.equal("Jane Doe"); + db.settings.set('properties.primary_key', 'id'); + done(); + }); + }); + }); + + describe("with empty object as options", function () { + before(setup()); + + it("should return item with id 1 like previously", function (done) { + Person.getAsync(John[Person.id], {}) + .then(function (John) { + John.should.be.a.Object(); + John.should.have.property(Person.id, John[Person.id]); + John.should.have.property("name", "John Doe"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); +}); \ No newline at end of file From 172e2c661ba880942a635329b4a5443e79235801 Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 6 Sep 2017 14:34:23 +0300 Subject: [PATCH 43/98] add promise support to hooks --- lib/Hook.js | 20 +- test/integration/hook-promise.js | 457 +++++++++++++++++++++++++++++++ test/integration/hook.js | 190 ++++++------- 3 files changed, 568 insertions(+), 99 deletions(-) create mode 100644 test/integration/hook-promise.js diff --git a/lib/Hook.js b/lib/Hook.js index 1d74f4d8..8f4d2a66 100644 --- a/lib/Hook.js +++ b/lib/Hook.js @@ -1,3 +1,5 @@ +var Promise = require('bluebird'); + exports.trigger = function () { var args = Array.prototype.slice.apply(arguments); var self = args.shift(); @@ -11,15 +13,25 @@ exports.trigger = function () { exports.wait = function () { var args = Array.prototype.slice.apply(arguments); var self = args.shift(); - var cb = args.shift(); + var hook = args.shift(); var next = args.shift(); args.push(next); + if (typeof hook === "function") { + var hookValue = hook.apply(self, args); - if (typeof cb === "function") { - cb.apply(self, args); + var isHasCallback = hook.length < args.length; - if (cb.length < args.length) { + if (isHasCallback) { + if (hookValue instanceof Promise) { + return hookValue + .then(function () { + next(); + }) + .catch(function (err) { + next(err); + }); + } return next(); } } else { diff --git a/test/integration/hook-promise.js b/test/integration/hook-promise.js new file mode 100644 index 00000000..a8ad7503 --- /dev/null +++ b/test/integration/hook-promise.js @@ -0,0 +1,457 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var async = require('async'); +var Promise = require('bluebird'); + +describe("HookPromise", function() { + var db = null; + var Person = null; + var triggeredHooks = {}; + var getTimestamp; // Calling it 'getTime' causes strangeness. + + getTimestamp = function () { return Date.now(); }; + + var checkHook = function (hook) { + triggeredHooks[hook] = false; + + return function () { + triggeredHooks[hook] = getTimestamp(); + }; + }; + + var setup = function (hooks) { + if (typeof hooks == "undefined") { + hooks = { + afterCreate : checkHook("afterCreate"), + beforeCreate : checkHook("beforeCreate"), + afterSave : checkHook("afterSave"), + beforeSave : checkHook("beforeSave"), + beforeValidation : checkHook("beforeValidation"), + beforeRemove : checkHook("beforeRemove"), + afterRemove : checkHook("afterRemove") + }; + } + + return function (done) { + Person = db.define("person", { + name : String + }, { + hooks : hooks + }); + + Person.settings.set("instance.returnAllErrors", false); + + return helper.dropSync(Person, done); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe('afterCreate', function () { + beforeEach(setup()); + it("should trigger after model creation", function (done) { + var triggered = false; + + Person.afterCreate(function () { + return new Promise(function (resolve) { + setTimeout(function () { + triggered = true; + resolve(); + }, 700); + }); + }); + + Person.create([{ name: "John Doe" }], function () { + triggered.should.be.true; + return done(); + }); + }); + }); + + describe("beforeCreate", function () { + beforeEach(setup()); + it("should allow modification of instance", function (done) { + Person.beforeCreate(function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + self.name = "Hook Worked"; + resolve(); + }, 200); + }); + }); + + Person.create([{ }], function (err, people) { + should.not.exist(err); + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "Hook Worked"); + // garantee it was correctly saved on database + Person.one({ name: "Hook Worked" }, function (err, person) { + should.not.exist(err); + should.exist(person); + }); + done(); + }); + }); + + it("should trigger error", function (done) { + Person.beforeCreate(function () { + return new Promise(function (resolve, reject) { + setTimeout(function () { + reject(new Error('beforeCreate-error')); + }, 200); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function () { + done(new Error('Should throw err.')); + }) + .catch(function (err) { + err.should.be.a.Object(); + err.message.should.equal("beforeCreate-error"); + done(); + }); + }); + }); + + describe("beforeSave", function () { + beforeEach(setup()); + it("should trigger and wait before save hook", function (done) { + Person.beforeSave(function () { + return new Promise(function (_, reject) { + setTimeout(function () { + reject(new Error('beforeSave-error')); + }, 400); + }); + }); + + Person.createAsync([{ name: "Jane Doe" }]) + .then(function () { + done(new Error('Should throw error')); + }) + .catch(function (err) { + err.should.be.a.Object(); + err.message.should.equal("beforeSave-error"); + return done(); + }); + }); + + it("should trigger error when saving", function (done) { + Person.beforeSave(function () { + return new Promise(function (_, reject) { + setTimeout(function () { + reject(new Error('beforeSave-error')); + }, 400); + }); + }); + + Person.createAsync([{ name: "Jane Doe" }]) + .then(function (John) { + return John[0].saveAsync(); + }) + .catch(function (err) { + err.should.be.a.Object(); + err.message.should.equal("beforeSave-error"); + return done(); + }); + }); + + it("should trigger and wait", function (done) { + Person.beforeSave(function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + self.name = 'John Doe'; + resolve(); + }, 400); + }); + }); + + Person.createAsync([{ name: "Jane Doe" }]) + .then(function (John) { + return John[0].saveAsync(); + }) + .then(function (John) { + should.equal(John.name, 'John Doe'); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("afterSave", function () { + beforeEach(setup()); + it("should call and wait after save hook", function (done) { + var triggered = false; + + Person.afterSave(function () { + return new Promise(function (resolve) { + setTimeout(function () { + triggered = true; + resolve(); + }, 700); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function (Jhon) { + return Jhon[0].saveAsync(); + }) + .then(function () { + triggered.should.be.true; + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); + + describe("beforeValidation", function () { + beforeEach(setup()); + it("should trigger and wait", function (done) { + Person.beforeValidation(function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + self.name = "John Snow"; + resolve(); + }, 500); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function (Jhon) { + should.equal(Jhon[0].name, "John Snow"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should throw error", function (done) { + Person.beforeValidation(function () { + var self = this; + return new Promise(function (_, reject) { + setTimeout(function () { + self.name = "John Snow"; + reject(new Error("beforeValidation-error")); + }, 500); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function () { + done(new Error("Should throw error")); + }) + .catch(function (err) { + should.equal(err.message, "beforeValidation-error"); + done(); + }); + }) + }); + + describe("afterLoad", function () { + beforeEach(setup()); + it("should trigger and wait", function (done) { + var afterLoad = false; + Person.afterLoad(function () { + return new Promise(function (resolve) { + setTimeout(function () { + afterLoad = true; + resolve(); + }, 500); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function () { + afterLoad.should.be.true; + done(); + }) + .catch(function (err) { + console.log('hererere'); + done(err); + }); + }); + + it("should trigger and wait", function (done) { + var afterLoad = false; + Person.afterLoad(function () { + return new Promise(function (_, reject) { + setTimeout(function () { + afterLoad = true; + reject(new Error("afterLoad-error")); + }, 500); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function () { + afterLoad.should.be.true; + done(new Error("Should throw.")); + }) + .catch(function (err) { + err.should.exist; + err.message.should.equal("afterLoad-error"); + done(); + }); + }); + }); + + describe("afterAutoFetch", function () { + beforeEach(setup()); + it("should trigger and wait", function (done) { + var afterAutoFetch = false; + Person.afterAutoFetch(function () { + return new Promise(function (resolve) { + setTimeout(function () { + afterAutoFetch = true; + resolve(); + }, 500); + }); + }); + + Person.createAsync({ name: "John" }) + .then(function () { + afterAutoFetch.should.be.true; + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should trigger and wait", function (done) { + var afterAutoFetch = false; + Person.afterAutoFetch(function () { + return new Promise(function (_, reject) { + setTimeout(function () { + afterAutoFetch = true; + reject(new Error("afterAutoFetch-error")); + }, 500); + }); + }); + + Person.createAsync({ name: "John" }) + .then(function () { + done(new Error("Should throw error")); + }) + .catch(function (err) { + should.equal(err.message, "afterAutoFetch-error"); + done(); + }); + }); + }); + + describe("beforeRemove", function () { + before(setup()); + + it("should trigger and wait", function (done) { + var beforeRemove = false; + Person.beforeRemove(function () { + return new Promise(function (resolve) { + beforeRemove = true; + resolve(); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function (items) { + return items[0].removeAsync(); + }) + .then(function () { + beforeRemove.should.be.true; + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it("should throw error", function (done) { + Person.beforeRemove(function () { + return new Promise(function (_, reject) { + setTimeout(function () { + reject(new Error('beforeRemove-error')); + }, 600); + }); + }); + + Person.createAsync([{ name: "John Doe" }]) + .then(function (items) { + return items[0].removeAsync(); + }) + .then(function () { + done(new Error('Should throw error')); + }) + .catch(function (err) { + should.equal(err.message, 'beforeRemove-error'); + done(); + }); + }); + }); + + describe("instance modifications", function () { + before(setup({ + beforeValidation: function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + should.equal(self.name, "John Doe"); + self.name = "beforeValidation"; + resolve(); + }, 800); + }); + }, + beforeCreate: function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + should.equal(self.name, "beforeValidation"); + self.name = "beforeCreate"; + resolve(); + }, 700); + }); + }, + beforeSave: function () { + var self = this; + return new Promise(function (resolve) { + setTimeout(function () { + should.equal(self.name, "beforeCreate"); + self.name = "beforeSave"; + resolve(); + }, 500); + }); + } + })); + + it("should propagate down hooks", function (done) { + Person.createAsync([{ name: "John Doe" }]) + .then(function (people) { + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "beforeSave"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + }); +}); diff --git a/test/integration/hook.js b/test/integration/hook.js index 596dce3f..77dfe346 100644 --- a/test/integration/hook.js +++ b/test/integration/hook.js @@ -1,16 +1,16 @@ -var _ = require('lodash'); -var should = require('should'); -var helper = require('../support/spec_helper'); -var async = require('async'); -var ORM = require('../../'); +var should = require('should'); +var helper = require('../support/spec_helper'); +var async = require('async'); -describe("Hook", function() { +describe("Hook", function () { var db = null; var Person = null; var triggeredHooks = {}; var getTimestamp; // Calling it 'getTime' causes strangeness. - getTimestamp = function () { return Date.now(); }; + getTimestamp = function () { + return Date.now(); + }; // this next lines are failing... // if (process.hrtime) { // getTimestamp = function () { return parseFloat(process.hrtime().join('.')); }; @@ -29,21 +29,21 @@ describe("Hook", function() { var setup = function (hooks) { if (typeof hooks == "undefined") { hooks = { - afterCreate : checkHook("afterCreate"), - beforeCreate : checkHook("beforeCreate"), - afterSave : checkHook("afterSave"), - beforeSave : checkHook("beforeSave"), - beforeValidation : checkHook("beforeValidation"), - beforeRemove : checkHook("beforeRemove"), - afterRemove : checkHook("afterRemove") + afterCreate: checkHook("afterCreate"), + beforeCreate: checkHook("beforeCreate"), + afterSave: checkHook("afterSave"), + beforeSave: checkHook("beforeSave"), + beforeValidation: checkHook("beforeValidation"), + beforeRemove: checkHook("beforeRemove"), + afterRemove: checkHook("afterRemove") }; } return function (done) { Person = db.define("person", { - name : String + name: String }, { - hooks : hooks + hooks: hooks }); Person.settings.set("instance.returnAllErrors", false); @@ -65,7 +65,7 @@ describe("Hook", function() { }); describe("after Model creation", function () { - before(setup({})); + before(setup()); it("can be changed", function (done) { var triggered = false; @@ -73,9 +73,9 @@ describe("Hook", function() { Person.afterCreate(function () { triggered = true; }); + Person.create([{ name: "John Doe" }], function () { triggered.should.be.true; - return done(); }); }); @@ -116,36 +116,36 @@ describe("Hook", function() { }); it("should allow modification of instance", function (done) { - Person.beforeCreate(function (next) { - this.name = "Hook Worked"; - next(); - }); + Person.beforeCreate(function (next) { + this.name = "Hook Worked"; + next(); + }); - Person.create([{ }], function (err, people) { - should.not.exist(err); - should.exist(people); - should.equal(people.length, 1); - should.equal(people[0].name, "Hook Worked"); + Person.create([{}], function (err, people) { + should.not.exist(err); + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "Hook Worked"); - // garantee it was correctly saved on database - Person.one({ name: "Hook Worked" }, function (err, person) { - should.not.exist(err); - should.exist(person); + // garantee it was correctly saved on database + Person.one({ name: "Hook Worked" }, function (err, person) { + should.not.exist(err); + should.exist(person); - return done(); - }); + return done(); }); + }); }); describe("when setting properties", function () { before(setup({ - beforeCreate : function () { + beforeCreate: function () { this.name = "Jane Doe"; } })); it("should not be discarded", function (done) { - Person.create([{ }], function (err, items) { + Person.create([{}], function (err, items) { should.equal(err, null); items.should.be.a.Object(); @@ -167,7 +167,7 @@ describe("Hook", function() { var beforeCreate = false; before(setup({ - beforeCreate : function (next) { + beforeCreate: function (next) { setTimeout(function () { beforeCreate = true; @@ -186,7 +186,7 @@ describe("Hook", function() { describe("if hook triggers error", function () { before(setup({ - beforeCreate : function (next) { + beforeCreate: function (next) { setTimeout(function () { return next(new Error('beforeCreate-error')); }, 200); @@ -233,15 +233,15 @@ describe("Hook", function() { }); it("should allow modification of instance", function (done) { - Person.beforeSave(function () { - this.name = "Hook Worked"; - }); + Person.beforeSave(function () { + this.name = "Hook Worked"; + }); - Person.create([{ name: "John Doe" }], function (err, people) { - should.not.exist(err); - should.exist(people); - should.equal(people.length, 1); - should.equal(people[0].name, "Hook Worked"); + Person.create([{ name: "John Doe" }], function (err, people) { + should.not.exist(err); + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "Hook Worked"); // garantee it was correctly saved on database Person.find({ name: "Hook Worked" }, { identityCache: false }, 1, function (err, people) { @@ -250,18 +250,18 @@ describe("Hook", function() { return done(); }); - }); + }); }); describe("when setting properties", function () { before(setup({ - beforeSave : function () { + beforeSave: function () { this.name = "Jane Doe"; } })); it("should not be discarded", function (done) { - Person.create([{ }], function (err, items) { + Person.create([{}], function (err, items) { should.equal(err, null); items.should.be.a.Object(); @@ -283,7 +283,7 @@ describe("Hook", function() { var beforeSave = false; before(setup({ - beforeSave : function (next) { + beforeSave: function (next) { setTimeout(function () { beforeSave = true; @@ -303,7 +303,7 @@ describe("Hook", function() { describe("if hook triggers error", function () { before(setup({ - beforeSave : function (next) { + beforeSave: function (next) { if (this.name == "John Doe") { return next(); } @@ -400,24 +400,24 @@ describe("Hook", function() { it("should allow modification of instance", function (done) { - Person.beforeValidation(function () { - this.name = "Hook Worked"; - }); + Person.beforeValidation(function () { + this.name = "Hook Worked"; + }); - Person.create([{ name: "John Doe" }], function (err, people) { - should.not.exist(err); - should.exist(people); - should.equal(people.length, 1); - should.equal(people[0].name, "Hook Worked"); - done(); - }); + Person.create([{ name: "John Doe" }], function (err, people) { + should.not.exist(err); + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "Hook Worked"); + done(); + }); }); describe("if hook method has 1 argument", function () { var beforeValidation = false; before(setup({ - beforeValidation : function (next) { + beforeValidation: function (next) { setTimeout(function () { beforeValidation = true; @@ -483,7 +483,7 @@ describe("Hook", function() { var afterLoad = false; before(setup({ - afterLoad : function (next) { + afterLoad: function (next) { setTimeout(function () { afterLoad = true; @@ -502,7 +502,7 @@ describe("Hook", function() { describe("if hook returns an error", function () { before(setup({ - afterLoad : function (next) { + afterLoad: function (next) { return next(new Error("AFTERLOAD_FAIL")); } })); @@ -540,7 +540,7 @@ describe("Hook", function() { var afterAutoFetch = false; before(setup({ - afterAutoFetch : function (next) { + afterAutoFetch: function (next) { setTimeout(function () { afterAutoFetch = true; @@ -559,7 +559,7 @@ describe("Hook", function() { describe("if hook returns an error", function () { before(setup({ - afterAutoFetch : function (next) { + afterAutoFetch: function (next) { return next(new Error("AFTERAUTOFETCH_FAIL")); } })); @@ -595,7 +595,7 @@ describe("Hook", function() { var beforeRemove = false; before(setup({ - beforeRemove : function (next) { + beforeRemove: function (next) { setTimeout(function () { beforeRemove = true; @@ -617,7 +617,7 @@ describe("Hook", function() { describe("if hook triggers error", function () { before(setup({ - beforeRemove : function (next) { + beforeRemove: function (next) { setTimeout(function () { return next(new Error('beforeRemove-error')); }, 200); @@ -657,12 +657,12 @@ describe("Hook", function() { describe("if model has autoSave", function () { before(function (done) { Person = db.define("person", { - name : String, - surname : String + name: String, + surname: String }, { - autoSave : true, - hooks : { - afterSave : checkHook("afterSave") + autoSave: true, + hooks: { + afterSave: checkHook("afterSave") } }); @@ -672,7 +672,7 @@ describe("Hook", function() { }); it("should trigger for single property changes", function (done) { - Person.create({ name : "John", surname : "Doe" }, function (err, John) { + Person.create({ name: "John", surname: "Doe" }, function (err, John) { should.equal(err, null); triggeredHooks.afterSave.should.be.a.Number(); @@ -690,29 +690,29 @@ describe("Hook", function() { }); describe("instance modifications", function () { - before(setup({ - beforeValidation: function () { - should.equal(this.name, "John Doe"); - this.name = "beforeValidation"; - }, - beforeCreate: function () { - should.equal(this.name, "beforeValidation"); - this.name = "beforeCreate"; - }, - beforeSave: function () { - should.equal(this.name, "beforeCreate"); - this.name = "beforeSave"; - } - })); + before(setup({ + beforeValidation: function () { + should.equal(this.name, "John Doe"); + this.name = "beforeValidation"; + }, + beforeCreate: function () { + should.equal(this.name, "beforeValidation"); + this.name = "beforeCreate"; + }, + beforeSave: function () { + should.equal(this.name, "beforeCreate"); + this.name = "beforeSave"; + } + })); - it("should propagate down hooks", function (done) { - Person.create([{ name: "John Doe" }], function (err, people) { - should.not.exist(err); - should.exist(people); - should.equal(people.length, 1); - should.equal(people[0].name, "beforeSave"); - done(); - }); + it("should propagate down hooks", function (done) { + Person.create([{ name: "John Doe" }], function (err, people) { + should.not.exist(err); + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "beforeSave"); + done(); }); + }); }); }); From 45553e8e84193945806eca886f3f4dd67ff36926 Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 6 Sep 2017 17:08:33 +0300 Subject: [PATCH 44/98] add Promise checking via chack that "then" is a function --- lib/Hook.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Hook.js b/lib/Hook.js index 8f4d2a66..2cb4256b 100644 --- a/lib/Hook.js +++ b/lib/Hook.js @@ -20,17 +20,16 @@ exports.wait = function () { if (typeof hook === "function") { var hookValue = hook.apply(self, args); - var isHasCallback = hook.length < args.length; + var hookDoesntExpectCallback = hook.length < args.length; + var isPromise = hookValue && typeof(hookValue.then) === "function"; - if (isHasCallback) { - if (hookValue instanceof Promise) { + if (hookDoesntExpectCallback) { + if (isPromise) { return hookValue .then(function () { next(); }) - .catch(function (err) { - next(err); - }); + .catch(next); } return next(); } From ecc74a493362c0feb3f12d1f8249217ceabbb80c Mon Sep 17 00:00:00 2001 From: mradko Date: Fri, 8 Sep 2017 11:25:37 +0300 Subject: [PATCH 45/98] adjust code style --- lib/Hook.js | 4 +- test/integration/hook-promise.js | 70 +++++++++++++------------------- 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/lib/Hook.js b/lib/Hook.js index 2cb4256b..22285460 100644 --- a/lib/Hook.js +++ b/lib/Hook.js @@ -1,5 +1,3 @@ -var Promise = require('bluebird'); - exports.trigger = function () { var args = Array.prototype.slice.apply(arguments); var self = args.shift(); @@ -13,7 +11,7 @@ exports.trigger = function () { exports.wait = function () { var args = Array.prototype.slice.apply(arguments); var self = args.shift(); - var hook = args.shift(); + var hook = args.shift(); var next = args.shift(); args.push(next); diff --git a/test/integration/hook-promise.js b/test/integration/hook-promise.js index a8ad7503..fb2575f7 100644 --- a/test/integration/hook-promise.js +++ b/test/integration/hook-promise.js @@ -71,10 +71,12 @@ describe("HookPromise", function() { }); }); - Person.create([{ name: "John Doe" }], function () { - triggered.should.be.true; - return done(); - }); + Person.createAsync([{ name: "John Doe" }]) + .then(function () { + triggered.should.be.true; + done(); + }) + .catch(done); }); }); @@ -91,18 +93,19 @@ describe("HookPromise", function() { }); }); - Person.create([{ }], function (err, people) { - should.not.exist(err); - should.exist(people); - should.equal(people.length, 1); - should.equal(people[0].name, "Hook Worked"); - // garantee it was correctly saved on database - Person.one({ name: "Hook Worked" }, function (err, person) { - should.not.exist(err); - should.exist(person); - }); - done(); - }); + Person.createAsync([{ }]) + .then(function (people) { + should.exist(people); + should.equal(people.length, 1); + should.equal(people[0].name, "Hook Worked"); + // garantee it was correctly saved on database + Person.one({ name: "Hook Worked" }, function (err, person) { + should.not.exist(err); + should.exist(person); + }); + done(); + }) + .catch(done); }); it("should trigger error", function (done) { @@ -148,7 +151,7 @@ describe("HookPromise", function() { }); }); - it("should trigger error when saving", function (done) { + it("should trigger error", function (done) { Person.beforeSave(function () { return new Promise(function (_, reject) { setTimeout(function () { @@ -187,9 +190,7 @@ describe("HookPromise", function() { should.equal(John.name, 'John Doe'); done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); }); @@ -215,9 +216,7 @@ describe("HookPromise", function() { triggered.should.be.true; done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); }); @@ -239,9 +238,7 @@ describe("HookPromise", function() { should.equal(Jhon[0].name, "John Snow"); done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); it("should throw error", function (done) { @@ -284,13 +281,10 @@ describe("HookPromise", function() { afterLoad.should.be.true; done(); }) - .catch(function (err) { - console.log('hererere'); - done(err); - }); + .catch(done); }); - it("should trigger and wait", function (done) { + it("should throw error", function (done) { var afterLoad = false; Person.afterLoad(function () { return new Promise(function (_, reject) { @@ -332,12 +326,10 @@ describe("HookPromise", function() { afterAutoFetch.should.be.true; done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); - it("should trigger and wait", function (done) { + it("should throw error", function (done) { var afterAutoFetch = false; Person.afterAutoFetch(function () { return new Promise(function (_, reject) { @@ -379,9 +371,7 @@ describe("HookPromise", function() { beforeRemove.should.be.true; done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); it("should throw error", function (done) { @@ -449,9 +439,7 @@ describe("HookPromise", function() { should.equal(people[0].name, "beforeSave"); done(); }) - .catch(function (err) { - done(err); - }); + .catch(done); }); }); }); From 4d09077b039aad6b9a15d182041c24200fec292c Mon Sep 17 00:00:00 2001 From: Arek W Date: Mon, 11 Sep 2017 10:36:58 +0000 Subject: [PATCH 46/98] Fix broken tests --- test/integration/db.js | 10 +++++++--- test/integration/orm-exports.js | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/integration/db.js b/test/integration/db.js index 3b5284cd..af06368a 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -313,7 +313,11 @@ describe('DB', function () { only: ['name', 'id'], keys: ['id'], }, - expectedQuery: 'SELECT "t1"."name", "t1"."id", "t2"."dog_id" AS "$p" FROM "dog" "t1" JOIN "dog_family" "t2" ON "t2"."family_id" = "t1"."id" WHERE "t2"."dog_id" IN (1, 5)' + expectedQuery: { + postgres: 'SELECT "t1"."name", "t1"."id", "t2"."dog_id" AS "$p" FROM "dog" "t1" JOIN "dog_family" "t2" ON "t2"."family_id" = "t1"."id" WHERE "t2"."dog_id" IN (1, 5)', + mysql: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)', + sqlite: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)' + } }; describe('cb', function () { @@ -329,7 +333,7 @@ describe('DB', function () { done(err); } should.equal(execSimpleQueryStub.calledOnce, true); - should.equal(execSimpleQueryStub.calledWith(fixture.expectedQuery), true); + should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); execSimpleQueryStub.restore(); done(); }); @@ -345,7 +349,7 @@ describe('DB', function () { db.driver.eagerQueryAsync(fixture.association, fixture.opts, [ 1, 5 ]) .then(function () { should.equal(execSimpleQueryStub.calledOnce, true); - should.equal(execSimpleQueryStub.calledWith(fixture.expectedQuery), true); + should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); execSimpleQueryStub.restore(); done(); }) diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index dd5ba615..e5fce2d2 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -53,12 +53,12 @@ describe("ORM", function() { ORM.connectAsync.should.be.a.Function() }); - it('should throw error with correct message when protocol not supported', function () { + it('should throw error with correct message when protocol not supported', function (done) { ORM.connectAsync("pg://127.0.0.6") .then(function () { done('Fail.'); }) - .catch(function () { + .catch(function (err) { should.exist(err); err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); done(); From c43910f0284bca7202739dad112779a023e2d2de Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 11 Sep 2017 17:52:28 +0300 Subject: [PATCH 47/98] add useAsync and loadAsync --- lib/ORM.js | 62 ++-- test/integration/db.js | 604 ++++++++++++++++++-------------- test/integration/orm-exports.js | 39 ++- 3 files changed, 416 insertions(+), 289 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index 5cee3f1c..176f1bd2 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -39,6 +39,19 @@ var optsChecker = function (opts) { return [OPTS_TYPE_STRING, OPTS_TYPE_OBJ].some(function (element) { return typeof(opts) === element }) }; +var fileLoader = function (filePaths, cb) { + var self = this; + var iterator = function (filePath, cb) { + try { + require(filePath)(self, cb); + } catch (err) { + return cb(err) + } + }; + + async.eachSeries(filePaths, iterator, cb); +}; + var connect = function (opts, cb) { if (arguments.length === 0 || !opts || !optsChecker(opts)) { cb = typeof(cb) !== 'function' ? opts : cb; @@ -123,16 +136,7 @@ var connect = function (opts, cb) { return db; }; -exports.Text = Query.Text; -for (var k in Query.Comparators) { - exports[Query.Comparators[k]] = Query[Query.Comparators[k]]; -} - -exports.express = function () { - return require("./Express").apply(this, arguments); -}; - -exports.use = function (connection, proto, opts, cb) { +var use = function (connection, proto, opts, cb) { if (DriverAliases[proto]) { proto = DriverAliases[proto]; } @@ -153,8 +157,20 @@ exports.use = function (connection, proto, opts, cb) { } catch (ex) { return cb(ex); } +} + +exports.Text = Query.Text; +for (var k in Query.Comparators) { + exports[Query.Comparators[k]] = Query[Query.Comparators[k]]; +} + +exports.express = function () { + return require("./Express").apply(this, arguments); }; +exports.use = use; +exports.useAsync = Promise.promisify(use); + /** * * @param opts @@ -226,7 +242,7 @@ ORM.prototype.use = function (plugin_const, opts) { return this; }; ORM.prototype.define = function (name, properties, opts) { - var i; + var i; properties = properties || {}; opts = opts || {}; @@ -290,7 +306,6 @@ ORM.prototype.closeAsync = Promise.promisify(ORM.prototype.close); ORM.prototype.load = function () { var files = _.flatten(Array.prototype.slice.apply(arguments)); - var self = this; var cb = function () {}; if (typeof files[files.length - 1] == "function") { @@ -307,15 +322,22 @@ ORM.prototype.load = function () { }()); } - var iterator = function (filePath, cb) { - try { - require(filePath)(self, cb); - } catch (err) { - return cb(err) - } - }; + fileLoader.call(this, filesWithPath, cb); +}; + +ORM.prototype.loadAsync = function () { + var files = _.flatten(Array.prototype.slice.apply(arguments)); + var filesWithPath = []; + // Due to intricacies of `Utilities.getRealPath` the following + // code has to look as it does. + + for(var a = 0; a < files.length; a++) { + filesWithPath.push(function () { + return Utilities.getRealPath(files[a], 4) + }()); + } - async.eachSeries(filesWithPath, iterator, cb); + return Promise.promisify(fileLoader, { context: this })(filesWithPath) }; ORM.prototype.sync = function (cb) { diff --git a/test/integration/db.js b/test/integration/db.js index af06368a..804c6986 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -1,146 +1,364 @@ var should = require('should'); +var path = require('path'); var helper = require('../support/spec_helper'); -var sinon = require('sinon'); +var sinon = require('sinon'); var common = require('../common'); -describe('DB', function () { +describe("db.driver", function () { var db = null; - beforeEach(function (done) { + + before(function (done) { helper.connect(function (connection) { db = connection; - return done(); + var Log = db.define('log', { + what: { type: 'text' }, + when: { type: 'date', time: true }, + who: { type: 'text' } + }); + + helper.dropSync(Log, function (err) { + if (err) return done(err); + + Log.create([ + { what: "password reset", when: new Date('2013/04/07 12:33:05'), who: "jane" }, + { what: "user login", when: new Date('2013/04/07 13:01:44'), who: "jane" }, + { what: "user logout", when: new Date('2013/05/12 04:09:31'), who: "john" } + ], done); + }); }); }); - afterEach(function () { + after(function () { return db.close(); }); - describe('db.syncPromise()', function () { - it('should call sync for each model', function (done) { - db.define("my_model", { - property: String + it("should be available", function () { + should.exist(db.driver); + }); + + if (common.protocol() == "mongodb") return; + + describe("query", function () { + it("should be available", function () { + should.exist(db.driver.query); + }); + + describe('#execQueryAsync', function () { + it('should execute sql queries', function (done) { + db.driver.execQueryAsync('SELECT id FROM log') + .then(function (data) { + should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); + done() + }) + .catch(function (err) { + done(err); + }); }); - db.define("my_model2", { - property: String + + it("should escape sql queries", function (done) { + var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; + var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; + db.driver.execQueryAsync(query, args) + .then(function (data) { + should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); + done(); + }) + .catch(function (err) { + done(err); + }); }); - var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { - cb(null, {}) + }); + + describe('#eagerQuery', function () { + var fixture = { + association: { + model: { + table: 'dog' + }, + field: { + dog_id: { + type: 'serial', + key: true, + required: false, + klass: 'primary', + enumerable: true, + mapsTo: 'dog_id', + name: 'dog_id' + } + }, + mergeAssocId: { + family_id: { + type: 'integer', + required: true, + klass: 'primary', + enumerable: true, + mapsTo: 'family_id', + name: 'family_id' + } + }, + mergeTable: 'dog_family', + }, + opts: { + only: ['name', 'id'], + keys: ['id'], + }, + expectedQuery: { + postgres: 'SELECT "t1"."name", "t1"."id", "t2"."dog_id" AS "$p" FROM "dog" "t1" JOIN "dog_family" "t2" ON "t2"."family_id" = "t1"."id" WHERE "t2"."dog_id" IN (1, 5)', + mysql: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)', + sqlite: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)' + } + }; + + describe('cb', function () { + it('should build correct query', function (done) { + var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') + .callsFake(function (q, cb) { + cb(); + }); + + db.driver.eagerQuery(fixture.association, fixture.opts, [1, 5], function (err, data) { + if (err) { + execSimpleQueryStub.restore(); + done(err); + } + should.equal(execSimpleQueryStub.calledOnce, true); + should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); + execSimpleQueryStub.restore(); + done(); + }); + }); }); - var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { - cb(null, {}) + + describe('async', function () { + it('should build correct query', function (done) { + var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') + .callsFake(function (q, cb) { + cb(); + }); + db.driver.eagerQueryAsync(fixture.association, fixture.opts, [1, 5]) + .then(function () { + should.equal(execSimpleQueryStub.calledOnce, true); + should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); + execSimpleQueryStub.restore(); + done(); + }) + .catch(function (err) { + execSimpleQueryStub.restore(); + done(err); + }); + }); }); - db.syncPromise() - .then(function () { - should.equal(syncStub.calledOnce, true); - should.equal(syncStub2.calledOnce, true); + }); + + describe("#execQuery", function () { + it("should execute sql queries", function (done) { + db.driver.execQuery("SELECT id FROM log", function (err, data) { + should.not.exist(err); + + should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); + done(); + }); + }); + + it("should escape sql queries", function (done) { + var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; + var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; + db.driver.execQuery(query, args, function (err, data) { + should.not.exist(err); + + should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); done(); - }) - .catch(function (err) { - done(err) }); + }); }); }); - describe("db.dropAsync()", function () { - it('should should call drop for each model', function (done) { - db.define("my_model", { - property: String - }); + describe('DB', function () { + var db = null; + beforeEach(function (done) { + helper.connect(function (connection) { + db = connection; - db.define("my_model2", { - property: String + return done(); }); + }); - var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { - cb(null, {}) - }); - var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { - cb(null, {}) + afterEach(function () { + return db.close(); + }); + + describe('db.syncPromise()', function () { + it('should call sync for each model', function (done) { + db.define("my_model", { + property: String + }); + db.define("my_model2", { + property: String + }); + var syncStub = sinon.stub(db.models['my_model'], 'sync').callsFake(function (cb) { + cb(null, {}) + }); + var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { + cb(null, {}) + }); + db.syncPromise() + .then(function () { + should.equal(syncStub.calledOnce, true); + should.equal(syncStub2.calledOnce, true); + done(); + }) + .catch(function (err) { + done(err) + }); }); - db.dropAsync() - .then(function () { - should.equal(dropStub.calledOnce, true); - should.equal(dropStub2.calledOnce, true); - done(); - }) - .catch(function (err) { - done(err) + }); + + describe("db.dropAsync()", function () { + it('should should call drop for each model', function (done) { + db.define("my_model", { + property: String + }); + + db.define("my_model2", { + property: String }); + + var dropStub = sinon.stub(db.models['my_model'], 'drop').callsFake(function (cb) { + cb(null, {}) + }); + var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { + cb(null, {}) + }); + db.dropAsync() + .then(function () { + should.equal(dropStub.calledOnce, true); + should.equal(dropStub2.calledOnce, true); + done(); + }) + .catch(function (err) { + done(err) + }); + }); }); - }); - describe("db.use()", function () { - it("should be able to register a plugin", function (done) { - var MyPlugin = require("../support/my_plugin"); - var opts = { - option : true, - calledDefine : false - }; + describe("db.use()", function () { + it("should be able to register a plugin", function (done) { + var MyPlugin = require("../support/my_plugin"); + var opts = { + option: true, + calledDefine: false + }; + + db.use(MyPlugin, opts); - db.use(MyPlugin, opts); + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); + + opts.calledDefine.should.be.true; - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + return done(); }); - opts.calledDefine.should.be.true; + it("a plugin should be able to catch models before defining them", function (done) { + var MyPlugin = require("../support/my_plugin"); + var opts = { + option: true, + calledDefine: false, + beforeDefine: function (name, props, opts) { + props.otherprop = Number; + } + }; - return done(); - }); + db.use(MyPlugin, opts); - it("a plugin should be able to catch models before defining them", function (done) { - var MyPlugin = require("../support/my_plugin"); - var opts = { - option : true, - calledDefine : false, - beforeDefine : function (name, props, opts) { - props.otherprop = Number; - } - }; + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); - db.use(MyPlugin, opts); + opts.calledDefine.should.be.true; + MyModel.properties.should.have.property("otherprop"); - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + done(); }); - opts.calledDefine.should.be.true; - MyModel.properties.should.have.property("otherprop"); + it("should be able to register a plugin as string", function (done) { + var opts = { + option: true, + calledDefine: false + }; - done(); - }); + db.use("../support/my_plugin", opts); - it("should be able to register a plugin as string", function (done) { - var opts = { - option : true, - calledDefine : false - }; + var MyModel = db.define("my_model", { // db.define should call plugin.define method + property: String + }); - db.use("../support/my_plugin", opts); + opts.calledDefine.should.be.true; - var MyModel = db.define("my_model", { // db.define should call plugin.define method - property: String + return done(); }); + }); - opts.calledDefine.should.be.true; + describe("db.define()", function () { + it("should use setting model.namePrefix as table prefix if defined", function (done) { + db.settings.set("model.namePrefix", "orm_"); - return done(); + var Person = db.define("person", { + name: String + }); + + Person.table.should.equal("orm_person"); + + return done(); + }); }); }); - describe("db.define()", function() { - it("should use setting model.namePrefix as table prefix if defined", function (done) { - db.settings.set("model.namePrefix", "orm_"); + describe("db.loadAsync()", function () { + it("should require a file if array", function (done) { + var filePath = "../support/spec_load"; + db.loadAsync([filePath]) + .then(function () { + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + done() + }) + .catch(done); + }); - var Person = db.define("person", { - name: String - }); + it("should require a file if single file path string", function (done) { + var filePath = "../support/spec_load"; + db.loadAsync(filePath) + .then(function () { + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + done() + }) + .catch(done); + }); + + it("should be able to load more than one file", function (done) { + var filePaths = ["../support/spec_load_second", "../support/spec_load_third"]; - Person.table.should.equal("orm_person"); + db.loadAsync(filePaths) + .then(function () { + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + done() + }) + .catch(done); + }); - return done(); + it("should throw error if files passed like arguments", function (done) { + db.loadAsync("../support/spec_load_second", "../support/spec_load_third") + .then(function () { + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + done() + }) + .catch(done); }); }); @@ -160,6 +378,16 @@ describe('DB', function () { db.load("../support/spec_load_second", "../support/spec_load_third", function (err) { should.not.exist(err); + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + return done(); + }); + }); + }); + + describe("db.load()", function () { + it("should be able to load more than one file", function (done) { + db.load("../support/spec_load_second", "../support/spec_load_third", function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); @@ -168,9 +396,19 @@ describe('DB', function () { }); it("should be able to load more than one file passed as Array", function (done) { - db.load([ "../support/spec_load_second", "../support/spec_load_third" ], function (err) { + db.load(["../support/spec_load_second", "../support/spec_load_third"], function (err) { should.not.exist(err); + db.models.should.have.property("person"); + db.models.should.have.property("pet"); + return done(); + }); + }); + }); + + describe("db.load()", function () { + it("should be able to load more than one file passed as Array", function (done) { + db.load(["../support/spec_load_second", "../support/spec_load_third"], function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); @@ -182,17 +420,17 @@ describe('DB', function () { describe("db.serial()", function () { it("should be able to execute chains in serial", function (done) { var Person = db.define("person", { - name : String, - surname : String + name: String, + surname: String }); helper.dropSync(Person, function () { Person.create([ - { name : "John", surname : "Doe" }, - { name : "Jane", surname : "Doe" } + { name: "John", surname: "Doe" }, + { name: "Jane", surname: "Doe" } ], function () { db.serial( - Person.find({ surname : "Doe" }), - Person.find({ name : "John" }) + Person.find({ surname: "Doe" }), + Person.find({ name: "John" }) ).get(function (err, DoeFamily, JohnDoe) { should.equal(err, null); @@ -214,174 +452,4 @@ describe('DB', function () { }); }); - describe("db.driver", function () { - var db = null; - - before(function (done) { - helper.connect(function (connection) { - db = connection; - - var Log = db.define('log', { - what : { type: 'text' }, - when : { type: 'date', time: true }, - who : { type: 'text' } - }); - - helper.dropSync(Log, function (err) { - if (err) return done(err); - - Log.create([ - { what: "password reset", when: new Date('2013/04/07 12:33:05'), who: "jane" }, - { what: "user login", when: new Date('2013/04/07 13:01:44'), who: "jane" }, - { what: "user logout", when: new Date('2013/05/12 04:09:31'), who: "john" } - ], done); - }); - }); - }); - - after(function () { - return db.close(); - }); - - it("should be available", function () { - should.exist(db.driver); - }); - - if (common.protocol() == "mongodb") return; - - describe("query", function () { - it("should be available", function () { - should.exist(db.driver.query); - }); - - describe('#execQueryAsync', function () { - it('should execute sql queries', function (done) { - db.driver.execQueryAsync('SELECT id FROM log') - .then(function (data) { - should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); - done() - }) - .catch(function (err) { - done(err); - }); - }); - - it("should escape sql queries", function (done) { - var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; - var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; - db.driver.execQueryAsync(query, args) - .then(function (data) { - should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); - done(); - }) - .catch(function (err) { - done(err); - }); - }); - }); - - describe('#eagerQuery', function () { - var fixture = { - association: { - model: { - table: 'dog' - }, - field: { - dog_id: { - type: 'serial', - key: true, - required: false, - klass: 'primary', - enumerable: true, - mapsTo: 'dog_id', - name: 'dog_id' - } - }, - mergeAssocId: { - family_id: { - type: 'integer', - required: true, - klass: 'primary', - enumerable: true, - mapsTo: 'family_id', - name: 'family_id' - } - }, - mergeTable: 'dog_family', - }, - opts: { - only: ['name', 'id'], - keys: ['id'], - }, - expectedQuery: { - postgres: 'SELECT "t1"."name", "t1"."id", "t2"."dog_id" AS "$p" FROM "dog" "t1" JOIN "dog_family" "t2" ON "t2"."family_id" = "t1"."id" WHERE "t2"."dog_id" IN (1, 5)', - mysql: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)', - sqlite: 'SELECT `t1`.`name`, `t1`.`id`, `t2`.`dog_id` AS `$p` FROM `dog` `t1` JOIN `dog_family` `t2` ON `t2`.`family_id` = `t1`.`id` WHERE `t2`.`dog_id` IN (1, 5)' - } - }; - - describe('cb', function () { - it('should build correct query', function (done) { - var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') - .callsFake(function (q, cb) { - cb(); - }); - - db.driver.eagerQuery(fixture.association, fixture.opts, [ 1, 5 ], function (err, data) { - if (err) { - execSimpleQueryStub.restore(); - done(err); - } - should.equal(execSimpleQueryStub.calledOnce, true); - should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); - execSimpleQueryStub.restore(); - done(); - }); - }); - }); - - describe('async', function () { - it('should build correct query', function (done) { - var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') - .callsFake(function (q, cb) { - cb(); - }); - db.driver.eagerQueryAsync(fixture.association, fixture.opts, [ 1, 5 ]) - .then(function () { - should.equal(execSimpleQueryStub.calledOnce, true); - should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); - execSimpleQueryStub.restore(); - done(); - }) - .catch(function (err) { - execSimpleQueryStub.restore(); - done(err); - }); - }); - }); - }); - - describe("#execQuery", function () { - it("should execute sql queries", function (done) { - db.driver.execQuery("SELECT id FROM log", function (err, data) { - should.not.exist(err); - - should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); - done(); - }); - }); - - it("should escape sql queries", function (done) { - var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; - var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; - db.driver.execQuery(query, args, function (err, data) { - should.not.exist(err); - - should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); - done(); - }); - }); - }); - }); - }); -}); +}); \ No newline at end of file diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index e5fce2d2..c613ca90 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -12,7 +12,9 @@ describe("ORM", function() { it("should expose .express(), .use() and .connect()", function (done) { ORM.express.should.be.a.Function(); ORM.use.should.be.a.Function(); + ORM.useAsync.should.be.a.Function(); ORM.connect.should.be.a.Function(); + ORM.connectAsync.should.be.a.Function(); return done(); }); @@ -54,7 +56,7 @@ describe("ORM", function() { }); it('should throw error with correct message when protocol not supported', function (done) { - ORM.connectAsync("pg://127.0.0.6") + ORM.connectAsync("bd://127.0.0.6") .then(function () { done('Fail.'); }) @@ -599,4 +601,39 @@ describe("ORM", function() { }); }); }); + + describe("ORM.useAsync()", function () { + it("should be able to use an established connection", function (done) { + var db = new sqlite.Database(':memory:'); + + ORM.useAsync(db, "sqlite") + .then(function () { + done(); + }) + .catch(done); + }); + + it("should be accept protocol alias", function (done) { + var db = new pg.Client(); + + ORM.useAsync(db, "pg") + .then(function () { + done(); + }) + .catch(done); + }); + + it("should throw an error in callback if protocol not supported", function (done) { + var db = new pg.Client(); + + ORM.useAsync(db, "unknowndriver") + .then(function () { + done(new Error('Should throw')); + }) + .catch(function (err) { + should.exist(err); + done(); + }); + }); + }); }); \ No newline at end of file From 53db079d5b5410be55616c55c6556237aee62a78 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 11:42:47 +0300 Subject: [PATCH 48/98] Promisified neccessary functions --- lib/ChainFind.js | 33 ++-- test/integration/model-find-chain-async.js | 194 +++++++++++++++++++++ 2 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 test/integration/model-find-chain-async.js diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 230679c7..b1e52498 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -2,8 +2,7 @@ var _ = require("lodash"); var async = require("async"); var Utilities = require("./Utilities"); var ChainInstance = require("./ChainInstance"); -var Promise = require("./Promise").Promise; -var Bluebird = require("bluebird"); +var Promise = require("bluebird"); module.exports = ChainFind; @@ -90,8 +89,6 @@ function ChainFind(Model, opts) { }); } - var chainRunAsync = Bluebird.promisify(chainRun); - var promise = null; var chain = { find: function () { @@ -151,9 +148,9 @@ function ChainFind(Model, opts) { opts.order = []; } if (property[0] === "-") { - opts.order.push([ property.substr(1), "Z" ]); + opts.order.push([property.substr(1), "Z"]); } else { - opts.order.push([ property, (order && order.toUpperCase() === "Z" ? "Z" : "A") ]); + opts.order.push([property, (order && order.toUpperCase() === "Z" ? "Z" : "A")]); } return this; }, @@ -161,7 +158,7 @@ function ChainFind(Model, opts) { if (!Array.isArray(opts.order)) { opts.order = []; } - opts.order.push([ str, args || [] ]); + opts.order.push([str, args || []]); return this; }, count: function (cb) { @@ -209,6 +206,7 @@ function ChainFind(Model, opts) { }); return this; }, + first: function (cb) { return this.run(function (err, items) { return cb(err, items && items.length > 0 ? items[0] : null); @@ -226,21 +224,6 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, - runAsync: chainRunAsync, - success: function (cb) { - if (!promise) { - promise = new Promise(); - promise.handle(this.all); - } - return promise.success(cb); - }, - fail: function (cb) { - if (!promise) { - promise = new Promise(); - promise.handle(this.all); - } - return promise.fail(cb); - }, eager: function () { // This will allow params such as ("abc", "def") or (["abc", "def"]) var associations = _.flatten(arguments); @@ -259,6 +242,12 @@ function ChainFind(Model, opts) { }; chain.all = chain.where = chain.find; + chain.findAsync = Promise.promisify(chain.find); + chain.firstAsync = Promise.promisify(chain.first); + chain.lastAsync = Promise.promisify(chain.last); + chain.runAsync = Promise.promisify(chain.run); + chain.removeAsync = Promise.promisify(chain.remove); + if (opts.associations) { for (var i = 0; i < opts.associations.length; i++) { addChainMethod(chain, opts.associations[i], opts); diff --git a/test/integration/model-find-chain-async.js b/test/integration/model-find-chain-async.js new file mode 100644 index 00000000..05970ebd --- /dev/null +++ b/test/integration/model-find-chain-async.js @@ -0,0 +1,194 @@ +var async = require('async'); +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); +var common = require('../common'); + +describe("Model.find() chaining", function() { + var db = null; + var Person = null; + var Dog = null; + + var setup = function (extraOpts) { + if (!extraOpts) extraOpts = {}; + + return function (done) { + Person = db.define("person", { + name : String, + surname : String, + age : Number + }, extraOpts); + Person.hasMany("parents"); + Person.hasOne("friend"); + + ORM.singleton.clear(); // clear identityCache cache + + return helper.dropSync(Person, function () { + Person.create([{ + name : "John", + surname : "Doe", + age : 18, + friend_id : 1 + }, { + name : "Jane", + surname : "Doe", + age : 20, + friend_id : 1 + }, { + name : "Jane", + surname : "Dean", + age : 18, + friend_id : 1 + }], done); + }); + }; + }; + + var setup2 = function () { + return function (done) { + Dog = db.define("dog", { + name: String, + }); + Dog.hasMany("friends"); + Dog.hasMany("family"); + + ORM.singleton.clear(); // clear identityCache cache + + return helper.dropSync(Dog, function () { + Dog.create([{ + name : "Fido", + friends : [{ name: "Gunner" }, { name: "Chainsaw" }], + family : [{ name: "Chester" }] + }, { + name : "Thumper", + friends : [{ name: "Bambi" }], + family : [{ name: "Princess" }, { name: "Butch" }] + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe(".firstAsync()", function () { + before(setup()); + + it("should return only the first element", function () { + return Person.find() + .order("-age") + .firstAsync() + .then(function (JaneDoe) { + JaneDoe.name.should.equal("Jane"); + JaneDoe.surname.should.equal("Doe"); + JaneDoe.age.should.equal(20); + }); + }); + + it("should return null if not found", function () { + return Person.find({ name: "Jack" }) + .firstAsync() + .then(function (Jack) { + should.equal(Jack, null); + }); + }); + }); + + describe(".lastAsync()", function () { + before(setup()); + + it("should return only the last element", function () { + return Person.find() + .order("age") + .lastAsync() + .then(function (JaneDoe) { + JaneDoe.name.should.equal("Jane"); + JaneDoe.surname.should.equal("Doe"); + JaneDoe.age.should.equal(20); + }); + }); + + it("should return null if not found", function () { + return Person.find({ name: "Jack" }) + .lastAsync() + .then(function (Jack) { + should.equal(Jack, null); + }); + }); + }); + + describe(".findAsync()", function () { + before(setup()); + + it("should not change find if no arguments", function () { + return Person.find() + .findAsync() + .then(function(Person) { + should.equal(Person.length, 3); + }); + }); + + it("should restrict conditions if passed", function (done) { + Person.find() + .findAsync({ age: 18 }) + .then(function(Person) { + should.equal(Person.length, 2); + done(); + }) + .catch(function(err) { + done(err); + }); + }); + + if (common.protocol() == "mongodb") return; + + }); + + describe(".removeAsync()", function () { + var hookFired = false; + + before(setup({ + hooks: { + beforeRemove: function () { + hookFired = true; + } + } + })); + + it("should have no problems if no results found", function (done) { + Person.find({ age: 22 }) + .removeAsync() + .then(function () { + Person.find(function(err, data) { + should.equal(data.length, 3); + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + + it("should remove results without calling hooks", function (done) { + Person.find({ age: 20 }) + .removeAsync() + .then(function () { + should.equal(hookFired, false); + Person.find(function (err, data) { + should.equal(data.length, 2); + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); +}); \ No newline at end of file From 41a2d6aa20feffb144cb82226783e39166d24839 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 11:46:37 +0300 Subject: [PATCH 49/98] Remove success and fail tests --- test/integration/model-find-chain.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/test/integration/model-find-chain.js b/test/integration/model-find-chain.js index 6c5f7498..75d0f321 100644 --- a/test/integration/model-find-chain.js +++ b/test/integration/model-find-chain.js @@ -822,32 +822,4 @@ describe("Model.find() chaining", function() { }); }); }); - - describe(".success()", function () { - before(setup()); - - it("should return a Promise with .fail() method", function (done) { - Person.find().success(function (people) { - should(Array.isArray(people)); - - return done(); - }).fail(function (err) { - // never called.. - }); - }); - }); - - describe(".fail()", function () { - before(setup()); - - it("should return a Promise with .success() method", function (done) { - Person.find().fail(function (err) { - // never called.. - }).success(function (people) { - should(Array.isArray(people)); - - return done(); - }); - }); - }); }); From 986606a240570eb285f529a177ba28dfe4d1b232 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 12 Sep 2017 11:50:59 +0300 Subject: [PATCH 50/98] add existAsync, oneAsync, findAsync, countAsync methods --- lib/Model.js | 16 ++ test/integration/model-countAsync.js | 68 +++++ test/integration/model-create.js | 1 - test/integration/model-existsAsync.js | 122 +++++++++ test/integration/model-findAsync.js | 352 ++++++++++++++++++++++++++ test/integration/model-one.js | 1 - test/integration/model-oneAsync.js | 96 +++++++ 7 files changed, 654 insertions(+), 2 deletions(-) create mode 100644 test/integration/model-countAsync.js create mode 100644 test/integration/model-existsAsync.js create mode 100644 test/integration/model-findAsync.js create mode 100644 test/integration/model-oneAsync.js diff --git a/lib/Model.js b/lib/Model.js index ab34f9dc..6cafd884 100644 --- a/lib/Model.js +++ b/lib/Model.js @@ -49,6 +49,7 @@ function Model(opts) { return this; }; }; + var createInstance = function (data, inst_opts, cb) { if (!inst_opts) { inst_opts = {}; @@ -148,6 +149,8 @@ function Model(opts) { return instance; }; + + var model = function () { var instance, i; @@ -221,6 +224,8 @@ function Model(opts) { return cb(new ORMError("Driver does not support Model.drop()", 'NO_SUPPORT', { model: opts.table })); }; + model.dropAsync = Promise.promisify(model.drop); + model.sync = function (cb) { if (arguments.length === 0) { cb = function () {}; @@ -249,6 +254,8 @@ function Model(opts) { return cb(new ORMError("Driver does not support Model.sync()", 'NO_SUPPORT', { model: opts.table })); }; + model.syncPromise = Promise.promisify(model.sync); + model.get = function () { var conditions = {}; var options = {}; @@ -436,7 +443,10 @@ function Model(opts) { } }; + model.findAsync = Promise.promisify(model.find); + model.where = model.all = model.find; + model.whereAsync = model.allAsync = model.findAsync; model.one = function () { var args = Array.prototype.slice.apply(arguments); @@ -466,6 +476,8 @@ function Model(opts) { return this.find.apply(this, args); }; + model.oneAsync = Promise.promisify(model.one); + model.count = function () { var conditions = null; var cb = null; @@ -498,6 +510,8 @@ function Model(opts) { return this; }; + model.countAsync = Promise.promisify(model.count); + model.aggregate = function () { var conditions = {}; var propertyList = []; @@ -563,6 +577,8 @@ function Model(opts) { return this; }; + model.existsAsync = Promise.promisify(model.exists); + model.create = function () { var itemsParams = []; var items = []; diff --git a/test/integration/model-countAsync.js b/test/integration/model-countAsync.js new file mode 100644 index 00000000..fc817b96 --- /dev/null +++ b/test/integration/model-countAsync.js @@ -0,0 +1,68 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); + +describe("Model.count()", function() { + var db = null; + var Person = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + + return helper.dropSync(Person, function () { + Person.create([{ + id : 1, + name: "John Doe" + }, { + id : 2, + name: "Jane Doe" + }, { + id : 3, + name: "John Doe" + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("without conditions", function () { + before(setup()); + + it("should return all items in model", function (done) { + Person.countAsync() + .then(function (count) { + count.should.equal(3); + + return done(); + }) + .catch(done); + }); + }); + + describe("with conditions", function () { + before(setup()); + + it("should return only matching items", function (done) { + Person.countAsync({ name: "John Doe" }) + .then(function (count) { + count.should.equal(2); + + return done(); + }) + .catch(done); + }); + }); +}); diff --git a/test/integration/model-create.js b/test/integration/model-create.js index 4e2b7c37..9217c957 100644 --- a/test/integration/model-create.js +++ b/test/integration/model-create.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("Model.create()", function() { var db = null; diff --git a/test/integration/model-existsAsync.js b/test/integration/model-existsAsync.js new file mode 100644 index 00000000..cf4a7546 --- /dev/null +++ b/test/integration/model-existsAsync.js @@ -0,0 +1,122 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); + +describe("Model.exists()", function() { + var db = null; + var Person = null; + var good_id, bad_id; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + + return helper.dropSync(Person, function () { + Person.create([{ + name: "Jeremy Doe" + }, { + name: "John Doe" + }, { + name: "Jane Doe" + }], function (err, people) { + good_id = people[0][Person.id]; + + if (typeof good_id == "number") { + // numeric ID + bad_id = good_id * 100; + } else { + // string ID, keep same length.. + bad_id = good_id.split('').reverse().join(''); + } + + return done(); + }); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("with an id", function () { + before(setup()); + + it("should return true if found", function (done) { + Person.existsAsync(good_id) + .then(function (exists) { + exists.should.be.true; + + return done(); + }) + .catch(done); + }); + + it("should return false if not found", function (done) { + Person.existsAsync(bad_id) + .then(function (exists) { + exists.should.be.false; + + return done(); + }) + .catch(done); + }); + }); + + describe("with a list of ids", function () { + before(setup()); + + it("should return true if found", function (done) { + Person.existsAsync([ good_id ]) + .then(function (exists) { + exists.should.be.true; + + return done(); + }) + .catch(done); + }); + + it("should return false if not found", function (done) { + Person.exists([ bad_id ], function (err, exists) { + should.equal(err, null); + + exists.should.be.false; + + return done(); + }); + }); + }); + + describe("with a conditions object", function () { + before(setup()); + + it("should return true if found", function (done) { + Person.existsAsync({ name: "John Doe" }) + .then(function (exists) { + exists.should.be.true; + + return done(); + }) + .catch(done); + }); + + it("should return false if not found", function (done) { + Person.existsAsync({ name: "Jack Doe" }) + .then(function (exists) { + exists.should.be.false; + + return done(); + }) + .catch(done); + }); + }); +}); diff --git a/test/integration/model-findAsync.js b/test/integration/model-findAsync.js new file mode 100644 index 00000000..58a05f5a --- /dev/null +++ b/test/integration/model-findAsync.js @@ -0,0 +1,352 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("Model.findAsync()", function() { + var db = null; + var Person = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String, + surname : String, + age : Number, + male : Boolean + }); + + return helper.dropSync(Person, function () { + Person.create([{ + name : "John", + surname : "Doe", + age : 18, + male : true + }, { + name : "Jane", + surname : "Doe", + age : 16, + male : false + }, { + name : "Jeremy", + surname : "Dean", + age : 18, + male : true + }, { + name : "Jack", + surname : "Dean", + age : 20, + male : true + }, { + name : "Jasmine", + surname : "Doe", + age : 20, + male : false + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("without arguments", function () { + before(setup()); + + it("should return all items", function (done) { + Person.findAsync() + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + + return done(); + }) + .catch(done); + }); + }); + + describe("with a number as argument", function () { + before(setup()); + + it("should use it as limit", function (done) { + Person.findAsync(2) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 2); + + return done(); + }) + .catch(done); + }); + }); + + describe("with a string argument", function () { + before(setup()); + + it("should use it as property ascending order", function (done) { + Person.findAsync("age") + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(16); + people[4].age.should.equal(20); + + return done(); + }) + .catch(done); + }); + + it("should use it as property descending order if starting with '-'", function (done) { + Person.findAsync("-age") + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(20); + people[4].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + }); + + describe("with an Array as argument", function () { + before(setup()); + + it("should use it as property ascending order", function (done) { + Person.findAsync([ "age" ]) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(16); + people[4].age.should.equal(20); + + return done(); + }) + .catch(done); + }); + + it("should use it as property descending order if starting with '-'", function (done) { + Person.findAsync([ "-age" ]) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(20); + people[4].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + + it("should use it as property descending order if element is 'Z'", function (done) { + Person.findAsync([ "age", "Z" ]) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(20); + people[4].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + + it("should accept multiple ordering", function (done) { + Person.findAsync([ "age", "name", "Z" ]) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(16); + people[4].age.should.equal(20); + + return done(); + }) + .catch(done); + }); + + it("should accept multiple ordering using '-' instead of 'Z'", function (done) { + Person.findAsync([ "age", "-name" ]) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 5); + people[0].age.should.equal(16); + people[4].age.should.equal(20); + + return done(); + }) + .catch(done); + }); + }); + + describe("with an Object as argument", function () { + before(setup()); + + it("should use it as conditions", function (done) { + Person.findAsync({ age: 16 }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + + it("should accept comparison objects", function (done) { + Person.findAsync({ age: ORM.gt(18) }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 2); + people[0].age.should.equal(20); + people[1].age.should.equal(20); + + return done(); + }) + .catch(done); + }); + + describe("with another Object as argument", function () { + before(setup()); + + it("should use it as options", function (done) { + Person.findAsync({ age: 18 }, 1, { cache: false }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].age.should.equal(18); + + return done(); + }) + .catch(done); + }); + + describe("if a limit is passed", function () { + before(setup()); + + it("should use it", function (done) { + Person.findAsync({ age: 18 }, { limit: 1 }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].age.should.equal(18); + + return done(); + }) + .catch(done); + }); + }); + + describe("if an offset is passed", function () { + before(setup()); + + it("should use it", function (done) { + Person.findAsync({}, { offset: 1 }, "age") + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 4); + people[0].age.should.equal(18); + + return done(); + }) + .catch(done); + }); + }); + + describe("if an order is passed", function () { + before(setup()); + + it("should use it", function (done) { + Person.findAsync({ surname: "Doe" }, { order: "age" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + + it("should use it and ignore previously defined order", function (done) { + Person.findAsync({ surname: "Doe" }, "-age", { order: "age" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].age.should.equal(16); + + return done(); + }) + .catch(done); + }); + }); + }); + }); + + describe("with identityCache disabled", function () { + before(setup()); + it("should not return singletons", function (done) { + Person.findAsync({ name: "Jasmine" }, { identityCache: false }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].name.should.equal("Jasmine"); + people[0].surname.should.equal("Doe"); + + people[0].surname = "Dux"; + + return Person.findAsync({ name: "Jasmine" }, { identityCache: false }); + }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].name.should.equal("Jasmine"); + people[0].surname.should.equal("Doe"); + + return done(); + }) + .catch(done); + }); + }); + + describe("when using Model.allAsync()", function () { + before(setup()); + it("should work exactly the same", function (done) { + Person.allAsync({ surname: "Doe" }, "-age", 1) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].name.should.equal("Jasmine"); + people[0].surname.should.equal("Doe"); + + return done(); + }) + .catch(done); + }); + }); + + describe("when using Model.whereAsync()", function () { + before(setup()); + it("should work exactly the same", function (done) { + Person.whereAsync({ surname: "Doe" }, "-age", 1) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 1); + people[0].name.should.equal("Jasmine"); + people[0].surname.should.equal("Doe"); + + return done(); + }) + .catch(done); + }); + }); +}); diff --git a/test/integration/model-one.js b/test/integration/model-one.js index 0ed6e153..dfe62d3b 100644 --- a/test/integration/model-one.js +++ b/test/integration/model-one.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("Model.one()", function() { var db = null; diff --git a/test/integration/model-oneAsync.js b/test/integration/model-oneAsync.js new file mode 100644 index 00000000..cf3324de --- /dev/null +++ b/test/integration/model-oneAsync.js @@ -0,0 +1,96 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); + +describe("Model.oneAsync()", function() { + var db = null; + var Person = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + + return helper.dropSync(Person, function () { + Person.create([{ + id : 1, + name: "Jeremy Doe" + }, { + id : 2, + name: "John Doe" + }, { + id : 3, + name: "Jane Doe" + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("without arguments", function () { + before(setup()); + + it("should return first item in model", function (done) { + Person.oneAsync() + .then(function (person) { + person.name.should.equal("Jeremy Doe"); + + return done(); + }) + .catch(done); + }); + }); + + describe("with order", function () { + before(setup()); + + it("should return first item in model based on order", function (done) { + Person.oneAsync("-name") + .then(function (person) { + person.name.should.equal("John Doe"); + + return done(); + }) + .catch(done); + }); + }); + + describe("with conditions", function () { + before(setup()); + + it("should return first item in model based on conditions", function (done) { + Person.oneAsync({ name: "Jane Doe" }) + .then(function (person) { + person.name.should.equal("Jane Doe"); + + return done(); + }) + .catch(done); + }); + + describe("if no match", function () { + before(setup()); + + it("should return null", function (done) { + Person.oneAsync({ name: "Jack Doe" }) + .then(function (person) { + should.equal(person, null); + + return done(); + }) + .catch(done); + }); + }); + }); +}); From c06df3e6ba19f625734d87d559affe3ed499f96f Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 12 Sep 2017 11:53:12 +0300 Subject: [PATCH 51/98] add existAsync, oneAsync, findAsync, countAsync methods --- test/integration/model-findAsync-mapsto.js | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 test/integration/model-findAsync-mapsto.js diff --git a/test/integration/model-findAsync-mapsto.js b/test/integration/model-findAsync-mapsto.js new file mode 100644 index 00000000..1986d690 --- /dev/null +++ b/test/integration/model-findAsync-mapsto.js @@ -0,0 +1,98 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("Model.pkMapTo.find()", function() { + var db = null; + var Person = null; + + var setup = function () { + return function (done) { + + // The fact that we've applied mapsTo to the key + // property of the model - will break the cache. + + // Without Stuart's little bugfix, 2nd (and subsequent) calls to find() + // will return the repeats of the first obect retrieved and placed in the cache. + Person = db.define("person", { + personId : {type : "integer", key: true, mapsTo: "id"}, + name : String, + surname : String, + age : Number, + male : Boolean + }); + + return helper.dropSync(Person, function () { + Person.create([{ + personId: 1001, + name : "John", + surname : "Doe", + age : 18, + male : true + }, { + personId: 1002, + name : "Jane", + surname : "Doe", + age : 16, + male : false + }, { + personId: 1003, + name : "Jeremy", + surname : "Dean", + age : 18, + male : true + }, { + personId: 1004, + name : "Jack", + surname : "Dean", + age : 20, + male : true + }, { + personId: 1005, + name : "Jasmine", + surname : "Doe", + age : 20, + male : false + }], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + + describe("Cache should work with mapped key field", function () { + before(setup()); + + it("1st find should work", function (done) { + Person.find({ surname: "Dean" }, function (err, people) { + should.not.exist(err); + people.should.be.a.Object(); + people.should.have.property("length", 2); + people[0].surname.should.equal("Dean"); + + return done(); + }); + }); + it("2nd find should should also work", function (done) { + Person.find({ surname: "Doe" }, function (err, people) { + should.not.exist(err); + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].surname.should.equal("Doe"); + + return done(); + }); + }); + }); +}); From 28ce1206fa744c1cdeae2fcd7e81362d87285471 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 12 Sep 2017 11:54:06 +0300 Subject: [PATCH 52/98] add test for findAsync mapsto --- test/integration/model-find-mapsto.js | 31 +++++++++++----------- test/integration/model-findAsync-mapsto.js | 1 - 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/integration/model-find-mapsto.js b/test/integration/model-find-mapsto.js index 1986d690..9f32bfa4 100644 --- a/test/integration/model-find-mapsto.js +++ b/test/integration/model-find-mapsto.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("Model.pkMapTo.find()", function() { var db = null; @@ -75,24 +74,26 @@ describe("Model.pkMapTo.find()", function() { before(setup()); it("1st find should work", function (done) { - Person.find({ surname: "Dean" }, function (err, people) { - should.not.exist(err); - people.should.be.a.Object(); - people.should.have.property("length", 2); - people[0].surname.should.equal("Dean"); + Person.findAsync({ surname: "Dean" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 2); + people[0].surname.should.equal("Dean"); - return done(); - }); + return done(); + }) + .catch(done); }); it("2nd find should should also work", function (done) { - Person.find({ surname: "Doe" }, function (err, people) { - should.not.exist(err); - people.should.be.a.Object(); - people.should.have.property("length", 3); - people[0].surname.should.equal("Doe"); + Person.findAsync({ surname: "Doe" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].surname.should.equal("Doe"); - return done(); - }); + return done(); + }) + .catch(done); }); }); }); diff --git a/test/integration/model-findAsync-mapsto.js b/test/integration/model-findAsync-mapsto.js index 1986d690..32fb2587 100644 --- a/test/integration/model-findAsync-mapsto.js +++ b/test/integration/model-findAsync-mapsto.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("Model.pkMapTo.find()", function() { var db = null; From 5f120b6971d6fcd61075367f5aa80b1fd1a91d01 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 12:17:38 +0300 Subject: [PATCH 53/98] Resolve PR comments: add postFix to async methods; rewrite tests --- lib/ChainFind.js | 12 +++--- test/integration/model-find-chain-async.js | 43 +++++++++------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index b1e52498..547c8087 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -7,6 +7,8 @@ var Promise = require("bluebird"); module.exports = ChainFind; function ChainFind(Model, opts) { + var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); + var prepareConditions = function () { return Utilities.transformPropertyNames( opts.conditions, opts.properties @@ -242,11 +244,11 @@ function ChainFind(Model, opts) { }; chain.all = chain.where = chain.find; - chain.findAsync = Promise.promisify(chain.find); - chain.firstAsync = Promise.promisify(chain.first); - chain.lastAsync = Promise.promisify(chain.last); - chain.runAsync = Promise.promisify(chain.run); - chain.removeAsync = Promise.promisify(chain.remove); + chain['find' + promiseFunctionPostfix] = Promise.promisify(chain.find); + chain['first' + promiseFunctionPostfix] = Promise.promisify(chain.first); + chain['last' + promiseFunctionPostfix] = Promise.promisify(chain.last); + chain['run' + promiseFunctionPostfix] = Promise.promisify(chain.run); + chain['remove' + promiseFunctionPostfix] = Promise.promisify(chain.remove); if (opts.associations) { for (var i = 0; i < opts.associations.length; i++) { diff --git a/test/integration/model-find-chain-async.js b/test/integration/model-find-chain-async.js index 05970ebd..6c6d5b53 100644 --- a/test/integration/model-find-chain-async.js +++ b/test/integration/model-find-chain-async.js @@ -132,20 +132,16 @@ describe("Model.find() chaining", function() { it("should not change find if no arguments", function () { return Person.find() .findAsync() - .then(function(Person) { - should.equal(Person.length, 3); + .then(function(person) { + should.equal(person.length, 3); }); }); - it("should restrict conditions if passed", function (done) { - Person.find() + it("should restrict conditions if passed", function () { + return Person.find() .findAsync({ age: 18 }) - .then(function(Person) { - should.equal(Person.length, 2); - done(); - }) - .catch(function(err) { - done(err); + .then(function(person) { + should.equal(person.length, 2); }); }); @@ -164,30 +160,25 @@ describe("Model.find() chaining", function() { } })); - it("should have no problems if no results found", function (done) { - Person.find({ age: 22 }) + it("should have no problems if no results found", function () { + return Person.find({ age: 22 }) .removeAsync() .then(function () { - Person.find(function(err, data) { - should.equal(data.length, 3); - done(); - }); - }).catch(function(err) { - done(err); + return Person.find().findAsync(); + }).then(function(person) { + should.equal(person.length, 3); }); }); - it("should remove results without calling hooks", function (done) { - Person.find({ age: 20 }) + it("should remove results without calling hooks", function () { + return Person.find({ age: 20 }) .removeAsync() .then(function () { should.equal(hookFired, false); - Person.find(function (err, data) { - should.equal(data.length, 2); - done(); - }); - }).catch(function(err) { - done(err); + return Person.find().findAsync(); + }) + .then(function(person) { + should.equal(person.length, 2); }); }); }); From faf36f1edb3a242507a35e9ff8d17d6ff9bc618d Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 12 Sep 2017 15:06:27 +0300 Subject: [PATCH 54/98] remove done from tests which use Promise --- test/integration/hook-promise.js | 70 ++++----- test/integration/model-clearAsync.js | 25 ++- test/integration/model-countAsync.js | 20 +-- test/integration/model-existsAsync.js | 60 +++----- test/integration/model-find-mapsto.js | 32 ++-- test/integration/model-findAsync-mapsto.js | 35 ++--- test/integration/model-findAsync.js | 171 ++++++++------------- test/integration/model-getAsync.js | 81 +++------- test/integration/model-oneAsync.js | 36 ++--- test/integration/model-sync.js | 2 - test/integration/orm-exports.js | 84 +++------- 11 files changed, 211 insertions(+), 405 deletions(-) diff --git a/test/integration/hook-promise.js b/test/integration/hook-promise.js index fb2575f7..cd696413 100644 --- a/test/integration/hook-promise.js +++ b/test/integration/hook-promise.js @@ -59,7 +59,7 @@ describe("HookPromise", function() { describe('afterCreate', function () { beforeEach(setup()); - it("should trigger after model creation", function (done) { + it("should trigger after model creation", function () { var triggered = false; Person.afterCreate(function () { @@ -71,18 +71,16 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function () { triggered.should.be.true; - done(); - }) - .catch(done); + }); }); }); describe("beforeCreate", function () { beforeEach(setup()); - it("should allow modification of instance", function (done) { + it("should allow modification of instance", function () { Person.beforeCreate(function () { var self = this; return new Promise(function (resolve) { @@ -103,9 +101,7 @@ describe("HookPromise", function() { should.not.exist(err); should.exist(person); }); - done(); - }) - .catch(done); + }); }); it("should trigger error", function (done) { @@ -171,7 +167,7 @@ describe("HookPromise", function() { }); }); - it("should trigger and wait", function (done) { + it("should trigger and wait", function () { Person.beforeSave(function () { var self = this; return new Promise(function (resolve) { @@ -182,21 +178,19 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "Jane Doe" }]) + return Person.createAsync([{ name: "Jane Doe" }]) .then(function (John) { return John[0].saveAsync(); }) .then(function (John) { should.equal(John.name, 'John Doe'); - done(); - }) - .catch(done); + }); }); }); describe("afterSave", function () { beforeEach(setup()); - it("should call and wait after save hook", function (done) { + it("should call and wait after save hook", function () { var triggered = false; Person.afterSave(function () { @@ -208,21 +202,19 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function (Jhon) { return Jhon[0].saveAsync(); }) .then(function () { triggered.should.be.true; - done(); - }) - .catch(done); + }); }); }); describe("beforeValidation", function () { beforeEach(setup()); - it("should trigger and wait", function (done) { + it("should trigger and wait", function () { Person.beforeValidation(function () { var self = this; return new Promise(function (resolve) { @@ -233,12 +225,10 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function (Jhon) { should.equal(Jhon[0].name, "John Snow"); - done(); - }) - .catch(done); + }); }); it("should throw error", function (done) { @@ -265,7 +255,7 @@ describe("HookPromise", function() { describe("afterLoad", function () { beforeEach(setup()); - it("should trigger and wait", function (done) { + it("should trigger and wait", function () { var afterLoad = false; Person.afterLoad(function () { return new Promise(function (resolve) { @@ -276,12 +266,10 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function () { afterLoad.should.be.true; - done(); - }) - .catch(done); + }); }); it("should throw error", function (done) { @@ -310,7 +298,7 @@ describe("HookPromise", function() { describe("afterAutoFetch", function () { beforeEach(setup()); - it("should trigger and wait", function (done) { + it("should trigger and wait", function () { var afterAutoFetch = false; Person.afterAutoFetch(function () { return new Promise(function (resolve) { @@ -321,12 +309,10 @@ describe("HookPromise", function() { }); }); - Person.createAsync({ name: "John" }) + return Person.createAsync({ name: "John" }) .then(function () { afterAutoFetch.should.be.true; - done(); - }) - .catch(done); + }); }); it("should throw error", function (done) { @@ -354,7 +340,7 @@ describe("HookPromise", function() { describe("beforeRemove", function () { before(setup()); - it("should trigger and wait", function (done) { + it("should trigger and wait", function () { var beforeRemove = false; Person.beforeRemove(function () { return new Promise(function (resolve) { @@ -363,15 +349,13 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function (items) { return items[0].removeAsync(); }) .then(function () { beforeRemove.should.be.true; - done(); - }) - .catch(done); + }); }); it("should throw error", function (done) { @@ -431,15 +415,13 @@ describe("HookPromise", function() { } })); - it("should propagate down hooks", function (done) { - Person.createAsync([{ name: "John Doe" }]) + it("should propagate down hooks", function () { + return Person.createAsync([{ name: "John Doe" }]) .then(function (people) { should.exist(people); should.equal(people.length, 1); should.equal(people[0].name, "beforeSave"); - done(); - }) - .catch(done); + }); }); }); }); diff --git a/test/integration/model-clearAsync.js b/test/integration/model-clearAsync.js index 5a6c34a4..e0392b5d 100644 --- a/test/integration/model-clearAsync.js +++ b/test/integration/model-clearAsync.js @@ -39,14 +39,11 @@ describe("Model.clearAsync()", function() { describe("with callback", function () { before(setup()); - it("should call when done", function (done) { - Person.clearAsync() - .then(function () { - Person.find().count(function (err, count) { - count.should.equal(0); - - return done(); - }); + it("should call when done", function () { + return Person.clearAsync() + .then(Person.countAsync) + .then(function (count) { + count.should.equal(0); }); }); }); @@ -54,16 +51,12 @@ describe("Model.clearAsync()", function() { describe("without callback", function () { before(setup()); - it("should still remove", function (done) { - Person.clearAsync(); - - setTimeout(function () { - Person.find().count(function (err, count) { + it("should still remove", function () { + return Person.clearAsync() + .then(Person.countAsync) + .then(function (count) { count.should.equal(0); - - return done(); }); - }, 200); }); }); }); diff --git a/test/integration/model-countAsync.js b/test/integration/model-countAsync.js index fc817b96..3ce46e1b 100644 --- a/test/integration/model-countAsync.js +++ b/test/integration/model-countAsync.js @@ -1,7 +1,7 @@ var should = require('should'); var helper = require('../support/spec_helper'); -describe("Model.count()", function() { +describe("Model.countAsync()", function() { var db = null; var Person = null; @@ -41,28 +41,22 @@ describe("Model.count()", function() { describe("without conditions", function () { before(setup()); - it("should return all items in model", function (done) { - Person.countAsync() + it("should return all items in model", function () { + return Person.countAsync() .then(function (count) { count.should.equal(3); - - return done(); - }) - .catch(done); + }); }); }); describe("with conditions", function () { before(setup()); - it("should return only matching items", function (done) { - Person.countAsync({ name: "John Doe" }) + it("should return only matching items", function () { + return Person.countAsync({ name: "John Doe" }) .then(function (count) { count.should.equal(2); - - return done(); - }) - .catch(done); + }); }); }); }); diff --git a/test/integration/model-existsAsync.js b/test/integration/model-existsAsync.js index cf4a7546..235c3229 100644 --- a/test/integration/model-existsAsync.js +++ b/test/integration/model-existsAsync.js @@ -1,7 +1,7 @@ var should = require('should'); var helper = require('../support/spec_helper'); -describe("Model.exists()", function() { +describe("Model.existsAsync()", function() { var db = null; var Person = null; var good_id, bad_id; @@ -51,72 +51,54 @@ describe("Model.exists()", function() { describe("with an id", function () { before(setup()); - it("should return true if found", function (done) { - Person.existsAsync(good_id) + it("should return true if found", function () { + return Person.existsAsync(good_id) .then(function (exists) { exists.should.be.true; - - return done(); - }) - .catch(done); + }); }); - it("should return false if not found", function (done) { - Person.existsAsync(bad_id) + it("should return false if not found", function () { + return Person.existsAsync(bad_id) .then(function (exists) { exists.should.be.false; - - return done(); - }) - .catch(done); + }); }); }); describe("with a list of ids", function () { before(setup()); - it("should return true if found", function (done) { - Person.existsAsync([ good_id ]) + it("should return true if found", function () { + return Person.existsAsync([ good_id ]) .then(function (exists) { exists.should.be.true; - - return done(); - }) - .catch(done); + }); }); - it("should return false if not found", function (done) { - Person.exists([ bad_id ], function (err, exists) { - should.equal(err, null); - - exists.should.be.false; - - return done(); - }); + it("should return false if not found", function () { + return Person.existsAsync([ bad_id ]) + .then(function (exists) { + exists.should.be.false; + }); }); }); describe("with a conditions object", function () { before(setup()); - it("should return true if found", function (done) { - Person.existsAsync({ name: "John Doe" }) + it("should return true if found", function () { + return Person.existsAsync({ name: "John Doe" }) .then(function (exists) { exists.should.be.true; - - return done(); - }) - .catch(done); + }); }); - it("should return false if not found", function (done) { - Person.existsAsync({ name: "Jack Doe" }) + it("should return false if not found", function () { + return Person.existsAsync({ name: "Jack Doe" }) .then(function (exists) { exists.should.be.false; - - return done(); - }) - .catch(done); + }); }); }); }); diff --git a/test/integration/model-find-mapsto.js b/test/integration/model-find-mapsto.js index 9f32bfa4..5b2877d5 100644 --- a/test/integration/model-find-mapsto.js +++ b/test/integration/model-find-mapsto.js @@ -74,26 +74,24 @@ describe("Model.pkMapTo.find()", function() { before(setup()); it("1st find should work", function (done) { - Person.findAsync({ surname: "Dean" }) - .then(function (people) { - people.should.be.a.Object(); - people.should.have.property("length", 2); - people[0].surname.should.equal("Dean"); + Person.find({ surname: "Dean" }, function (err, people) { + should.not.exist(err); + people.should.be.a.Object(); + people.should.have.property("length", 2); + people[0].surname.should.equal("Dean"); - return done(); - }) - .catch(done); + return done(); + }); }); it("2nd find should should also work", function (done) { - Person.findAsync({ surname: "Doe" }) - .then(function (people) { - people.should.be.a.Object(); - people.should.have.property("length", 3); - people[0].surname.should.equal("Doe"); + Person.find({ surname: "Doe" }, function (err, people) { + should.not.exist(err); + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].surname.should.equal("Doe"); - return done(); - }) - .catch(done); + return done(); + }); }); }); -}); +}); \ No newline at end of file diff --git a/test/integration/model-findAsync-mapsto.js b/test/integration/model-findAsync-mapsto.js index 32fb2587..0ab265c1 100644 --- a/test/integration/model-findAsync-mapsto.js +++ b/test/integration/model-findAsync-mapsto.js @@ -1,7 +1,7 @@ var should = require('should'); var helper = require('../support/spec_helper'); -describe("Model.pkMapTo.find()", function() { +describe("Model.pkMapTo.findAsync()", function() { var db = null; var Person = null; @@ -73,25 +73,22 @@ describe("Model.pkMapTo.find()", function() { describe("Cache should work with mapped key field", function () { before(setup()); - it("1st find should work", function (done) { - Person.find({ surname: "Dean" }, function (err, people) { - should.not.exist(err); - people.should.be.a.Object(); - people.should.have.property("length", 2); - people[0].surname.should.equal("Dean"); - - return done(); - }); + it("1st find should work", function () { + Person.findAsync({ surname: "Dean" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 2); + people[0].surname.should.equal("Dean"); + }); }); - it("2nd find should should also work", function (done) { - Person.find({ surname: "Doe" }, function (err, people) { - should.not.exist(err); - people.should.be.a.Object(); - people.should.have.property("length", 3); - people[0].surname.should.equal("Doe"); - - return done(); - }); + it("2nd find should should also work", function () { + Person.findAsync({ surname: "Doe" }) + .then(function (people) { + people.should.be.a.Object(); + people.should.have.property("length", 3); + people[0].surname.should.equal("Doe"); + }); }); }); }); + diff --git a/test/integration/model-findAsync.js b/test/integration/model-findAsync.js index 58a05f5a..21b3e33a 100644 --- a/test/integration/model-findAsync.js +++ b/test/integration/model-findAsync.js @@ -61,232 +61,194 @@ describe("Model.findAsync()", function() { describe("without arguments", function () { before(setup()); - it("should return all items", function (done) { - Person.findAsync() + it("should return all items", function () { + return Person.findAsync() .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); - - return done(); - }) - .catch(done); + }); }); }); describe("with a number as argument", function () { before(setup()); - it("should use it as limit", function (done) { - Person.findAsync(2) + it("should use it as limit", function () { + return Person.findAsync(2) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 2); - - return done(); - }) - .catch(done); + }); }); }); describe("with a string argument", function () { before(setup()); - it("should use it as property ascending order", function (done) { - Person.findAsync("age") + it("should use it as property ascending order", function () { + return Person.findAsync("age") .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(16); people[4].age.should.equal(20); - - return done(); - }) - .catch(done); + }); }); - it("should use it as property descending order if starting with '-'", function (done) { - Person.findAsync("-age") + it("should use it as property descending order if starting with '-'", function () { + return Person.findAsync("-age") .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(20); people[4].age.should.equal(16); - - return done(); - }) - .catch(done); + }); }); }); describe("with an Array as argument", function () { before(setup()); - it("should use it as property ascending order", function (done) { - Person.findAsync([ "age" ]) + it("should use it as property ascending order", function () { + return Person.findAsync([ "age" ]) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(16); people[4].age.should.equal(20); - - return done(); - }) - .catch(done); + }); }); - it("should use it as property descending order if starting with '-'", function (done) { - Person.findAsync([ "-age" ]) + it("should use it as property descending order if starting with '-'", function () { + return Person.findAsync([ "-age" ]) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(20); people[4].age.should.equal(16); - - return done(); - }) - .catch(done); + }); }); - it("should use it as property descending order if element is 'Z'", function (done) { - Person.findAsync([ "age", "Z" ]) + it("should use it as property descending order if element is 'Z'", function () { + return Person.findAsync([ "age", "Z" ]) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(20); people[4].age.should.equal(16); - - return done(); - }) - .catch(done); + }); }); - it("should accept multiple ordering", function (done) { - Person.findAsync([ "age", "name", "Z" ]) + it("should accept multiple ordering", function () { + return Person.findAsync([ "age", "name", "Z" ]) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(16); people[4].age.should.equal(20); - - return done(); - }) - .catch(done); + }); }); - it("should accept multiple ordering using '-' instead of 'Z'", function (done) { - Person.findAsync([ "age", "-name" ]) + it("should accept multiple ordering using '-' instead of 'Z'", function () { + return Person.findAsync([ "age", "-name" ]) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 5); people[0].age.should.equal(16); people[4].age.should.equal(20); - - return done(); - }) - .catch(done); + }); }); }); describe("with an Object as argument", function () { before(setup()); - it("should use it as conditions", function (done) { - Person.findAsync({ age: 16 }) + it("should use it as conditions", function () { + return Person.findAsync({ age: 16 }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); people[0].age.should.equal(16); - - return done(); - }) - .catch(done); + }); }); - it("should accept comparison objects", function (done) { - Person.findAsync({ age: ORM.gt(18) }) + it("should accept comparison objects", function () { + return Person.findAsync({ age: ORM.gt(18) }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 2); people[0].age.should.equal(20); people[1].age.should.equal(20); - - return done(); - }) - .catch(done); + }); }); describe("with another Object as argument", function () { before(setup()); - it("should use it as options", function (done) { - Person.findAsync({ age: 18 }, 1, { cache: false }) + it("should use it as options", function () { + return Person.findAsync({ age: 18 }, 1, { cache: false }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); people[0].age.should.equal(18); - return done(); - }) - .catch(done); + + }); }); describe("if a limit is passed", function () { before(setup()); - it("should use it", function (done) { - Person.findAsync({ age: 18 }, { limit: 1 }) + it("should use it", function () { + return Person.findAsync({ age: 18 }, { limit: 1 }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); people[0].age.should.equal(18); - return done(); - }) - .catch(done); + + }); }); }); describe("if an offset is passed", function () { before(setup()); - it("should use it", function (done) { - Person.findAsync({}, { offset: 1 }, "age") + it("should use it", function () { + return Person.findAsync({}, { offset: 1 }, "age") .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 4); people[0].age.should.equal(18); - return done(); - }) - .catch(done); + + }); }); }); describe("if an order is passed", function () { before(setup()); - it("should use it", function (done) { - Person.findAsync({ surname: "Doe" }, { order: "age" }) + it("should use it", function () { + return Person.findAsync({ surname: "Doe" }, { order: "age" }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 3); people[0].age.should.equal(16); - return done(); - }) - .catch(done); + + }); }); - it("should use it and ignore previously defined order", function (done) { - Person.findAsync({ surname: "Doe" }, "-age", { order: "age" }) + it("should use it and ignore previously defined order", function () { + return Person.findAsync({ surname: "Doe" }, "-age", { order: "age" }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 3); people[0].age.should.equal(16); - return done(); - }) - .catch(done); + + }); }); }); }); @@ -294,8 +256,8 @@ describe("Model.findAsync()", function() { describe("with identityCache disabled", function () { before(setup()); - it("should not return singletons", function (done) { - Person.findAsync({ name: "Jasmine" }, { identityCache: false }) + it("should not return singletons", function () { + return Person.findAsync({ name: "Jasmine" }, { identityCache: false }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); @@ -311,42 +273,33 @@ describe("Model.findAsync()", function() { people.should.have.property("length", 1); people[0].name.should.equal("Jasmine"); people[0].surname.should.equal("Doe"); - - return done(); - }) - .catch(done); + }); }); }); describe("when using Model.allAsync()", function () { before(setup()); - it("should work exactly the same", function (done) { - Person.allAsync({ surname: "Doe" }, "-age", 1) + it("should work exactly the same", function () { + return Person.allAsync({ surname: "Doe" }, "-age", 1) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); people[0].name.should.equal("Jasmine"); people[0].surname.should.equal("Doe"); - - return done(); - }) - .catch(done); + }); }); }); describe("when using Model.whereAsync()", function () { before(setup()); - it("should work exactly the same", function (done) { - Person.whereAsync({ surname: "Doe" }, "-age", 1) + it("should work exactly the same", function () { + return Person.whereAsync({ surname: "Doe" }, "-age", 1) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 1); people[0].name.should.equal("Jasmine"); people[0].surname.should.equal("Doe"); - - return done(); - }) - .catch(done); + }); }); }); }); diff --git a/test/integration/model-getAsync.js b/test/integration/model-getAsync.js index 2504cad1..526c83a9 100644 --- a/test/integration/model-getAsync.js +++ b/test/integration/model-getAsync.js @@ -64,17 +64,13 @@ describe("Model.getAsync()", function () { }); }); - it("should accept and try to fetch if passed an Array with ids", function (done) { - Person.getAsync([ John[Person.id] ]) + it("should accept and try to fetch if passed an Array with ids", function () { + return Person.getAsync([ John[Person.id] ]) .then(function (John) { John.should.be.a.Object(); John.should.have.property(Person.id, John[Person.id]); John.should.have.property("name", "John Doe"); - done(); }) - .catch(function (err) { - done(err); - }); }); it("should throw err", function (done) { @@ -89,45 +85,31 @@ describe("Model.getAsync()", function () { }); }); - it("should return item with id 1", function (done) { - Person.getAsync(John[Person.id]) + it("should return item with id 1", function () { + return Person.getAsync(John[Person.id]) .then(function (John) { John.should.be.a.Object(); John.should.have.property(Person.id, John[Person.id]); John.should.have.property("name", "John Doe"); - - done(); }) - .catch(function (err) { - done(err); - }); }); - it("should have an UID method", function (done) { - Person.getAsync(John[Person.id]) + it("should have an UID method", function () { + return Person.getAsync(John[Person.id]) .then(function (John) { John.UID.should.be.a.Function(); John.UID().should.equal(John[Person.id]); - - done(); }) - .catch(function (err) { - done(err) - }); }); - it("should return the original object with unchanged name", function (done) { - Person.getAsync(John[Person.id]) + it("should return the original object with unchanged name", function () { + return Person.getAsync(John[Person.id]) .then(function (John1) { John1.name = "James"; return Person.getAsync(John[Person.id]); }) .then(function (John2) { should.equal(John2.name, "John Doe"); - done(); - }) - .catch(function (err) { - done(err); }); }); @@ -136,8 +118,8 @@ describe("Model.getAsync()", function () { Person.settings.set("instance.identityCacheSaveCheck", false); }); - it("should return the same object with the changed name", function (done) { - Person.getAsync(John[Person.id]) + it("should return the same object with the changed name", function () { + return Person.getAsync(John[Person.id]) .then(function (John1) { John1.name = "James"; return [John1, Person.getAsync(John[Person.id])]; @@ -145,11 +127,6 @@ describe("Model.getAsync()", function () { .spread(function (John1, John2) { John1[Person.id].should.equal(John2[Person.id]); John2.name.should.equal("James"); - - done(); - }) - .catch(function (err) { - done(err); }); }); }); @@ -158,19 +135,14 @@ describe("Model.getAsync()", function () { describe("with no identityCache cache", function () { before(setup(false)); - it("should return different objects", function (done) { - Person.getAsync(John[Person.id]) + it("should return different objects", function () { + return Person.getAsync(John[Person.id]) .then(function (John1) { return [John1, Person.getAsync(John[Person.id])]; }) .spread(function (John1, John2) { John1[Person.id].should.equal(John2[Person.id]); John1.should.not.equal(John2); - - done(); - }) - .catch(function (err) { - done(err); }); }); }); @@ -178,8 +150,8 @@ describe("Model.getAsync()", function () { describe("with identityCache cache = 0.5 secs", function () { before(setup(0.5)); - it("should return same objects after 0.2 sec", function (done) { - Person.getAsync(John[Person.id]) + it("should return same objects after 0.2 sec", function () { + return Person.getAsync(John[Person.id]) .then(function (John1) { return [John1, Promise.delay(200)]; }) @@ -189,15 +161,11 @@ describe("Model.getAsync()", function () { .spread(function (John1, John2) { John1[Person.id].should.equal(John2[Person.id]); John1.should.equal(John2); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should return different objects after 0.7 sec", function (done) { - Person.getAsync(John[Person.id]) + it("should return different objects after 0.7 sec", function () { + return Person.getAsync(John[Person.id]) .then(function (John1) { return [John1, Promise.delay(700)]; }) @@ -206,10 +174,6 @@ describe("Model.getAsync()", function () { }) .spread(function (John1, John2) { John1.should.not.equal(John2); - done(); - }) - .catch(function (err) { - done(err); }); }); }); @@ -231,18 +195,17 @@ describe("Model.getAsync()", function () { }); }); - it("should search by key name and not 'id'", function (done) { + it("should search by key name and not 'id'", function () { db.settings.set('properties.primary_key', 'name'); var OtherPerson = db.define("person", { id : Number }); - OtherPerson.getAsync("Jane Doe") + return OtherPerson.getAsync("Jane Doe") .then(function (person) { person.name.should.equal("Jane Doe"); db.settings.set('properties.primary_key', 'id'); - done(); }); }); }); @@ -250,16 +213,12 @@ describe("Model.getAsync()", function () { describe("with empty object as options", function () { before(setup()); - it("should return item with id 1 like previously", function (done) { - Person.getAsync(John[Person.id], {}) + it("should return item with id 1 like previously", function () { + return Person.getAsync(John[Person.id], {}) .then(function (John) { John.should.be.a.Object(); John.should.have.property(Person.id, John[Person.id]); John.should.have.property("name", "John Doe"); - done(); - }) - .catch(function (err) { - done(err); }); }); }); diff --git a/test/integration/model-oneAsync.js b/test/integration/model-oneAsync.js index cf3324de..2e77a0cf 100644 --- a/test/integration/model-oneAsync.js +++ b/test/integration/model-oneAsync.js @@ -41,55 +41,43 @@ describe("Model.oneAsync()", function() { describe("without arguments", function () { before(setup()); - it("should return first item in model", function (done) { - Person.oneAsync() + it("should return first item in model", function () { + return Person.oneAsync() .then(function (person) { person.name.should.equal("Jeremy Doe"); - - return done(); - }) - .catch(done); + }); }); }); describe("with order", function () { before(setup()); - it("should return first item in model based on order", function (done) { - Person.oneAsync("-name") + it("should return first item in model based on order", function () { + return Person.oneAsync("-name") .then(function (person) { person.name.should.equal("John Doe"); - - return done(); - }) - .catch(done); + }); }); }); describe("with conditions", function () { before(setup()); - it("should return first item in model based on conditions", function (done) { - Person.oneAsync({ name: "Jane Doe" }) + it("should return first item in model based on conditions", function () { + return Person.oneAsync({ name: "Jane Doe" }) .then(function (person) { person.name.should.equal("Jane Doe"); - - return done(); - }) - .catch(done); + }); }); describe("if no match", function () { before(setup()); - it("should return null", function (done) { - Person.oneAsync({ name: "Jack Doe" }) + it("should return null", function () { + return Person.oneAsync({ name: "Jack Doe" }) .then(function (person) { should.equal(person, null); - - return done(); - }) - .catch(done); + }); }); }); }); diff --git a/test/integration/model-sync.js b/test/integration/model-sync.js index bf4199a3..585f6606 100644 --- a/test/integration/model-sync.js +++ b/test/integration/model-sync.js @@ -1,7 +1,5 @@ -var _ = require('lodash'); var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); var common = require('../common'); describe("Model.sync", function () { diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index c613ca90..0339734a 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -163,8 +163,8 @@ describe("ORM", function() { }); }); - it("should pass successful when opts is OK!", function (done) { - ORM.connectAsync(common.getConnectionString()) + it("should pass successful when opts is OK!", function () { + return ORM.connectAsync(common.getConnectionString()) .then(function (db) { should.exist(db); @@ -172,12 +172,7 @@ describe("ORM", function() { db.define.should.be.a.Function(); db.sync.should.be.a.Function(); db.load.should.be.a.Function(); - - done(); }) - .catch(function (err) { - done(err); - }); }); describe('POOL via connectAsync', function () { @@ -192,59 +187,43 @@ describe("ORM", function() { }); if (protocol !== 'mongodb') { - it("should understand pool `'false'` from query string", function (done) { + it("should understand pool `'false'` from query string", function () { var connString = connStr + "debug=false&pool=false"; - ORM.connectAsync(connString) + return ORM.connectAsync(connString) .then(function (db) { should.strictEqual(db.driver.opts.pool, false); should.strictEqual(db.driver.opts.debug, false); - done(); }) - .catch(function (err) { - done(err); - }); }); - it("should understand pool `'0'` from query string", function (done) { + it("should understand pool `'0'` from query string", function () { var connString = connStr + "debug=0&pool=0"; - ORM.connectAsync(connString) + return ORM.connectAsync(connString) .then(function (db) { should.strictEqual(db.driver.opts.pool, false); should.strictEqual(db.driver.opts.debug, false); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should understand pool `'true'` from query string", function (done) { + it("should understand pool `'true'` from query string", function () { var connString = connStr + "debug=true&pool=true"; - ORM.connectAsync(connString) + return ORM.connectAsync(connString) .then(function (db) { should.strictEqual(db.driver.opts.pool, true); should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should understand pool `'true'` from query string", function (done) { + it("should understand pool `'true'` from query string", function () { var connString = connStr + "debug=1&pool=1"; - ORM.connectAsync(connString) + return ORM.connectAsync(connString) .then(function (db) { should.strictEqual(db.driver.opts.pool, true); should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should understand pool `'true'` from query string", function (done) { + it("should understand pool `'true'` from query string", function () { var connCopy = _.cloneDeep(common.getConfig()); var connOpts = _.extend(connCopy, { protocol: common.protocol(), @@ -252,18 +231,15 @@ describe("ORM", function() { pool: true, debug: true } }); - ORM.connectAsync(connOpts) + + return ORM.connectAsync(connOpts) .then(function (db) { should.strictEqual(db.driver.opts.pool, true); should.strictEqual(db.driver.opts.debug, true); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should understand pool `false` from query options", function (done) { + it("should understand pool `false` from query options", function () { var connCopy = _.cloneDeep(common.getConfig()); var connOpts = _.extend(connCopy, { protocol: common.protocol(), @@ -271,14 +247,11 @@ describe("ORM", function() { pool: false, debug: false } }); - ORM.connectAsync(connOpts) + + return ORM.connectAsync(connOpts) .then(function (db) { should.strictEqual(db.driver.opts.pool, false); should.strictEqual(db.driver.opts.debug, false); - done(); - }) - .catch(function (err) { - done(err); }); }); } @@ -446,15 +419,12 @@ describe("ORM", function() { }); }); - it("should be able to pingAsync the server", function (done) { - db.pingAsync() - .then(function () { - return done(); - }); + it("should be able to pingAsync the server", function () { + return db.pingAsync(); }); }); - describe("if callback is passed", function (done) { + describe("if callback is passed", function () { it("should return an error if empty url is passed", function (done) { ORM.connect("", function (err) { err.message.should.equal("CONNECTION_URL_EMPTY"); @@ -603,24 +573,16 @@ describe("ORM", function() { }); describe("ORM.useAsync()", function () { - it("should be able to use an established connection", function (done) { + it("should be able to use an established connection", function () { var db = new sqlite.Database(':memory:'); - ORM.useAsync(db, "sqlite") - .then(function () { - done(); - }) - .catch(done); + return ORM.useAsync(db, "sqlite"); }); - it("should be accept protocol alias", function (done) { + it("should be accept protocol alias", function () { var db = new pg.Client(); - ORM.useAsync(db, "pg") - .then(function () { - done(); - }) - .catch(done); + return ORM.useAsync(db, "pg") }); it("should throw an error in callback if protocol not supported", function (done) { From 6ff98d27dbfcb325c38c4fc9cb234cb6cd06b5f8 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 12 Sep 2017 16:16:22 +0300 Subject: [PATCH 55/98] fix test --- test/integration/hook-promise.js | 63 ++++----------------------- test/integration/model-clearAsync.js | 4 +- test/integration/model-countAsync.js | 4 +- test/integration/model-createAsync.js | 4 +- test/integration/model-existsAsync.js | 12 ++--- 5 files changed, 21 insertions(+), 66 deletions(-) diff --git a/test/integration/hook-promise.js b/test/integration/hook-promise.js index cd696413..2ef2c2a5 100644 --- a/test/integration/hook-promise.js +++ b/test/integration/hook-promise.js @@ -57,27 +57,6 @@ describe("HookPromise", function() { return db.close(); }); - describe('afterCreate', function () { - beforeEach(setup()); - it("should trigger after model creation", function () { - var triggered = false; - - Person.afterCreate(function () { - return new Promise(function (resolve) { - setTimeout(function () { - triggered = true; - resolve(); - }, 700); - }); - }); - - return Person.createAsync([{ name: "John Doe" }]) - .then(function () { - triggered.should.be.true; - }); - }); - }); - describe("beforeCreate", function () { beforeEach(setup()); it("should allow modification of instance", function () { @@ -91,7 +70,7 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ }]) + return Person.createAsync([{ }]) .then(function (people) { should.exist(people); should.equal(people.length, 1); @@ -119,7 +98,7 @@ describe("HookPromise", function() { }) .catch(function (err) { err.should.be.a.Object(); - err.message.should.equal("beforeCreate-error"); + should.equal(err.message, "beforeCreate-error"); done(); }); }); @@ -142,7 +121,7 @@ describe("HookPromise", function() { }) .catch(function (err) { err.should.be.a.Object(); - err.message.should.equal("beforeSave-error"); + should.equal(err.message, "beforeSave-error"); return done(); }); }); @@ -162,7 +141,7 @@ describe("HookPromise", function() { }) .catch(function (err) { err.should.be.a.Object(); - err.message.should.equal("beforeSave-error"); + should.equal("beforeSave-error", err.message); return done(); }); }); @@ -188,30 +167,6 @@ describe("HookPromise", function() { }); }); - describe("afterSave", function () { - beforeEach(setup()); - it("should call and wait after save hook", function () { - var triggered = false; - - Person.afterSave(function () { - return new Promise(function (resolve) { - setTimeout(function () { - triggered = true; - resolve(); - }, 700); - }); - }); - - return Person.createAsync([{ name: "John Doe" }]) - .then(function (Jhon) { - return Jhon[0].saveAsync(); - }) - .then(function () { - triggered.should.be.true; - }); - }); - }); - describe("beforeValidation", function () { beforeEach(setup()); it("should trigger and wait", function () { @@ -268,7 +223,7 @@ describe("HookPromise", function() { return Person.createAsync([{ name: "John Doe" }]) .then(function () { - afterLoad.should.be.true; + should.equal(afterLoad, true); }); }); @@ -290,7 +245,7 @@ describe("HookPromise", function() { }) .catch(function (err) { err.should.exist; - err.message.should.equal("afterLoad-error"); + should.equal("afterLoad-error", err.message); done(); }); }); @@ -305,13 +260,13 @@ describe("HookPromise", function() { setTimeout(function () { afterAutoFetch = true; resolve(); - }, 500); + }, 1000); }); }); return Person.createAsync({ name: "John" }) .then(function () { - afterAutoFetch.should.be.true; + should.equal(afterAutoFetch, true); }); }); @@ -354,7 +309,7 @@ describe("HookPromise", function() { return items[0].removeAsync(); }) .then(function () { - beforeRemove.should.be.true; + should.equal(beforeRemove, true); }); }); diff --git a/test/integration/model-clearAsync.js b/test/integration/model-clearAsync.js index e0392b5d..c82aa057 100644 --- a/test/integration/model-clearAsync.js +++ b/test/integration/model-clearAsync.js @@ -43,7 +43,7 @@ describe("Model.clearAsync()", function() { return Person.clearAsync() .then(Person.countAsync) .then(function (count) { - count.should.equal(0); + should.equal(count, 0); }); }); }); @@ -55,7 +55,7 @@ describe("Model.clearAsync()", function() { return Person.clearAsync() .then(Person.countAsync) .then(function (count) { - count.should.equal(0); + should.equal(count, 0); }); }); }); diff --git a/test/integration/model-countAsync.js b/test/integration/model-countAsync.js index 3ce46e1b..a9708ab0 100644 --- a/test/integration/model-countAsync.js +++ b/test/integration/model-countAsync.js @@ -44,7 +44,7 @@ describe("Model.countAsync()", function() { it("should return all items in model", function () { return Person.countAsync() .then(function (count) { - count.should.equal(3); + should.equal(count, 3); }); }); }); @@ -55,7 +55,7 @@ describe("Model.countAsync()", function() { it("should return only matching items", function () { return Person.countAsync({ name: "John Doe" }) .then(function (count) { - count.should.equal(2); + should.equal(count, 2); }); }); }); diff --git a/test/integration/model-createAsync.js b/test/integration/model-createAsync.js index 5e3371d8..56c6665c 100644 --- a/test/integration/model-createAsync.js +++ b/test/integration/model-createAsync.js @@ -87,7 +87,7 @@ describe("Model.createAsync()", function() { John.pets[0].should.have.property("name", "Deco"); John.pets[0].should.have.property(Pet.id); - John.pets[0].saved().should.be.true; + should.equal(John.pets[0].saved(), true); done(); }) .catch(function (err) { @@ -107,7 +107,7 @@ describe("Model.createAsync()", function() { John.pets[0].should.have.property("name", "Deco"); John.pets[0].should.have.property(Pet.id); - John.pets[0].saved().should.be.true; + should.equal(John.pets[0].saved(), true); done(); }) .catch(function (err) { diff --git a/test/integration/model-existsAsync.js b/test/integration/model-existsAsync.js index 235c3229..b01f6222 100644 --- a/test/integration/model-existsAsync.js +++ b/test/integration/model-existsAsync.js @@ -54,14 +54,14 @@ describe("Model.existsAsync()", function() { it("should return true if found", function () { return Person.existsAsync(good_id) .then(function (exists) { - exists.should.be.true; + exists.should.be.true(); }); }); it("should return false if not found", function () { return Person.existsAsync(bad_id) .then(function (exists) { - exists.should.be.false; + exists.should.be.false(); }); }); }); @@ -72,14 +72,14 @@ describe("Model.existsAsync()", function() { it("should return true if found", function () { return Person.existsAsync([ good_id ]) .then(function (exists) { - exists.should.be.true; + exists.should.be.true(); }); }); it("should return false if not found", function () { return Person.existsAsync([ bad_id ]) .then(function (exists) { - exists.should.be.false; + exists.should.be.false(); }); }); }); @@ -90,14 +90,14 @@ describe("Model.existsAsync()", function() { it("should return true if found", function () { return Person.existsAsync({ name: "John Doe" }) .then(function (exists) { - exists.should.be.true; + exists.should.be.true(); }); }); it("should return false if not found", function () { return Person.existsAsync({ name: "Jack Doe" }) .then(function (exists) { - exists.should.be.false; + exists.should.be.false(); }); }); }); From 69a950d0d2c2a1d8a1a3353fba50d5274776e9dd Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 21 Aug 2017 18:07:38 +0300 Subject: [PATCH 56/98] add Promise support for connect, now connectAsync use promises --- test/config.example.js | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 test/config.example.js diff --git a/test/config.example.js b/test/config.example.js deleted file mode 100644 index bb9c2d73..00000000 --- a/test/config.example.js +++ /dev/null @@ -1,29 +0,0 @@ -// To test, rename this file to config.js and update -// the following configuration -// -// To run a single driver, go to root folder and do (mysql example): -// ORM_PROTOCOL=mysql node test/run -// -// To run all drivers: -// make test - -exports.mysql = { - user : "root", - password : "", - database : "test" -}; -exports.postgres = { - user : "root", - password : "", - database : "test" -}; -exports.redshift = { - user : "root", - password : "", - database : "test" -}; -exports.mongodb = { - host: "localhost", - database: "test" -}; -exports.sqlite = { }; // uses in-memory database From 2e93243cc34de909bf373ac4222c48e644541eff Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 22 Aug 2017 17:54:29 +0300 Subject: [PATCH 57/98] restore example file, fix after review --- test/config.example.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/config.example.js diff --git a/test/config.example.js b/test/config.example.js new file mode 100644 index 00000000..bb9c2d73 --- /dev/null +++ b/test/config.example.js @@ -0,0 +1,29 @@ +// To test, rename this file to config.js and update +// the following configuration +// +// To run a single driver, go to root folder and do (mysql example): +// ORM_PROTOCOL=mysql node test/run +// +// To run all drivers: +// make test + +exports.mysql = { + user : "root", + password : "", + database : "test" +}; +exports.postgres = { + user : "root", + password : "", + database : "test" +}; +exports.redshift = { + user : "root", + password : "", + database : "test" +}; +exports.mongodb = { + host: "localhost", + database: "test" +}; +exports.sqlite = { }; // uses in-memory database From e04615b54a94ae500af4af772013463b07534064 Mon Sep 17 00:00:00 2001 From: Yevheniy Miropolets Date: Mon, 4 Sep 2017 16:04:57 +0300 Subject: [PATCH 58/98] Add Promise support for get aggregation. Add tests for getAsync. --- test/config.example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.example.js b/test/config.example.js index bb9c2d73..0d9c232b 100644 --- a/test/config.example.js +++ b/test/config.example.js @@ -13,8 +13,8 @@ exports.mysql = { database : "test" }; exports.postgres = { - user : "root", - password : "", + user : "postgres", + password : "1111", database : "test" }; exports.redshift = { From af712b4df8a8799766b5b966f99ba367b7f0cde2 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 5 Sep 2017 18:09:01 +0300 Subject: [PATCH 59/98] Update Async tests (except Hooks) --- test/integration/association-hasmany-extra.js | 12 ++- .../integration/association-hasmany-mapsto.js | 18 ++-- test/integration/association-hasmany.js | 27 +++--- .../association-hasone-required.js | 89 +++++++++++++++++++ test/integration/association-hasone.js | 12 +-- test/integration/instance.js | 32 ++++--- test/integration/model-getAsync.js | 10 +-- test/integration/property-lazyload.js | 12 +-- 8 files changed, 159 insertions(+), 53 deletions(-) diff --git a/test/integration/association-hasmany-extra.js b/test/integration/association-hasmany-extra.js index 3d04bb25..e4289ed8 100644 --- a/test/integration/association-hasmany-extra.js +++ b/test/integration/association-hasmany-extra.js @@ -80,14 +80,14 @@ describe("hasMany extra properties", function() { before(setup()); it("should be added to association", function (done) { - Person.create([{ + Person.createAsync([{ name : "John" - }], function (err, people) { - Pet.create([{ + }]).then(function (people) { + Pet.createAsync([{ name : "Deco" }, { name : "Mutt" - }], function (err, pets) { + }]).then(function (pets) { var data = { adopted: true }; people[0].addPetsAsync(pets, { since : new Date(), data: data }).then(function () { @@ -114,7 +114,11 @@ describe("hasMany extra properties", function() { }).catch(function(err) { done(err); }); + }).catch(function(err) { + done(err); }); + }).catch(function(err) { + done(err); }); }); }); diff --git a/test/integration/association-hasmany-mapsto.js b/test/integration/association-hasmany-mapsto.js index 5e265fe5..df2ac501 100644 --- a/test/integration/association-hasmany-mapsto.js +++ b/test/integration/association-hasmany-mapsto.js @@ -1005,7 +1005,7 @@ describe("hasMany with mapsTo", function () { }); it("should accept array as list of associations", function (done) { - Pet.create([{ petName: 'Ruff' }, { petName: 'Spotty' }],function (err, pets) { + Pet.createAsync([{ petName: 'Ruff' }, { petName: 'Spotty' }]).then(function (pets) { Person.find({ firstName: "Justin" }).first(function (err, Justin) { should.equal(err, null); @@ -1031,6 +1031,8 @@ describe("hasMany with mapsTo", function () { done(err); }); }); + }).catch(function(err) { + done(err); }); }); @@ -1159,15 +1161,13 @@ describe("hasMany with mapsTo", function () { should.not.exist(err); should.equal(pets.length, 2); - Person.create({ firstName: 'Paul' }, function (err, paul) { - should.not.exist(err); + Person.createAsync({ firstName: 'Paul' }).then(function (paul) { Person.one({ firstName: 'Paul' }, function (err, paul2) { should.not.exist(err); should.equal(paul2.pets.length, 0); paul.setPetsAsync(pets).then(function () { - should.not.exist(err); // reload paul to make sure we have 2 pets Person.one({ firstName: 'Paul' }, function (err, paul) { @@ -1176,9 +1176,7 @@ describe("hasMany with mapsTo", function () { // Saving paul2 should NOT auto save associations and hence delete // the associations we just created. - paul2.save(function (err) { - should.not.exist(err); - + paul2.saveAsync().then(function () { // let's check paul - pets should still be associated Person.one({ firstName: 'Paul' }, function (err, paul) { should.not.exist(err); @@ -1186,13 +1184,17 @@ describe("hasMany with mapsTo", function () { done(); }); + }).catch(function(err) { + done(err); }); }); }).catch(function(err) { done(err); }); }); - }); + }).catch(function(err) { + done(err); + });; }); }); diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index 226f00ec..bf4a9998 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -317,12 +317,10 @@ describe("hasMany", function () { John.hasPetsAsync(pets).then(function (hasPets) { should.equal(hasPets, true); - db.driver.execQuery( + db.driver.execQueryAsync( "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id], - function (err) { - should.not.exist(err); - + [John.id, pets[0].id, John.id, pets[1].id]).then( + function () { John.hasPetsAsync(pets).then(function (hasPets) { should.equal(hasPets, true); done(); @@ -330,7 +328,9 @@ describe("hasMany", function () { done(err); }); } - ); + ).catch(function(err) { + done(err); + }); }).catch(function(err){ done(err); }); @@ -847,11 +847,10 @@ describe("hasMany", function () { John.hasPetsAsync(pets).then(function (hasPets) { should.equal(hasPets, true); - db.driver.execQuery( + db.driver.execQueryAsync( "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id], - function (err) { - should.not.exist(err); + [John.id, pets[0].id, John.id, pets[1].id]).then( + function () { John.hasPetsAsync(pets).then(function (hasPets) { should.equal(hasPets, true); @@ -860,7 +859,9 @@ describe("hasMany", function () { done(err); }); } - ); + ).catch(function(err){ + done(err); + }); }).catch(function(err){ done(err); }); @@ -1023,7 +1024,7 @@ describe("hasMany", function () { }); it("should accept array as list of associations (promise-based)", function (done) { - Pet.create([{ name: 'Ruff' }, { name: 'Spotty' }],function (err, pets) { + Pet.createAsync([{ name: 'Ruff' }, { name: 'Spotty' }]).then(function (pets) { Person.find({ name: "Justin" }).first(function (err, Justin) { should.equal(err, null); @@ -1048,6 +1049,8 @@ describe("hasMany", function () { done(err); }); }); + }).catch(function(err) { + done(err); }); }); }); diff --git a/test/integration/association-hasone-required.js b/test/integration/association-hasone-required.js index cde0160b..2b8b7f2c 100644 --- a/test/integration/association-hasone-required.js +++ b/test/integration/association-hasone-required.js @@ -87,3 +87,92 @@ describe("hasOne", function() { }); }); }); + +describe("hasOne Async", function() { + var db = null; + var Person = null; + + var setup = function (required) { + return function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + + Person = db.define('person', { + name : String + }); + Person.hasOne('parent', Person, { + required : required, + field : 'parentId' + }); + + return helper.dropSync(Person, done); + }; + }; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("required", function () { + before(setup(true)); + + it("should not accept empty association", function (done) { + var John = new Person({ + name : "John", + parentId : null + }); + John.saveAsync().then(function() { + throw new Error('Should catch an error'); + }).catch(function(err) { + should.exist(err); + should.equal(err.length, 1); + should.equal(err[0].type, 'validation'); + should.equal(err[0].msg, 'required'); + should.equal(err[0].property, 'parentId'); + done(); + }); + }); + + it("should accept association", function (done) { + var John = new Person({ + name : "John", + parentId : 1 + }); + John.saveAsync().then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + describe("not required", function () { + before(setup(false)); + + it("should accept empty association", function (done) { + var John = new Person({ + name : "John" + }); + John.saveAsync().then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + + it("should accept null association", function (done) { + var John = new Person({ + name : "John", + parent_id : null + }); + John.saveAsync().then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/integration/association-hasone.js b/test/integration/association-hasone.js index 75455bf1..6c85b248 100644 --- a/test/integration/association-hasone.js +++ b/test/integration/association-hasone.js @@ -596,17 +596,18 @@ describe("hasOne", function() { }); if (protocol != "mongodb") { - describe("mapsTo (promise-based tests)", function () { + describe("mapsTo Async (promise-based tests)", function () { describe("with `mapsTo` set via `hasOne`", function () { var leaf = null; before(setup()); before(function (done) { - Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) { - should.not.exist(err); + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { leaf = lf; done(); + }).catch(function(err) { + done(err); }); }); @@ -629,10 +630,11 @@ describe("hasOne", function() { before(setup()); before(function (done) { - Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) { - should.not.exist(err); + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { leaf = lf; done(); + }).catch(function(err) { + done(err); }); }); diff --git a/test/integration/instance.js b/test/integration/instance.js index 876f0d6b..1b17d844 100644 --- a/test/integration/instance.js +++ b/test/integration/instance.js @@ -402,20 +402,21 @@ describe("Model instance", function() { person1.saveAsync().then(function () { - Person.create({ height: 170 }, function (err, person2) { - should.not.exist(err); - - Person.get(person1[Person.id], function (err, item) { - should.not.exist(err); + Person.createAsync({ height: 170 }).then(function (person2) { + Person.getAsync(person1[Person.id]).then(function (item) { should.equal(item.height, 190); - - Person.get(person2[Person.id], function (err, item) { - should.not.exist(err); + Person.getAsync(person2[Person.id]).then(function (item) { should.equal(item.height, 170); done(); + }).catch(function(err) { + done(err); }); + }).catch(function(err) { + done(err); }); + }).catch(function(err) { + done(err); }); }).catch(function(err) { done(err); @@ -515,10 +516,11 @@ describe("Model instance", function() { should.equal(err.message, msg); - Person.create({ height: 'bugz' }, function (err, instance) { + Person.createAsync({ height: 'bugz' }).then(function () { + done(new Error('Function should catch an error instead of finish')); + }).catch(function(err) { should.exist(err); should.equal(err.message, msg); - done(); }); }); @@ -556,20 +558,22 @@ describe("Model instance", function() { person.saveAsync().then(function () { - Person.get(person[Person.id], function (err, person) { - should.not.exist(err); + Person.getAsync(person[Person.id]).then(function (person) { should(isNaN(person.weight)); person.saveAsync({ weight: Infinity, name: 'black hole' }).then(function () { - Person.get(person[Person.id], function (err, person) { - should.not.exist(err); + Person.getAsync(person[Person.id]).then(function (person) { should.strictEqual(person.weight, Infinity); done(); + }).catch(function(err) { + done(err); }); }).catch(function(err) { done(err); }); + }).catch(function(err) { + done(err); }); }).catch(function(err) { done(err); diff --git a/test/integration/model-getAsync.js b/test/integration/model-getAsync.js index 526c83a9..d265d903 100644 --- a/test/integration/model-getAsync.js +++ b/test/integration/model-getAsync.js @@ -25,15 +25,15 @@ describe("Model.getAsync()", function () { ORM.singleton.clear(); // clear identityCache cache return helper.dropSync(Person, function () { - Person.create([{ + Person.createAsync([{ name: "John Doe" }, { name: "Jane Doe" - }], function (err, people) { - if (err) done(err); + }]).then(function (people) { John = people[0]; - - return done(); + done(); + }).catch(function(err) { + done(err); }); }); }; diff --git a/test/integration/property-lazyload.js b/test/integration/property-lazyload.js index 0b146be0..fce83845 100644 --- a/test/integration/property-lazyload.js +++ b/test/integration/property-lazyload.js @@ -122,7 +122,7 @@ describe("LazyLoad properties", function() { }); }); - it("promise-based setAccessor should change property", function (done) { + it("promise-based setAccessor should change property (promise-based)", function (done) { Person.find().first(function (err, John) { should.equal(err, null); John.should.be.a.Object(); @@ -171,15 +171,13 @@ describe("LazyLoad properties", function() { }); }); - it("promise-based removeAccessor should change property", function (done) { + it("removeAccessor should change property (promise-based)", function (done) { Person.find().first(function (err, John) { should.equal(err, null); John.should.be.a.Object(); John.removePhotoAsync().then(function () { - should.equal(err, null); - Person.get(John[Person.id], function (err, John) { - should.equal(err, null); + Person.getAsync(John[Person.id]).then(function (John) { John.should.be.a.Object(); John.getPhotoAsync() @@ -190,7 +188,11 @@ describe("LazyLoad properties", function() { }).catch(function (err) { done(err); }); + }).catch(function(err) { + done(err); }); + }).catch(function(err) { + done(err); }); }); }); From ad4eebe581f8a9755362c04b1a039497cc0ad703 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 17:35:21 +0300 Subject: [PATCH 60/98] Add changes to the part of tests --- test/integration/association-extend-async.js | 244 +++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 test/integration/association-extend-async.js diff --git a/test/integration/association-extend-async.js b/test/integration/association-extend-async.js new file mode 100644 index 00000000..927cbe62 --- /dev/null +++ b/test/integration/association-extend-async.js @@ -0,0 +1,244 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("Model.extendsTo()", function() { + var db = null; + var Person = null; + var PersonAddress = null; + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String + }); + PersonAddress = Person.extendsTo("address", { + street : String, + number : Number + }); + + ORM.singleton.clear(); + + return helper.dropSync([ Person, PersonAddress ], function () { + Person.create({ + name: "John Doe" + }, function (err, person) { + should.not.exist(err); + + return person.setAddress(new PersonAddress({ + street : "Liberty", + number : 123 + }), done); + }); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("when calling hasAccessorAsync", function () { + before(setup()); + + it("should return true if found", function (done) { + Person.find() + .firstAsync() + .then(function (John) { + return John.hasAddressAsync(); + }) + .then(function (hasAddress) { + hasAddress.should.equal(true); + + done(); + }).catch(function(err){ + done(err); + }); + //}); + }); + + it("should return false if not found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + John.removeAddressAsync().then(function(){ + John.hasAddressAsync().then(function (hasAddress) { + }).catch(function(err) { + err.should.be.a.Object(); + done(); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.hasAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("when calling getAccessorAsync", function () { + before(setup()); + + it("should return extension if found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + John.getAddressAsync(John).then(function (Address) { + Address.should.be.a.Object(); + Address.should.have.property("street", "Liberty"); + done(); + }).catch(function (err) { + done(err); + }); + }); + }); + + it("should return error if not found", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + John.removeAddressAsync().then(function () { + John.getAddressAsync(John).catch(function(err){ + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_FOUND); + done(); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.getAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("when calling setAccessor + Async", function () { + before(setup()); + + it("should remove any previous extension", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(1); + + var addr = new PersonAddress({ + street : "4th Ave", + number : 4 + }); + + John.setAddressAsync(addr).then(function () { + John.getAddressAsync(addr).then(function (Address) { + Address.should.be.a.Object(); + Address.should.have.property("street", addr.street); + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(0); + done(); + }); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("when calling delAccessor + Async", function () { + before(setup()); + + it("should remove any extension", function (done) { + Person.find().first(function (err, John) { + should.equal(err, null); + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(1); + + var addr = new PersonAddress({ + street : "4th Ave", + number : 4 + }); + + John.removeAddressAsync().then(function () { + + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(0); + + done(); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should return error if instance not with an ID", function (done) { + var Jane = new Person({ + name: "Jane" + }); + Jane.removeAddressAsync().catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); + }); + }); + + describe("findBy()", function () { // TODO: make async after Models method include async support + before(setup()); + + it("should throw if no conditions passed", function (done) { + (function () { + Person.findByAddress(function () {}); + }).should.throw(); + + return done(); + }); + + it("should lookup in Model based on associated model properties", function (done) { + Person.findByAddress({ + number: 123 + }, function (err, people) { + should.equal(err, null); + should(Array.isArray(people)); + should(people.length == 1); + + return done(); + }); + }); + + it("should return a ChainFind if no callback passed", function (done) { + var ChainFind = Person.findByAddress({ + number: 123 + }); + ChainFind.run.should.be.a.Function(); + + return done(); + }); + }); +}); From 7ad36dcc5e8976c22633754faa0d78ac8e679ef9 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 17:35:59 +0300 Subject: [PATCH 61/98] Add changes to the part of tests --- test/integration/association-extend-async.js | 182 ++++++++----------- 1 file changed, 71 insertions(+), 111 deletions(-) diff --git a/test/integration/association-extend-async.js b/test/integration/association-extend-async.js index 927cbe62..f0bb20bb 100644 --- a/test/integration/association-extend-async.js +++ b/test/integration/association-extend-async.js @@ -46,96 +46,84 @@ describe("Model.extendsTo()", function() { return db.close(); }); - describe("when calling hasAccessorAsync", function () { + describe("when calling hasAccessorAsync", function () { // TODO: fix Model.find to async before(setup()); - it("should return true if found", function (done) { - Person.find() + it("should return true if found", function () { + return Person.find() .firstAsync() .then(function (John) { return John.hasAddressAsync(); }) .then(function (hasAddress) { - hasAddress.should.equal(true); - - done(); - }).catch(function(err){ - done(err); + should.equal(hasAddress, true); }); - //}); - }); - - it("should return false if not found", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - John.removeAddressAsync().then(function(){ - John.hasAddressAsync().then(function (hasAddress) { - }).catch(function(err) { - err.should.be.a.Object(); - done(); - }); - }); - }); }); it("should return error if instance not with an ID", function (done) { var Jane = new Person({ name: "Jane" }); - Jane.hasAddressAsync().catch(function(err) { - err.should.be.a.Object(); - err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); - done(); - }); + + Jane.hasAddressAsync() + .catch(function (err) { + err.should.be.a.Object(); + should.equal(Array.isArray(err), false); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); }); }); - describe("when calling getAccessorAsync", function () { + describe("when calling getAccessorAsync", function () { // TODO: fix Model.find to async before(setup()); - it("should return extension if found", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - - John.getAddressAsync(John).then(function (Address) { + it("should return extension if found", function () { + return Person.find() + .firstAsync() + .then(function (John) { + return John.getAddressAsync(); + }) + .then(function (Address) { Address.should.be.a.Object(); + should.equal(Array.isArray(Address), false); Address.should.have.property("street", "Liberty"); - done(); - }).catch(function (err) { - done(err); }); - }); }); it("should return error if not found", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - John.removeAddressAsync().then(function () { - John.getAddressAsync(John).catch(function(err){ - err.should.be.a.Object(); - err.should.have.property("code", ORM.ErrorCodes.NOT_FOUND); - done(); - }); + Person.find() + .firstAsync() + .then(function (John) { + return [John, John.removeAddressAsync()]; + }) + .spread(function(John) { + return John.getAddressAsync(); + }) + .catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_FOUND); + done(); }); - }); }); it("should return error if instance not with an ID", function (done) { var Jane = new Person({ name: "Jane" }); - Jane.getAddressAsync().catch(function(err) { - err.should.be.a.Object(); - err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); - done(); - }); + Jane.getAddressAsync() + .catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); }); }); - describe("when calling setAccessor + Async", function () { + describe("when calling setAccessorAsync", function () { before(setup()); - it("should remove any previous extension", function (done) { + it("should remove any previous extension", function (done) { // TODO: fix Model.find to async Person.find().first(function (err, John) { should.equal(err, null); @@ -148,25 +136,29 @@ describe("Model.extendsTo()", function() { number : 4 }); - John.setAddressAsync(addr).then(function () { - John.getAddressAsync(addr).then(function (Address) { + John.setAddressAsync(addr) + .then(function () { + return John.getAddressAsync(); + }) + .then(function (Address) { Address.should.be.a.Object(); + should.equal(Array.isArray(Address), false); Address.should.have.property("street", addr.street); - PersonAddress.find({ number: 123 }).count(function (err, c) { - should.equal(err, null); - c.should.equal(0); - done(); - }); + PersonAddress.find({ number: 123 }) + .count(function (err, c) { + should.equal(err, null); + c.should.equal(0); + done(); + }); + }).catch(function(err) { + done(err); }); - }).catch(function(err) { - done(err); - }); }); }); }); }); - describe("when calling delAccessor + Async", function () { + describe("when calling delAccessor + Async", function () { // TODO: fix .find to async before(setup()); it("should remove any extension", function (done) { @@ -182,17 +174,17 @@ describe("Model.extendsTo()", function() { number : 4 }); - John.removeAddressAsync().then(function () { - - PersonAddress.find({ number: 123 }).count(function (err, c) { - should.equal(err, null); - c.should.equal(0); + John.removeAddressAsync() + .then(function () { + PersonAddress.find({ number: 123 }).count(function (err, c) { + should.equal(err, null); + c.should.equal(0); - done(); + done(); + }); + }).catch(function(err) { + done(err); }); - }).catch(function(err) { - done(err); - }); }); }); }); @@ -201,44 +193,12 @@ describe("Model.extendsTo()", function() { var Jane = new Person({ name: "Jane" }); - Jane.removeAddressAsync().catch(function(err) { - err.should.be.a.Object(); - err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); - done(); - }); - }); - }); - - describe("findBy()", function () { // TODO: make async after Models method include async support - before(setup()); - - it("should throw if no conditions passed", function (done) { - (function () { - Person.findByAddress(function () {}); - }).should.throw(); - - return done(); - }); - - it("should lookup in Model based on associated model properties", function (done) { - Person.findByAddress({ - number: 123 - }, function (err, people) { - should.equal(err, null); - should(Array.isArray(people)); - should(people.length == 1); - - return done(); - }); - }); - - it("should return a ChainFind if no callback passed", function (done) { - var ChainFind = Person.findByAddress({ - number: 123 - }); - ChainFind.run.should.be.a.Function(); - - return done(); + Jane.removeAddressAsync() + .catch(function(err) { + err.should.be.a.Object(); + err.should.have.property("code", ORM.ErrorCodes.NOT_DEFINED); + done(); + }); }); }); }); From c0a00ce7c132163ddc633e91d845fe3bbe1f8f83 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Tue, 12 Sep 2017 18:11:42 +0300 Subject: [PATCH 62/98] Association hasmany async tests moved to separate --- test/integration/association-hasmany-async.js | 729 ++++++++++++++++++ test/integration/association-hasmany.js | 536 ------------- 2 files changed, 729 insertions(+), 536 deletions(-) create mode 100644 test/integration/association-hasmany-async.js diff --git a/test/integration/association-hasmany-async.js b/test/integration/association-hasmany-async.js new file mode 100644 index 00000000..a48fed80 --- /dev/null +++ b/test/integration/association-hasmany-async.js @@ -0,0 +1,729 @@ +var _ = require('lodash'); +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); +var common = require('../common'); +var protocol = common.protocol(); + +describe("hasMany", function () { + var db = null; + var Person = null; + var Pet = null; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("normal", function () { + + var setup = function (opts) { + opts = opts || {}; + + return function (done) { + db.settings.set('instance.identityCache', false); + + Person = db.define('person', { + name : String, + surname : String, + age : Number + }); + Pet = db.define('pet', { + name : String + }); + Person.hasMany('pets', Pet, {}, { autoFetch: opts.autoFetchPets }); + + helper.dropSync([ Person, Pet], function (err) { + should.not.exist(err); + + Pet.create([{ name: "Cat" }, { name: "Dog" }], function (err) { + should.not.exist(err); + + /** + * John --+---> Deco + * '---> Mutt <----- Jane + * + * Justin + */ + Person.create([ + { + name : "Bob", + surname : "Smith", + age : 30 + }, + { + name : "John", + surname : "Doe", + age : 20, + pets : [{ + name : "Deco" + }, { + name : "Mutt" + }] + }, { + name : "Jane", + surname : "Doe", + age : 16 + }, { + name : "Justin", + surname : "Dean", + age : 18 + } + ], function (err) { + should.not.exist(err); + + Person.find({ name: "Jane" }, function (err, people) { + should.not.exist(err); + + Pet.find({ name: "Mutt" }, function (err, pets) { + should.not.exist(err); + + people[0].addPets(pets, done); + }); + }); + }); + }); + }); + }; + }; + + describe("getAccessorAsync", function () { + before(setup()); + + it("should allow to specify order as string", function (done) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync("-name").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].model().should.equal(Pet); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it ("should return proper instance model", function(done){ + Person.find({ name: "John" }, function (err, people) { + people[0].getPetsAsync("-name").then(function (pets) { + pets[0].model().should.equal(Pet); + done(); + }).catch(function(err) { + done(err); + }) + }); + }); + + it("should allow to specify order as Array", function (done) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].getPetsAsync([ "name", "Z" ]).then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify a limit", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync(1).then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("should allow to specify conditions", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.getPetsAsync({ name: "Mutt" }).then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + if (common.protocol() == "mongodb") return; + + it("should allow chaining count()", function (done) { + Person.find({}, function (err, people) { + should.equal(err, null); + + people[1].getPetsAsync().then(function (count) { + should.strictEqual(count.length, 2); + + people[2].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 1); + + people[3].getPetsAsync().then(function (count) { + + should.strictEqual(count.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + describe("hasAccessorAsync", function () { + before(setup()); + + it("should return true if instance has associated item", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + should.equal(err, null); + + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets[0]).then(function (has_pets) { + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should return true if not passing any instance and has associated items", function (done) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync().then(function (has_pets) { + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + + it("should return true if all passed instances are associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.true; + done(); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should return false if any passed instances are not associated", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.hasPetsAsync(pets).then(function (has_pets) { + should.equal(err, null); + has_pets.should.be.false; + + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + if (common.protocol() != "mongodb") { + it("should return true if join table has duplicate entries", function (done) { + Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.find({ name: "John" }).first(function (err, John) { + should.not.exist(err); + + John.hasPets(pets, function (err, hasPets) { + should.equal(err, null); + should.equal(hasPets, true); + + db.driver.execQuery( + "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", + [John.id, pets[0].id, John.id, pets[1].id], + function (err) { + should.not.exist(err); + + John.hasPets(pets, function (err, hasPets) { + should.equal(err, null); + should.equal(hasPets, true); + + done() + }); + } + ); + }); + }); + }); + }); + it("should return true if join table has duplicate entries (promise-based)", function (done) { + Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 2); + + Person.find({ name: "John" }).first(function (err, John) { + should.not.exist(err); + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + + db.driver.execQueryAsync( + "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", + [John.id, pets[0].id, John.id, pets[1].id]).then( + function () { + + John.hasPetsAsync(pets).then(function (hasPets) { + should.equal(hasPets, true); + done(); + }).catch(function(err){ + done(err); + }); + } + ).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + } + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should accept arguments in different orders", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + people[0].getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + return done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove specific associations if passed", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "John" }, function (err, people) { + should.equal(err, null); + + people[0].removePetsAsync(pets[0]).then(function () { + people[0].getPetsAsync().then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if none passed", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.removePetsAsync().then(function () { + John.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + describe("addAccessorAsync", function () { + before(setup()); + + if (common.protocol() != "mongodb") { + + it("might add duplicates (promise-based)", function (done) { + Pet.find({ name: "Mutt" }, function (err, pets) { + Person.find({ name: "Jane" }, function (err, people) { + should.equal(err, null); + + people[0].addPetsAsync(pets[0]).then(function () { + people[0].getPetsAsync("name").then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Mutt"); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + } + + it("should keep associations and add new ones", function (done) { + Pet.find({ name: "Deco" }).first(function (err, Deco) { + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (janesPets) { + should.not.exist(err); + + var petsAtStart = janesPets.length; + + Jane.addPetsAsync(Deco).then(function () { + Jane.getPetsAsync("name").then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].name.should.equal("Deco"); + pets[1].name.should.equal("Mutt"); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should accept several arguments as associations (promise-based)", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.addPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err){ + done(err); + }); + }).catch(function(err){ + done(err); + }); + }); + }); + }); + + it("should accept array as list of associations (promise-based)", function (done) { + Pet.createAsync([{ name: 'Ruff' }, { name: 'Spotty' }]).then(function (pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.getPetsAsync().then(function (justinsPets) { + + var petCount = justinsPets.length; + + Justin.addPetsAsync(pets).then(function () { + + Justin.getPetsAsync().then(function (justinsPets) { + + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); + + done(); + }); + }).catch(function(err){ + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + + describe("setAccessorAsync", function () { + before(setup()); + + it("should accept several arguments as associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets[0], pets[1]).then(function () { + Justin.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function (err) { + done(err); + }); + }); + }); + }); + + it("should accept an array of associations", function (done) { + Pet.find(function (err, pets) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + + Justin.setPetsAsync(pets).then(function () { + Justin.getPetsAsync().then(function (all_pets) { + should(Array.isArray(all_pets)); + all_pets.length.should.equal(pets.length); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + it("should remove all associations if an empty array is passed", function (done) { + Person.find({ name: "Justin" }).first(function (err, Justin) { + should.equal(err, null); + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 4); + + Justin.setPetsAsync([]).then(function () { + + Justin.getPetsAsync().then(function (pets) { + should.equal(pets.length, 0); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + + it("clears current associations", function (done) { + Pet.find({ name: "Deco" }, function (err, pets) { + var Deco = pets[0]; + + Person.find({ name: "Jane" }).first(function (err, Jane) { + should.equal(err, null); + + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Mutt"); + + Jane.setPetsAsync(Deco).then(function () { + + Jane.getPetsAsync().then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal(Deco.name); + + done(); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }); + + describe("with autoFetch turned on", function () { + before(setup({ + autoFetchPets : true + })); + + it("should fetch associations", function (done) { + Person.find({ name: "John" }).first(function (err, John) { + should.equal(err, null); + + John.should.have.property("pets"); + should(Array.isArray(John.pets)); + John.pets.length.should.equal(2); + + return done(); + }); + }); + + it("should save existing", function (done) { + Person.create({ name: 'Bishan' }, function (err) { + should.not.exist(err); + + Person.one({ name: 'Bishan' }, function (err, person) { + should.not.exist(err); + + person.surname = 'Dominar'; + + person.save(function (err) { + should.not.exist(err); + + done(); + }); + }); + }); + }); + + it("should not auto save associations which were autofetched", function (done) { + Pet.all(function (err, pets) { + should.not.exist(err); + should.equal(pets.length, 4); + + Person.create({ name: 'Paul' }, function (err, paul) { + should.not.exist(err); + + Person.one({ name: 'Paul' }, function (err, paul2) { + should.not.exist(err); + should.equal(paul2.pets.length, 0); + + paul.setPets(pets, function (err) { + should.not.exist(err); + + // reload paul to make sure we have 2 pets + Person.one({ name: 'Paul' }, function (err, paul) { + should.not.exist(err); + should.equal(paul.pets.length, 4); + + // Saving paul2 should NOT auto save associations and hence delete + // the associations we just created. + paul2.save(function (err) { + should.not.exist(err); + + // let's check paul - pets should still be associated + Person.one({ name: 'Paul' }, function (err, paul) { + should.not.exist(err); + should.equal(paul.pets.length, 4); + + done(); + }); + }); + }); + }); + }); + }); + }); + }); + + it("should save associations set by the user", function (done) { + Person.one({ name: 'John' }, function (err, john) { + should.not.exist(err); + should.equal(john.pets.length, 2); + + john.pets = []; + + john.save(function (err) { + should.not.exist(err); + + // reload john to make sure pets were deleted + Person.one({ name: 'John' }, function (err, john) { + should.not.exist(err); + should.equal(john.pets.length, 0); + + done(); + }); + }); + }); + }); + + }); + }); + + if (protocol == "mongodb") return; + +}); diff --git a/test/integration/association-hasmany.js b/test/integration/association-hasmany.js index bf4a9998..3170b07e 100644 --- a/test/integration/association-hasmany.js +++ b/test/integration/association-hasmany.js @@ -625,542 +625,6 @@ describe("hasMany", function () { }); }); - describe("getAccessorAsync", function () { - before(setup()); - - it("should allow to specify order as string", function (done) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync("-name").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].model().should.equal(Pet); - pets[0].name.should.equal("Mutt"); - pets[1].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it ("should return proper instance model", function(done){ - Person.find({ name: "John" }, function (err, people) { - people[0].getPetsAsync("-name").then(function (pets) { - pets[0].model().should.equal(Pet); - done(); - }).catch(function(err) { - done(err); - }) - }); - }); - - it("should allow to specify order as Array", function (done) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync([ "name", "Z" ]).then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].name.should.equal("Mutt"); - pets[1].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should allow to specify a limit", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync(1).then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should allow to specify conditions", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync({ name: "Mutt" }).then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Mutt"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - if (common.protocol() == "mongodb") return; - - it("should allow chaining count()", function (done) { - Person.find({}, function (err, people) { - should.equal(err, null); - - people[1].getPetsAsync().then(function (count) { - should.strictEqual(count.length, 2); - - people[2].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 1); - - people[3].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - describe("hasAccessorAsync", function () { - before(setup()); - - it("should return true if instance has associated item", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - should.equal(err, null); - - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets[0]).then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should return true if not passing any instance and has associated items", function (done) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync().then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - - it("should return true if all passed instances are associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should return false if any passed instances are not associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.false; - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - if (common.protocol() != "mongodb") { - it("should return true if join table has duplicate entries", function (done) { - Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 2); - - Person.find({ name: "John" }).first(function (err, John) { - should.not.exist(err); - - John.hasPets(pets, function (err, hasPets) { - should.equal(err, null); - should.equal(hasPets, true); - - db.driver.execQuery( - "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id], - function (err) { - should.not.exist(err); - - John.hasPets(pets, function (err, hasPets) { - should.equal(err, null); - should.equal(hasPets, true); - - done() - }); - } - ); - }); - }); - }); - }); - it("should return true if join table has duplicate entries (promise-based)", function (done) { - Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 2); - - Person.find({ name: "John" }).first(function (err, John) { - should.not.exist(err); - - John.hasPetsAsync(pets).then(function (hasPets) { - should.equal(hasPets, true); - - db.driver.execQueryAsync( - "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id]).then( - function () { - - John.hasPetsAsync(pets).then(function (hasPets) { - should.equal(hasPets, true); - done(); - }).catch(function(err){ - done(err); - }); - } - ).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - } - }); - - describe("delAccessorAsync", function () { - before(setup()); - - it("should accept arguments in different orders", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - people[0].getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - return done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should remove specific associations if passed", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - people[0].getPetsAsync().then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should remove all associations if none passed", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.removePetsAsync().then(function () { - John.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - describe("addAccessorAsync", function () { - before(setup()); - - if (common.protocol() != "mongodb") { - - it("might add duplicates (promise-based)", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "Jane" }, function (err, people) { - should.equal(err, null); - - people[0].addPetsAsync(pets[0]).then(function () { - people[0].getPetsAsync("name").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].name.should.equal("Mutt"); - pets[1].name.should.equal("Mutt"); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - } - - it("should keep associations and add new ones", function (done) { - Pet.find({ name: "Deco" }).first(function (err, Deco) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (janesPets) { - should.not.exist(err); - - var petsAtStart = janesPets.length; - - Jane.addPetsAsync(Deco).then(function () { - Jane.getPetsAsync("name").then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(petsAtStart + 1); - pets[0].name.should.equal("Deco"); - pets[1].name.should.equal("Mutt"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should accept several arguments as associations (promise-based)", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.addPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should accept array as list of associations (promise-based)", function (done) { - Pet.createAsync([{ name: 'Ruff' }, { name: 'Spotty' }]).then(function (pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.getPetsAsync().then(function (justinsPets) { - - var petCount = justinsPets.length; - - Justin.addPetsAsync(pets).then(function () { - - Justin.getPetsAsync().then(function (justinsPets) { - - should(Array.isArray(justinsPets)); - // Mongo doesn't like adding duplicates here, so we add new ones. - should.equal(justinsPets.length, petCount + 2); - - done(); - }); - }).catch(function(err){ - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe("setAccessorAsync", function () { - before(setup()); - - it("should accept several arguments as associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }); - }); - }); - - it("should accept an array of associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets).then(function () { - Justin.getPetsAsync().then(function (all_pets) { - should(Array.isArray(all_pets)); - all_pets.length.should.equal(pets.length); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should remove all associations if an empty array is passed", function (done) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - Justin.getPetsAsync().then(function (pets) { - should.equal(pets.length, 4); - - Justin.setPetsAsync([]).then(function () { - - Justin.getPetsAsync().then(function (pets) { - should.equal(pets.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("clears current associations", function (done) { - Pet.find({ name: "Deco" }, function (err, pets) { - var Deco = pets[0]; - - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Mutt"); - - Jane.setPetsAsync(Deco).then(function () { - - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal(Deco.name); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - describe("with autoFetch turned on", function () { before(setup({ autoFetchPets : true From 1e38407df81cd1fdb85054ac75b9f62d5e633adc Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 13 Sep 2017 16:45:02 +0300 Subject: [PATCH 63/98] update has meny tests --- test/integration/association-hasmany-async.js | 840 +++++++----------- test/integration/association-hasmany-extra.js | 68 +- test/integration/association-hasmany-hooks.js | 109 +-- .../association-hasmany-mapsto-async.js | 474 ++++++++++ .../integration/association-hasmany-mapsto.js | 540 ----------- 5 files changed, 886 insertions(+), 1145 deletions(-) create mode 100644 test/integration/association-hasmany-mapsto-async.js diff --git a/test/integration/association-hasmany-async.js b/test/integration/association-hasmany-async.js index a48fed80..d49a7ce7 100644 --- a/test/integration/association-hasmany-async.js +++ b/test/integration/association-hasmany-async.js @@ -1,7 +1,5 @@ -var _ = require('lodash'); var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); var common = require('../common'); var protocol = common.protocol(); @@ -92,245 +90,170 @@ describe("hasMany", function () { describe("getAccessorAsync", function () { before(setup()); - it("should allow to specify order as string", function (done) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync("-name").then(function (pets) { - + it("should allow to specify order as string", function () { + return Person.findAsync({ name: "John" }) + .then(function (people) { + return people[0].getPetsAsync("-name"); + }) + .then(function (pets) { should(Array.isArray(pets)); pets.length.should.equal(2); pets[0].model().should.equal(Pet); pets[0].name.should.equal("Mutt"); pets[1].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); }); - }); }); - it ("should return proper instance model", function(done){ - Person.find({ name: "John" }, function (err, people) { - people[0].getPetsAsync("-name").then(function (pets) { - pets[0].model().should.equal(Pet); - done(); - }).catch(function(err) { - done(err); + it("should return proper instance model", function(){ + return Person.findAsync({ name: "John" }) + .then(function (people) { + return people[0].getPetsAsync("-name"); }) - }); + .then(function (pets) { + pets[0].model().should.equal(Pet); + }); }); - it("should allow to specify order as Array", function (done) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync([ "name", "Z" ]).then(function (pets) { - + it("should allow to specify order as Array", function () { + return Person.findAsync({ name: "John" }) + .then(function (people) { + return people[0].getPetsAsync([ "name", "Z" ]); + }) + .then(function (pets) { should(Array.isArray(pets)); pets.length.should.equal(2); pets[0].name.should.equal("Mutt"); pets[1].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); }); - }); }); - it("should allow to specify a limit", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync(1).then(function (pets) { + it("should allow to specify a limit", function () { + return Person.find({ name: "John" }) + .firstAsync() + .then(function (John) { + return John.getPetsAsync(1) + }) + .then(function (pets) { should(Array.isArray(pets)); pets.length.should.equal(1); - - done(); - }).catch(function(err) { - done(err); }); - }); }); - it("should allow to specify conditions", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync({ name: "Mutt" }).then(function (pets) { - + it("should allow to specify conditions", function () { + return Person.find({ name: "John" }).firstAsync() + .then(function (John) { + return John.getPetsAsync({ name: "Mutt" }); + }) + .then(function (pets) { should(Array.isArray(pets)); pets.length.should.equal(1); pets[0].name.should.equal("Mutt"); - - done(); - }).catch(function(err) { - done(err); }); - }); }); if (common.protocol() == "mongodb") return; - it("should allow chaining count()", function (done) { - Person.find({}, function (err, people) { - should.equal(err, null); - - people[1].getPetsAsync().then(function (count) { - should.strictEqual(count.length, 2); - - people[2].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 1); - - people[3].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); + it("should allow chaining count()", function () { + return Person.findAsync({}) + .then(function (people) { + return [people[1].getPetsAsync(), people[2].getPetsAsync(), people[3].getPetsAsync()]; + }) + .spread(function (count1, count2, count3) { + should.strictEqual(count1.length, 2); + should.strictEqual(count2.length, 1); + should.strictEqual(count3.length, 0); }); - }); }); }); describe("hasAccessorAsync", function () { before(setup()); - it("should return true if instance has associated item", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - should.equal(err, null); - - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets[0]).then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - it("should return true if not passing any instance and has associated items", function (done) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync().then(function (has_pets) { - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); - }); - }); - - it("should return true if all passed instances are associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.true; - done(); - }).catch(function(err){ - done(err); - }); + it("should return true if instance has associated item", function () { + return Pet.findAsync({ name: "Mutt" }) + .then(function (pets) { + return [pets, Person.find({ name: "Jane" }).firstAsync()]; + }) + .spread(function (pets, Jane) { + return Jane.hasPetsAsync(pets[0]); + }) + .then(function (has_pets) { + has_pets.should.be.true(); }); - }); }); - it("should return false if any passed instances are not associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets).then(function (has_pets) { - should.equal(err, null); - has_pets.should.be.false; - - done(); - }).catch(function(err) { - done(err); - }); + it("should return false if any passed instances are not associated", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ name: "Jane" }).firstAsync()]; + }) + .spread(function (pets, Jane) { + return Jane.hasPetsAsync(pets); + }) + .then(function (has_pets) { + has_pets.should.be.false(); }); - }); }); if (common.protocol() != "mongodb") { - it("should return true if join table has duplicate entries", function (done) { - Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 2); - - Person.find({ name: "John" }).first(function (err, John) { - should.not.exist(err); - - John.hasPets(pets, function (err, hasPets) { - should.equal(err, null); - should.equal(hasPets, true); - - db.driver.execQuery( + it("should return true if join table has duplicate entries", function () { + return Pet.findAsync({ name: ["Mutt", "Deco"] }) + .then(function (pets) { + should.equal(pets.length, 2); + + return [pets, Person.find({ name: "John" }).firstAsync()]; + }) + .spread(function (pets, John) { + return [John, pets, John.hasPetsAsync(pets)]; + }) + .spread(function (John, pets, hasPets) { + should.equal(hasPets, true); + + return [ + John, + pets, + db.driver.execQueryAsync( "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id], - function (err) { - should.not.exist(err); - - John.hasPets(pets, function (err, hasPets) { - should.equal(err, null); - should.equal(hasPets, true); - - done() - }); - } - ); - }); + [John.id, pets[0].id, John.id, pets[1].id] + ) + ]; + }) + .spread(function (John, pets) { + return John.hasPetsAsync(pets); + }) + .then(function (hasPets) { + should.equal(hasPets, true); }); - }); }); - it("should return true if join table has duplicate entries (promise-based)", function (done) { - Pet.find({ name: ["Mutt", "Deco"] }, function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 2); - - Person.find({ name: "John" }).first(function (err, John) { - should.not.exist(err); - - John.hasPetsAsync(pets).then(function (hasPets) { - should.equal(hasPets, true); + it("should return true if join table has duplicate entries (promise-based)", function () { + return Pet.findAsync({ name: ["Mutt", "Deco"] }) + .then(function (pets) { + should.equal(pets.length, 2); + + return [pets, Person.find({ name: "John" }).firstAsync()]; + }) + .spread(function (pets, John) { + return [ John, pets, John.hasPetsAsync(pets)]; + }) + .spread(function (John, pets, hasPets) { + should.equal(hasPets, true); + + return [ + John, + pets, db.driver.execQueryAsync( "INSERT INTO person_pets (person_id, pets_id) VALUES (?,?), (?,?)", - [John.id, pets[0].id, John.id, pets[1].id]).then( - function () { - - John.hasPetsAsync(pets).then(function (hasPets) { - should.equal(hasPets, true); - done(); - }).catch(function(err){ - done(err); - }); - } - ).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); + [John.id, pets[0].id, John.id, pets[1].id] + ) + ]; + }) + .spread(function (John, pets) { + return John.hasPetsAsync(pets); + }) + .then(function (hasPets) { + should.equal(hasPets, true); }); - }); }); } }); @@ -338,69 +261,54 @@ describe("hasMany", function () { describe("delAccessorAsync", function () { before(setup()); - it("should accept arguments in different orders", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - people[0].getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - return done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); + it("should accept arguments in different orders", function () { + return Pet.findAsync({ name: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ name: "John" })]; + }) + .spread(function (pets, people) { + return [people, people[0].removePetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); }); - }); }); - it("should remove specific associations if passed", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - people[0].getPetsAsync().then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); + it("should remove specific associations if passed", function () { + return Pet.findAsync({ name: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ name: "John" })]; + }) + .spread(function (pets, people) { + return [people, people[0].removePetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Deco"); }); - }); }); - it("should remove all associations if none passed", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.removePetsAsync().then(function () { - John.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); + it("should remove all associations if none passed", function () { + return Person.find({ name: "John" }).firstAsync() + .then(function (John) { + return [John, John.removePetsAsync()]; + }) + .spread(function (John) { + return John.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(0); }); - }); }); }); @@ -409,219 +317,171 @@ describe("hasMany", function () { if (common.protocol() != "mongodb") { - it("might add duplicates (promise-based)", function (done) { - Pet.find({ name: "Mutt" }, function (err, pets) { - Person.find({ name: "Jane" }, function (err, people) { - should.equal(err, null); - - people[0].addPetsAsync(pets[0]).then(function () { - people[0].getPetsAsync("name").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].name.should.equal("Mutt"); - pets[1].name.should.equal("Mutt"); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); + it("might add duplicates (promise-based)", function () { + return Pet.findAsync({ name: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ name: "Jane" })]; + }) + .spread(function (pets, people) { + return [people, people[0].addPetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync("name"); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].name.should.equal("Mutt"); + pets[1].name.should.equal("Mutt"); }); - }); }); } - it("should keep associations and add new ones", function (done) { - Pet.find({ name: "Deco" }).first(function (err, Deco) { - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (janesPets) { - should.not.exist(err); - - var petsAtStart = janesPets.length; - - Jane.addPetsAsync(Deco).then(function () { - Jane.getPetsAsync("name").then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(petsAtStart + 1); - pets[0].name.should.equal("Deco"); - pets[1].name.should.equal("Mutt"); + it("should keep associations and add new ones", function () { + return Pet.find({ name: "Deco" }).firstAsync() + .then(function (Deco) { + return [Deco, Person.find({ name: "Jane" }).firstAsync()]; + }) + .spread(function (Deco, Jane) { + return [Jane, Deco, Jane.getPetsAsync()] + }) + .spread(function (Jane, Deco, janesPets) { + var petsAtStart = janesPets.length; - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); + return [petsAtStart, Jane, Jane.addPetsAsync(Deco)]; + }) + .spread(function (petsAtStart, Jane) { + return [petsAtStart, Jane.getPetsAsync("name")]; + }) + .spread(function (petsAtStart, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].name.should.equal("Deco"); + pets[1].name.should.equal("Mutt"); }); - }); }); - it("should accept several arguments as associations (promise-based)", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.addPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err){ - done(err); - }); - }).catch(function(err){ - done(err); - }); + it("should accept several arguments as associations (promise-based)", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ name: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, Justin.addPetsAsync(pets[0], pets[1])]; + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); }); - }); }); - it("should accept array as list of associations (promise-based)", function (done) { - Pet.createAsync([{ name: 'Ruff' }, { name: 'Spotty' }]).then(function (pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.getPetsAsync().then(function (justinsPets) { - - var petCount = justinsPets.length; - - Justin.addPetsAsync(pets).then(function () { - - Justin.getPetsAsync().then(function (justinsPets) { - - should(Array.isArray(justinsPets)); - // Mongo doesn't like adding duplicates here, so we add new ones. - should.equal(justinsPets.length, petCount + 2); + it("should accept array as list of associations (promise-based)", function () { + return Pet.createAsync([{ name: 'Ruff' }, { name: 'Spotty' }]) + .then(function (pets) { + return [pets, Person.find({ name: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [pets, Justin, Justin.getPetsAsync()]; + }) + .spread(function (pets, Justin, justinsPets) { + var petCount = justinsPets.length; - done(); - }); - }).catch(function(err){ - done(err); - }); - }).catch(function(err) { - done(err); - }); + return [Justin, petCount, Justin.addPetsAsync(pets)]; + }) + .spread(function (Justin, petCount) { + return [petCount, Justin.getPetsAsync()]; + }) + .spread(function (petCount, justinsPets) { + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); }); - }).catch(function(err) { - done(err); - }); }); }); describe("setAccessorAsync", function () { before(setup()); - it("should accept several arguments as associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); + it("should accept several arguments as associations", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ name: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, Justin.setPetsAsync(pets[0], pets[1])] + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); }); - }); }); - it("should accept an array of associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets).then(function () { - Justin.getPetsAsync().then(function (all_pets) { - should(Array.isArray(all_pets)); - all_pets.length.should.equal(pets.length); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); + it("should accept an array of associations", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ name: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [pets, Justin, Justin.setPetsAsync(pets)]; + }) + .spread(function (pets, Justin) { + return [pets, Justin.getPetsAsync()]; + }) + .spread(function (pets, all_pets) { + should(Array.isArray(all_pets)); + all_pets.length.should.equal(pets.length); }); - }); }); - it("should remove all associations if an empty array is passed", function (done) { - Person.find({ name: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - Justin.getPetsAsync().then(function (pets) { + it("should remove all associations if an empty array is passed", function () { + return Person.find({ name: "Justin" }).firstAsync() + .then(function (Justin) { + return [Justin, Justin.getPetsAsync()]; + }) + .spread(function (Justin, pets) { should.equal(pets.length, 4); - Justin.setPetsAsync([]).then(function () { - - Justin.getPetsAsync().then(function (pets) { - should.equal(pets.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); + return [Justin, Justin.setPetsAsync([])]; + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should.equal(pets.length, 0); }); - }); }); - it("clears current associations", function (done) { - Pet.find({ name: "Deco" }, function (err, pets) { - var Deco = pets[0]; - - Person.find({ name: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal("Mutt"); + it("clears current associations", function () { + return Pet.findAsync({ name: "Deco" }) + .then(function (pets) { + var Deco = pets[0]; - Jane.setPetsAsync(Deco).then(function () { - - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].name.should.equal(Deco.name); + return [Deco, Person.find({ name: "Jane" }).firstAsync()]; + }) + .spread(function (Deco, Jane) { + return [Jane, Deco, Jane.getPetsAsync()]; + }) + .spread(function (Jane, Deco, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal("Mutt"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); + return [Jane, Deco, Jane.setPetsAsync(Deco)]; + }) + .spread(function (Jane, Deco) { + return [Deco, Jane.getPetsAsync()]; + }) + .spread(function (Deco, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].name.should.equal(Deco.name); }); - }); }); }); @@ -630,95 +490,81 @@ describe("hasMany", function () { autoFetchPets : true })); - it("should fetch associations", function (done) { - Person.find({ name: "John" }).first(function (err, John) { - should.equal(err, null); - - John.should.have.property("pets"); - should(Array.isArray(John.pets)); - John.pets.length.should.equal(2); - - return done(); - }); + it("should fetch associations", function () { + return Person.find({ name: "John" }).firstAsync() + .then(function (John) { + John.should.have.property("pets"); + should(Array.isArray(John.pets)); + John.pets.length.should.equal(2); + }); }); - it("should save existing", function (done) { - Person.create({ name: 'Bishan' }, function (err) { - should.not.exist(err); - - Person.one({ name: 'Bishan' }, function (err, person) { - should.not.exist(err); - + it("should save existing", function () { + return Person.createAsync({ name: 'Bishan' }) + .then(function () { + return Person.oneAsync({ name: 'Bishan' }); + }) + .then(function (person) { person.surname = 'Dominar'; - person.save(function (err) { - should.not.exist(err); - - done(); - }); + return person.saveAsync(); + }) + .then(function (person) { + should.equal(person.surname, 'Dominar'); }); - }); }); - it("should not auto save associations which were autofetched", function (done) { - Pet.all(function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 4); - - Person.create({ name: 'Paul' }, function (err, paul) { - should.not.exist(err); - - Person.one({ name: 'Paul' }, function (err, paul2) { - should.not.exist(err); - should.equal(paul2.pets.length, 0); - - paul.setPets(pets, function (err) { - should.not.exist(err); - - // reload paul to make sure we have 2 pets - Person.one({ name: 'Paul' }, function (err, paul) { - should.not.exist(err); - should.equal(paul.pets.length, 4); + it("should not auto save associations which were autofetched", function () { + return Pet.allAsync() + .then(function (pets) { + should.equal(pets.length, 4); - // Saving paul2 should NOT auto save associations and hence delete - // the associations we just created. - paul2.save(function (err) { - should.not.exist(err); + return [pets, Person.createAsync({ name: 'Paul' })]; + }) + .spread(function (pets) { + return [pets, Person.oneAsync({ name: 'Paul' })]; + }) + .spread(function (pets, paul) { + should.equal(paul.pets.length, 0); - // let's check paul - pets should still be associated - Person.one({ name: 'Paul' }, function (err, paul) { - should.not.exist(err); - should.equal(paul.pets.length, 4); + return paul.setPetsAsync(pets); + }) + .then(function () { + // reload paul to make sure we have 2 pets + return Person.oneAsync({ name: 'Paul' }); + }) + .then(function (paul) { + should.equal(paul.pets.length, 4); - done(); - }); - }); - }); - }); - }); + // Saving paul2 should NOT auto save associations and hence delete + // the associations we just created. + return paul.saveAsync(); + }) + .then(function () { + // let's check paul - pets should still be associated + return Person.oneAsync({ name: 'Paul' }); + }) + .then(function (paul) { + should.equal(paul.pets.length, 4); }); - }); }); - it("should save associations set by the user", function (done) { - Person.one({ name: 'John' }, function (err, john) { - should.not.exist(err); - should.equal(john.pets.length, 2); - - john.pets = []; + it("should save associations set by the user", function () { + return Person.oneAsync({ name: 'John' }) + .then(function (john) { + should.equal(john.pets.length, 2); - john.save(function (err) { - should.not.exist(err); + john.pets = []; + return john.saveAsync(); + }) + .then(function () { // reload john to make sure pets were deleted - Person.one({ name: 'John' }, function (err, john) { - should.not.exist(err); - should.equal(john.pets.length, 0); - - done(); - }); + return Person.oneAsync({ name: 'John' }); + }) + .then(function (john) { + should.equal(john.pets.length, 0); }); - }); }); }); diff --git a/test/integration/association-hasmany-extra.js b/test/integration/association-hasmany-extra.js index e4289ed8..2b07f164 100644 --- a/test/integration/association-hasmany-extra.js +++ b/test/integration/association-hasmany-extra.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("hasMany extra properties", function() { var db = null; @@ -79,47 +78,40 @@ describe("hasMany extra properties", function() { describe("if passed to addAccessorAsync", function () { before(setup()); - it("should be added to association", function (done) { - Person.createAsync([{ + it("should be added to association", function () { + return Person.createAsync([{ name : "John" - }]).then(function (people) { - Pet.createAsync([{ - name : "Deco" - }, { - name : "Mutt" - }]).then(function (pets) { + }]) + .then(function (people) { + return [people, Pet.createAsync([{ + name : "Deco" + }, { + name : "Mutt" + }])]; + }) + .spread(function (people, pets) { var data = { adopted: true }; - people[0].addPetsAsync(pets, { since : new Date(), data: data }).then(function () { - - Person.find({ name: "John" }, { autoFetch : true }).first(function (err, John) { - should.equal(err, null); - - John.should.have.property("pets"); - should(Array.isArray(pets)); - - John.pets.length.should.equal(2); - - John.pets[0].should.have.property("name"); - John.pets[0].should.have.property("extra"); - John.pets[0].extra.should.be.a.Object(); - John.pets[0].extra.should.have.property("since"); - should(John.pets[0].extra.since instanceof Date); - - should.equal(typeof John.pets[0].extra.data, 'object'); - should.equal(JSON.stringify(data), JSON.stringify(John.pets[0].extra.data)); - - done(); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); + return [pets, data, people[0].addPetsAsync(pets, { since : new Date(), data: data })]; + }) + .spread(function (pets, data) { + return [pets, data, Person.find({ name: "John" }, { autoFetch : true }).firstAsync()]; + }) + .spread(function (pets, data, John) { + John.should.have.property("pets"); + should(Array.isArray(pets)); + + John.pets.length.should.equal(2); + + John.pets[0].should.have.property("name"); + John.pets[0].should.have.property("extra"); + John.pets[0].extra.should.be.a.Object(); + John.pets[0].extra.should.have.property("since"); + should(John.pets[0].extra.since instanceof Date); + + should.equal(typeof John.pets[0].extra.data, 'object'); + should.equal(JSON.stringify(data), JSON.stringify(John.pets[0].extra.data)); }); - }).catch(function(err) { - done(err); - }); }); }); }); diff --git a/test/integration/association-hasmany-hooks.js b/test/integration/association-hasmany-hooks.js index 54bbf504..fa201d3f 100644 --- a/test/integration/association-hasmany-hooks.js +++ b/test/integration/association-hasmany-hooks.js @@ -1,6 +1,5 @@ var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); describe("hasMany hooks", function() { var db = null; @@ -64,8 +63,6 @@ describe("hasMany hooks", function() { }); describe("beforeSave", function () { - var had_extra = false; - before(setup({}, { hooks : { beforeSave: function (next) { @@ -93,8 +90,6 @@ describe("hasMany hooks", function() { }); describe("beforeSave", function () { - var had_extra = false; - before(setup({}, { hooks : { beforeSave: function (next) { @@ -130,90 +125,64 @@ describe("hasMany hooks", function() { born : Date }, { hooks : { - beforeSave: function (extra, next) { - had_extra = (typeof extra == "object"); - return next(); - } - } - })); - - it("should pass extra data to hook if extra defined", function (done) { - Person.create({ - name : "John" - }, function (err, John) { - Pet.create({ - name : "Deco" - }, function (err, Deco) { - John.addPetsAsync(Deco).then(function () { - should.not.exist(err); - - had_extra.should.equal(true); - - done(); - }).catch(function(err) { - done(err); + beforeSave: function (extra) { + return new Promise(function (resolve) { + setTimeout(function () { + had_extra = (typeof extra == "object"); + resolve() + }, 1000); }); - }); - }); - }); - }); - - describe("beforeSaveAsync", function () { - var had_extra = false; - - before(setup({}, { - hooks : { - beforeSave: function (next) { - next.should.be.a.Function(); - return next(); } } })); - it("should not pass extra data to hook if extra defined", function (done) { - Person.create({ + it("should pass extra data to hook if extra defined", function () { + return Person.createAsync({ name : "John" - }, function (err, John) { - Pet.create({ - name : "Deco" - }, function (err, Deco) { - John.addPetsAsync(Deco).then(function () { - done(); - }).catch(function(err) { - done(err); - }); + }) + .then(function (John) { + return [John, Pet.createAsync({ + name : "Deco" + })]; + }) + .spread(function (John, Deco) { + return John.addPetsAsync(Deco); + }) + .then(function () { + had_extra.should.equal(true); }); - }); }); }); describe("beforeSaveAsync", function () { - var had_extra = false; - before(setup({}, { hooks : { - beforeSave: function (next) { - setTimeout(function () { - return next(new Error('blocked')); - }, 100); + beforeSave: function () { + return new Promise(function (resolve, reject) { + setTimeout(function () { + return reject(new Error('blocked')); + }, 1000); + }); } } })); - it("should block if error returned", function (done) { - Person.create({ + it("should block if error returned", function () { + return Person.createAsync({ name : "John" - }, function (err, John) { - Pet.create({ - name : "Deco" - }, function (err, Deco) { - John.addPetsAsync(Deco).catch(function(err) { - should.exist(err); - err.message.should.equal('blocked'); - done() - }); + }) + .then(function (John) { + return [John, Pet.createAsync({ + name : "Deco" + })]; + }) + .spread(function (John, Deco) { + return John.addPetsAsync(Deco); + }) + .catch(function(err) { + should.exist(err); + err.message.should.equal('blocked'); }); - }); }); }); }); diff --git a/test/integration/association-hasmany-mapsto-async.js b/test/integration/association-hasmany-mapsto-async.js new file mode 100644 index 00000000..1088bdfe --- /dev/null +++ b/test/integration/association-hasmany-mapsto-async.js @@ -0,0 +1,474 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var common = require('../common'); + +if (common.protocol() == "mongodb") return; // Can't do mapsTo testing on mongoDB () + +describe("hasMany with MapsTo Async", function () { + var db = null; + var Person = null; + var Pet = null; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + var setup = function (opts) { + opts = opts || {}; + + return function (done) { + db.settings.set('instance.identityCache', false); + + Person = db.define('person', { + id : {type : "serial", size:"8", mapsTo: "personID", key:true}, + firstName : {type : "text", size:"255", mapsTo: "name"}, + lastName : {type : "text", size:"255", mapsTo: "surname"}, + ageYears : {type : "number", size:"8", mapsTo: "age"} + }); + + Pet = db.define('pet', { + id : {type : "serial", size:"8", mapsTo:"petID", key:true}, + petName : {type : "text", size:"255", mapsTo: "name"} + }); + + Person.hasMany('pets', Pet, {}, + { autoFetch: opts.autoFetchPets, + mergeTable: 'person_pet', + mergeId: 'person_id', + mergeAssocId: 'pet_id'}); + + helper.dropSync([ Person, Pet ], function (err) { + if (err) return done(err); + // + // John --+---> Deco + // '---> Mutt <----- Jane + // + // Justin + // + Person.create([{ + firstName : "John", + lastName : "Doe", + ageYears : 20, + pets : [{ + petName : "Deco" + }, { + petName : "Mutt" + }] + }, { + firstName : "Jane", + lastName : "Doe", + ageYears : 16 + }, { + firstName : "Justin", + lastName : "Dean", + ageYears : 18 + }], function () { + Person.find({ firstName: "Jane" }, function (err, people) { + Pet.find({ petName: "Mutt" }, function (err, pets) { + people[0].addPets(pets, done); + }); + }); + }); + }); + }; + }; + + describe("getAccessorAsync", function () { + before(setup()); + + it("should allow to specify order as string", function () { + return Person.findAsync({ firstName: "John" }) + .then(function (people) { + return people[0].getPetsAsync("-petName"); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].model().should.equal(Pet); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Deco"); + }); + }); + + it("should return proper instance model", function(){ + return Person.findAsync({ firstName: "John" }) + .then(function (people) { + return people[0].getPetsAsync("-petName"); + }) + .then(function (pets) { + pets[0].model().should.equal(Pet); + }); + }); + + it("should allow to specify order as Array", function () { + return Person.findAsync({ firstName: "John" }) + .then(function (people) { + return people[0].getPetsAsync([ "petName", "Z" ]); + }) + .then(function (pets) { + + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Deco"); + }); + }); + + it("should allow to specify a limit", function () { + return Person.find({ firstName: "John" }).firstAsync() + .then(function (John) { + return John.getPetsAsync(1); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + }); + }); + + it("should allow to specify conditions", function () { + return Person.find({ firstName: "John" }).firstAsync() + .then(function (John) { + return John.getPetsAsync({ petName: "Mutt" }); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Mutt"); + }); + }); + + if (common.protocol() == "mongodb") return; + + it("should allow chaining count()", function () { + return Person.findAsync({}) + .then(function (people) { + return [people, people[0].getPetsAsync()]; + }) + .spread(function (people, count) { + should.strictEqual(count.length, 2); + return [people, people[1].getPetsAsync()]; + }) + .spread(function (people, count) { + should.strictEqual(count.length, 1); + return people[2].getPetsAsync(); + }) + .then(function (count) { + should.strictEqual(count.length, 0); + }); + }); + }); + + describe("hasAccessorAsync", function () { + before(setup()); + + it("should return true if instance has associated item", function () { + Pet.findAsync({ petName: "Mutt" }) + .then(function (pets) { + return [pets, Person.find({ firstName: "Jane" }).firstAsync()]; + }) + .spread(function (pets, Jane) { + return Jane.hasPetsAsync(pets[0]); + }) + .then(function (has_pets) { + has_pets.should.equal(true); + }); + }); + + it("should return false if any passed instances are not associated", function () { + Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ firstName: "Jane" }).firstAsync()]; + }) + .spread(function (pets, Jane) { + return Jane.hasPetsAsync(pets); + }) + .then(function (has_pets) { + has_pets.should.be.false(); + }); + }); + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should accept arguments in different orders", function () { + return Pet.findAsync({ petName: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ firstName: "John" })]; + }) + .spread(function (pets, people) { + return [people, people[0].removePetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Deco"); + + }); + }); + }); + + describe("delAccessorAsync", function () { + before(setup()); + + it("should remove specific associations if passed", function () { + return Pet.findAsync({ petName: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ firstName: "John" })]; + }) + .spread(function (pets, people) { + return [people, people[0].removePetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Deco"); + }); + }); + + it("should remove all associations if none passed", function () { + return Person.find({ firstName: "John" }).firstAsync() + .then(function (John) { + return [John, John.removePetsAsync()]; + }) + .spread(function (John) { + return John.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(0); + }); + }); + }); + + describe("addAccessorAsync", function () { + before(setup()); + + if (common.protocol() != "mongodb") { + it("might add duplicates", function () { + return Pet.findAsync({ petName: "Mutt" }) + .then(function (pets) { + return [pets, Person.findAsync({ firstName: "Jane" })]; + }) + .spread(function (pets, people) { + return [people, people[0].addPetsAsync(pets[0])]; + }) + .spread(function (people) { + return people[0].getPetsAsync("petName"); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); + pets[0].petName.should.equal("Mutt"); + pets[1].petName.should.equal("Mutt"); + }); + }); + } + + it("should keep associations and add new ones", function () { + return Pet.find({ petName: "Deco" }).firstAsync() + .then(function (Deco) { + return [Deco, Person.find({ firstName: "Jane" }).firstAsync()]; + }) + .spread(function (Deco, Jane) { + return [Deco, Jane, Jane.getPetsAsync()]; + }) + .spread(function (Deco, Jane, janesPets) { + var petsAtStart = janesPets.length; + return [petsAtStart, Jane, Jane.addPetsAsync(Deco)]; + }) + .spread(function (petsAtStart, Jane) { + return [petsAtStart, Jane.getPetsAsync("petName")]; + }) + .spread(function (petsAtStart, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(petsAtStart + 1); + pets[0].petName.should.equal("Deco"); + pets[1].petName.should.equal("Mutt"); + }); + }); + + it("should accept several arguments as associations", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ firstName: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, Justin.addPetsAsync(pets[0], pets[1])]; + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); + }); + }); + + it("should accept array as list of associations", function () { + return Pet.createAsync([{ petName: 'Ruff' }, { petName: 'Spotty' }]) + .then(function (pets) { + return [pets, Person.find({ firstName: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, pets, Justin.getPetsAsync()]; + }) + .spread(function (Justin, pets, justinsPets) { + var petCount = justinsPets.length; + return [petCount, Justin, Justin.addPetsAsync(pets)]; + }) + .spread(function (petCount, Justin) { + return [petCount, Justin.getPetsAsync()]; + }) + .spread(function (petCount, justinsPets) { + should(Array.isArray(justinsPets)); + // Mongo doesn't like adding duplicates here, so we add new ones. + should.equal(justinsPets.length, petCount + 2); + }); + }); + + it("should throw if no items passed", function () { + return Person.oneAsync() + .then(function (person) { + return person.addPetsAsync() + }) + .catch(function(err) { + should.exists(err); + }); + }); + }); + + describe("setAccessorAsync", function () { + before(setup()); + + it("should accept several arguments as associations", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ firstName: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, Justin.setPetsAsync(pets[0], pets[1])]; + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should(Array.isArray(pets)); + pets.length.should.equal(2); + }); + }); + + it("should accept an array of associations", function () { + return Pet.findAsync() + .then(function (pets) { + return [pets, Person.find({ firstName: "Justin" }).firstAsync()]; + }) + .spread(function (pets, Justin) { + return [Justin, pets, Justin.setPetsAsync(pets)]; + }) + .spread(function (Justin, pets) { + return [Justin.getPetsAsync(), pets]; + }) + .spread(function (all_pets, pets) { + should(Array.isArray(all_pets)); + all_pets.length.should.equal(pets.length); + }); + }); + + it("should remove all associations if an empty array is passed", function () { + return Person.find({ firstName: "Justin" }).firstAsync() + .then(function (Justin) { + return [Justin, Justin.getPetsAsync()]; + }) + .spread(function (Justin, pets) { + should.equal(pets.length, 2); + + return [Justin, Justin.setPetsAsync([])]; + }) + .spread(function (Justin) { + return Justin.getPetsAsync(); + }) + .then(function (pets) { + should.equal(pets.length, 0); + }); + }); + + it("clears current associations", function () { + return Pet.findAsync({ petName: "Deco" }) + .then(function (pets) { + var Deco = pets[0]; + + return [Deco, Person.find({ firstName: "Jane" }).firstAsync()]; + }) + .spread(function (Deco, Jane) { + return [Deco, Jane, Jane.getPetsAsync()]; + }) + .spread(function (Deco, Jane, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal("Mutt"); + + return [pets, Jane, Deco, Jane.setPetsAsync(Deco)] + }) + .spread(function (pets, Jane, Deco) { + return [Deco, Jane.getPetsAsync()]; + }) + .spread(function (Deco, pets) { + should(Array.isArray(pets)); + pets.length.should.equal(1); + pets[0].petName.should.equal(Deco.petName); + }); + }); + }); + + describe("with autoFetch turned on (promised-based test)", function () { + before(setup({ + autoFetchPets : true + })); + + it("should not auto save associations which were autofetched", function () { + return Pet.allAsync() + .then(function (pets) { + should.equal(pets.length, 2); + + return [pets, Person.createAsync({ firstName: 'Paul' })]; + }) + .spread(function (pets, paul) { + return [pets, paul, Person.oneAsync({ firstName: 'Paul' })]; + }) + .spread(function (pets, paul, paul2) { + should.equal(paul2.pets.length, 0); + + return [pets, paul, paul2, paul.setPetsAsync(pets)]; + }) + .spread(function (pets, paul2) { + + // reload paul to make sure we have 2 pets + return [pets, Person.oneAsync({ firstName: 'Paul' }), paul2]; + }) + .spread(function (pets, paul, paul2) { + should.equal(paul.pets.length, 2); + + // Saving paul2 should NOT auto save associations and hence delete + // the associations we just created. + return paul2.saveAsync(); + }) + .then(function () { + // let's check paul - pets should still be associated + return Person.oneAsync({ firstName: 'Paul' }); + }) + .then(function (paul) { + should.equal(paul.pets.length, 2); + }); + }); + }); +}); diff --git a/test/integration/association-hasmany-mapsto.js b/test/integration/association-hasmany-mapsto.js index df2ac501..05a3b1a8 100644 --- a/test/integration/association-hasmany-mapsto.js +++ b/test/integration/association-hasmany-mapsto.js @@ -1,9 +1,6 @@ -var _ = require('lodash'); var should = require('should'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); var common = require('../common'); -var protocol = common.protocol(); if (common.protocol() == "mongodb") return; // Can't do mapsTo testing on mongoDB () @@ -661,543 +658,6 @@ describe("hasMany with mapsTo", function () { }); }); }); - - }); - - describe("getAccessorAsync", function () { - before(setup()); - - it("should allow to specify order as string", function (done) { - Person.find({ firstName: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync("-petName").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].model().should.equal(Pet); - pets[0].petName.should.equal("Mutt"); - pets[1].petName.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it ("should return proper instance model", function(done){ - Person.find({ firstName: "John" }, function (err, people) { - people[0].getPetsAsync("-petName").then(function (pets) { - pets[0].model().should.equal(Pet); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should allow to specify order as Array", function (done) { - Person.find({ firstName: "John" }, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync([ "petName", "Z" ]).then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].petName.should.equal("Mutt"); - pets[1].petName.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should allow to specify a limit", function (done) { - Person.find({ firstName: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync(1).then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should allow to specify conditions", function (done) { - Person.find({ firstName: "John" }).first(function (err, John) { - should.equal(err, null); - - John.getPetsAsync({ petName: "Mutt" }).then(function (pets) { - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].petName.should.equal("Mutt"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - if (common.protocol() == "mongodb") return; - - it("should allow chaining count()", function (done) { - Person.find({}, function (err, people) { - should.equal(err, null); - - people[0].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 2); - - people[1].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 1); - - people[2].getPetsAsync().then(function (count) { - - should.strictEqual(count.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - describe("hasAccessorAsync", function () { - before(setup()); - - it("should return true if instance has associated item", function (done) { - Pet.find({ petName: "Mutt" }, function (err, pets) { - should.equal(err, null); - - Person.find({ firstName: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets[0]).then(function (has_pets) { - has_pets.should.equal(true); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should return true if not passing any instance and has associated items", function (done) { - Person.find({ firstName: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync().then(function (has_pets) { - has_pets.should.be.true; - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should return true if all passed instances are associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ firstName: "John" }).first(function (err, John) { - should.equal(err, null); - - John.hasPetsAsync(pets).then(function (has_pets) { - has_pets.should.be.true; - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should return false if any passed instances are not associated", function (done) { - Pet.find(function (err, pets) { - Person.find({ firstName: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.hasPetsAsync(pets).then(function (has_pets) { - has_pets.should.be.false; - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - - describe("delAccessorAsync", function () { - before(setup()); - - it("should accept arguments in different orders", function (done) { - Pet.find({ petName: "Mutt" }, function (err, pets) { - Person.find({ firstName: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - - people[0].getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].petName.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - - describe("delAccessorAsync", function () { - before(setup()); - - it("should remove specific associations if passed", function (done) { - Pet.find({ petName: "Mutt" }, function (err, pets) { - Person.find({ firstName: "John" }, function (err, people) { - should.equal(err, null); - - people[0].removePetsAsync(pets[0]).then(function () { - - people[0].getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].petName.should.equal("Deco"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should remove all associations if none passed", function (done) { - Person.find({ firstName: "John" }).first(function (err, John) { - should.equal(err, null); - - John.removePetsAsync().then(function () { - - John.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err){ - done(err); - }); - }); - }); - }); - - describe("addAccessorAsync", function () { - before(setup()); - - if (common.protocol() != "mongodb") { - it("might add duplicates", function (done) { - Pet.find({ petName: "Mutt" }, function (err, pets) { - Person.find({ firstName: "Jane" }, function (err, people) { - should.equal(err, null); - - people[0].addPetsAsync(pets[0]).then(function () { - - people[0].getPetsAsync("petName").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - pets[0].petName.should.equal("Mutt"); - pets[1].petName.should.equal("Mutt"); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - } - - it("should keep associations and add new ones", function (done) { - Pet.find({ petName: "Deco" }).first(function (err, Deco) { - Person.find({ firstName: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (janesPets) { - - var petsAtStart = janesPets.length; - - Jane.addPetsAsync(Deco).then(function () { - Jane.getPetsAsync("petName").then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(petsAtStart + 1); - pets[0].petName.should.equal("Deco"); - pets[1].petName.should.equal("Mutt"); - - done(); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should accept several arguments as associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ firstName: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.addPetsAsync(pets[0], pets[1]).then(function () { - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should accept array as list of associations", function (done) { - Pet.createAsync([{ petName: 'Ruff' }, { petName: 'Spotty' }]).then(function (pets) { - Person.find({ firstName: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.getPetsAsync().then(function (justinsPets) { - - var petCount = justinsPets.length; - - Justin.addPetsAsync(pets).then(function () { - Justin.getPetsAsync().then(function (justinsPets) { - - should(Array.isArray(justinsPets)); - // Mongo doesn't like adding duplicates here, so we add new ones. - should.equal(justinsPets.length, petCount + 2); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }).catch(function(err) { - done(err); - }); - }); - - it("should throw if no items passed", function (done) { - Person.one(function (err, person) { - should.equal(err, null); - person.addPetsAsync().catch(function(err) { - should.exists(err); - done(); - }); - }); - }); - }); - - describe("setAccessorAsync", function () { - before(setup()); - - it("should accept several arguments as associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ firstName: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets[0], pets[1]).then(function () { - - Justin.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(2); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should accept an array of associations", function (done) { - Pet.find(function (err, pets) { - Person.find({ firstName: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - - Justin.setPetsAsync(pets).then(function () { - Justin.getPetsAsync().then(function (all_pets) { - - should(Array.isArray(all_pets)); - all_pets.length.should.equal(pets.length); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("should remove all associations if an empty array is passed", function (done) { - Person.find({ firstName: "Justin" }).first(function (err, Justin) { - should.equal(err, null); - Justin.getPetsAsync().then(function (pets) { - should.equal(pets.length, 2); - - Justin.setPetsAsync([]).then(function () { - Justin.getPetsAsync().then(function (pets) { - should.equal(pets.length, 0); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("clears current associations", function (done) { - Pet.find({ petName: "Deco" }, function (err, pets) { - var Deco = pets[0]; - - Person.find({ firstName: "Jane" }).first(function (err, Jane) { - should.equal(err, null); - - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].petName.should.equal("Mutt"); - - Jane.setPetsAsync(Deco).then(function () { - Jane.getPetsAsync().then(function (pets) { - - should(Array.isArray(pets)); - pets.length.should.equal(1); - pets[0].petName.should.equal(Deco.petName); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - - describe("with autoFetch turned on (promised-based test)", function () { - before(setup({ - autoFetchPets : true - })); - - it("should not auto save associations which were autofetched", function (done) { - Pet.all(function (err, pets) { - should.not.exist(err); - should.equal(pets.length, 2); - - Person.createAsync({ firstName: 'Paul' }).then(function (paul) { - - Person.one({ firstName: 'Paul' }, function (err, paul2) { - should.not.exist(err); - should.equal(paul2.pets.length, 0); - - paul.setPetsAsync(pets).then(function () { - - // reload paul to make sure we have 2 pets - Person.one({ firstName: 'Paul' }, function (err, paul) { - should.not.exist(err); - should.equal(paul.pets.length, 2); - - // Saving paul2 should NOT auto save associations and hence delete - // the associations we just created. - paul2.saveAsync().then(function () { - // let's check paul - pets should still be associated - Person.one({ firstName: 'Paul' }, function (err, paul) { - should.not.exist(err); - should.equal(paul.pets.length, 2); - - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }).catch(function(err) { - done(err); - }); - }); - }).catch(function(err) { - done(err); - });; - }); - }); - }); }); }); From 373398b57eb406febd6e283dcce66b523b95924f Mon Sep 17 00:00:00 2001 From: root Date: Wed, 13 Sep 2017 16:46:46 +0300 Subject: [PATCH 64/98] Update part of hasone async tests --- test/integration/association-hasone-async.js | 244 +++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 test/integration/association-hasone-async.js diff --git a/test/integration/association-hasone-async.js b/test/integration/association-hasone-async.js new file mode 100644 index 00000000..51efb2e7 --- /dev/null +++ b/test/integration/association-hasone-async.js @@ -0,0 +1,244 @@ +var ORM = require('../../'); +var helper = require('../support/spec_helper'); +var should = require('should'); +var async = require('async'); +var _ = require('lodash'); +var common = require('../common'); +var protocol = common.protocol(); + +describe("hasOne", function() { + var db = null; + var Tree = null; + var Stalk = null; + var Leaf = null; + var leafId = null; + var treeId = null; + var stalkId = null; + var holeId = null; + + var setup = function (opts) { + opts = opts || {}; + return function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + Tree = db.define("tree", { type: { type: 'text' } }); + Stalk = db.define("stalk", { length: { type: 'integer' } }); + Hole = db.define("hole", { width: { type: 'integer' } }); + Leaf = db.define("leaf", { + size: { type: 'integer' }, + holeId: { type: 'integer', mapsTo: 'hole_id' } + }, { + validations: opts.validations + }); + Leaf.hasOne('tree', Tree, { field: 'treeId', autoFetch: !!opts.autoFetch }); + Leaf.hasOne('stalk', Stalk, { field: 'stalkId', mapsTo: 'stalk_id' }); + Leaf.hasOne('hole', Hole, { field: 'holeId' }); + + return helper.dropSync([Tree, Stalk, Hole, Leaf], function() { + Tree.create({ type: 'pine' }, function (err, tree) { + should.not.exist(err); + treeId = tree[Tree.id]; + Leaf.create({ size: 14 }, function (err, leaf) { + should.not.exist(err); + leafId = leaf[Leaf.id]; + leaf.setTree(tree, function (err) { + should.not.exist(err); + Stalk.create({ length: 20 }, function (err, stalk) { + should.not.exist(err); + should.exist(stalk); + stalkId = stalk[Stalk.id]; + Hole.create({ width: 3 }, function (err, hole) { + should.not.exist(err); + holeId = hole.id; + done(); + }); + }); + }); + }); + }); + }); + }; + }; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("accessors Async", function () { + before(setup()); + + it("get should get the association", function () { + return Leaf + .oneAsync({ size: 14 }) + .then(function(leaf) { + should.exist(leaf); + return leaf.getTreeAsync(); + }) + .then(function (tree) { + should.exist(tree); + }); + }); + + it("should return proper instance model", function () { + return Leaf + .oneAsync({ size: 14 }) + .then(function (leaf) { + return leaf.getTreeAsync(); + }) + .then(function (tree) { + tree.model().should.equal(Tree); + }); + }); + + it("get should get the association with a shell model", function () { + return Leaf(leafId) + .getTreeAsync() + .then(function (tree) { + should.exist(tree); + should.equal(tree[Tree.id], treeId); + }); + }); + + it("has should indicate if there is an association present", function () { + return Leaf.oneAsync({ size: 14 }) + .then(function (leaf) { + should.exist(leaf); + return [leaf, leaf.hasTreeAsync()]; + }) + .spread(function (leaf, has) { + should.equal(has, true); + return leaf.hasStalkAsync(); + }) + .then(function (has) { + should.equal(has, false); + }); + }); + + it("set should associate another instance", function () { + return Stalk + .oneAsync({ length: 20 }) + .then(function (stalk) { + should.exist(stalk); + return [stalk, Leaf.oneAsync({ size: 14 })]; + }) + .spread(function (stalk, leaf) { + should.exist(leaf); + should.not.exist(leaf.stalkId); + return [stalk, leaf.setStalkAsync(stalk)]; + }) + .then(function (stalk) { + return [stalk, Leaf.oneAsync({ size: 14 })]; + }) + .spread(function (stalk, leafOne) { + should.equal(leafOne.stalkId, stalk[0][Stalk.id]); + }); + }); + + it("remove should unassociation another instance", function () { + return Stalk + .oneAsync({ length: 20 }) + .then(function (stalk) { + should.exist(stalk); + return Leaf.oneAsync({ size: 14 }); + }) + .then(function (leaf) { + should.exist(leaf); + should.exist(leaf.stalkId); + return leaf.removeStalkAsync(); + }) + .then(function () { + return Leaf.oneAsync({ size: 14 }); + }) + .then(function (leaf) { + should.equal(leaf.stalkId, null); + }); + }); + }); + + describe("if not passing another Model (promise-based test)", function () { + it("should use same model", function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + + var Person = db.define("person", { + name : String + }); + Person.hasOne("parent", { + autoFetch : true + }); + + helper.dropSync(Person, function () { + var child = new Person({ + name : "Child" + }); + child.setParentAsync(new Person({ name: "Parent" })).then(function () { + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + + if (protocol != "mongodb") { + describe("mapsTo Async (promise-based tests)", function () { + describe("with `mapsTo` set via `hasOne`", function () { + var leaf = null; + + before(setup()); + + before(function (done) { + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { + leaf = lf; + done(); + }).catch(function(err) { + done(err); + }); + }); + + it("should get parent", function (done) { + leaf.getStalkAsync().then(function (stalk) { + + should.exist(stalk); + should.equal(stalk.id, stalkId); + should.equal(stalk.length, 20); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + + describe("with `mapsTo` set via property definition", function () { + var leaf = null; + + before(setup()); + + before(function (done) { + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { + leaf = lf; + done(); + }).catch(function(err) { + done(err); + }); + }); + + it("should get parent", function (done) { + leaf.getHoleAsync().then(function (hole) { + + should.exist(hole); + should.equal(hole.id, stalkId); + should.equal(hole.width, 3); + done(); + }).catch(function(err) { + done(err); + }); + }); + }); + }); + }; + +}); From ad128861bf7666826719d4ea12ecc1ad0d71de88 Mon Sep 17 00:00:00 2001 From: mradko Date: Wed, 13 Sep 2017 18:18:41 +0300 Subject: [PATCH 65/98] update db and extend promise test --- test/integration/association-extend-async.js | 83 ++++++++---------- test/integration/db.js | 91 +++++++------------- test/integration/hook-promise.js | 57 +++--------- 3 files changed, 81 insertions(+), 150 deletions(-) diff --git a/test/integration/association-extend-async.js b/test/integration/association-extend-async.js index f0bb20bb..ffe1065e 100644 --- a/test/integration/association-extend-async.js +++ b/test/integration/association-extend-async.js @@ -123,70 +123,55 @@ describe("Model.extendsTo()", function() { describe("when calling setAccessorAsync", function () { before(setup()); - it("should remove any previous extension", function (done) { // TODO: fix Model.find to async - Person.find().first(function (err, John) { - should.equal(err, null); - - PersonAddress.find({ number: 123 }).count(function (err, c) { - should.equal(err, null); - c.should.equal(1); + it("should remove any previous extension", function () { // TODO: fix Model.find to async + return Person.find().firstAsync() + .then(function (John) { + return [John, PersonAddress.find({ number: 123 }).countAsync()]; + }) + .spread(function (John, count) { + count.should.equal(1); var addr = new PersonAddress({ street : "4th Ave", number : 4 }); - John.setAddressAsync(addr) - .then(function () { - return John.getAddressAsync(); - }) - .then(function (Address) { - Address.should.be.a.Object(); - should.equal(Array.isArray(Address), false); - Address.should.have.property("street", addr.street); - PersonAddress.find({ number: 123 }) - .count(function (err, c) { - should.equal(err, null); - c.should.equal(0); - done(); - }); - }).catch(function(err) { - done(err); - }); + return [John, addr, John.setAddressAsync(addr)]; + }) + .spread(function (John, addr) { + return [addr, John.getAddressAsync()]; + }) + .spread(function (addr, Address) { + Address.should.be.a.Object(); + should.equal(Array.isArray(Address), false); + Address.should.have.property("street", addr.street); + return PersonAddress.findAsync({ number: 123 }); + }) + .then(function (addres) { + addres.length.should.equal(0); }); - }); }); }); describe("when calling delAccessor + Async", function () { // TODO: fix .find to async before(setup()); - it("should remove any extension", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - - PersonAddress.find({ number: 123 }).count(function (err, c) { - should.equal(err, null); - c.should.equal(1); - - var addr = new PersonAddress({ - street : "4th Ave", - number : 4 - }); + it("should remove any extension", function () { + return Person.find().firstAsync() + .then(function (John) { + return [John, PersonAddress.find({ number: 123 }).countAsync()]; + }) + .spread(function (John, count) { + count.should.equal(1); - John.removeAddressAsync() - .then(function () { - PersonAddress.find({ number: 123 }).count(function (err, c) { - should.equal(err, null); - c.should.equal(0); - - done(); - }); - }).catch(function(err) { - done(err); - }); + return John.removeAddressAsync(); + }) + .then(function () { + return PersonAddress.findAsync({ number: 123 }); + }) + .then(function (addres) { + addres.length.should.equal(0); }); - }); }); it("should return error if instance not with an ID", function (done) { diff --git a/test/integration/db.js b/test/integration/db.js index 804c6986..c7dd2ca8 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -45,27 +45,19 @@ describe("db.driver", function () { }); describe('#execQueryAsync', function () { - it('should execute sql queries', function (done) { - db.driver.execQueryAsync('SELECT id FROM log') + it('should execute sql queries', function () { + return db.driver.execQueryAsync('SELECT id FROM log') .then(function (data) { - should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); - done() + should(JSON.stringify(data) === JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); }) - .catch(function (err) { - done(err); - }); }); - it("should escape sql queries", function (done) { + it("should escape sql queries", function () { var query = "SELECT log.?? FROM log WHERE log.?? LIKE ? AND log.?? > ?"; var args = ['what', 'who', 'jane', 'when', new Date('2013/04/07 12:40:00')]; - db.driver.execQueryAsync(query, args) + return db.driver.execQueryAsync(query, args) .then(function (data) { - should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); - done(); - }) - .catch(function (err) { - done(err); + should(JSON.stringify(data) === JSON.stringify([{ "what": "user login" }])); }); }); }); @@ -131,21 +123,19 @@ describe("db.driver", function () { }); describe('async', function () { - it('should build correct query', function (done) { + it('should build correct query', function () { var execSimpleQueryStub = sinon.stub(db.driver, 'execSimpleQuery') .callsFake(function (q, cb) { cb(); }); - db.driver.eagerQueryAsync(fixture.association, fixture.opts, [1, 5]) + return db.driver.eagerQueryAsync(fixture.association, fixture.opts, [1, 5]) .then(function () { should.equal(execSimpleQueryStub.calledOnce, true); should.equal(execSimpleQueryStub.lastCall.args[0], fixture.expectedQuery[common.protocol()]); execSimpleQueryStub.restore(); - done(); }) - .catch(function (err) { + .catch(function () { execSimpleQueryStub.restore(); - done(err); }); }); }); @@ -156,7 +146,7 @@ describe("db.driver", function () { db.driver.execQuery("SELECT id FROM log", function (err, data) { should.not.exist(err); - should(JSON.stringify(data) == JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); + should(JSON.stringify(data) === JSON.stringify([{ id: 1 }, { id: 2 }, { id: 3 }])); done(); }); }); @@ -167,7 +157,7 @@ describe("db.driver", function () { db.driver.execQuery(query, args, function (err, data) { should.not.exist(err); - should(JSON.stringify(data) == JSON.stringify([{ "what": "user login" }])); + should(JSON.stringify(data) === JSON.stringify([{ "what": "user login" }])); done(); }); }); @@ -189,7 +179,7 @@ describe("db.driver", function () { }); describe('db.syncPromise()', function () { - it('should call sync for each model', function (done) { + it('should call sync for each model', function () { db.define("my_model", { property: String }); @@ -202,20 +192,16 @@ describe("db.driver", function () { var syncStub2 = sinon.stub(db.models['my_model2'], 'sync').callsFake(function (cb) { cb(null, {}) }); - db.syncPromise() + return db.syncPromise() .then(function () { should.equal(syncStub.calledOnce, true); should.equal(syncStub2.calledOnce, true); - done(); - }) - .catch(function (err) { - done(err) }); }); }); describe("db.dropAsync()", function () { - it('should should call drop for each model', function (done) { + it('should should call drop for each model', function () { db.define("my_model", { property: String }); @@ -230,14 +216,11 @@ describe("db.driver", function () { var dropStub2 = sinon.stub(db.models['my_model2'], 'drop').callsFake(function (cb) { cb(null, {}) }); - db.dropAsync() + + return db.dropAsync() .then(function () { should.equal(dropStub.calledOnce, true); should.equal(dropStub2.calledOnce, true); - done(); - }) - .catch(function (err) { - done(err) }); }); }); @@ -252,11 +235,11 @@ describe("db.driver", function () { db.use(MyPlugin, opts); - var MyModel = db.define("my_model", { // db.define should call plugin.define method + db.define("my_model", { // db.define should call plugin.define method property: String }); - opts.calledDefine.should.be.true; + opts.calledDefine.should.be.true(); return done(); }); @@ -277,7 +260,7 @@ describe("db.driver", function () { property: String }); - opts.calledDefine.should.be.true; + opts.calledDefine.should.be.true(); MyModel.properties.should.have.property("otherprop"); done(); @@ -291,11 +274,11 @@ describe("db.driver", function () { db.use("../support/my_plugin", opts); - var MyModel = db.define("my_model", { // db.define should call plugin.define method + db.define("my_model", { // db.define should call plugin.define method property: String }); - opts.calledDefine.should.be.true; + opts.calledDefine.should.be.true(); return done(); }); @@ -317,48 +300,40 @@ describe("db.driver", function () { }); describe("db.loadAsync()", function () { - it("should require a file if array", function (done) { + it("should require a file if array", function () { var filePath = "../support/spec_load"; - db.loadAsync([filePath]) + return db.loadAsync([filePath]) .then(function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); - done() - }) - .catch(done); + }); }); - it("should require a file if single file path string", function (done) { + it("should require a file if single file path string", function () { var filePath = "../support/spec_load"; - db.loadAsync(filePath) + return db.loadAsync(filePath) .then(function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); - done() - }) - .catch(done); + }); }); - it("should be able to load more than one file", function (done) { + it("should be able to load more than one file", function () { var filePaths = ["../support/spec_load_second", "../support/spec_load_third"]; - db.loadAsync(filePaths) + return db.loadAsync(filePaths) .then(function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); - done() - }) - .catch(done); + }); }); - it("should throw error if files passed like arguments", function (done) { - db.loadAsync("../support/spec_load_second", "../support/spec_load_third") + it("should throw error if files passed like arguments", function () { + return db.loadAsync("../support/spec_load_second", "../support/spec_load_third") .then(function () { db.models.should.have.property("person"); db.models.should.have.property("pet"); - done() - }) - .catch(done); + }); }); }); diff --git a/test/integration/hook-promise.js b/test/integration/hook-promise.js index 2ef2c2a5..5969917b 100644 --- a/test/integration/hook-promise.js +++ b/test/integration/hook-promise.js @@ -83,7 +83,7 @@ describe("HookPromise", function() { }); }); - it("should trigger error", function (done) { + it("should trigger error", function () { Person.beforeCreate(function () { return new Promise(function (resolve, reject) { setTimeout(function () { @@ -92,21 +92,17 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) - .then(function () { - done(new Error('Should throw err.')); - }) + return Person.createAsync([{ name: "John Doe" }]) .catch(function (err) { err.should.be.a.Object(); should.equal(err.message, "beforeCreate-error"); - done(); }); }); }); describe("beforeSave", function () { beforeEach(setup()); - it("should trigger and wait before save hook", function (done) { + it("should trigger and wait before save hook", function () { Person.beforeSave(function () { return new Promise(function (_, reject) { setTimeout(function () { @@ -115,18 +111,14 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "Jane Doe" }]) - .then(function () { - done(new Error('Should throw error')); - }) + return Person.createAsync([{ name: "Jane Doe" }]) .catch(function (err) { err.should.be.a.Object(); should.equal(err.message, "beforeSave-error"); - return done(); }); }); - it("should trigger error", function (done) { + it("should trigger error", function () { Person.beforeSave(function () { return new Promise(function (_, reject) { setTimeout(function () { @@ -135,14 +127,10 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "Jane Doe" }]) - .then(function (John) { - return John[0].saveAsync(); - }) + return Person.createAsync([{ name: "Jane Doe" }]) .catch(function (err) { err.should.be.a.Object(); should.equal("beforeSave-error", err.message); - return done(); }); }); @@ -186,7 +174,7 @@ describe("HookPromise", function() { }); }); - it("should throw error", function (done) { + it("should throw error", function () { Person.beforeValidation(function () { var self = this; return new Promise(function (_, reject) { @@ -197,13 +185,9 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) - .then(function () { - done(new Error("Should throw error")); - }) + return Person.createAsync([{ name: "John Doe" }]) .catch(function (err) { should.equal(err.message, "beforeValidation-error"); - done(); }); }) }); @@ -227,7 +211,7 @@ describe("HookPromise", function() { }); }); - it("should throw error", function (done) { + it("should throw error", function () { var afterLoad = false; Person.afterLoad(function () { return new Promise(function (_, reject) { @@ -238,15 +222,10 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) - .then(function () { - afterLoad.should.be.true; - done(new Error("Should throw.")); - }) + return Person.createAsync([{ name: "John Doe" }]) .catch(function (err) { err.should.exist; should.equal("afterLoad-error", err.message); - done(); }); }); }); @@ -270,7 +249,7 @@ describe("HookPromise", function() { }); }); - it("should throw error", function (done) { + it("should throw error", function () { var afterAutoFetch = false; Person.afterAutoFetch(function () { return new Promise(function (_, reject) { @@ -281,13 +260,9 @@ describe("HookPromise", function() { }); }); - Person.createAsync({ name: "John" }) - .then(function () { - done(new Error("Should throw error")); - }) + return Person.createAsync({ name: "John" }) .catch(function (err) { should.equal(err.message, "afterAutoFetch-error"); - done(); }); }); }); @@ -313,7 +288,7 @@ describe("HookPromise", function() { }); }); - it("should throw error", function (done) { + it("should throw error", function () { Person.beforeRemove(function () { return new Promise(function (_, reject) { setTimeout(function () { @@ -322,16 +297,12 @@ describe("HookPromise", function() { }); }); - Person.createAsync([{ name: "John Doe" }]) + return Person.createAsync([{ name: "John Doe" }]) .then(function (items) { return items[0].removeAsync(); }) - .then(function () { - done(new Error('Should throw error')); - }) .catch(function (err) { should.equal(err.message, 'beforeRemove-error'); - done(); }); }); }); From 6d49ff0ee1fdb0a4342ec00250ea68b43e4c54bf Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 12:03:47 +0300 Subject: [PATCH 66/98] Update part of hasone async tests --- test/integration/association-hasone-async.js | 92 +++---- .../association-hasone-required-async.js | 81 ++++++ .../association-hasone-required.js | 89 ------- .../association-hasone-reverse-async.js | 233 ++++++++++++++++++ .../integration/association-hasone-reverse.js | 170 +------------ test/integration/association-hasone.js | 184 -------------- 6 files changed, 349 insertions(+), 500 deletions(-) create mode 100644 test/integration/association-hasone-required-async.js create mode 100644 test/integration/association-hasone-reverse-async.js diff --git a/test/integration/association-hasone-async.js b/test/integration/association-hasone-async.js index 51efb2e7..0eb0e155 100644 --- a/test/integration/association-hasone-async.js +++ b/test/integration/association-hasone-async.js @@ -158,57 +158,31 @@ describe("hasOne", function() { }); }); - describe("if not passing another Model (promise-based test)", function () { - it("should use same model", function (done) { - db.settings.set('instance.identityCache', false); - db.settings.set('instance.returnAllErrors', true); - - var Person = db.define("person", { - name : String - }); - Person.hasOne("parent", { - autoFetch : true - }); - - helper.dropSync(Person, function () { - var child = new Person({ - name : "Child" - }); - child.setParentAsync(new Person({ name: "Parent" })).then(function () { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - if (protocol != "mongodb") { - describe("mapsTo Async (promise-based tests)", function () { - describe("with `mapsTo` set via `hasOne`", function () { + describe("mapsTo Async", function () { + describe("with `mapsTo` get via `getOneAsync`", function () { var leaf = null; before(setup()); before(function (done) { - Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { - leaf = lf; - done(); - }).catch(function(err) { - done(err); - }); + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }) + .then(function (lf) { + leaf = lf; + done(); + }).catch(function(err) { + done(err); + }); }); - it("should get parent", function (done) { - leaf.getStalkAsync().then(function (stalk) { - - should.exist(stalk); - should.equal(stalk.id, stalkId); - should.equal(stalk.length, 20); - done(); - }).catch(function(err) { - done(err); - }); + it("should get parent", function () { + return leaf + .getStalkAsync() + .then(function (stalk) { + should.exist(stalk); + should.equal(stalk.id, stalkId); + should.equal(stalk.length, 20); + }); }); }); @@ -218,27 +192,25 @@ describe("hasOne", function() { before(setup()); before(function (done) { - Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { - leaf = lf; - done(); - }).catch(function(err) { - done(err); - }); + Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }) + .then(function (lf) { + leaf = lf; + done(); + }).catch(function(err) { + done(err); + }); }); - it("should get parent", function (done) { - leaf.getHoleAsync().then(function (hole) { - - should.exist(hole); - should.equal(hole.id, stalkId); - should.equal(hole.width, 3); - done(); - }).catch(function(err) { - done(err); - }); + it("should get parent", function () { + return leaf + .getHoleAsync() + .then(function (hole) { + should.exist(hole); + should.equal(hole.id, stalkId); + should.equal(hole.width, 3); + }); }); }); }); }; - }); diff --git a/test/integration/association-hasone-required-async.js b/test/integration/association-hasone-required-async.js new file mode 100644 index 00000000..7bddbd71 --- /dev/null +++ b/test/integration/association-hasone-required-async.js @@ -0,0 +1,81 @@ +var ORM = require('../../'); +var helper = require('../support/spec_helper'); +var should = require('should'); +var async = require('async'); +var _ = require('lodash'); + +describe("hasOne Async", function() { + var db = null; + var Person = null; + + var setup = function (required) { + return function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + + Person = db.define('person', { + name : String + }); + Person.hasOne('parent', Person, { + required : required, + field : 'parentId' + }); + + return helper.dropSync(Person, done); + }; + }; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("required", function () { + before(setup(true)); + + it("should not accept empty association", function (done) { + var John = new Person({ + name : "John", + parentId : null + }); + John.saveAsync() + .catch(function(err) { + should.exist(err); + should.equal(err.length, 1); + should.equal(err[0].type, 'validation'); + should.equal(err[0].msg, 'required'); + should.equal(err[0].property, 'parentId'); + done(); + }); + }); + + it("should accept association", function () { + var John = new Person({ + name : "John", + parentId : 1 + }); + return John.saveAsync(); + }); + }); + + describe("not required", function () { + before(setup(false)); + + it("should accept empty association", function () { + var John = new Person({ + name : "John" + }); + return John.saveAsync(); + }); + + it("should accept null association", function () { + var John = new Person({ + name : "John", + parent_id : null + }); + return John.saveAsync(); + }); + }); +}); \ No newline at end of file diff --git a/test/integration/association-hasone-required.js b/test/integration/association-hasone-required.js index 2b8b7f2c..137bd7d2 100644 --- a/test/integration/association-hasone-required.js +++ b/test/integration/association-hasone-required.js @@ -86,93 +86,4 @@ describe("hasOne", function() { }); }); }); -}); - -describe("hasOne Async", function() { - var db = null; - var Person = null; - - var setup = function (required) { - return function (done) { - db.settings.set('instance.identityCache', false); - db.settings.set('instance.returnAllErrors', true); - - Person = db.define('person', { - name : String - }); - Person.hasOne('parent', Person, { - required : required, - field : 'parentId' - }); - - return helper.dropSync(Person, done); - }; - }; - - before(function(done) { - helper.connect(function (connection) { - db = connection; - done(); - }); - }); - - describe("required", function () { - before(setup(true)); - - it("should not accept empty association", function (done) { - var John = new Person({ - name : "John", - parentId : null - }); - John.saveAsync().then(function() { - throw new Error('Should catch an error'); - }).catch(function(err) { - should.exist(err); - should.equal(err.length, 1); - should.equal(err[0].type, 'validation'); - should.equal(err[0].msg, 'required'); - should.equal(err[0].property, 'parentId'); - done(); - }); - }); - - it("should accept association", function (done) { - var John = new Person({ - name : "John", - parentId : 1 - }); - John.saveAsync().then(function () { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe("not required", function () { - before(setup(false)); - - it("should accept empty association", function (done) { - var John = new Person({ - name : "John" - }); - John.saveAsync().then(function () { - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("should accept null association", function (done) { - var John = new Person({ - name : "John", - parent_id : null - }); - John.saveAsync().then(function () { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); }); \ No newline at end of file diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js new file mode 100644 index 00000000..1da35125 --- /dev/null +++ b/test/integration/association-hasone-reverse-async.js @@ -0,0 +1,233 @@ +var ORM = require('../../'); +var helper = require('../support/spec_helper'); +var should = require('should'); +var async = require('async'); +var common = require('../common'); +var _ = require('lodash'); + +describe("hasOne Async", function () { + var db = null; + var Person = null; + var Pet = null; + + var setup = function () { + return function (done) { + Person = db.define('person', { + name: String + }); + Pet = db.define('pet', { + name: String + }); + Person.hasOne('pet', Pet, { + reverse: 'owners', + field: 'pet_id' + }); + + return helper.dropSync([Person, Pet], function () { + // Running in series because in-memory sqlite encounters problems + async.series([ + Person.create.bind(Person, { name: "John Doe" }), + Person.create.bind(Person, { name: "Jane Doe" }), + Pet.create.bind(Pet, { name: "Deco" }), + Pet.create.bind(Pet, { name: "Fido" }) + ], done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("reverse", function () { + removeHookRun = false; + + before(setup({ + hooks: { + beforeRemove: function () { + removeHookRun = true; + } + } + })); + + it("should create methods in both models", function (done) { + var person = Person(1); + var pet = Pet(1); + + person.getPet.should.be.a.Function(); + person.setPet.should.be.a.Function(); + person.removePet.should.be.a.Function(); + person.hasPet.should.be.a.Function(); + + pet.getOwners.should.be.a.Function(); + pet.setOwners.should.be.a.Function(); + pet.hasOwners.should.be.a.Function(); + + person.getPetAsync.should.be.a.Function(); + person.setPetAsync.should.be.a.Function(); + person.removePetAsync.should.be.a.Function(); + person.hasPetAsync.should.be.a.Function(); + + pet.getOwnersAsync.should.be.a.Function(); + pet.setOwnersAsync.should.be.a.Function(); + pet.hasOwnersAsync.should.be.a.Function(); + + return done(); + }); + + describe(".getAccessorAsync()", function () { + + it("compare if model updated", function () { + return Person + .findAsync({ name: "John Doe" }) + .then(function (John) { + return [John, Pet.findAsync({ name: "Deco" })]; + }) + .spread(function (John, deco) { + return [John[0], deco[0], deco[0].hasOwnersAsync()]; + }) + .spread(function (John, deco, has_owner) { + has_owner.should.equal(false); + return [deco.setOwnersAsync(John), deco]; + }) + .spread(function (John, deco) { + return [John, deco.getOwnersAsync()]; + }) + .spread(function (John, JohnCopy) { + should(Array.isArray(JohnCopy)); + John.should.eql(JohnCopy[0]); + }); + }); + + }); + + it("should be able to set an array of people as the owner", function () { + return Person + .findAsync({ name: ["John Doe", "Jane Doe"] }) + .then(function (owners) { + return [owners, Pet.findAsync({ name: "Fido" })]; + }) + .spread(function (owners, Fido) { + return [Fido[0], owners, Fido[0].hasOwnersAsync()]; + }) + .spread(function (Fido, owners, has_owner) { + has_owner.should.equal(false); + return [Fido, owners, Fido.setOwnersAsync(owners)]; + }) + .spread(function (Fido, owners) { + return [owners, Fido.getOwnersAsync()]; + }) + .spread(function (owners, ownersCopy) { + should(Array.isArray(owners)); + owners.length.should.equal(2); + // Don't know which order they'll be in. + var idProp = common.protocol() == 'mongodb' ? '_id' : 'id' + + if (owners[0][idProp] == ownersCopy[0][idProp]) { + owners[0].should.eql(ownersCopy[0]); + owners[1].should.eql(ownersCopy[1]); + } else { + owners[0].should.eql(ownersCopy[1]); + owners[1].should.eql(ownersCopy[0]); + } + }); + }); + }); + + describe("reverse find", function () { + before(setup()); + it("should be able to find given an association id", function (done) { + common.retry(setup(), function () { + return Person + .findAsync({ name: "John Doe" }) + .then(function (John) { + should.exist(John); + return [John, Pet.findAsync({ name: "Deco" })]; + }) + .spread(function (John, Deco) { + should.exist(Deco); + return [John[0], Deco[0], Deco[0].hasOwnersAsync()]; + }) + .spread(function (John, Deco, has_owner) { + has_owner.should.equal(false); + return [John, Deco.setOwnersAsync(John)]; + }) + .spread(function (John, Deco) { + return [John, Person.findAsync({ pet_id: 1 })]; + }) + .spread(function (John, owner) { + should.exist(owner[0]); + should.equal(owner[0].name, John.name); + }); + }, 3, done); + }); + + it("should be able to find given an association instance", function (done) { + common.retry(setup(), function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + should.not.exist(err); + should.exist(John); + Pet.find({ name: "Deco" }).first(function (err, Deco) { + should.not.exist(err); + should.exist(Deco); + Deco.hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + Deco.setOwnersAsync(John).then(function () { + + Person.find({ pet: Deco }).first(function (err, owner) { + should.not.exist(err); + should.exist(owner); + should.equal(owner.name, John.name); + done(); + }); + + }).catch(function(err) { + done(err); + }); + }).done(function(err) { + done(err); + }); + }); + }); + }, 3, done); + }); + + it("should be able to find given a number of association instances with a single primary key", function (done) { + common.retry(setup(), function (done) { + Person.find({ name: "John Doe" }).first(function (err, John) { + should.not.exist(err); + should.exist(John); + Pet.all(function (err, pets) { + should.not.exist(err); + should.exist(pets); + should.equal(pets.length, 2); + + pets[0].hasOwnersAsync().then(function (has_owner) { + has_owner.should.equal(false); + + pets[0].setOwnersAsync(John).then(function () { + + Person.find({ pet: pets }, function (err, owners) { + should.not.exist(err); + should.exist(owners); + owners.length.should.equal(1); + + should.equal(owners[0].name, John.name); + done(); + }); + }).catch(function(err) { + done(err); + }); + }).catch(function(err) { + done(err); + }); + }); + }); + }, 3, done); + }); + }); +}); diff --git a/test/integration/association-hasone-reverse.js b/test/integration/association-hasone-reverse.js index dd0b8362..bb215182 100644 --- a/test/integration/association-hasone-reverse.js +++ b/test/integration/association-hasone-reverse.js @@ -92,32 +92,7 @@ describe("hasOne", function () { }); }); }); - - it("should work (promise-based)", function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - Pet.find({ name: "Deco" }).first(function (err, Deco) { - Deco.hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - Deco.setOwnersAsync(John).then(function () { - Deco.getOwnersAsync().then(function (JohnCopy) { - - should(Array.isArray(JohnCopy)); - John.should.eql(JohnCopy[0]); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); + describe("Chain", function () { before(function (done) { @@ -187,7 +162,7 @@ describe("hasOne", function () { Fido.hasOwners(function (err, has_owner) { should.not.exist(err); has_owner.should.be.false; - + Fido.setOwners(owners, function (err) { should.not.exist(err); @@ -214,48 +189,7 @@ describe("hasOne", function () { }); }); }); - - it("should be able to set an array of people as the owner (promise-based test)", function (done) { - Person.find({ name: ["John Doe", "Jane Doe"] }, function (err, owners) { - should.not.exist(err); - - Pet.find({ name: "Fido" }).first(function (err, Fido) { - should.not.exist(err); - - Fido.hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - Fido.setOwnersAsync(owners).then(function () { - - Fido.getOwnersAsync().then(function (ownersCopy) { - should(Array.isArray(owners)); - owners.length.should.equal(2); - - // Don't know which order they'll be in. - var idProp = common.protocol() == 'mongodb' ? '_id' : 'id' - - if (owners[0][idProp] == ownersCopy[0][idProp]) { - owners[0].should.eql(ownersCopy[0]); - owners[1].should.eql(ownersCopy[1]); - } else { - owners[0].should.eql(ownersCopy[1]); - owners[1].should.eql(ownersCopy[0]); - } - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - + // broken in mongo if (common.protocol() != "mongodb") { describe("findBy()", function () { @@ -400,102 +334,4 @@ describe("hasOne", function () { }, 3, done); }); }); - - describe("reverse find (promise-based)", function () { - it("should be able to find given an association id", function (done) { - common.retry(setup(), function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - should.not.exist(err); - should.exist(John); - Pet.find({ name: "Deco" }).first(function (err, Deco) { - should.not.exist(err); - should.exist(Deco); - Deco.hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - Deco.setOwnersAsync(John).then(function () { - - Person.find({ pet_id: Deco[Pet.id[0]] }).first(function (err, owner) { - should.not.exist(err); - should.exist(owner); - should.equal(owner.name, John.name); - done(); - }); - - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }, 3, done); - }); - - it("should be able to find given an association instance", function (done) { - common.retry(setup(), function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - should.not.exist(err); - should.exist(John); - Pet.find({ name: "Deco" }).first(function (err, Deco) { - should.not.exist(err); - should.exist(Deco); - Deco.hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - Deco.setOwnersAsync(John).then(function () { - - Person.find({ pet: Deco }).first(function (err, owner) { - should.not.exist(err); - should.exist(owner); - should.equal(owner.name, John.name); - done(); - }); - - }).catch(function(err) { - done(err); - }); - }).done(function(err) { - done(err); - }); - }); - }); - }, 3, done); - }); - - it("should be able to find given a number of association instances with a single primary key", function (done) { - common.retry(setup(), function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - should.not.exist(err); - should.exist(John); - Pet.all(function (err, pets) { - should.not.exist(err); - should.exist(pets); - should.equal(pets.length, 2); - - pets[0].hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - pets[0].setOwnersAsync(John).then(function () { - - Person.find({ pet: pets }, function (err, owners) { - should.not.exist(err); - should.exist(owners); - owners.length.should.equal(1); - - should.equal(owners[0].name, John.name); - done(); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }, 3, done); - }); - }); }); diff --git a/test/integration/association-hasone.js b/test/integration/association-hasone.js index 6c85b248..fa68dffb 100644 --- a/test/integration/association-hasone.js +++ b/test/integration/association-hasone.js @@ -469,188 +469,4 @@ describe("hasOne", function() { }); }; - describe("accessors (promise-based)", function () { - before(setup()); - - it("get should get the association", function (done) { - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.exist(leaf); - leaf.getTreeAsync().then(function (tree) { - should.not.exist(err); - should.exist(tree); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should return proper instance model", function (done) { - Leaf.one({ size: 14 }, function (err, leaf) { - leaf.getTreeAsync().then(function (tree) { - tree.model().should.equal(Tree); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("get should get the association with a shell model", function (done) { - Leaf(leafId).getTreeAsync().then(function (tree) { - should.exist(tree); - should.equal(tree[Tree.id], treeId); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("has should indicate if there is an association present", function (done) { - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.exist(leaf); - - leaf.hasTreeAsync().then(function (has) { - should.equal(has, true); - - leaf.hasStalkAsync().then(function (has) { - should.equal(has, false); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("set should associate another instance", function (done) { - Stalk.one({ length: 20 }, function (err, stalk) { - should.not.exist(err); - should.exist(stalk); - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.exist(leaf); - should.not.exist(leaf.stalkId); - leaf.setStalkAsync(stalk).then(function () { - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.equal(leaf.stalkId, stalk[Stalk.id]); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - it("remove should unassociation another instance", function (done) { - Stalk.one({ length: 20 }, function (err, stalk) { - should.not.exist(err); - should.exist(stalk); - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.exist(leaf); - should.exist(leaf.stalkId); - leaf.removeStalkAsync(function () { - Leaf.one({ size: 14 }, function (err, leaf) { - should.not.exist(err); - should.equal(leaf.stalkId, null); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }); - - describe("if not passing another Model (promise-based test)", function () { - it("should use same model", function (done) { - db.settings.set('instance.identityCache', false); - db.settings.set('instance.returnAllErrors', true); - - var Person = db.define("person", { - name : String - }); - Person.hasOne("parent", { - autoFetch : true - }); - - helper.dropSync(Person, function () { - var child = new Person({ - name : "Child" - }); - child.setParentAsync(new Person({ name: "Parent" })).then(function () { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - if (protocol != "mongodb") { - describe("mapsTo Async (promise-based tests)", function () { - describe("with `mapsTo` set via `hasOne`", function () { - var leaf = null; - - before(setup()); - - before(function (done) { - Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { - leaf = lf; - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("should get parent", function (done) { - leaf.getStalkAsync().then(function (stalk) { - - should.exist(stalk); - should.equal(stalk.id, stalkId); - should.equal(stalk.length, 20); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe("with `mapsTo` set via property definition", function () { - var leaf = null; - - before(setup()); - - before(function (done) { - Leaf.createAsync({ size: 444, stalkId: stalkId, holeId: holeId }).then(function (lf) { - leaf = lf; - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("should get parent", function (done) { - leaf.getHoleAsync().then(function (hole) { - - should.exist(hole); - should.equal(hole.id, stalkId); - should.equal(hole.width, 3); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - }; - }); From 3d352c382f67eac53bf8efee452f83d9b992363e Mon Sep 17 00:00:00 2001 From: mradko Date: Thu, 14 Sep 2017 12:31:56 +0300 Subject: [PATCH 67/98] adjust test code style --- test/integration/model-createAsync.js | 40 +++--------- test/integration/model-findAsync-mapsto.js | 4 +- test/integration/model-getAsync.js | 19 ++---- test/integration/orm-exports.js | 72 ++++++---------------- 4 files changed, 36 insertions(+), 99 deletions(-) diff --git a/test/integration/model-createAsync.js b/test/integration/model-createAsync.js index 56c6665c..cb95d53c 100644 --- a/test/integration/model-createAsync.js +++ b/test/integration/model-createAsync.js @@ -35,25 +35,21 @@ describe("Model.createAsync()", function() { describe("if passing an object", function () { before(setup()); - it("should accept it as the only item to create", function (done) { - Person.createAsync({ + it("should accept it as the only item to create", function () { + return Person.createAsync({ name : "John Doe" }) .then(function (John) { John.should.have.property("name", "John Doe"); - done(); }) - .catch(function (err) { - done(err); - }); }); }); describe("if passing an array", function () { before(setup()); - it("should accept it as a list of items to create", function (done) { - Person.createAsync([{ + it("should accept it as a list of items to create", function () { + return Person.createAsync([{ name : "John Doe" }, { name : "Jane Doe" @@ -64,19 +60,15 @@ describe("Model.createAsync()", function() { people.should.have.property("length", 2); people[0].should.have.property("name", "John Doe"); people[1].should.have.property("name", "Jane Doe"); - done(); }) - .catch(function (err) { - done(err); - }); }); }); describe("if element has an association", function () { before(setup()); - it("should also create it or save it", function (done) { - Person.createAsync({ + it("should also create it or save it", function () { + return Person.createAsync({ name : "John Doe", pets : [ new Pet({ name: "Deco" }) ] }) @@ -88,15 +80,11 @@ describe("Model.createAsync()", function() { John.pets[0].should.have.property("name", "Deco"); John.pets[0].should.have.property(Pet.id); should.equal(John.pets[0].saved(), true); - done(); - }) - .catch(function (err) { - done(err); }); }); - it("should also create it or save it even if it's an object and not an instance", function (done) { - Person.createAsync({ + it("should also create it or save it even if it's an object and not an instance", function () { + return Person.createAsync({ name : "John Doe", pets : [ { name: "Deco" } ] }) @@ -108,10 +96,6 @@ describe("Model.createAsync()", function() { John.pets[0].should.have.property("name", "Deco"); John.pets[0].should.have.property(Pet.id); should.equal(John.pets[0].saved(), true); - done(); - }) - .catch(function (err) { - done(err); }); }); }); @@ -119,14 +103,10 @@ describe("Model.createAsync()", function() { describe("when not passing a property", function () { before(setup()); - it("should use defaultValue if defined", function (done) { - Pet.createAsync({}) + it("should use defaultValue if defined", function () { + return Pet.createAsync({}) .then(function (Mutt) { Mutt.should.have.property("name", "Mutt"); - done(); - }) - .catch(function (err) { - done(err); }); }); }); diff --git a/test/integration/model-findAsync-mapsto.js b/test/integration/model-findAsync-mapsto.js index 0ab265c1..efec9e94 100644 --- a/test/integration/model-findAsync-mapsto.js +++ b/test/integration/model-findAsync-mapsto.js @@ -74,7 +74,7 @@ describe("Model.pkMapTo.findAsync()", function() { before(setup()); it("1st find should work", function () { - Person.findAsync({ surname: "Dean" }) + return Person.findAsync({ surname: "Dean" }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 2); @@ -82,7 +82,7 @@ describe("Model.pkMapTo.findAsync()", function() { }); }); it("2nd find should should also work", function () { - Person.findAsync({ surname: "Doe" }) + return Person.findAsync({ surname: "Doe" }) .then(function (people) { people.should.be.a.Object(); people.should.have.property("length", 3); diff --git a/test/integration/model-getAsync.js b/test/integration/model-getAsync.js index d265d903..7f8b223a 100644 --- a/test/integration/model-getAsync.js +++ b/test/integration/model-getAsync.js @@ -54,13 +54,10 @@ describe("Model.getAsync()", function () { describe('with identityCache cache', function () { before(setup(true)); - it("should throw if passed a wrong number of ids", function (done) { - Person.getAsync(1, 2) - .then(function () { - done(new Error('Fail')); - }) - .catch(function () { - done(); + it("should throw if passed a wrong number of ids", function () { + return Person.getAsync(1, 2) + .catch(function (err) { + err.should.be.an.Object(); }); }); @@ -73,15 +70,11 @@ describe("Model.getAsync()", function () { }) }); - it("should throw err", function (done) { - Person.getAsync(999) - .then(function () { - done(new Error('Fail!')); - }) + it("should throw err", function () { + return Person.getAsync(999) .catch(function (err) { err.should.be.a.Object(); err.message.should.equal("Not found"); - done(); }); }); diff --git a/test/integration/orm-exports.js b/test/integration/orm-exports.js index 0339734a..9fa4871e 100644 --- a/test/integration/orm-exports.js +++ b/test/integration/orm-exports.js @@ -52,95 +52,67 @@ describe("ORM", function() { }); describe('ORM.connectAsync()', function () { it('should be a function', function () { - ORM.connectAsync.should.be.a.Function() + return ORM.connectAsync.should.be.a.Function() }); - it('should throw error with correct message when protocol not supported', function (done) { - ORM.connectAsync("bd://127.0.0.6") - .then(function () { - done('Fail.'); - }) + it('should throw error with correct message when protocol not supported', function () { + return ORM.connectAsync("bd://127.0.0.6") .catch(function (err) { should.exist(err); err.message.should.not.equal("CONNECTION_PROTOCOL_NOT_SUPPORTED"); - done(); }); }); - it('should throw error with correct message when connection URL doesn\'t exist', function (done) { + it('should throw error with correct message when connection URL doesn\'t exist', function () { ORM.connectAsync() - .then(function () { - done('Fail') - }) .catch(function (err) { err.message.should.equal("CONNECTION_URL_EMPTY"); - done(); }); }); - it("should throw error when passed empty string like connection URL", function (done) { - ORM.connectAsync("") - .then(function () { - done('Fail'); - }) + it("should throw error when passed empty string like connection URL", function () { + return ORM.connectAsync("") .catch(function (err) { err.message.should.equal("CONNECTION_URL_EMPTY"); - done() }); }); - it("should throw error when passed string with spaces only", function (done) { - ORM.connectAsync(" ") - .then(function () { - done('Fail'); - }) + it("should throw error when passed string with spaces only", function () { + return ORM.connectAsync(" ") .catch(function (err) { err.message.should.equal("CONNECTION_URL_EMPTY"); - done() }); }); - it("should throw error when passed invalid protocol", function (done) { - ORM.connectAsync("user@db") - .then(function () { - done('Fail'); - }) + it("should throw error when passed invalid protocol", function () { + return ORM.connectAsync("user@db") .catch(function (err) { err.message.should.equal("CONNECTION_URL_NO_PROTOCOL"); - done() }); }); - it("should throw error when passed unknown protocol", function (done) { - ORM.connectAsync("unknown://db") - .then(function () { - done('Fail'); - }) + it("should throw error when passed unknown protocol", function () { + return ORM.connectAsync("unknown://db") .catch(function (err) { should.equal(err.literalCode, 'NO_SUPPORT'); should.equal( err.message, "Connection protocol not supported - have you installed the database driver for unknown?" ); - done() }); }); - it("should throw error when passed invalid connection db link", function (done) { - ORM.connectAsync("mysql://fakeuser:nopassword@127.0.0.1/unknowndb") - .then(function () { - done('Fail'); - }) + it("should throw error when passed invalid connection db link", function () { + return ORM.connectAsync("mysql://fakeuser:nopassword@127.0.0.1/unknowndb") .catch(function (err) { should.exist(err); should.equal(err.message.indexOf("Connection protocol not supported"), -1); err.message.should.not.equal("CONNECTION_URL_NO_PROTOCOL"); err.message.should.not.equal("CONNECTION_URL_EMPTY"); - done() }); }); - it("should do not mutate opts", function (done) { + it("should do not mutate opts", function () { var opts = { protocol : 'mysql', user : 'notauser', @@ -150,16 +122,12 @@ describe("ORM", function() { var expected = JSON.stringify(opts); - ORM.connectAsync(opts) - .then(function () { - done('Fail'); - }) + return ORM.connectAsync(opts) .catch(function () { should.equal( JSON.stringify(opts), expected ); - done(); }); }); @@ -585,16 +553,12 @@ describe("ORM", function() { return ORM.useAsync(db, "pg") }); - it("should throw an error in callback if protocol not supported", function (done) { + it("should throw an error in callback if protocol not supported", function () { var db = new pg.Client(); - ORM.useAsync(db, "unknowndriver") - .then(function () { - done(new Error('Should throw')); - }) + return ORM.useAsync(db, "unknowndriver") .catch(function (err) { should.exist(err); - done(); }); }); }); From ff245cdc4609f57d886ff0321e406d6cb98af46d Mon Sep 17 00:00:00 2001 From: mradko Date: Thu, 14 Sep 2017 13:35:06 +0300 Subject: [PATCH 68/98] add model save async tests --- test/integration/model-save.js | 1 - test/integration/model-saveAsync.js | 415 ++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 test/integration/model-saveAsync.js diff --git a/test/integration/model-save.js b/test/integration/model-save.js index 30d48cf6..9d3f4bce 100644 --- a/test/integration/model-save.js +++ b/test/integration/model-save.js @@ -1,7 +1,6 @@ var should = require('should'); var helper = require('../support/spec_helper'); var common = require('../common'); -var ORM = require('../../'); describe("Model.save()", function() { var db = null; diff --git a/test/integration/model-saveAsync.js b/test/integration/model-saveAsync.js new file mode 100644 index 00000000..df78332f --- /dev/null +++ b/test/integration/model-saveAsync.js @@ -0,0 +1,415 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var common = require('../common'); + +describe("Model.saveAsync()", function() { + var db = null; + var Person = null; + + var setup = function (nameDefinition, opts) { + opts = opts || {}; + + return function (done) { + Person = db.define("person", { + name : nameDefinition || String + }, opts || {}); + + Person.hasOne("parent", Person, opts.hasOneOpts); + if ('saveAssociationsByDefault' in opts) { + Person.settings.set( + 'instance.saveAssociationsByDefault', opts.saveAssociationsByDefault + ); + } + + return helper.dropSync(Person, done); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("if properties have default values", function () { + before(setup({ type: "text", defaultValue: "John" })); + + it("should use it if not defined", function () { + var John = new Person(); + + return John.saveAsync() + .then(function () { + John.name.should.equal("John"); + }); + }); + }); + + describe("with callback", function () { + before(setup()); + + it("should save item and return id", function () { + var John = new Person({ + name: "John" + }); + + return John.saveAsync() + .then(function () { + should.exist(John[Person.id]); + return Person.getAsync(John[Person.id]); + }) + .then(function (JohnCopy) { + JohnCopy[Person.id].should.equal(John[Person.id]); + JohnCopy.name.should.equal(John.name); + }); + }); + }); + + describe("without callback", function () { + before(setup()); + + it("should still save item and return id", function (done) { + var John = new Person({ + name: "John" + }); + John.saveAsync(); + John.on("save", function (err) { + should.equal(err, null); + should.exist(John[Person.id]); + + Person.getAsync(John[Person.id]) + .then(function (JohnCopy) { + JohnCopy[Person.id].should.equal(John[Person.id]); + JohnCopy.name.should.equal(John.name); + + return done(); + }); + }); + }); + }); + + describe("with properties object", function () { + before(setup()); + + it("should update properties, save item and return id", function () { + var John = new Person({ + name: "Jane" + }); + return John.saveAsync({ name: "John" }) + .then(function () { + should.exist(John[Person.id]); + John.name.should.equal("John"); + + return Person.getAsync(John[Person.id]) + }) + .then(function (JohnCopy) { + JohnCopy[Person.id].should.equal(John[Person.id]); + JohnCopy.name.should.equal(John.name); + }); + }); + }); + + describe("with unknown argument type", function () { + before(setup()); + + it("should should throw", function () { + var John = new Person({ + name: "Jane" + }); + return John.saveAsync("will-fail") + .catch(function (err) { + err.should.be.an.Object(); + }); + }); + }); + + describe("if passed an association instance", function () { + before(setup()); + + it("should save association first and then save item and return id", function () { + var Jane = new Person({ + name : "Jane" + }); + var John = new Person({ + name : "John", + parent: Jane + }); + + return John.saveAsync() + .then(function () { + John.saved().should.be.true(); + Jane.saved().should.be.true(); + + should.exist(John[Person.id]); + should.exist(Jane[Person.id]); + }); + }); + }); + + describe("if passed an association object", function () { + before(setup()); + + it("should save association first and then save item and return id", function () { + var John = new Person({ + name : "John", + parent: { + name : "Jane" + } + }); + + return John.saveAsync() + .then(function () { + John.saved().should.be.true(); + John.parent.saved().should.be.true(); + + should.exist(John[Person.id]); + should.exist(John.parent[Person.id]); + should.equal(John.parent.name, "Jane"); + }); + }); + }); + + describe("if autoSave is on", function () { + before(setup(null, { autoSave: true })); + + it("should save the instance as soon as a property is changed", function (done) { + var John = new Person({ + name : "Jhon" + }); + + John.saveAsync() + .then(function () { + John.on("save", function () { + return done(); + }); + + John.name = "John"; + }); + }); + }); + + describe("with saveAssociations", function () { + var afterSaveCalled = false; + + if (common.protocol() == 'mongodb') return; + + describe("default on in settings", function () { + beforeEach(function (done) { + function afterSave () { + afterSaveCalled = true; + } + var hooks = { afterSave: afterSave }; + + setup(null, { hooks: hooks, cache: false, hasOneOpts: { autoFetch: true } })(function (err) { + should.not.exist(err); + + Person.create({ name: 'Olga' }, function (err, olga) { + should.not.exist(err); + + should.exist(olga); + Person.create({ name: 'Hagar', parent_id: olga.id }, function (err, hagar) { + should.not.exist(err); + should.exist(hagar); + afterSaveCalled = false; + done(); + }); + }); + }); + }); + + it("should be on", function () { + should.equal(Person.settings.get('instance.saveAssociationsByDefault'), true); + }); + + it("off should not save associations but save itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({name: 'Hagar2'}, { saveAssociations: false })]; + }) + .spread(function (hagar) { + should.equal(afterSaveCalled, true); + + return Person.getAsync(hagar.parent.id); + }) + .then(function (olga) { + should.equal(olga.name, 'Olga'); + }); + }); + + it("off should not save associations or itself if there are no changes", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + return [hagar, hagar.saveAsync({}, { saveAssociations: false })]; + }) + .spread(function (hagar) { + should.equal(afterSaveCalled, false); + return Person.getAsync(hagar.parent.id); + }) + .then(function (olga) { + should.equal(olga.name, 'Olga'); + }); + }); + + it("unspecified should save associations and itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({name: 'Hagar2'})]; + }) + .spread(function (hagar) { + return [hagar, Person.getAsync(hagar.parent.id)]; + }) + .spread(function (hagar, olga) { + should.equal(olga.name, 'Olga2'); + + return Person.getAsync(hagar.id); + }) + .then(function (person) { + should.equal(person.name, 'Hagar2'); + }); + }); + + it("on should save associations and itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({name: 'Hagar2'}, { saveAssociations: true })]; + }) + .spread(function (hagar) { + return [hagar, Person.getAsync(hagar.parent.id)]; + }) + .spread(function (hagar, olga) { + should.equal(olga.name, 'Olga2'); + + return Person.getAsync(hagar.id); + }) + .then(function (person) { + should.equal(person.name, 'Hagar2'); + }); + }); + }); + + describe("turned off in settings", function () { + beforeEach(function (done) { + function afterSave () { + afterSaveCalled = true; + } + var hooks = { afterSave: afterSave }; + + setup(null, { + hooks: hooks, cache: false, hasOneOpts: { autoFetch: true }, + saveAssociationsByDefault: false + })(function (err) { + should.not.exist(err); + + Person.create({ name: 'Olga' }, function (err, olga) { + should.not.exist(err); + + should.exist(olga); + Person.create({ name: 'Hagar', parent_id: olga.id }, function (err, hagar) { + should.not.exist(err); + should.exist(hagar); + afterSaveCalled = false; + done(); + }); + }); + }); + }); + + it("should be off", function () { + should.equal(Person.settings.get('instance.saveAssociationsByDefault'), false); + }); + + it("unspecified should not save associations but save itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({ name: 'Hagar2' })]; + }) + .spread(function (hagar) { + return [hagar, Person.getAsync(hagar.parent.id)]; + }) + .spread(function (hagar, olga) { + should.equal(olga.name, 'Olga'); + + return Person.getAsync(hagar.id); + }) + .then(function (person) { + should.equal(person.name, 'Hagar2'); + }); + }); + + it("off should not save associations but save itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({ name: 'Hagar2' }, { saveAssociations: false })]; + }) + .spread(function (hagar) { + should.equal(afterSaveCalled, true); + + return Person.getAsync(hagar.parent.id); + }) + .then(function (olga) { + should.equal(olga.name, 'Olga'); + }); + }); + + it("on should save associations and itself", function () { + return Person.oneAsync({ name: 'Hagar' }) + .then(function (hagar) { + should.exist(hagar.parent); + + hagar.parent.name = 'Olga2'; + return [hagar, hagar.saveAsync({ name: 'Hagar2' }, { saveAssociations: true })]; + }) + .spread(function (hagar) { + return [hagar, Person.getAsync(hagar.parent.id)]; + }) + .spread(function (hagar, olga) { + should.equal(olga.name, 'Olga2'); + + return Person.getAsync(hagar.id); + }) + .then(function (person) { + should.equal(person.name, 'Hagar2'); + }); + }); + }); + }); + + describe("with a point property", function () { + if (common.protocol() == 'sqlite' || common.protocol() == 'mongodb') return; + before(function (done) { + setup({ type: "point" })(done); + }); + + it("should save the instance as a geospatial point", function () { + var John = new Person({ + name: { x: 51.5177, y: -0.0968 } + }); + return John.saveAsync() + .then(function () { + John.name.should.be.an.instanceOf(Object); + John.name.should.have.property('x', 51.5177); + John.name.should.have.property('y', -0.0968); + }); + }); + }); +}); From c1204cc9b32b5cc0ee329223f85b043e91fc6de3 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 13:36:50 +0300 Subject: [PATCH 69/98] Tests changes before branch pull --- .../association-hasone-reverse-async.js | 79 +++++++++---------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js index 1da35125..2d2c6b21 100644 --- a/test/integration/association-hasone-reverse-async.js +++ b/test/integration/association-hasone-reverse-async.js @@ -155,7 +155,7 @@ describe("hasOne Async", function () { has_owner.should.equal(false); return [John, Deco.setOwnersAsync(John)]; }) - .spread(function (John, Deco) { + .spread(function (John) { return [John, Person.findAsync({ pet_id: 1 })]; }) .spread(function (John, owner) { @@ -164,49 +164,48 @@ describe("hasOne Async", function () { }); }, 3, done); }); - + it("should be able to find given an association instance", function (done) { - common.retry(setup(), function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - should.not.exist(err); - should.exist(John); - Pet.find({ name: "Deco" }).first(function (err, Deco) { - should.not.exist(err); + common.retry(setup(), function () { + return Person + .findAsync({ name: "John Doe" }) + .then(function (John) { + var John = John[0]; + should.exist(John); + return [John, Pet.findAsync({ name: "Deco" })]; + }) + .spread(function (John, Deco) { + var Deco = Deco[0]; should.exist(Deco); - Deco.hasOwnersAsync().then(function (has_owner) { - has_owner.should.equal(false); - - Deco.setOwnersAsync(John).then(function () { - - Person.find({ pet: Deco }).first(function (err, owner) { - should.not.exist(err); - should.exist(owner); - should.equal(owner.name, John.name); - done(); - }); - - }).catch(function(err) { - done(err); - }); - }).done(function(err) { - done(err); - }); + return [John, Deco, Deco.hasOwnersAsync()]; + }) + .spread(function (John, Deco, has_owner) { + has_owner.should.equal(false); + return [John, Deco.setOwnersAsync(John)]; + }) + .spread(function (John, Deco) { + return [John, Person.findAsync({ pet: Deco })]; + }) + .spread(function(John, owner){ + should.exist(owner[1]); + should.equal(owner[1].name, John.name); }); - }); }, 3, done); }); - it("should be able to find given a number of association instances with a single primary key", function (done) { - common.retry(setup(), function (done) { - Person.find({ name: "John Doe" }).first(function (err, John) { - should.not.exist(err); - should.exist(John); - Pet.all(function (err, pets) { - should.not.exist(err); + it.only("should be able to find given a number of association instances with a single primary key", function (done) { + //common.retry(setup(), function (done) { + Person.findAsync({ name: "John Doe" }) + .then(function (John) { + should.exist(John); + return [John, Pet.allAsync()]; + }) + .then(function (John, pets) { should.exist(pets); should.equal(pets.length, 2); - - pets[0].hasOwnersAsync().then(function (has_owner) { + return [John, pets[0], pets[0].hasOwnersAsync()]; + }) + .then(function (John, pets, has_owner) { has_owner.should.equal(false); pets[0].setOwnersAsync(John).then(function () { @@ -219,15 +218,9 @@ describe("hasOne Async", function () { should.equal(owners[0].name, John.name); done(); }); - }).catch(function(err) { - done(err); }); - }).catch(function(err) { - done(err); }); - }); - }); - }, 3, done); + //}, 3, done); }); }); }); From dadc4448e2c73220ac4d9f7473f15a7151026eea Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 14:58:29 +0300 Subject: [PATCH 70/98] Final commit for hasone association async tests --- .../association-hasone-reverse-async.js | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js index 2d2c6b21..2b817be0 100644 --- a/test/integration/association-hasone-reverse-async.js +++ b/test/integration/association-hasone-reverse-async.js @@ -193,34 +193,31 @@ describe("hasOne Async", function () { }, 3, done); }); - it.only("should be able to find given a number of association instances with a single primary key", function (done) { - //common.retry(setup(), function (done) { - Person.findAsync({ name: "John Doe" }) + it("should be able to find given a number of association instances with a single primary key", function (done) { + common.retry(setup(), function (done) { + return Person.findAsync({ name: "John Doe" }) .then(function (John) { should.exist(John); return [John, Pet.allAsync()]; }) - .then(function (John, pets) { + .spread(function (John, pets) { should.exist(pets); should.equal(pets.length, 2); - return [John, pets[0], pets[0].hasOwnersAsync()]; + return [John[0], pets, pets[0].hasOwnersAsync()]; }) - .then(function (John, pets, has_owner) { - has_owner.should.equal(false); - - pets[0].setOwnersAsync(John).then(function () { - - Person.find({ pet: pets }, function (err, owners) { - should.not.exist(err); - should.exist(owners); - owners.length.should.equal(1); - - should.equal(owners[0].name, John.name); - done(); - }); - }); - }); - //}, 3, done); + .spread(function (John, pets, has_owner) { + has_owner.should.equal(false); + return [John, pets, pets[0].setOwnersAsync(John)]; + }) + .spread(function (John, pets) { + return [John, Person.findAsync({ pet: pets })]; + }) + .spread(function (John, owners) { + should.exist(owners[0]); + owners.length.should.equal(1); + should.equal(owners[0].name, John.name); + }); + }, 3, done); }); }); }); From 2900c3d78717d9d02bfbf1c941f6770f7971f1b0 Mon Sep 17 00:00:00 2001 From: mradko Date: Thu, 14 Sep 2017 15:22:28 +0300 Subject: [PATCH 71/98] update instance tests --- test/integration/instance.js | 182 +++++++++++++++-------------------- 1 file changed, 76 insertions(+), 106 deletions(-) diff --git a/test/integration/instance.js b/test/integration/instance.js index 1b17d844..91e6f9c4 100644 --- a/test/integration/instance.js +++ b/test/integration/instance.js @@ -97,14 +97,11 @@ describe("Model instance", function() { }); }); - it("should have a saving state to avoid loops (promise-based)", function (done) { - main_item.find({ name : "new name" }).first(function (err, mainItem) { - mainItem.saveAsync({ name : "new name test" }).then(function () { - done(); - }).catch(function(err) { - done(err); + it("should have a saving state to avoid loops (promise-based)", function () { + return main_item.find({ name : "new name" }).firstAsync() + .then(function (mainItem) { + return mainItem.saveAsync({ name : "new name test" }); }); - }); }); }); @@ -347,28 +344,23 @@ describe("Model instance", function() { }); }); - it("should return validation errors if invalid (promise-based)", function (done) { + it("should return validation errors if invalid (promise-based)", function () { var person = new Person({ age: -1 }); - person.validateAsync().then(function (validationErrors) { - should.equal(Array.isArray(validationErrors), true); - - done(); - }).catch(function(err) { - done(err); - }); + return person.validateAsync() + .then(function (validationErrors) { + should.equal(Array.isArray(validationErrors), true); + }); }); - it("should return false if valid (promise-based)", function (done) { + it("should return false if valid (promise-based)", function () { var person = new Person({ name: 'Janette' }); - person.validateAsync().then(function (validationErrors) { - should.equal(validationErrors, false); + return person.validateAsync() + .then(function (validationErrors) { + should.equal(validationErrors, false); - done(); - }).catch(function(err) { - done(err); - }); + }); }); }); @@ -397,29 +389,21 @@ describe("Model instance", function() { }); }); - it("should be saved for valid numbers, using both save & create (promise-based)", function (done) { + it("should be saved for valid numbers, using both save & create (promise-based)", function () { var person1 = new Person({ height: 190 }); - person1.saveAsync().then(function () { - - Person.createAsync({ height: 170 }).then(function (person2) { - Person.getAsync(person1[Person.id]).then(function (item) { + return person1.saveAsync().then(function () { + return Person.createAsync({ height: 170 }) + .then(function (person2) { + return [person2, Person.getAsync(person1[Person.id])]; + }) + .spread(function (person2, item) { should.equal(item.height, 190); - Person.getAsync(person2[Person.id]).then(function (item) { - should.equal(item.height, 170); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); + return Person.getAsync(person2[Person.id]); + }) + .then(function (item) { + should.equal(item.height, 170); }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); }); }); @@ -476,54 +460,50 @@ describe("Model instance", function() { }); }); - it("should raise an error for NaN integers (promise-based)", function (done) { + it("should raise an error for NaN integers (promise-based)", function () { var person = new Person({ height: NaN }); - person.saveAsync().catch(function(err) { - var msg = { - postgres : 'invalid input syntax for integer: "NaN"' - }[protocol]; - - should.equal(err.message, msg); + return person.saveAsync() + .catch(function(err) { + var msg = { + postgres : 'invalid input syntax for integer: "NaN"' + }[protocol]; - done(); - }); + should.equal(err.message, msg); + }); }); - it("should raise an error for Infinity integers (promise-based)", function (done) { + it("should raise an error for Infinity integers (promise-based)", function () { var person = new Person({ height: Infinity }); - person.saveAsync().catch(function (err) { - should.exist(err); - var msg = { - postgres : 'invalid input syntax for integer: "Infinity"' - }[protocol]; - - should.equal(err.message, msg); + return person.saveAsync() + .catch(function (err) { + should.exist(err); + var msg = { + postgres : 'invalid input syntax for integer: "Infinity"' + }[protocol]; - done(); - }); + should.equal(err.message, msg); + }); }); - it("should raise an error for nonsensical integers, for both save & create (promise-based)", function (done) { + it("should raise an error for nonsensical integers, for both save & create (promise-based)", function () { var person = new Person({ height: 'bugz' }); + var msg = { + postgres : 'invalid input syntax for integer: "bugz"' + }[protocol]; - person.saveAsync().catch(function (err) { - should.exist(err); - var msg = { - postgres : 'invalid input syntax for integer: "bugz"' - }[protocol]; - - should.equal(err.message, msg); + return person.saveAsync() + .catch(function (err) { + should.exist(err); - Person.createAsync({ height: 'bugz' }).then(function () { - done(new Error('Function should catch an error instead of finish')); - }).catch(function(err) { + should.equal(err.message, msg); + return Person.createAsync({ height: 'bugz' }); + }) + .catch(function(err) { should.exist(err); should.equal(err.message, msg); - done(); }); - }); }); } @@ -553,31 +533,24 @@ describe("Model instance", function() { }); }); - it("should store NaN & Infinite floats (promise-based)", function (done) { + it("should store NaN & Infinite floats (promise-based)", function () { var person = new Person({ weight: NaN }); - person.saveAsync().then(function () { - - Person.getAsync(person[Person.id]).then(function (person) { + return person.saveAsync() + .then(function () { + return Person.getAsync(person[Person.id]); + }) + .then(function (person) { should(isNaN(person.weight)); - person.saveAsync({ weight: Infinity, name: 'black hole' }).then(function () { - Person.getAsync(person[Person.id]).then(function (person) { - should.strictEqual(person.weight, Infinity); - - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); + return person.saveAsync({ weight: Infinity, name: 'black hole' }); + }) + .then(function () { + return Person.getAsync(person[Person.id]); + }) + .then(function (person) { + should.strictEqual(person.weight, Infinity); }); - }).catch(function(err) { - done(err); - }); }); } }); @@ -634,20 +607,17 @@ describe("Model instance", function() { }); }); - it("should delete an item and send an error", function (done) { - main_item.find({ name : "Main Item" }).first(function (err, mainItem) { - mainItem.removeAsync().then(function () { - main_item.find({ name : "Main Item" }).first(function (err, itemFound) { - if (err && !itemFound) { - done(); - } - - done(err); - }); - }).catch(function(err) { - done(err); + it("should delete an item and send an error", function () { + return main_item.find({ name : "Main Item" }).firstAsync() + .then(function (mainItem) { + return mainItem.removeAsync(); + }) + .then(function () { + return main_item.find({ name : "Main Item" }).firstAsync(); + }) + .then(function (item) { + should.equal(item, null) }); - }); }); }); }); From bf6f37e8e08fb2c232f0fe2767b8eeab506ac9f6 Mon Sep 17 00:00:00 2001 From: mradko Date: Thu, 14 Sep 2017 15:52:32 +0300 Subject: [PATCH 72/98] update ignore files --- .gitignore | 2 +- .npmignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index afba1e84..2450dd37 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ test/config.js test/coverage.html lib-cov/ *~ -/.idea +.idea diff --git a/.npmignore b/.npmignore index 0c0d8ef4..7b93b7d6 100644 --- a/.npmignore +++ b/.npmignore @@ -6,3 +6,4 @@ node_modules test* lib-cov/ *~ +.idea \ No newline at end of file From 1275d848e095f25fbefcaf0f708da500d6392022 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 15:55:31 +0300 Subject: [PATCH 73/98] Add and update lazyload async methods --- test/integration/property-lazyload-async.js | 133 ++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 test/integration/property-lazyload-async.js diff --git a/test/integration/property-lazyload-async.js b/test/integration/property-lazyload-async.js new file mode 100644 index 00000000..bc963f8d --- /dev/null +++ b/test/integration/property-lazyload-async.js @@ -0,0 +1,133 @@ +var should = require('should'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("LazyLoad Async properties", function() { + var db = null; + var Person = null; + var PersonPhoto = new Buffer(1024); // fake photo + var OtherPersonPhoto = new Buffer(1024); // other fake photo + + var setup = function () { + return function (done) { + Person = db.define("person", { + name : String, + photo : { type: "binary", lazyload: true } + }); + + ORM.singleton.clear(); + + return helper.dropSync(Person, function () { + Person.create({ + name : "John Doe", + photo : PersonPhoto + }, done); + }); + }; + }; + + before(function (done) { + helper.connect(function (connection) { + db = connection; + + return done(); + }); + }); + + after(function () { + return db.close(); + }); + + describe("when defined Async methods", function () { + before(setup()); + + it("should not be available when fetching an instance", function () { + return Person + .findAsync() + .then(function (John) { + var john = John[0]; + + should.equal(typeof john, 'object'); + should.equal(Array.isArray(john), false); + john.should.have.property("name", "John Doe"); + john.should.have.property("photo", null); + }); + }); + + it("should have apropriate accessors", function () { + return Person + .findAsync() + .then(function (persons) { + var John = persons[0]; + should.equal(typeof John, 'object'); + should.equal(Array.isArray(John), false); + + John.getPhotoAsync.should.be.a.Function(); + John.setPhotoAsync.should.be.a.Function(); + John.removePhotoAsync.should.be.a.Function(); + }); + }); + + it("getAccessorAsync should return property", function () { + return Person + .findAsync() + .then(function (persons) { + var John = persons[0]; + + should.equal(typeof John, 'object'); + should.equal(Array.isArray(John), false); + return John.getPhotoAsync(); + }) + .then(function (photo) { + photo.toString().should.equal(PersonPhoto.toString()); + }); + }); + + it("setAccessorAsync should change property", function () { + return Person + .findAsync() + .then(function (persons) { + var John = persons[0]; + should.equal(typeof John, 'object'); + return John.setPhotoAsync(OtherPersonPhoto); + }) + .then(function (johnPhotoUpdated) { + should.equal(typeof johnPhotoUpdated, 'object'); + return Person.findAsync(); + }) + .then(function (persons) { + var John = persons[0]; + + should.equal(typeof John, 'object'); + should.equal(Array.isArray(John), false); + return John.getPhotoAsync(); + }) + .then(function (photo) { + photo.toString().should.equal(OtherPersonPhoto.toString()); + }); + }); + + it("removeAccessorAsync should change property", function () { + return Person + .findAsync() + .then(function (persons) { + var John = persons[0]; + + should.equal(typeof John, 'object'); + should.equal(Array.isArray(John), false); + return John.removePhotoAsync(); + }) + .then(function (John) { + return Person.getAsync(John[Person.id]); + }) + .then(function (John) { + should.equal(typeof John, 'object'); + should.equal(Array.isArray(John), false); + return John.getPhotoAsync(); + }) + .then(function (photo) { + should.equal(photo, null); + }); + }); + }); +}); From bc2d54a062af2a04a37d514ec7d6ebe51ec1af93 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 16:22:31 +0300 Subject: [PATCH 74/98] Add hasone-zeroid async tests; Removed test duplicates --- .../association-hasone-zeroid-async.js | 155 ++++++++++++++++++ test/integration/association-hasone-zeroid.js | 59 ------- test/integration/property-lazyload.js | 69 -------- 3 files changed, 155 insertions(+), 128 deletions(-) create mode 100644 test/integration/association-hasone-zeroid-async.js diff --git a/test/integration/association-hasone-zeroid-async.js b/test/integration/association-hasone-zeroid-async.js new file mode 100644 index 00000000..b872a99d --- /dev/null +++ b/test/integration/association-hasone-zeroid-async.js @@ -0,0 +1,155 @@ +var _ = require('lodash'); +var should = require('should'); +var async = require('async'); +var helper = require('../support/spec_helper'); +var ORM = require('../../'); + +describe("hasOne promise-based methods", function() { + var db = null; + var Person = null; + var Pet = null; + + var setup = function (autoFetch) { + return function (done) { + db.settings.set('instance.identityCache', false); + db.settings.set('instance.returnAllErrors', true); + + Person = db.define('person', { + id : { type : "integer", mapsTo: "personID", key: true }, + firstName : { type : "text", size: "255" }, + lastName : { type : "text", size: "255" } + }); + + Pet = db.define('pet', { + id : { type : "integer", mapsTo: "petID", key: true }, + petName : { type : "text", size: "255" }, + ownerID : { type : "integer", size: "4" } + }); + + Pet.hasOne('owner', Person, { field: 'ownerID', autoFetch: autoFetch }); + + helper.dropSync([Person, Pet], function(err) { + if (err) return done(err); + + Pet.create([ + { + id: 10, + petName: 'Muttley', + owner: { + id: 12, + firstName: 'Stuey', + lastName: 'McG' + } + }, + { + id: 11, + petName: 'Snagglepuss', + owner: { + id: 0, + firstName: 'John', + lastName: 'Doe' + } + } + ], done); + }); + }; + }; + + before(function(done) { + helper.connect(function (connection) { + db = connection; + done(); + }); + }); + + describe("auto fetch", function () { + before(setup(true)); + + it("should work for non-zero ownerID ", function () { + return Pet + .findAsync({petName: "Muttley"}) + .then(function(pets) { + pets[0].petName.should.equal("Muttley"); + pets[0].should.have.property("id"); + pets[0].id.should.equal(10); + pets[0].ownerID.should.equal(12); + pets[0].should.have.property("owner"); + pets[0].owner.firstName.should.equal("Stuey"); + }); + }); + + it("should work for zero ownerID ", function () { + return Pet + .findAsync({petName: "Snagglepuss"}) + .then(function(pets) { + pets[0].petName.should.equal("Snagglepuss"); + pets[0].should.have.property("id"); + pets[0].id.should.equal(11); + + return Person.allAsync(); + }) + .then(function (people) { + should.equal(typeof people[0], 'object'); + should.equal(Array.isArray(people), true); + people[0].should.have.property("firstName", "Stuey"); + }); + }); + }); + + describe("no auto fetch", function () { + before(setup(false)); + + it("should work for non-zero ownerID (promise-based)", function () { + return Pet + .findAsync({petName: "Muttley"}) + .then(function(pets) { + var pets = pets[0]; + + pets.petName.should.equal("Muttley"); + pets.should.have.property("id"); + pets.id.should.equal(10); + pets.ownerID.should.equal(12); + + pets.should.not.have.property("owner"); + + // But we should be able to see if its there + return [pets, pets.hasOwnerAsync()]; + }) + .spread(function(pets, hasOwner) { + should.equal(hasOwner, true); + // ...and then get it + return pets.getOwnerAsync(); + }) + .then(function(petOwner) { + petOwner.firstName.should.equal("Stuey"); + }); + }); + + it("should work for zero ownerID", function () { + return Pet + .findAsync({petName: "Snagglepuss"}) + .then(function(pets) { + var pets = pets[0]; + + pets.petName.should.equal("Snagglepuss"); + pets.should.have.property("id"); + pets.id.should.equal(11); + pets.ownerID.should.equal(0); + + pets.should.not.have.property("owner"); + + // But we should be able to see if its there + return [pets, pets.hasOwnerAsync()]; + }) + .spread(function(pets, hasOwner) { + should.equal(hasOwner, true); + + // ...and then get it + return pets.getOwnerAsync(); + }) + .then(function(petOwner) { + petOwner.firstName.should.equal("John"); + }); + }); + }); +}); diff --git a/test/integration/association-hasone-zeroid.js b/test/integration/association-hasone-zeroid.js index 17296866..8fd2bd17 100644 --- a/test/integration/association-hasone-zeroid.js +++ b/test/integration/association-hasone-zeroid.js @@ -125,64 +125,5 @@ describe("hasOne", function() { }); }); }); - - it("should work for non-zero ownerID (promise-based)", function (done) { - Pet.find({petName: "Muttley"}, function(err, pets) { - should.not.exist(err); - - pets[0].petName.should.equal("Muttley"); - pets[0].should.have.property("id"); - pets[0].id.should.equal(10); - pets[0].ownerID.should.equal(12); - - pets[0].should.not.have.property("owner"); - - // But we should be able to see if its there - pets[0].hasOwnerAsync().then(function(result) { - should.equal(result, true); - - // ...and then get it - pets[0].getOwnerAsync().then(function(result) { - result.firstName.should.equal("Stuey"); - - done() - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it("should work for zero ownerID (promise-based)", function (done) { - Pet.find({petName: "Snagglepuss"}, function(err, pets) { - should.not.exist(err); - - pets[0].petName.should.equal("Snagglepuss"); - pets[0].should.have.property("id"); - pets[0].id.should.equal(11); - pets[0].ownerID.should.equal(0); - - pets[0].should.not.have.property("owner"); - - // But we should be able to see if its there - pets[0].hasOwnerAsync().then(function(result) { - should.equal(result, true); - - // ...and then get it - pets[0].getOwnerAsync().then(function(result) { - should.not.exist(err); - result.firstName.should.equal("John"); - - done() - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); }); }); diff --git a/test/integration/property-lazyload.js b/test/integration/property-lazyload.js index fce83845..0b83f00c 100644 --- a/test/integration/property-lazyload.js +++ b/test/integration/property-lazyload.js @@ -63,9 +63,6 @@ describe("LazyLoad properties", function() { John.getPhoto.should.be.a.Function(); John.setPhoto.should.be.a.Function(); John.removePhoto.should.be.a.Function(); - John.getPhotoAsync.should.be.a.Function(); - John.setPhotoAsync.should.be.a.Function(); - John.removePhotoAsync.should.be.a.Function(); return done(); }); @@ -86,20 +83,6 @@ describe("LazyLoad properties", function() { }); }); - it("promise-based getAccessor should return property", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - John.should.be.a.Object(); - John.getPhotoAsync() - .then(function (photo) { - photo.toString().should.equal(PersonPhoto.toString()); - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - it("setAccessor should change property", function (done) { Person.find().first(function (err, John) { should.equal(err, null); @@ -122,32 +105,6 @@ describe("LazyLoad properties", function() { }); }); - it("promise-based setAccessor should change property (promise-based)", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - John.should.be.a.Object(); - - John.setPhotoAsync(OtherPersonPhoto) - .then(function (johnPhotoUpdated) { - johnPhotoUpdated.should.be.a.Object(); - - Person.find().first(function (err, John) { - should.equal(err, null); - John.should.be.a.Object(); - - John.getPhotoAsync() - .then(function (photo) { - should.equal(err, null); - photo.toString().should.equal(OtherPersonPhoto.toString()); - done(); - }).catch(function (err) { - done(err); - }); - }); - }); - }); - }); - it("removeAccessor should change property", function (done) { Person.find().first(function (err, John) { should.equal(err, null); @@ -170,31 +127,5 @@ describe("LazyLoad properties", function() { }); }); }); - - it("removeAccessor should change property (promise-based)", function (done) { - Person.find().first(function (err, John) { - should.equal(err, null); - John.should.be.a.Object(); - - John.removePhotoAsync().then(function () { - Person.getAsync(John[Person.id]).then(function (John) { - John.should.be.a.Object(); - - John.getPhotoAsync() - .then(function (photo) { - should.equal(err, null); - should.equal(photo, null); - done(); - }).catch(function (err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - }); }); }); From 4257227e3a50ab433eb76a61f014e6b8dcc3bb7d Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 16:27:04 +0300 Subject: [PATCH 75/98] Reset test config to default --- test/config.example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.example.js b/test/config.example.js index 0d9c232b..bb9c2d73 100644 --- a/test/config.example.js +++ b/test/config.example.js @@ -13,8 +13,8 @@ exports.mysql = { database : "test" }; exports.postgres = { - user : "postgres", - password : "1111", + user : "root", + password : "", database : "test" }; exports.redshift = { From ec4d5263210e78bbfcfccb60490e7953bc1113ac Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Thu, 14 Sep 2017 18:29:44 +0300 Subject: [PATCH 76/98] Resolve PR comments: format some indents; removed done() --- test/integration/association-hasone-async.js | 18 +-- .../association-hasone-reverse-async.js | 144 +++++++++--------- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/test/integration/association-hasone-async.js b/test/integration/association-hasone-async.js index 0eb0e155..81b165d6 100644 --- a/test/integration/association-hasone-async.js +++ b/test/integration/association-hasone-async.js @@ -73,8 +73,8 @@ describe("hasOne", function() { it("get should get the association", function () { return Leaf .oneAsync({ size: 14 }) - .then(function(leaf) { - should.exist(leaf); + .then(function (leaf) { + should.exist(leaf); return leaf.getTreeAsync(); }) .then(function (tree) { @@ -127,8 +127,8 @@ describe("hasOne", function() { .spread(function (stalk, leaf) { should.exist(leaf); should.not.exist(leaf.stalkId); - return [stalk, leaf.setStalkAsync(stalk)]; - }) + return [stalk, leaf.setStalkAsync(stalk)]; + }) .then(function (stalk) { return [stalk, Leaf.oneAsync({ size: 14 })]; }) @@ -142,8 +142,8 @@ describe("hasOne", function() { .oneAsync({ length: 20 }) .then(function (stalk) { should.exist(stalk); - return Leaf.oneAsync({ size: 14 }); - }) + return Leaf.oneAsync({size: 14}); + }) .then(function (leaf) { should.exist(leaf); should.exist(leaf.stalkId); @@ -152,9 +152,9 @@ describe("hasOne", function() { .then(function () { return Leaf.oneAsync({ size: 14 }); }) - .then(function (leaf) { - should.equal(leaf.stalkId, null); - }); + .then(function (leaf) { + should.equal(leaf.stalkId, null); + }); }); }); diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js index 2b817be0..6d10d79b 100644 --- a/test/integration/association-hasone-reverse-async.js +++ b/test/integration/association-hasone-reverse-async.js @@ -139,85 +139,79 @@ describe("hasOne Async", function () { describe("reverse find", function () { before(setup()); - it("should be able to find given an association id", function (done) { - common.retry(setup(), function () { - return Person - .findAsync({ name: "John Doe" }) - .then(function (John) { - should.exist(John); - return [John, Pet.findAsync({ name: "Deco" })]; - }) - .spread(function (John, Deco) { - should.exist(Deco); - return [John[0], Deco[0], Deco[0].hasOwnersAsync()]; - }) - .spread(function (John, Deco, has_owner) { - has_owner.should.equal(false); - return [John, Deco.setOwnersAsync(John)]; - }) - .spread(function (John) { - return [John, Person.findAsync({ pet_id: 1 })]; - }) - .spread(function (John, owner) { - should.exist(owner[0]); - should.equal(owner[0].name, John.name); - }); - }, 3, done); + it("should be able to find given an association id", function () { + return Person + .findAsync({ name: "John Doe" }) + .then(function (John) { + should.exist(John); + return [John, Pet.findAsync({ name: "Deco" })]; + }) + .spread(function (John, Deco) { + should.exist(Deco); + return [John[0], Deco[0], Deco[0].hasOwnersAsync()]; + }) + .spread(function (John, Deco, has_owner) { + has_owner.should.equal(false); + return [John, Deco.setOwnersAsync(John)]; + }) + .spread(function (John) { + return [John, Person.findAsync({ pet_id: 1 })]; + }) + .spread(function (John, owner) { + should.exist(owner[0]); + should.equal(owner[0].name, John.name); + }); }); - it("should be able to find given an association instance", function (done) { - common.retry(setup(), function () { - return Person - .findAsync({ name: "John Doe" }) - .then(function (John) { - var John = John[0]; - should.exist(John); - return [John, Pet.findAsync({ name: "Deco" })]; - }) - .spread(function (John, Deco) { - var Deco = Deco[0]; - should.exist(Deco); - return [John, Deco, Deco.hasOwnersAsync()]; - }) - .spread(function (John, Deco, has_owner) { - has_owner.should.equal(false); - return [John, Deco.setOwnersAsync(John)]; - }) - .spread(function (John, Deco) { - return [John, Person.findAsync({ pet: Deco })]; - }) - .spread(function(John, owner){ - should.exist(owner[1]); - should.equal(owner[1].name, John.name); - }); - }, 3, done); + it("should be able to find given an association instance", function () { + return Person + .findAsync({ name: "John Doe" }) + .then(function (John) { + var John = John[0]; + should.exist(John); + return [John, Pet.findAsync({ name: "Deco" })]; + }) + .spread(function (John, Deco) { + var Deco = Deco[0]; + should.exist(Deco); + return [John, Deco, Deco.hasOwnersAsync()]; + }) + .spread(function (John, Deco, has_owner) { + has_owner.should.equal(false); + return [John, Deco.setOwnersAsync(John)]; + }) + .spread(function (John, Deco) { + return [John, Person.findAsync({ pet: Deco })]; + }) + .spread(function(John, owner){ + should.exist(owner[1]); + should.equal(owner[1].name, John.name); + }); }); - it("should be able to find given a number of association instances with a single primary key", function (done) { - common.retry(setup(), function (done) { - return Person.findAsync({ name: "John Doe" }) - .then(function (John) { - should.exist(John); - return [John, Pet.allAsync()]; - }) - .spread(function (John, pets) { - should.exist(pets); - should.equal(pets.length, 2); - return [John[0], pets, pets[0].hasOwnersAsync()]; - }) - .spread(function (John, pets, has_owner) { - has_owner.should.equal(false); - return [John, pets, pets[0].setOwnersAsync(John)]; - }) - .spread(function (John, pets) { - return [John, Person.findAsync({ pet: pets })]; - }) - .spread(function (John, owners) { - should.exist(owners[0]); - owners.length.should.equal(1); - should.equal(owners[0].name, John.name); - }); - }, 3, done); + it("should be able to find given a number of association instances with a single primary key", function () { + return Person.findAsync({ name: "John Doe" }) + .then(function (John) { + should.exist(John); + return [John, Pet.allAsync()]; + }) + .spread(function (John, pets) { + should.exist(pets); + should.equal(pets.length, 2); + return [John[0], pets, pets[0].hasOwnersAsync()]; + }) + .spread(function (John, pets, has_owner) { + has_owner.should.equal(false); + return [John, pets, pets[0].setOwnersAsync(John)]; + }) + .spread(function (John, pets) { + return [John, Person.findAsync({ pet: pets })]; + }) + .spread(function (John, owners) { + should.exist(owners[0]); + owners.length.should.equal(1); + should.equal(owners[0].name, John.name); + }); }); }); }); From e9a8a1b33b1e24193e74dc1a8e9780bec79ecd74 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Fri, 15 Sep 2017 15:56:31 +0300 Subject: [PATCH 77/98] Lazyload global scope variable fix --- lib/LazyLoad.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index e6a39a1c..a81e2624 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -6,7 +6,13 @@ var extend = function (Instance, Model, properties) { addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); } } -}; +} + +var conditionAssign = function (instance, model) { + var conditions = {}; + conditions[model.id] = instance[model.id]; + return conditions; +} function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); @@ -26,12 +32,10 @@ function addLazyLoadProperty(name, Instance, Model, property) { } } - var conditions = {}; - conditions[Model.id] = Instance[Model.id]; - Object.defineProperty(Instance, functionNames.get.callback, { value: function (cb) { - + var conditions = conditionAssign(Instance, Model); + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { return cb(err, item ? item[property] : null); }); @@ -43,7 +47,8 @@ function addLazyLoadProperty(name, Instance, Model, property) { Object.defineProperty(Instance, functionNames.remove.callback, { value: function (cb) { - + var conditions = conditionAssign(Instance, Model); + Model.find(conditions, { identityCache: false }).only(Model.id.concat(property)).first(function (err, item) { if (err) { return cb(err); @@ -64,7 +69,8 @@ function addLazyLoadProperty(name, Instance, Model, property) { Object.defineProperty(Instance, functionNames.set.callback, { value: function (data, cb) { - + var conditions = conditionAssign(Instance, Model); + Model.find(conditions, { identityCache: false }).first(function (err, item) { if (err) { return cb(err); From f30e56a7fd085883b1449d81f08888e1d3cfcf3d Mon Sep 17 00:00:00 2001 From: mradko Date: Fri, 15 Sep 2017 15:57:49 +0300 Subject: [PATCH 78/98] fix unused vars --- lib/ChainFind.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 547c8087..f3f050f5 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -44,10 +44,8 @@ function ChainFind(Model, opts) { if (dataItems.length === 0) { return done(null, []); } - var pending = dataItems.length; var eagerLoad = function (err, items) { - var pending = opts.__eager.length; var idMap = {}; var keys = _.map(items, function (item, index) { @@ -91,7 +89,6 @@ function ChainFind(Model, opts) { }); } - var promise = null; var chain = { find: function () { var cb = null; From 0588a99276b8a5a6a327402813e9d41109803244 Mon Sep 17 00:00:00 2001 From: mradko Date: Fri, 15 Sep 2017 15:57:49 +0300 Subject: [PATCH 79/98] fix unused vars --- lib/ChainFind.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 547c8087..f3f050f5 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -44,10 +44,8 @@ function ChainFind(Model, opts) { if (dataItems.length === 0) { return done(null, []); } - var pending = dataItems.length; var eagerLoad = function (err, items) { - var pending = opts.__eager.length; var idMap = {}; var keys = _.map(items, function (item, index) { @@ -91,7 +89,6 @@ function ChainFind(Model, opts) { }); } - var promise = null; var chain = { find: function () { var cb = null; From 365d714990366ee525c714ffa27dc03e66588082 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 10:23:36 +0300 Subject: [PATCH 80/98] add promise documentation --- .gitignore | 1 + Readme.md | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 271 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 2450dd37..a09ad17e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ test/config.js test/coverage.html lib-cov/ *~ +npm-debug.log .idea diff --git a/Readme.md b/Readme.md index 1360fc2e..7e01b49c 100755 --- a/Readme.md +++ b/Readme.md @@ -95,16 +95,280 @@ orm.connect("mysql://username:password@host/database", function (err, db) { }); }); ``` +------ +## Promise -## Promises +- Read documentation about [bluebird](http://bluebirdjs.com/docs/api-reference.html) `Promise` for more advanced knowledge how to use `Promises`. -The methods what has Async like a postfix, like example `connectAsync` -can apply the same arguments as a original method but return a promise. -Exclusion method `sync` they called `syncPromise`. -More details [wiki](linkToWikiHere). // TODO add link to wiki where described async methods. +###Connect -You can use the [promise enabled wrapper library](https://github.com/rafaelkaufmann/q-orm). +The connection URL has the following syntax: `driver://username:password@hostname/database?option1=value1&option2=value2..` +```javascript +var orm = require('orm'); + +orm.connectAsync('mysql://root:password@localhost/test') + .then(function(db) { + // connected + // ... + }) + .catch(function() { + console.error('Connection error: ' + err); + }); +``` + +Valid options are: + +- `debug` (default: **false**): prints queries to console; +- `pool` (default: **false**): manages a connection pool (only for `mysql` and `postgres`) using built-in driver pool; +- `strdates` (default: **false**): saves dates as strings (only for `sqlite`). +- `timezone` (default: **'local'**): store dates in the database using this timezone (`mysql` and `postgres` only) + +```javascript +var orm = require('orm'); + +var opts = { + host: host, + database: database, + protocol: 'mysql', + port: '3306', + query: {pool: true} + }; + +orm.connectAsync(opts) + .then(function(db) { + // connected + // ... + }) + .catch(function() { + console.error('Connection error: ' + err); + }); +``` +------- + +###Model Hooks + +If you want to listen for a type of event than occurs in instances of a Model, you can attach a function that +will be called when that event happens. For each hook above implemented Promise support, with backward capability via use next callback. +For use promise you should return `Promise`, look at example. + +Currently the following events are supported: + +- `beforeValidation` : (no parameters) Before all validations and prior to `beforeCreate` and `beforeSave`; +- `beforeCreate` : (no parameters) Right before trying to save a new instance (prior to `beforeSave`); +- `beforeSave` : (no parameters) Right before trying to save; +- `afterSave` : (bool success) Right after saving; +- `afterCreate` : (bool success) Right after saving a new instance; +- `afterLoad` : (no parameters) Right after loading and preparing an instance to be used; +- `afterAutoFetch` : (no parameters) Right after auto-fetching associations (if any), it will trigger regardless of having associations or not; +- `beforeRemove` : (no parameters) Right before trying to remove an instance; +- `afterRemove` : (bool success) Right after removing an instance; + +All hook function are called with `this` as the instance so you can access anything you want related to it. +Here's an example: + +```js +var Person = db.define("person", { + name : String, + surname : String +}, { + hooks: { + beforeCreate: function () { + return new Promise(function(resolve, reject) { + if (this.surname == "Doe") { + return reject(new Error("No Does allowed")); + } + return resolve(); + }); + } + } +}); +``` +------- +###Editing Syncing and dropping models +Syncing is an utility method that creates all the necessary tables in the database for your models and associations to work. Tables are not replaced, they are only created if they don't exist. + +There are 2 ways of syncing: + +1. Calling `Model.syncPromise()` will only synchronize the model +2. Calling `db.syncPromise()` will synchronize all models + +Dropping is a similar method but instead it drops all tables involved in your models, even if they were not created by ORM. There also 2 ways of dropping. + +```js +var orm = require("orm"); + +orm.connectAsync("....") + .then(function (db) { + var Person = db.define("person", { + name : String + }); + + return [Person, db.dropAsync()]; + }) + .spread(function(Person) { + return Person.syncPromise(); + }) + .then(function () { + // created tables for Person model + }); +``` +------- +###Finding items + +#### findAsync +Find records with matching criteria, can be chained (see below): +```javascript +Person.find({status:'active'}) + .then(function(results) { + // ... + }); +``` + +You can limit your results as well. This limits our results to 10 +```javascript +Person.find({status:'active'}, 10) + .then(function(results) { + // ... + }); +``` + +`Person.all` is an alias to `Person.find` + +#### getAsync +Find record by primary key. +```javascript +Person.getAsync(1) + .then(function(person) { + // ... + }); +``` +#### oneAsync +Find one record with similar syntax to find. +```javascript +Person.oneAsync({status:'active'}) + .then(function(person) { + // ... + }); +``` + +#### countAsync +Get the number of matching records. +```javascript +Person.countAsync({status:'active'}) + .then(function(activePeopleCount) { + // ... + }); +``` + +#### existsAsync +Test a record matching your conditions exists. +```javascript +Person.exists({id:1, status:'active'}) + .then(function(personIsActive) { + // ... + }); +``` + +#### Filtering and sorting +We accept 2 objects to perform filtering (first) and aggregate (second). The aggregate object accepts `limit`, `order`, and `groupBy`. + +```javascript +Person.findAsync({status:'active'}, {limit:10}) + .then(function(results) { + // ... + }); +``` + +#### Conditions for find/count/one etc. +All comma separated key/values are AND'd together in the query. You may prefix a set of conditions with logical operators. +```javascript +Person.findAsync({or:[{col1: 1}, {col2: 2}]}) + .then(function(res) { + // ... + }); +``` + +#### Finding with an `IN` +`sql-query` (underlying SQL engine) will automatically coerce any array to an `IN` based query. + +```javascript +Person.findAsync({id: [1, 2]}) + .then(function(persons) { + // Finds people with id's 1 and 2 (e.g. `WHERE id IN (1, 2)`) + }); +``` +------- +###Creating and Updating Items +#### createAsync +```javascript +var newRecord = {}; +newRecord.id = 1; +newRecord.name = "John"; + +Person.createAsync(newRecord) + .then(function(results) { + // ... + }); +``` + +#### saveAsync +```js + Person.findAsync({ surname: "Doe" }) + .then(function (people) { + // SQL: "SELECT * FROM person WHERE surname = 'Doe'" + + console.log("People found: %d", people.length); + console.log("First person: %s, age %d", people[0].fullName(), people[0].age); + + people[0].age = 16; + return people[0].saveAsync(); + }) + .then(function () { + // saved + }); +``` +------- +###Aggregation +If you need to get some aggregated values from a Model, you can use `Model.aggregate()`. Here's an example to better +illustrate: + +```js +Person.aggregate({ surname: "Doe" }).min("age").max("age").getAsync() + .then(function(result) { + var [min, max] = result; // you should use destructuring here + + console.log(min, max); + }); +``` + +An `Array` of properties can be passed to select only a few properties. An `Object` is also accepted to define conditions. + +Here's an example to illustrate how to use `.groupBy()`: + +```js +//The same as "select avg(weight), age from person where country='someCountry' group by age;" +Person.aggregate(["age"], { country: "someCountry" }).avg("weight").groupBy("age").getAsync() + .then(function (stats) { + // stats is an Array, each item should have 'age' and 'avg_weight' + }); +``` + +### Base `.aggregate()` methods + +- `.limit()`: you can pass a number as a limit, or two numbers as offset and limit respectively +- `.order()`: same as `Model.find().order()` + +### Additional `.aggregate()` methods + +- `min` +- `max` +- `avg` +- `sum` + +There are more aggregate functions depending on the driver (Math functions for example). + +------- ## Express From 48124485f91bc385021576e2bbc06ebe7d289001 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 10:57:14 +0300 Subject: [PATCH 81/98] 4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 511c8787..dfc98558 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "sqlite", "mongodb" ], - "version": "3.2.4", + "version": "4.0.0", "license": "MIT", "homepage": "http://dresende.github.io/node-orm2", "repository": "http://github.com/dresende/node-orm2.git", From 4729f591266980d28a6b81cae16bb4148597060e Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:07:37 +0300 Subject: [PATCH 82/98] fix after review --- Readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 7e01b49c..96526d9e 100755 --- a/Readme.md +++ b/Readme.md @@ -100,7 +100,7 @@ orm.connect("mysql://username:password@host/database", function (err, db) { - Read documentation about [bluebird](http://bluebirdjs.com/docs/api-reference.html) `Promise` for more advanced knowledge how to use `Promises`. -###Connect +### Connect The connection URL has the following syntax: `driver://username:password@hostname/database?option1=value1&option2=value2..` @@ -146,7 +146,7 @@ orm.connectAsync(opts) ``` ------- -###Model Hooks +### Model Hooks If you want to listen for a type of event than occurs in instances of a Model, you can attach a function that will be called when that event happens. For each hook above implemented Promise support, with backward capability via use next callback. @@ -185,7 +185,7 @@ var Person = db.define("person", { }); ``` ------- -###Editing Syncing and dropping models +### Editing Syncing and dropping models Syncing is an utility method that creates all the necessary tables in the database for your models and associations to work. Tables are not replaced, they are only created if they don't exist. There are 2 ways of syncing: @@ -214,7 +214,7 @@ orm.connectAsync("....") }); ``` ------- -###Finding items +### Finding items #### findAsync Find records with matching criteria, can be chained (see below): @@ -299,7 +299,7 @@ Person.findAsync({id: [1, 2]}) }); ``` ------- -###Creating and Updating Items +### Creating and Updating Items #### createAsync ```javascript var newRecord = {}; @@ -329,7 +329,7 @@ Person.createAsync(newRecord) }); ``` ------- -###Aggregation +### Aggregation If you need to get some aggregated values from a Model, you can use `Model.aggregate()`. Here's an example to better illustrate: From fda27876c5f8ab94cbd9756307c2616071361323 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:09:13 +0300 Subject: [PATCH 83/98] fix after review --- Readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Readme.md b/Readme.md index 96526d9e..7a2dc467 100755 --- a/Readme.md +++ b/Readme.md @@ -173,15 +173,15 @@ var Person = db.define("person", { surname : String }, { hooks: { - beforeCreate: function () { - return new Promise(function(resolve, reject) { - if (this.surname == "Doe") { - return reject(new Error("No Does allowed")); - } + beforeCreate: function () { + return new Promise(function(resolve, reject) { + if (this.surname == "Doe") { + return reject(new Error("No Does allowed")); + } return resolve(); - }); - } - } + }); + } + } }); ``` ------- From eb49dc8378b49cca23072cb01403e8099eaa206c Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:10:56 +0300 Subject: [PATCH 84/98] fix after review --- Readme.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Readme.md b/Readme.md index 7a2dc467..8ba49c65 100755 --- a/Readme.md +++ b/Readme.md @@ -169,19 +169,19 @@ Here's an example: ```js var Person = db.define("person", { - name : String, - surname : String + name : String, + surname : String }, { - hooks: { - beforeCreate: function () { - return new Promise(function(resolve, reject) { - if (this.surname == "Doe") { - return reject(new Error("No Does allowed")); - } - return resolve(); - }); - } + hooks: { + beforeCreate: function () { + return new Promise(function(resolve, reject) { + if (this.surname == "Doe") { + return reject(new Error("No Does allowed")); } + return resolve(); + }); + } + } }); ``` ------- From 031075a6fe28c27e33f630a2f6ac3f135230031f Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:32:05 +0300 Subject: [PATCH 85/98] add deprecation message for fail and success --- lib/ChainFind.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index f3f050f5..6234466c 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -223,6 +223,20 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, + success: function (cb) { // deprecated use original Promise + if (!promise) { + promise = new Promise(); + promise.handle(this.all); + } + return promise.success(cb); + }, + fail: function (cb) { // deprecated use original Promise + if (!promise) { + promise = new Promise(); + promise.handle(this.all); + } + return promise.fail(cb); + }, eager: function () { // This will allow params such as ("abc", "def") or (["abc", "def"]) var associations = _.flatten(arguments); From 24d6102db05f03b1041cc67094c803abb9eaffbf Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:37:17 +0300 Subject: [PATCH 86/98] add deprecation log message --- lib/ChainFind.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 6234466c..5e205182 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -224,6 +224,7 @@ function ChainFind(Model, opts) { return this; }, success: function (cb) { // deprecated use original Promise + console.warn("ChainFind.success() function is deprecated & will be removed in a future version"); if (!promise) { promise = new Promise(); promise.handle(this.all); @@ -231,6 +232,7 @@ function ChainFind(Model, opts) { return promise.success(cb); }, fail: function (cb) { // deprecated use original Promise + console.warn("ChainFind.fail() function is deprecated & will be removed in a future version"); if (!promise) { promise = new Promise(); promise.handle(this.all); From 7ec05d1cd14775f599a151a82648ac1718bbf953 Mon Sep 17 00:00:00 2001 From: mradko Date: Mon, 18 Sep 2017 11:44:18 +0300 Subject: [PATCH 87/98] add deprecated promise backward capability --- lib/ChainFind.js | 13 +++++++------ lib/{Promise.js => DeprecatedPromise.js} | 0 2 files changed, 7 insertions(+), 6 deletions(-) rename lib/{Promise.js => DeprecatedPromise.js} (100%) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 5e205182..671732d4 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -3,6 +3,7 @@ var async = require("async"); var Utilities = require("./Utilities"); var ChainInstance = require("./ChainInstance"); var Promise = require("bluebird"); +var DeprecatedPromise = require("./DeprecatedPromise").Promise; module.exports = ChainFind; @@ -87,8 +88,8 @@ function ChainFind(Model, opts) { return completeFn(null, items); }); }); - } - + }; + var promise = null; var chain = { find: function () { var cb = null; @@ -223,18 +224,18 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, - success: function (cb) { // deprecated use original Promise + success: function (cb) { console.warn("ChainFind.success() function is deprecated & will be removed in a future version"); if (!promise) { - promise = new Promise(); + promise = new DeprecatedPromise(); promise.handle(this.all); } return promise.success(cb); }, - fail: function (cb) { // deprecated use original Promise + fail: function (cb) { console.warn("ChainFind.fail() function is deprecated & will be removed in a future version"); if (!promise) { - promise = new Promise(); + promise = new DeprecatedPromise(); promise.handle(this.all); } return promise.fail(cb); diff --git a/lib/Promise.js b/lib/DeprecatedPromise.js similarity index 100% rename from lib/Promise.js rename to lib/DeprecatedPromise.js From 1934b1546f841416a9718a382c1195e0dd06e13b Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 18 Sep 2017 15:02:50 +0300 Subject: [PATCH 88/98] Fixed failed mysql tests --- test/integration/association-hasone-reverse-async.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js index 6d10d79b..c393855e 100644 --- a/test/integration/association-hasone-reverse-async.js +++ b/test/integration/association-hasone-reverse-async.js @@ -181,11 +181,11 @@ describe("hasOne Async", function () { return [John, Deco.setOwnersAsync(John)]; }) .spread(function (John, Deco) { - return [John, Person.findAsync({ pet: Deco })]; + return [John, Person.findAsync({ pet: Deco, id: John.id })]; }) .spread(function(John, owner){ - should.exist(owner[1]); - should.equal(owner[1].name, John.name); + should.exist(owner[0]); + should.equal(owner[0].name, John.name); }); }); @@ -205,7 +205,7 @@ describe("hasOne Async", function () { return [John, pets, pets[0].setOwnersAsync(John)]; }) .spread(function (John, pets) { - return [John, Person.findAsync({ pet: pets })]; + return [John, Person.findAsync({ pet: pets, id: John.id })]; }) .spread(function (John, owners) { should.exist(owners[0]); From c4e886df85a6fc8cafa895fe21c11538d8f448cc Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 18 Sep 2017 16:13:14 +0300 Subject: [PATCH 89/98] Fix hasone-zeroid-async test according to old test logic --- test/integration/association-hasone-zeroid-async.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/association-hasone-zeroid-async.js b/test/integration/association-hasone-zeroid-async.js index b872a99d..29f3777e 100644 --- a/test/integration/association-hasone-zeroid-async.js +++ b/test/integration/association-hasone-zeroid-async.js @@ -82,16 +82,16 @@ describe("hasOne promise-based methods", function() { return Pet .findAsync({petName: "Snagglepuss"}) .then(function(pets) { - pets[0].petName.should.equal("Snagglepuss"); - pets[0].should.have.property("id"); - pets[0].id.should.equal(11); + var pet = pets[0]; + pet.petName.should.equal("Snagglepuss"); + pet.should.have.property("id"); + pet.id.should.equal(11); return Person.allAsync(); }) .then(function (people) { should.equal(typeof people[0], 'object'); should.equal(Array.isArray(people), true); - people[0].should.have.property("firstName", "Stuey"); }); }); }); From 9e3ea6223e827ba08d64d02a93ce4f670fc16b62 Mon Sep 17 00:00:00 2001 From: Anton Kovylin Date: Mon, 18 Sep 2017 17:25:24 +0300 Subject: [PATCH 90/98] Fixed tests according to sync tests logic --- .../association-hasone-reverse-async.js | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/integration/association-hasone-reverse-async.js b/test/integration/association-hasone-reverse-async.js index c393855e..a15bd675 100644 --- a/test/integration/association-hasone-reverse-async.js +++ b/test/integration/association-hasone-reverse-async.js @@ -142,23 +142,25 @@ describe("hasOne Async", function () { it("should be able to find given an association id", function () { return Person .findAsync({ name: "John Doe" }) - .then(function (John) { + .then(function (persons) { + var John = persons[0]; should.exist(John); return [John, Pet.findAsync({ name: "Deco" })]; }) .spread(function (John, Deco) { + var Deco = Deco[0]; should.exist(Deco); - return [John[0], Deco[0], Deco[0].hasOwnersAsync()]; + return [John, Deco, Deco.hasOwnersAsync()]; }) .spread(function (John, Deco, has_owner) { has_owner.should.equal(false); - return [John, Deco.setOwnersAsync(John)]; + return [John, Deco, Deco.setOwnersAsync(John)]; }) - .spread(function (John) { - return [John, Person.findAsync({ pet_id: 1 })]; + .spread(function (John, Deco) { + return [John, Person.findAsync({ pet_id: Deco[Pet.id] })]; }) .spread(function (John, owner) { - should.exist(owner[0]); + should.exist(owner); should.equal(owner[0].name, John.name); }); }); @@ -178,10 +180,10 @@ describe("hasOne Async", function () { }) .spread(function (John, Deco, has_owner) { has_owner.should.equal(false); - return [John, Deco.setOwnersAsync(John)]; + return [John, Deco, Deco.setOwnersAsync(John)]; }) .spread(function (John, Deco) { - return [John, Person.findAsync({ pet: Deco, id: John.id })]; + return [John, Person.findAsync({ pet: Deco })]; }) .spread(function(John, owner){ should.exist(owner[0]); @@ -205,7 +207,7 @@ describe("hasOne Async", function () { return [John, pets, pets[0].setOwnersAsync(John)]; }) .spread(function (John, pets) { - return [John, Person.findAsync({ pet: pets, id: John.id })]; + return [John, Person.findAsync({ pet: pets })]; }) .spread(function (John, owners) { should.exist(owners[0]); From ffdaba7e8d09e2fccec06ad61e57a09b1ef41224 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 10:20:53 +0300 Subject: [PATCH 91/98] skip eagerQuery test for redshift --- test/integration/association-hasmany-hooks.js | 2 +- test/integration/db.js | 3 ++- test/integration/property-number-size.js | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/association-hasmany-hooks.js b/test/integration/association-hasmany-hooks.js index fa201d3f..2eeb9607 100644 --- a/test/integration/association-hasmany-hooks.js +++ b/test/integration/association-hasmany-hooks.js @@ -130,7 +130,7 @@ describe("hasMany hooks", function() { setTimeout(function () { had_extra = (typeof extra == "object"); resolve() - }, 1000); + }, 3000); }); } } diff --git a/test/integration/db.js b/test/integration/db.js index c7dd2ca8..016824b1 100644 --- a/test/integration/db.js +++ b/test/integration/db.js @@ -37,7 +37,7 @@ describe("db.driver", function () { should.exist(db.driver); }); - if (common.protocol() == "mongodb") return; + if (common.protocol() === "mongodb") return; describe("query", function () { it("should be available", function () { @@ -63,6 +63,7 @@ describe("db.driver", function () { }); describe('#eagerQuery', function () { + if (common.protocol() === "redshift") return; var fixture = { association: { model: { diff --git a/test/integration/property-number-size.js b/test/integration/property-number-size.js index b4b3e2dd..bf175518 100644 --- a/test/integration/property-number-size.js +++ b/test/integration/property-number-size.js @@ -1,7 +1,6 @@ var should = require('should'); var common = require('../common'); var helper = require('../support/spec_helper'); -var ORM = require('../../'); var protocol = common.protocol().toLowerCase(); // Round because different systems store floats in different From a6e7037e949da6eb4f455512920bf98b049c3ab5 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 12:22:54 +0300 Subject: [PATCH 92/98] fix code duplication --- lib/Associations/Extend.js | 31 ++++++++++------------------ lib/Associations/Many.js | 41 +++++++++---------------------------- lib/Associations/One.js | 31 ++++++++++------------------ lib/ChainFind.js | 10 +++++---- lib/Instance.js | 26 +++++++++-------------- lib/LazyLoad.js | 42 +++++++++++++++++++++++--------------- lib/ORM.js | 5 +++-- 7 files changed, 77 insertions(+), 109 deletions(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index 51eae7ac..920ac619 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -5,6 +5,8 @@ var Singleton = require("../Singleton"); var util = require("../Utilities"); var Promise = require("bluebird"); +var ACCESSOR_METHODS = ["hasAccessor", "getAccessor", "setAccessor", "delAccessor"]; + exports.prepare = function (db, Model, associations) { Model.extendsTo = function (name, properties, opts) { opts = opts || {}; @@ -207,25 +209,14 @@ function extendInstance(Model, Instance, Driver, association, opts) { enumerable : false }); - Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { - value : Promise.promisify(Instance[association.hasAccessor]), - enumerable : false - }); - - Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.getAccessor]), - enumerable : false - }); - - Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { - value : Promise.promisify(Instance[association.setAccessor]), - enumerable : false - }); - - Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { - value : Promise.promisify(Instance[association.delAccessor]), - enumerable : false - }); + for (var i = 0; i < ACCESSOR_METHODS.length; i++) { + var name = ACCESSOR_METHODS[i]; + Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association[name]]), + enumerable: false, + writable: true + }); + } } function autoFetchInstance(Instance, association, opts, cb) { @@ -233,7 +224,7 @@ function autoFetchInstance(Instance, association, opts, cb) { return cb(); } - if (!opts.hasOwnProperty("autoFetchLimit") || typeof opts.autoFetchLimit == "undefined") { + if (!opts.hasOwnProperty("autoFetchLimit") || typeof opts.autoFetchLimit === "undefined") { opts.autoFetchLimit = association.autoFetchLimit; } diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index c6472ae5..deec72d5 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -1,5 +1,4 @@ var _ = require("lodash"); -var InstanceConstructor = require("../Instance").Instance; var Hook = require("../Hook"); var Settings = require("../Settings"); var Property = require("../Property"); @@ -7,6 +6,8 @@ var ORMError = require("../Error"); var util = require("../Utilities"); var Promise = require("bluebird"); +var ACCESSOR_METHODS = ["hasAccessor", "getAccessor", "setAccessor", "delAccessor", "addAccessor"]; + exports.prepare = function (db, Model, associations) { Model.hasMany = function () { var promiseFunctionPostfix = Model.settings.get('promiseFunctionPostfix'); @@ -491,36 +492,14 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: true }); - Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.hasAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.getAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.setAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.delAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.addAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.addAccessor]), - enumerable: false, - writable: true - }); - + for (var i = 0; i < ACCESSOR_METHODS.length; i++) { + var name = ACCESSOR_METHODS[i]; + Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association[name]]), + enumerable: false, + writable: true + }); + } } function autoFetchInstance(Instance, association, opts, cb) { diff --git a/lib/Associations/One.js b/lib/Associations/One.js index 48a48872..b0c429cc 100644 --- a/lib/Associations/One.js +++ b/lib/Associations/One.js @@ -4,6 +4,8 @@ var ORMError = require("../Error"); var Accessors = { "get": "get", "set": "set", "has": "has", "del": "remove" }; var Promise = require("bluebird"); +var ACCESSOR_METHODS = ["hasAccessor", "getAccessor", "setAccessor", "delAccessor"]; + exports.prepare = function (Model, associations) { Model.hasOne = function () { var assocName; @@ -266,6 +268,7 @@ function extendInstance(Model, Instance, Driver, association) { enumerable: false, writable: true }); + if (!association.reversed) { Object.defineProperty(Instance, association.delAccessor, { value: function (cb) { @@ -287,31 +290,19 @@ function extendInstance(Model, Instance, Driver, association) { enumerable: false, writable: true }); + } + + for (var i = 0; i < ACCESSOR_METHODS.length; i++) { + var name = ACCESSOR_METHODS[i]; + + if (name === "delAccessor" && !Instance[association.delAccessor]) continue; - Object.defineProperty(Instance, association.delAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.delAccessor]), + Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association[name]]), enumerable: false, writable: true }); } - - Object.defineProperty(Instance, association.hasAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.hasAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.getAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.getAccessor]), - enumerable: false, - writable: true - }); - - Object.defineProperty(Instance, association.setAccessor + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association.setAccessor]), - enumerable: false, - writable: true - }); } function autoFetchInstance(Instance, association, opts, cb) { diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 671732d4..310e2538 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -224,19 +224,21 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, + promiseAssign: function () { + promise = new DeprecatedPromise(); + promise.handle(this.all); + }, success: function (cb) { console.warn("ChainFind.success() function is deprecated & will be removed in a future version"); if (!promise) { - promise = new DeprecatedPromise(); - promise.handle(this.all); + chain.promiseAssign(); } return promise.success(cb); }, fail: function (cb) { console.warn("ChainFind.fail() function is deprecated & will be removed in a future version"); if (!promise) { - promise = new DeprecatedPromise(); - promise.handle(this.all); + chain.promiseAssign(); } return promise.fail(cb); }, diff --git a/lib/Instance.js b/lib/Instance.js index 376cbc61..69343ca7 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -6,6 +6,8 @@ var Promise = require("bluebird"); exports.Instance = Instance; +var INSTNCE_METHOD_NAMES = ["save", "remove", "validate"]; + function Instance(Model, opts) { opts = opts || {}; opts.data = opts.data || {}; @@ -703,22 +705,14 @@ function Instance(Model, opts) { enumerable: false }); - Object.defineProperty(instance, 'save' + promiseFunctionPostfix, { - value: Promise.promisify(instance['save']), - enumerable: false, - writable: true - }); - Object.defineProperty(instance, 'remove' + promiseFunctionPostfix, { - value: Promise.promisify(instance['remove']), - enumerable: false, - writable: true - }); - - Object.defineProperty(instance, 'validate' + promiseFunctionPostfix, { - value: Promise.promisify(instance['validate']), - enumerable: false, - writable: true - }); + for (var k = 0; k < INSTNCE_METHOD_NAMES.length; k++) { + var name = INSTNCE_METHOD_NAMES[k]; + Object.defineProperty(instance, name + promiseFunctionPostfix, { + value: Promise.promisify(instance[name]), + enumerable: false, + writable: true + }); + } for (i = 0; i < opts.keyProperties.length; i++) { var prop = opts.keyProperties[i]; diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index a81e2624..74a618f5 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -1,18 +1,20 @@ var Promise = require("bluebird"); +var LAZY_METHOD_NAMES = ["get", "remove", "set"]; + var extend = function (Instance, Model, properties) { for (var k in properties) { if (properties[k].lazyload === true) { addLazyLoadProperty(properties[k].lazyname || k, Instance, Model, k); } } -} +}; var conditionAssign = function (instance, model) { var conditions = {}; conditions[model.id] = instance[model.id]; return conditions; -} +}; function addLazyLoadProperty(name, Instance, Model, property) { var method = ucfirst(name); @@ -30,7 +32,7 @@ function addLazyLoadProperty(name, Instance, Model, property) { callback : "set" + method, promise : "set" + method + promiseFunctionPostfix } - } + }; Object.defineProperty(Instance, functionNames.get.callback, { value: function (cb) { @@ -89,20 +91,28 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); - Object.defineProperty(Instance, functionNames.get.promise, { - value: Promise.promisify(Instance[functionNames.get.callback]), - enumerable: false - }); - - Object.defineProperty(Instance, functionNames.remove.promise, { - value: Promise.promisify(Instance[functionNames.remove.callback]), - enumerable: false - }); + for(var i = 0; i < LAZY_METHOD_NAMES.length; i++) { + var name = LAZY_METHOD_NAMES[i]; + Object.defineProperty(Instance, functionNames[name].promise, { + value: Promise.promisify(Instance[functionNames[name].callback]), + enumerable: false + }); + } - Object.defineProperty(Instance, functionNames.set.promise, { - value: Promise.promisify(Instance[functionNames.set.callback]), - enumerable: false - }); + // Object.defineProperty(Instance, functionNames.get.promise, { + // value: Promise.promisify(Instance[functionNames.get.callback]), + // enumerable: false + // }); + // + // Object.defineProperty(Instance, functionNames.remove.promise, { + // value: Promise.promisify(Instance[functionNames.remove.callback]), + // enumerable: false + // }); + // + // Object.defineProperty(Instance, functionNames.set.promise, { + // value: Promise.promisify(Instance[functionNames.set.callback]), + // enumerable: false + // }); } function ucfirst(text) { diff --git a/lib/ORM.js b/lib/ORM.js index 176f1bd2..42641863 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -331,9 +331,10 @@ ORM.prototype.loadAsync = function () { // Due to intricacies of `Utilities.getRealPath` the following // code has to look as it does. - for(var a = 0; a < files.length; a++) { + for(var i = 0; i < files.length; i++) { + var file = files[i]; filesWithPath.push(function () { - return Utilities.getRealPath(files[a], 4) + return Utilities.getRealPath(file, 4) }()); } From ea923e35be06a1103348ed8f15016df019984f56 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 12:25:16 +0300 Subject: [PATCH 93/98] fix code duplication --- lib/LazyLoad.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 74a618f5..9bc30ca5 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -98,21 +98,6 @@ function addLazyLoadProperty(name, Instance, Model, property) { enumerable: false }); } - - // Object.defineProperty(Instance, functionNames.get.promise, { - // value: Promise.promisify(Instance[functionNames.get.callback]), - // enumerable: false - // }); - // - // Object.defineProperty(Instance, functionNames.remove.promise, { - // value: Promise.promisify(Instance[functionNames.remove.callback]), - // enumerable: false - // }); - // - // Object.defineProperty(Instance, functionNames.set.promise, { - // value: Promise.promisify(Instance[functionNames.set.callback]), - // enumerable: false - // }); } function ucfirst(text) { From a3a43139ef499de5ee610aaa12a3828f2203f2c8 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 12:48:46 +0300 Subject: [PATCH 94/98] fix if statement --- lib/Associations/Extend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index 920ac619..66ad350f 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -224,7 +224,7 @@ function autoFetchInstance(Instance, association, opts, cb) { return cb(); } - if (!opts.hasOwnProperty("autoFetchLimit") || typeof opts.autoFetchLimit === "undefined") { + if (!opts.hasOwnProperty("autoFetchLimit") || !opts.autoFetchLimit) { opts.autoFetchLimit = association.autoFetchLimit; } From 3fae37a957bff35a6d8ccf5f01b8d99b60897357 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 13:31:17 +0300 Subject: [PATCH 95/98] fix duplication --- lib/Associations/Many.js | 8 ++++---- lib/ChainFind.js | 12 +++++------- lib/Instance.js | 4 ++-- lib/LazyLoad.js | 6 +++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/Associations/Many.js b/lib/Associations/Many.js index deec72d5..adb7b1ff 100644 --- a/lib/Associations/Many.js +++ b/lib/Associations/Many.js @@ -492,10 +492,10 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan enumerable: true }); - for (var i = 0; i < ACCESSOR_METHODS.length; i++) { - var name = ACCESSOR_METHODS[i]; - Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { - value: Promise.promisify(Instance[association[name]]), + for (var y = 0; y < ACCESSOR_METHODS.length; y++) { + var accessorMethodName = ACCESSOR_METHODS[y]; + Object.defineProperty(Instance, association[accessorMethodName] + promiseFunctionPostfix, { + value: Promise.promisify(Instance[association[accessorMethodName]]), enumerable: false, writable: true }); diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 310e2538..99985839 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -224,22 +224,20 @@ function ChainFind(Model, opts) { chainRun(cb); return this; }, - promiseAssign: function () { - promise = new DeprecatedPromise(); - promise.handle(this.all); - }, success: function (cb) { console.warn("ChainFind.success() function is deprecated & will be removed in a future version"); if (!promise) { - chain.promiseAssign(); + promise = new DeprecatedPromise(); + promise.handle(this.all); } return promise.success(cb); }, fail: function (cb) { - console.warn("ChainFind.fail() function is deprecated & will be removed in a future version"); if (!promise) { - chain.promiseAssign(); + promise = new DeprecatedPromise(); + promise.handle(this.all); } + console.warn("ChainFind.fail() function is deprecated & will be removed in a future version"); return promise.fail(cb); }, eager: function () { diff --git a/lib/Instance.js b/lib/Instance.js index 69343ca7..e0778fda 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -705,8 +705,8 @@ function Instance(Model, opts) { enumerable: false }); - for (var k = 0; k < INSTNCE_METHOD_NAMES.length; k++) { - var name = INSTNCE_METHOD_NAMES[k]; + for (var j = 0; j < INSTNCE_METHOD_NAMES.length; j++) { + var name = INSTNCE_METHOD_NAMES[j]; Object.defineProperty(instance, name + promiseFunctionPostfix, { value: Promise.promisify(instance[name]), enumerable: false, diff --git a/lib/LazyLoad.js b/lib/LazyLoad.js index 9bc30ca5..d5bd78b1 100644 --- a/lib/LazyLoad.js +++ b/lib/LazyLoad.js @@ -92,9 +92,9 @@ function addLazyLoadProperty(name, Instance, Model, property) { }); for(var i = 0; i < LAZY_METHOD_NAMES.length; i++) { - var name = LAZY_METHOD_NAMES[i]; - Object.defineProperty(Instance, functionNames[name].promise, { - value: Promise.promisify(Instance[functionNames[name].callback]), + var methodName = LAZY_METHOD_NAMES[i]; + Object.defineProperty(Instance, functionNames[methodName].promise, { + value: Promise.promisify(Instance[functionNames[methodName].callback]), enumerable: false }); } From b03cea7717736897836b8b0b8ebe9295f23e5234 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 14:06:46 +0300 Subject: [PATCH 96/98] fix indentation --- lib/ChainFind.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 99985839..a4df9574 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -1,8 +1,8 @@ -var _ = require("lodash"); -var async = require("async"); -var Utilities = require("./Utilities"); -var ChainInstance = require("./ChainInstance"); -var Promise = require("bluebird"); +var _ = require("lodash"); +var async = require("async"); +var Utilities = require("./Utilities"); +var ChainInstance = require("./ChainInstance"); +var Promise = require("bluebird"); var DeprecatedPromise = require("./DeprecatedPromise").Promise; module.exports = ChainFind; From dc137314a1bcc2257f864e453ecc817cba455237 Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 14:26:20 +0300 Subject: [PATCH 97/98] fix indentation --- lib/Associations/Extend.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Associations/Extend.js b/lib/Associations/Extend.js index 66ad350f..99a520cb 100644 --- a/lib/Associations/Extend.js +++ b/lib/Associations/Extend.js @@ -211,7 +211,8 @@ function extendInstance(Model, Instance, Driver, association, opts) { for (var i = 0; i < ACCESSOR_METHODS.length; i++) { var name = ACCESSOR_METHODS[i]; - Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { + var asyncName = association[name] + promiseFunctionPostfix; + Object.defineProperty(Instance, asyncName, { value: Promise.promisify(Instance[association[name]]), enumerable: false, writable: true From b9809a367396cb79bcf37bcc2e1e65096ba00f4c Mon Sep 17 00:00:00 2001 From: mradko Date: Tue, 19 Sep 2017 14:32:27 +0300 Subject: [PATCH 98/98] fix duplication --- lib/Associations/One.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Associations/One.js b/lib/Associations/One.js index b0c429cc..e9a3d3f8 100644 --- a/lib/Associations/One.js +++ b/lib/Associations/One.js @@ -294,10 +294,10 @@ function extendInstance(Model, Instance, Driver, association) { for (var i = 0; i < ACCESSOR_METHODS.length; i++) { var name = ACCESSOR_METHODS[i]; + var asyncNameAccessorName = association[name] + promiseFunctionPostfix; if (name === "delAccessor" && !Instance[association.delAccessor]) continue; - - Object.defineProperty(Instance, association[name] + promiseFunctionPostfix, { + Object.defineProperty(Instance, asyncNameAccessorName, { value: Promise.promisify(Instance[association[name]]), enumerable: false, writable: true