Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into pubsub-support

Conflicts:
	lib/nohm.js
	test/tests.js
  • Loading branch information...
commit 90955b447f5c868ecea3f5ff81811da5789a5e67 2 parents fa1469d + 7b35536
@maritz authored
View
6 HISTORY.md
@@ -1,3 +1,9 @@
+### v0.7.2 - 2012-01-19
+ - add .sort()
+
+### v0.7.1
+ - Fix unique handling bugs
+
### v0.7.0
- BREAKS BACKWARDS COMPATIBILTY! change validate() to be async only (validations all need to be async now), also changed validation usage syntax (see docs)
- add nohm.connect() connect middleware that delivers browser validation js
View
90 README.md
@@ -4,10 +4,11 @@
Nohm is an object relational mapper (ORM) written for node.js and redis.
-## Install
-### If you haven't done this yet: install npm
+## Requirements
+
+* redis >= 2.4
- curl http://npmjs.org/install.sh | sh
+## Install
### Installing nohm
@@ -18,7 +19,88 @@ http://maritz.github.com/nohm/
## Examples
-* [nohm/examples/rest-user-server](https://github.com/maritz/nohm/tree/master/examples/rest-user-server) very basic user example (needs express)
+~~~~ javascript
+ var nohm = require('nohm').Nohm;
+ var redis = require('redis').createClient();
+
+ nohm.setClient(redis);
+
+ nohm.model('User', {
+ properties: {
+ name: {
+ type: 'string',
+ unique: true,
+ validations: [
+ 'notEmpty'
+ ]
+ },
+ email: {
+ type: 'string',
+ unique: true,
+ validations: [
+ 'email'
+ ]
+ },
+ country: {
+ type: 'string',
+ defaultValue: 'Tibet',
+ validations: [
+ 'notEmpty'
+ ]
+ },
+ visits: {
+ type: function incrVisitsBy(value, key, old) {
+ return old + value;
+ },
+ defaultValue: 0,
+ index: true
+ }
+ },
+ methods: {
+ getContryFlag: function () {
+ return 'http://example.com/flag_'+this.p('country')+'.png';
+ },
+ }
+ });
+
+ var user = nohm.factory('User');
+ user.p({
+ name: 'Mark',
+ email: 'mark@example.com',
+ country: 'Mexico',
+ visits: 1
+ });
+ user.save(function (err) {
+ if (err === 'invalid') {
+ console.log('properties were invalid: ', user.errors);
+ } else if (err) {
+ console.log(err); // database or unknown error
+ } else {
+ console.log('saved user! :-)');
+ user.remove(function (err) {
+ if (err) {
+ console.log(err); // database or unknown error
+ } else {
+ console.log('successfully removed user');
+ }
+ });
+ }
+ });
+
+ // try to load a user from the db
+ var otherUser = nohm.factory('User', 522, function (err) {
+ if (err === 'not found') {
+ console.log('no user with id 522 found :-(');
+ } else if (err) {
+ console.log(err); // database or unknown error
+ } else {
+ console.log(otherUser.allProperties());
+ }
+ });
+~~~~
+
+
+* [nohm/examples/rest-user-server](https://github.com/maritz/nohm/tree/master/examples/rest-user-server) (needs express)
* [Beauvoir](https://github.com/yuchi/Beauvoir) Simple project management app - by yuchi
Do you have code that should/could be listed here? Message me!
View
54 lib/nohm.js
@@ -1,4 +1,5 @@
var h = require(__dirname + '/helpers');
+var async = require('async');
/**
* The Nohm object used for some general configuration and model creation.
@@ -59,10 +60,11 @@ Nohm.model = function (name, options) {
obj.prototype = new Nohm();
// this creates a few functions for short-form like: SomeModel.load(1, function (err, props) { /* `this` is someModelInstance here */ });
- Nohm.shortFormFuncs.forEach(function (val) {
+ var shortFormFuncs = ['load', 'find', 'save', 'sort', 'subscribe', 'subscribeOnce', 'unsubscribe'];
+ shortFormFuncs.forEach(function (val) {
obj[val] = function () {
var instance = new obj();
- instance[val].apply(instance, arguments);
+ instance[val].apply(instance, Array.prototype.slice.call(arguments, 0));
};
});
@@ -78,8 +80,6 @@ Nohm.model = function (name, options) {
return obj;
};
-Nohm.shortFormFuncs = ['load', 'find', 'save'];
-
/**
* Factory to produce instances of models
*
@@ -157,11 +157,13 @@ Nohm.setExtraValidations = function (files) {
files = [files];
}
files.forEach(function (path) {
- __extraValidators.push(path);
- var validators = require(path);
- Object.keys(validators).forEach(function (name) {
- Nohm.__validators[name] = validators[name];
- });
+ if (__extraValidators.indexOf(path) === -1) {
+ __extraValidators.push(path);
+ var validators = require(path);
+ Object.keys(validators).forEach(function (name) {
+ Nohm.__validators[name] = validators[name];
+ });
+ }
});
};
@@ -255,6 +257,40 @@ Nohm.prototype.init = function (name, options) {
this.__loaded = false;
};
+/**
+ * DO NOT USE THIS UNLESS YOU ARE ABSOLUTELY SURE ABOUT IT!
+ *
+ * Deletes any keys from the db that start with nohm prefixes.
+ *
+ * DO NOT USE THIS UNLESS YOU ARE ABSOLUTELY SURE ABOUT IT!
+ *
+ * @param {Object} [redis] You can specify the redis client to use. Default: Nohm.client
+ * @param {Function} [callback] Called after all keys are deleted.
+ */
+Nohm.purgeDb = function (redis, callback) {
+ callback = h.getCallback(arguments);
+ redis = typeof(redis) !== 'function' || Nohm.client;
+ var delKeys = function (prefix, next) {
+ redis.keys(prefix+'*', function (err, keys) {
+ if (err || keys.length === 0) {
+ next(err);
+ } else {
+ keys.push(next);
+ redis.del.apply(redis, keys);
+ }
+ });
+ };
+ var deletes = [];
+
+ Object.keys(Nohm.prefix).forEach(function (key) {
+ deletes.push(async.apply(delKeys, Nohm.prefix[key]));
+ });
+
+ async.series(deletes, function (err) {
+ callback(err);
+ });
+};
+
var moduleNames = ['properties', 'retrieve', 'validation', 'store', 'relations', 'connectMiddleware', 'pubsub'],
modules = {};
View
4 lib/pubsub.js
@@ -231,9 +231,5 @@ var initialize = function () {
});
};
- Nohm.shortFormFuncs.push(
- 'subscribe', 'subscribeOnce', 'unsubscribe', 'unsubscribeAll'
- );
-
};
View
145 lib/retrieve.js
@@ -16,6 +16,15 @@ exports.exists = function (id, callback) {
});
};
+var convertIdsToInt = function (ids, callback) {
+ if (this.idGenerator === 'increment' && Array.isArray(ids)) {
+ ids = ids.map(function (val) {
+ return parseInt(val, 10);
+ });
+ }
+ callback(ids);
+}
+
/**
* Retrieves the hash data by id and puts it into the properties.
*/
@@ -71,8 +80,12 @@ exports.find = function find(searches, callback) {
found.push(val);
}
});
+ } else if ( ! Array.isArray(values)) {
+ found = [values];
}
- callback.call(self, err, found);
+ convertIdsToInt(found, function (ids) {
+ callback.call(self, err, ids);
+ });
},
getSets = function (callback) {
self.getClient().sinter(sets, callback);
@@ -82,12 +95,6 @@ exports.find = function find(searches, callback) {
if (err) {
callback(err);
} else {
- values.forEach(function (val, key) {
- if (self.idGenerator === 'increment') {
- var parsed = parseInt(val, 10);
- if (!isNaN(parsed)) values[key] = parsed;
- }
- });
callback(null, values);
}
};
@@ -109,8 +116,8 @@ exports.find = function find(searches, callback) {
},
getZSets = function (callback) {
async.map(zsetKeys, getSingleZSet, function done (err, arr) {
- var test = h.idIntersection.apply(null, arr);
- callback(null, test.sort());
+ var ids = h.idIntersection.apply(null, arr);
+ callback(err, ids);
});
};
@@ -126,11 +133,10 @@ exports.find = function find(searches, callback) {
if ( ! searches[s].toLowerCase) {
return returnFunction('Invalid search parameters: Searching for a unique with a non-string value is not supported.');
}
- return this.getClient().mget(Nohm.prefix.unique + self.modelName + ':' + s +
- ':' + searches[s].toLowerCase(),
- returnFunction);
+ var key = Nohm.prefix.unique+self.modelName+':'+s+':'+searches[s].toLowerCase();
+ return this.getClient().get([key], returnFunction);
}
- var isNum = !isNaN(parseInt(searches[s], 10));
+ var isNum = ! isNaN(parseInt(searches[s], 10));
if (prop.index && ( ! prop.__numericIndex || isNum) ) {
sets.push(Nohm.prefix.index + self.modelName + ':' + s + ':' + searches[s]);
} else if (prop.__numericIndex) {
@@ -143,25 +149,13 @@ exports.find = function find(searches, callback) {
}
if (sets.length === 0 && zsetKeys.length === 0) {
// no specific searches, retrieve all ids
- this.getClient().smembers(Nohm.prefix.idsets + this.modelName, function (err, ids) {
- if (self.idGenerator === 'increment' && Array.isArray(ids)) {
- ids = ids.map(function (val) {
- return parseInt(val, 10);
- });
- }
- returnFunction(err, ids);
- });
+ this.getClient().smembers(Nohm.prefix.idsets + this.modelName, returnFunction);
} else if (zsetKeys.length === 0) {
getSets(returnFunction);
} else if (sets.length === 0) {
getZSets(returnFunction);
} else {
getSets(function (err, setids) {
- if (self.idGenerator === 'increment' && Array.isArray(setids)) {
- setids = setids.map(function (val) {
- return parseInt(val.toString(), 10);
- });
- }
getZSets(function (err2, zsetids) {
if (err2) {
err = [err, err2];
@@ -171,3 +165,102 @@ exports.find = function find(searches, callback) {
});
}
};
+
+exports.sort = function (options, ids) {
+ var callback = h.getCallback(arguments);
+ if ( ! Array.isArray(ids) || ids.length === 0) {
+ ids = false;
+ }
+ options = typeof(options) !== 'function' && typeof(options) === 'object' && Object.keys(options).length > 0 ? options : {};
+
+ if (ids.length > 0 && options === {}) {
+ return callback(ids.sort());
+ }
+
+ if ( ! options.field || ! this.properties.hasOwnProperty(options.field)) {
+ callback('invalid field in options', ids);
+ return Nohm.logError('Invalid field in sort() options: ' + options.field);
+ }
+
+ var field_type = this.properties[options.field].type;
+
+ var alpha = options.alpha || field_type === 'string' ? 'ALPHA' : '';
+ var direction = options.direction ? options.direction : 'ASC';
+ var scored = Nohm.indexNumberTypes.indexOf(field_type) !== -1;
+ var start = 0;
+ var stop = 100;
+ if (Array.isArray(options.limit) && options.limit.length > 0) {
+ start = options.limit[0];
+ if (scored) { // the limit arguments for sets and sorted sets work differently
+ // stop is a 0-based index from the start of all zset members
+ stop = options.limit[1] ? start+options.limit[1] : start+stop;
+ stop--;
+ } else {
+ // stop is a 1-based index from the defined start limit (the wanted behaviour)
+ stop = options.limit[1] || stop;
+ }
+ }
+ var idset_key = Nohm.prefix.idsets+this.modelName;
+ var zset_key = Nohm.prefix.scoredindex+this.modelName+':'+options.field;
+ var client = this.getClient();
+ var tmp_key;
+
+ if (ids) {
+ // to get the intersection of the given ids and all ids on the server we first
+ // temporarily store the given ids either in a set or sorted set and then return the intersection
+
+ client = client.multi();
+
+ if (scored) {
+ tmp_key = zset_key+':tmp_sort:'+(+ new Date()) + Math.ceil(Math.random()*1000);
+ var tmp_zadd_args = [tmp_key];
+ ids.forEach(function (id) {
+ tmp_zadd_args.push(0, id);
+ });
+ client.zadd(tmp_zadd_args);
+ client.zinterstore([tmp_key, 2, tmp_key, zset_key]);
+ zset_key = tmp_key;
+ } else {
+ tmp_key = idset_key+':tmp_sort:'+(+ new Date()) + Math.ceil(Math.random()*1000);
+ ids.unshift(tmp_key);
+ client.SADD(ids);
+ client.SINTERSTORE([tmp_key, tmp_key, idset_key]);
+ idset_key = tmp_key;
+ }
+ }
+ if (scored) {
+ sortScored.call(this, client, zset_key, direction, start, stop, callback);
+ } else {
+ sortNormal.call(this, client, idset_key, options.field, alpha, direction, start, stop, callback);
+ }
+ if (ids) {
+ client.del(tmp_key);
+ client.exec(Nohm.logError);
+ }
+};
+
+var sortNormal = function (client, idset_key, field, alpha, direction, start, stop, callback) {
+ var hash_key = Nohm.prefix.hash+this.modelName;
+ client.sort([idset_key,
+ 'BY', hash_key+':*->'+field,
+ 'LIMIT', start, stop,
+ direction,
+ alpha],
+ callback);
+};
+
+
+var sortScored = function (client, zset_key, direction, start, stop, callback) {
+ var method = direction && direction === 'DESC' ? 'ZREVRANGE' : 'ZRANGE';
+ if (start < 0 || stop < 0) {
+ Nohm.logError('Notice: tried to limit a scored sort with a negative start('+start+') or stop('+stop+').');
+ }
+ if (stop < start) {
+ Nohm.logError('Notice: tried to limit a scored sort with a higher start('+start+') than stop('+stop+').');
+ }
+ client[method](
+ [zset_key,
+ start, stop],
+ callback
+ );
+};
View
23 lib/validation.js
@@ -33,9 +33,14 @@ exports.valid = function valid(key, setDirectly) {
Nohm.logError('Some validation caused an error');
}
+ if ( ! validbool) {
+ // if others failed we must not set unique locks directly
+ setDirectly = false;
+ }
+
self.__checkUniques(setDirectly, function (success) {
- if (!success) {
+ if ( ! success) {
validbool = false;
}
callback(validbool);
@@ -60,9 +65,11 @@ exports.__checkUniques = function __checkUniques(setDirectly, saveCallback, p) {
(!p || propName === p) && // if all props are to be checked or the current one matches the 1
tmp[propName].value !== '' &&
(tmp[propName].__updated || !self.__inDB)) {
+ var propLower = self.p(propName).toLowerCase();
+ var unique_key = Nohm.prefix.unique + self.modelName + ':' + propName + ':' + propLower
var checkCallback = function (err, value) {
if (setDirectly && value) {
- tmpUniques.push(Nohm.prefix.unique + self.modelName + ':' + propName + ':' + self.p(propName));
+ tmpUniques.push(unique_key);
}
if (!setDirectly) {
// client.exists returns 1 if the value exists, client.setnx returns 1 if the value did not exist.
@@ -76,14 +83,13 @@ exports.__checkUniques = function __checkUniques(setDirectly, saveCallback, p) {
});
};
- var propLower = self.p(propName).toLowerCase();
if (setDirectly) {
/*
* We lock the unique value here if it's not locked yet, then later remove the old uniquelock when really saving it. (or we free the unique slot if we're not saving)
*/
- client.setnx(Nohm.prefix.unique + self.modelName + ':' + propName + ':' + propLower, self.id, checkCallback);
+ client.setnx(unique_key, self.id, checkCallback);
} else {
- client.exists(Nohm.prefix.unique + self.modelName + ':' + propName + ':' + propLower, checkCallback);
+ client.exists(unique_key, checkCallback);
}
} else {
callback(null, null);
@@ -125,10 +131,11 @@ exports.__setUniqueIds = function __setUniqueIds(id, cb) {
if (this.properties.hasOwnProperty(p) && this.properties[p].unique &&
this.properties[p].value !== '' &&
(this.properties[p].__updated || !this.__inDB)) {
- args.push(Nohm.prefix.unique + this.modelName + ':' + p + ':' + this.p(p));
+ args.push(Nohm.prefix.unique + this.modelName + ':' + p + ':' + this.p(p).toLowerCase());
args.push(id);
}
}
+
if (args.length > 0) {
this.getClient().mset(args, cb);
} else {
@@ -137,11 +144,11 @@ exports.__setUniqueIds = function __setUniqueIds(id, cb) {
};
/**
- * Validates a given property.
+ * Returns an array of functions that validate a given property.
*
* Important: Any changes here should proably be done in validators.js for the browser validation functions as well!
*/
-exports.__validateProperty = function __validateProperty(p, callback) {
+exports.__validateProperty = function __validateProperty(p) {
if (!p || !this.properties[p]) {
nohm.logError('Trying to validate undefined property or accessing __validateProperty without giving a property');
}
View
196 test/connectTests.js
@@ -1,5 +1,12 @@
-var Nohm = require(__dirname+'/../lib/nohm').Nohm;
-Nohm.model('UserConnectMockup', {
+var nohm = require(__dirname+'/../lib/nohm').Nohm;
+var args = require(__dirname+'/testArgs.js');
+var redis = args.redis;
+var h = require(__dirname+'/helper.js');
+var vm = require('vm');
+
+nohm.setExtraValidations(__dirname + '/custom_validations.js');
+
+nohm.model('UserConnectMockup', {
properties: {
name: {
type: 'string',
@@ -46,7 +53,7 @@ Nohm.model('UserConnectMockup', {
}
}
});
-Nohm.model('ExcludedConnectMockup', {
+nohm.model('ExcludedConnectMockup', {
properties: {
name: {
type: 'string',
@@ -57,7 +64,6 @@ Nohm.model('ExcludedConnectMockup', {
}
}
});
-var vm = require('vm');
var setup = function (t, expected, options, callback) {
t.expect(3+expected);
@@ -96,107 +102,121 @@ var setup = function (t, expected, options, callback) {
var url = (options && options.url) ? options.url : '/nohmValidations.js';
- Nohm.connect(options)({url: url }, dummyRes, function () {
+ nohm.connect(options)({url: url }, dummyRes, function () {
t.ok(false, 'Connect middleware called next with valid url.');
t.done();
});
};
-exports.connectNoOptions = function (t) {
-
- setup(t, 2, undefined, function (sandbox, str) {
- var val = sandbox.nohmValidations.models.UserConnectMockup;
- t.ok(val.name.indexOf('notEmpty') === 0, 'UserConnectMockup did not have the proper validations');
- t.same(val.name[1], [
- 'length', {
- min: 2
- }
- ], 'UserConnectMockup did not have the proper validations');
- t.done();
- });
-
-};
-
-exports.connectValidate = function (t) {
-
- setup(t, 2, undefined, function (sandbox) {
- var val = sandbox.nohmValidations.validate;
- val('UserConnectMockup', {name: 'asd', excludedProperty: 'asd', excludedValidation: 'asd'}, function (valid) {
- t.same(valid, true, 'Validate did not work as expected.');
-
- val('UserConnectMockup', {name: 'a', excludedProperty: '', excludedValidation: 'a'}, function (valid, errors) {
- t.same(errors, {name: ['length'], excludedProperty: ['notEmpty'], excludedValidation: ['length']}, 'Validate did not work as expected.');
- t.done();
- });
- });
- });
-};
-exports.connectOptions = function (t) {
+exports.connect = {
- setup(t, 1, {url: './nohm.js', namespace: 'hurgel'}, function (sandbox) {
- t.ok(sandbox.hurgel, 'Namespace option not successful');
- t.done();
- });
+ setUp: function (next) {
+ if ( ! nohm.client) {
+ nohm.setClient(redis);
+ }
+ next();
+ },
+ tearDown: function (next) {
+ h.cleanUp(redis, args.prefix, next);
+ },
-};
-
-exports.connectExtraFiles = function (t) {
- setup(t, 1, {extraFiles: __dirname+'/custom_validations2.js'}, function (sandbox) {
- sandbox.nohmValidations.validate('UserConnectMockup', {
- customValidationFile: 'NOPE',
- customValidationFileTimesTwo: 'NOPE'
- }, function (valid, errors) {
- t.same(errors, {
- customValidationFile: ['customValidationFile'],
- customValidationFileTimesTwo: ['customValidationFileTimesTwo']
- }, 'Validate did not work as expected.');
- t.done();
- });
- });
+ connectNoOptions: function (t) {
-};
+ setup(t, 2, undefined, function (sandbox, str) {
+ var val = sandbox.nohmValidations.models.UserConnectMockup;
+ t.ok(val.name.indexOf('notEmpty') === 0, 'UserConnectMockup did not have the proper validations');
+ t.same(val.name[1], [
+ 'length', {
+ min: 2
+ }
+ ], 'UserConnectMockup did not have the proper validations');
+ t.done();
+ });
+ },
-exports.connectExceptions = function (t) {
+ connectValidate: function (t) {
+
+ setup(t, 2, undefined, function (sandbox) {
+ var val = sandbox.nohmValidations.validate;
+ val('UserConnectMockup', {name: 'asd', excludedProperty: 'asd', excludedValidation: 'asd'}, function (valid) {
+ t.same(valid, true, 'Validate did not work as expected.');
+
+ val('UserConnectMockup', {name: 'a', excludedProperty: '', excludedValidation: 'a'}, function (valid, errors) {
+ t.same(errors, {name: ['length'], excludedProperty: ['notEmpty'], excludedValidation: ['length']}, 'Validate did not work as expected.');
+ t.done();
+ });
+ });
+ });
+ },
- setup(t, 2, {exclusions: {
- UserConnectMockup: {
- excludedValidation: [1],
- excludedProperty: true
- },
- ExcludedConnectMockup: true
- }}, function (sandbox) {
- var validate = sandbox.nohmValidations.validate;
- validate('UserConnectMockup', {
- excludedValidation: 'a',
- excludedProperty: ''
- }, function (valid) {
- t.same(valid, true, 'Validate did not work as expected with exclusions.');
+ connectOptions: function (t) {
+
+ setup(t, 1, {url: './nohm.js', namespace: 'hurgel'}, function (sandbox) {
+ t.ok(sandbox.hurgel, 'Namespace option not successful');
+ t.done();
+ });
+
+ },
- try {
- validate('ExcludedConnectMockup', {
- name: ''
- }, function () {
- t.ok(false, 'Validate should have thrown an error about an invalid modelname');
+ connectExtraFiles: function (t) {
+
+ setup(t, 1, {extraFiles: __dirname+'/custom_validations2.js'}, function (sandbox) {
+ sandbox.nohmValidations.validate('UserConnectMockup', {
+ customValidationFile: 'NOPE',
+ customValidationFileTimesTwo: 'NOPE'
+ }, function (valid, errors) {
+ t.same(errors, {
+ customValidationFile: ['customValidationFile'],
+ customValidationFileTimesTwo: ['customValidationFileTimesTwo']
+ }, 'Validate did not work as expected.');
t.done();
});
- } catch (e) {
- t.same(e.message, 'Invalid modelName passed to nohm or model was not properly exported.', 'Validate did not work as expected with exclusions.');
- }
- t.done();
});
- });
+
+ },
-};
-
-exports.connectValidateEmpty = function (t) {
+ connectExceptions: function (t) {
+
+ setup(t, 2, {exclusions: {
+ UserConnectMockup: {
+ excludedValidation: [1],
+ excludedProperty: true
+ },
+ ExcludedConnectMockup: true
+ }}, function (sandbox) {
+ var validate = sandbox.nohmValidations.validate;
+ validate('UserConnectMockup', {
+ excludedValidation: 'a',
+ excludedProperty: ''
+ }, function (valid) {
+ t.same(valid, true, 'Validate did not work as expected with exclusions.');
+
+ try {
+ validate('ExcludedConnectMockup', {
+ name: ''
+ }, function () {
+ t.ok(false, 'Validate should have thrown an error about an invalid modelname');
+ t.done();
+ });
+ } catch (e) {
+ t.same(e.message, 'Invalid modelName passed to nohm or model was not properly exported.', 'Validate did not work as expected with exclusions.');
+ }
+ t.done();
+ });
+ });
+
+ },
- setup(t, 1, undefined, function (sandbox) {
- var val = sandbox.nohmValidations.validate;
- val('UserConnectMockup', {excludedProperty: 'asd', excludedValidation: 'asd'}, function (valid) {
- t.same(valid, true, 'Validate did not work as expected.');
- t.done();
+ connectValidateEmpty: function (t) {
+
+ setup(t, 1, undefined, function (sandbox) {
+ var val = sandbox.nohmValidations.validate;
+ val('UserConnectMockup', {excludedProperty: 'asd', excludedValidation: 'asd'}, function (valid) {
+ t.same(valid, true, 'Validate did not work as expected.');
+ t.done();
+ });
});
- });
+ }
};
View
436 test/featureTests.js
@@ -82,235 +82,212 @@ var redis = args.redis,
},
idGenerator: 'increment'
});
-
-exports.redisClean = function (t) {
- t.expect(1);
- redis.keys(prefix + ':*:*Mockup:*', function (err, value) {
- var check = (Array.isArray(value) && value.length === 0) || value === null;
- t.ok(check, 'The redis database seems to contain fragments from previous nohm testruns. Use the redis command "KEYS '+prefix+':*:*Mockup:*" to see what keys could be the cause.');
- t.done();
- });
-};
-
-exports.idIntersection = function (t) {
- var arr1 = [1,2,3,4,5,6,7,8,9],
- arr2 = [2,3,4,10],
- arr3 = [2,3,4,10],
- arr4 = [],
- arr5 = [16,28,39],
- arr6 = ['hurgelwurz',28,39],
- arr7 = ['hurgelwurz',28,39],
- arr8 = [10,3,2],
- testIntersection = function (arrs, resultTest) {
+
+
+exports.prepare = {
+ redisClean: function(t) {
+ t.expect(1);
+ redis.keys(prefix + ':*:*Mockup:*', function(err, value) {
+ var check = (Array.isArray(value) && value.length === 0) || value === null;
+ t.ok(check, 'The redis database seems to contain fragments from previous nohm testruns. Use the redis command "KEYS ' + prefix + ':*:*Mockup:*" to see what keys could be the cause.');
+ t.done();
+ });
+ },
+
+ idIntersection: function(t) {
+ var arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9],
+ arr2 = [2, 3, 4, 10],
+ arr3 = [2, 3, 4, 10],
+ arr4 = [],
+ arr5 = [16, 28, 39],
+ arr6 = ['hurgelwurz', 28, 39],
+ arr7 = ['hurgelwurz', 28, 39],
+ arr8 = [10, 3, 2],
+ testIntersection = function(arrs, resultTest) {
var result;
-
+
result = helper.idIntersection.apply(null, arrs);
t.same(result, resultTest, 'idIntersection did not perform correctly.');
- };
- t.expect(9);
-
- testIntersection(
- [arr1],
- arr1
- );
-
- testIntersection(
- [arr1, arr2],
- [2,3,4]
- );
-
- testIntersection(
- [arr1, arr2, arr3],
- [2,3,4]
- );
-
- testIntersection(
- [arr2, arr3],
- [2,3,4, 10]
- );
-
- testIntersection(
- [arr1, arr2, arr3, arr4],
- []
- );
-
- testIntersection(
- [arr1, arr2, arr3, arr5],
- []
- );
-
- testIntersection(
- [arr5, arr6],
- [28,39]
- );
-
- testIntersection(
- [arr6, arr7],
- ['hurgelwurz',28,39]
- );
-
- testIntersection(
- [arr3, arr8],
- [10, 3, 2]
- );
-
- t.done();
-};
+ };
+ t.expect(9);
-exports.setRedisClient = function (t) {
- t.expect(2);
- console.log('Note: there should be an error message in the next line. (intended behaviour)');
- var user = new UserMockup();
- t.same(user, {}, 'Creating a model without having a nohm client set did not return false.');
-
- nohm.setClient(redis);
- user = new UserMockup();
- t.equals(typeof(user.modelName), 'string', 'Creating a model having a nohm client set did not work.');
- t.done();
-};
+ testIntersection([arr1], arr1);
-exports.setPrefix = function (t) {
- var oldPrefix = nohm.prefix;
- t.expect(1);
- nohm.setPrefix('hurgel');
- t.same(nohm.prefix, helper.getPrefix('hurgel'), 'Setting a custom prefix did not work as expected');
- nohm.prefix = oldPrefix;
- t.done();
+ testIntersection([arr1, arr2], [2, 3, 4]);
+
+ testIntersection([arr1, arr2, arr3], [2, 3, 4]);
+
+ testIntersection([arr2, arr3], [2, 3, 4, 10]);
+
+ testIntersection([arr1, arr2, arr3, arr4], []);
+
+ testIntersection([arr1, arr2, arr3, arr5], []);
+
+ testIntersection([arr5, arr6], [28, 39]);
+
+ testIntersection([arr6, arr7], ['hurgelwurz', 28, 39]);
+
+ testIntersection([arr3, arr8], [10, 3, 2]);
+
+ t.done();
+ },
+
+ setRedisClient: function(t) {
+ t.expect(2);
+ console.log('Note: there should be an error message in the next line. (intended behaviour)');
+ nohm.client = null;
+ var user = new UserMockup();
+ t.same(user, {}, 'Creating a model without having a nohm client set did not return false.');
+
+ nohm.setClient(redis);
+ user = new UserMockup();
+ t.equals(typeof(user.modelName), 'string', 'Creating a model having a nohm client set did not work.');
+ t.done();
+ },
+
+ setPrefix: function(t) {
+ var oldPrefix = nohm.prefix;
+ t.expect(1);
+ nohm.setPrefix('hurgel');
+ t.same(nohm.prefix, helper.getPrefix('hurgel'), 'Setting a custom prefix did not work as expected');
+ nohm.prefix = oldPrefix;
+ t.done();
+ }
};
-exports.propertyGetter = function (t) {
- var user = new UserMockup();
- t.expect(7);
+exports.propertyTests = {
+ propertyGetter: function(t) {
+ var user = new UserMockup();
+ t.expect(7);
- t.equals(typeof(user.p), 'function', 'Property getter short p is not available.');
+ t.equals(typeof(user.p), 'function', 'Property getter short p is not available.');
- t.equals(typeof(user.prop), 'function', 'Property getter short prop is not available.');
+ t.equals(typeof(user.prop), 'function', 'Property getter short prop is not available.');
- t.equals(typeof(user.property), 'function', 'Property getter is not available.');
+ t.equals(typeof(user.property), 'function', 'Property getter is not available.');
- t.equals(user.p('email'), 'email@email.de', 'Property getter did not return the correct value for email.');
+ t.equals(user.p('email'), 'email@email.de', 'Property getter did not return the correct value for email.');
- t.equals(user.p('name'), 'test', 'Property getter did not return the correct value for name.');
+ t.equals(user.p('name'), 'test', 'Property getter did not return the correct value for name.');
- console.log('Note: there should be an error message in the next line. (intended behaviour)');
- t.ok(!user.p('hurgelwurz'), 'Accessing an undefined property did not return false');
+ console.log('Note: there should be an error message in the next line. (intended behaviour)');
+ t.ok(!user.p('hurgelwurz'), 'Accessing an undefined property did not return false');
- t.same(user.p('json'), {}, 'Property getter did not return the correct value for json.');
+ t.same(user.p('json'), {}, 'Property getter did not return the correct value for json.');
- t.done();
-};
+ t.done();
+ },
-exports.propertySetter = function (t) {
- var user = new UserMockup();
- var controlUser = new UserMockup();
- t.expect(6);
+ propertySetter: function(t) {
+ var user = new UserMockup();
+ var controlUser = new UserMockup();
+ t.expect(6);
- t.same(user.p('email', 123), '', 'Setting a property did not return the new value that was set (with casting).');
-
- user.p('email', 'asdasd');
- t.equals(user.p('email'), 'asdasd', 'Setting a property did not actually set the property to the correct value');
+ t.same(user.p('email', 123), '', 'Setting a property did not return the new value that was set (with casting).');
- user.p('email', 'test@test.de');
- t.ok(user.p('email') !== controlUser.p('email'), 'Creating a new instance of an Object does not create fresh properties.');
+ user.p('email', 'asdasd');
+ t.equals(user.p('email'), 'asdasd', 'Setting a property did not actually set the property to the correct value');
- user.p({
- name: 'objectTest',
- email: 'object@test.de'
- });
+ user.p('email', 'test@test.de');
+ t.ok(user.p('email') !== controlUser.p('email'), 'Creating a new instance of an Object does not create fresh properties.');
- t.equals(user.p('name'), 'objectTest', 'Setting multiple properties by providing one object did not work correctly for the name.');
- t.equals(user.p('email'), 'object@test.de', 'Setting multiple properties by providing one object did not work correctly for the email.');
+ user.p({
+ name: 'objectTest',
+ email: 'object@test.de'
+ });
- user.p('json', {
- test: 1
- });
+ t.equals(user.p('name'), 'objectTest', 'Setting multiple properties by providing one object did not work correctly for the name.');
+ t.equals(user.p('email'), 'object@test.de', 'Setting multiple properties by providing one object did not work correctly for the email.');
- t.equals(user.p('json').test, 1, 'Setting a json property did not work correctly.');
+ user.p('json', {
+ test: 1
+ });
- t.done();
-};
+ t.equals(user.p('json').test, 1, 'Setting a json property did not work correctly.');
+ t.done();
+ },
-exports.propertyDiff = function (t) {
- var user = new UserMockup(),
- should = [],
- beforeName = user.p('name'),
- beforeEmail = user.p('email');
- t.expect(5);
- t.ok(user.propertyDiff(), 'Property diff returned changes even though there were none');
+ propertyDiff: function(t) {
+ var user = new UserMockup(),
+ should = [],
+ beforeName = user.p('name'),
+ beforeEmail = user.p('email');
+ t.expect(5);
- user.p('name', 'hurgelwurz');
- should.push({
- key: 'name',
- before: beforeName,
- after: 'hurgelwurz'
- });
- t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the changed property `name`.');
+ t.ok(user.propertyDiff(), 'Property diff returned changes even though there were none');
- user.p('email', 'asdasd');
- t.same(should, user.propertyDiff('name'), 'Property diff did not correctly search for changes only in `name`.');
+ user.p('name', 'hurgelwurz');
+ should.push({
+ key: 'name',
+ before: beforeName,
+ after: 'hurgelwurz'
+ });
+ t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the changed property `name`.');
- should.push({
- key: 'email',
- before: beforeEmail,
- after: 'asdasd'
- });
- t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the changed properties `name` and `email`.');
+ user.p('email', 'asdasd');
+ t.same(should, user.propertyDiff('name'), 'Property diff did not correctly search for changes only in `name`.');
- should.shift();
- user.p('name', beforeName);
- t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the reset property `name`.');
+ should.push({
+ key: 'email',
+ before: beforeEmail,
+ after: 'asdasd'
+ });
+ t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the changed properties `name` and `email`.');
- t.done();
-};
+ should.shift();
+ user.p('name', beforeName);
+ t.same(should, user.propertyDiff(), 'Property diff did not correctly recognize the reset property `name`.');
+ t.done();
+ },
-exports.propertyReset = function (t) {
- var user = new UserMockup(),
- beforeName = user.p('name'),
- beforeEmail = user.p('email');
- t.expect(4);
- user.p('name', user.p('name') + 'hurgelwurz');
- user.p('email', user.p('email') + 'asdasd');
- t.ok(user.propertyReset('name'), 'Property reset did not return true.'); // uhm... needed? i don't know
+ propertyReset: function(t) {
+ var user = new UserMockup(),
+ beforeName = user.p('name'),
+ beforeEmail = user.p('email');
+ t.expect(4);
- t.ok(user.p('name') === beforeName, 'Property reset did not properly reset `name`.');
+ user.p('name', user.p('name') + 'hurgelwurz');
+ user.p('email', user.p('email') + 'asdasd');
+ t.ok(user.propertyReset('name'), 'Property reset did not return true.'); // uhm... needed? i don't know
+ t.ok(user.p('name') === beforeName, 'Property reset did not properly reset `name`.');
- t.ok(user.p('email') !== beforeEmail, 'Property reset reset `email` when it shouldn\'t have.');
+ t.ok(user.p('email') !== beforeEmail, 'Property reset reset `email` when it shouldn\'t have.');
- user.p('name', user.p('name') + 'hurgelwurz');
- user.propertyReset();
- t.ok(user.p('name') === beforeName && user.p('email') === beforeEmail, 'Property reset did not properly reset `name` and `email`.');
+ user.p('name', user.p('name') + 'hurgelwurz');
+ user.propertyReset();
+ t.ok(user.p('name') === beforeName && user.p('email') === beforeEmail, 'Property reset did not properly reset `name` and `email`.');
- t.done();
-};
+ t.done();
+ },
-exports.allProperties = function (t) {
- var user = new UserMockup(),
- should;
- t.expect(2);
+ allProperties: function(t) {
+ var user = new UserMockup(),
+ should;
+ t.expect(2);
- user.p('name', 'hurgelwurz');
- user.p('email', 'hurgelwurz@test.de');
- should = {
- name: user.p('name'),
- visits: user.p('visits'),
- email: user.p('email'),
- emailOptional: user.p('emailOptional'),
- country: user.p('country'),
- json: {},
- id: user.id
- }; // yes, this absolutely must be set correct for this test to work. sorry
-
- t.same(should, user.allProperties(), 'Getting all properties failed.');
-
- t.ok(user.allProperties(true) === JSON.stringify(should), 'Getting all properties as JSON failed.');
+ user.p('name', 'hurgelwurz');
+ user.p('email', 'hurgelwurz@test.de');
+ should = {
+ name: user.p('name'),
+ visits: user.p('visits'),
+ email: user.p('email'),
+ emailOptional: user.p('emailOptional'),
+ country: user.p('country'),
+ json: {},
+ id: user.id
+ }; // yes, this absolutely must be set correct for this test to work. sorry
+ t.same(should, user.allProperties(), 'Getting all properties failed.');
+
+ t.ok(user.allProperties(true) === JSON.stringify(should), 'Getting all properties as JSON failed.');
- t.done();
+ t.done();
+ }
};
exports.create = function (t) {
@@ -453,18 +430,18 @@ exports.unique = function (t) {
user2.p('email', 'dubbplicateTest@test.de'); // intentional typo dubb
user1.save(function (err) {
t.ok(!err, 'There was an unexpected problem: ' + util.inspect(err));
- redis.get(prefix + ':uniques:UserMockup:name:dubplicateTest', function (err, value) {
+ redis.get(prefix + ':uniques:UserMockup:name:dubplicatetest', function (err, value) {
t.ok(user1.id, 'Userid b0rked while checking uniques');
t.equals(parseInt(value, 10), user1.id, 'The unique key did not have the correct id');
user2.valid(false, false, function (valid) {
t.ok(!valid, 'A unique property was not recognized as a duplicate in valid without setDirectly');
user2.save(function (err) {
t.equals(err, 'invalid', 'A saved unique property was not recognized as a duplicate');
- redis.exists(prefix + ':uniques:UserMockup:email:dubbplicateTest@test.de', function (err, value) {
+ redis.exists(prefix + ':uniques:UserMockup:email:dubbplicatetest@test.de', function (err, value) {
t.equals(value, 0, 'The tmp unique lock was not deleted for a failed save.');
- redis.get(prefix + ':uniques:UserMockup:name:dubplicateTest', function (err, value) {
+ redis.get(prefix + ':uniques:UserMockup:name:dubplicatetest', function (err, value) {
t.ok(!err, 'There was an unexpected probllem: ' + util.inspect(err));
- t.ok(parseInt(value, 10) === user1.id, 'The unique key did not have the correct id after trying to save another unique.');
+ t.same(parseInt(value, 10), user1.id, 'The unique key did not have the correct id after trying to save another unique.');
t.done();
});
});
@@ -477,6 +454,54 @@ exports.unique = function (t) {
});
};
+exports.uniqueLowerCase = function (t) {
+ var user1 = new UserMockup(),
+ user2 = new UserMockup();
+ t.expect(6);
+
+ user1.p('name', 'LowerCaseTest');
+ user1.p('email', 'LowerCaseTest@test.de');
+ user2.p('name', 'lowercasetest');
+ user2.p('email', 'lowercasetest@test.de');
+ user1.save(function (err) {
+ t.ok(!err, 'There was an unexpected problem: ' + util.inspect(err));
+ redis.get(prefix + ':uniques:UserMockup:name:'+user1.p('name').toLowerCase(), function (err, value) {
+ t.equals(parseInt(value, 10), user1.id, 'The unique key did not have the correct id');
+ user2.valid(false, false, function (valid) {
+ t.ok(!valid, 'A unique property was not recognized as a duplicate in valid without setDirectly.');
+ user2.save(function (err) {
+ t.equals(err, 'invalid', 'A saved unique property was not recognized as a duplicate');
+ redis.get(prefix + ':uniques:UserMockup:name:lowercasetest', function (err, value) {
+ t.ok(!err, 'There was an unexpected probllem: ' + util.inspect(err));
+ t.same(parseInt(value, 10), user1.id, 'The unique key did not have the correct id after trying to save another unique.');
+ t.done();
+ });
+ });
+ });
+ });
+ if (err) {
+ t.done();
+ }
+ });
+};
+
+exports.uniqueDeleteWhenOtherFails = function (t) {
+ var user1 = new UserMockup(),
+ user2 = new UserMockup();
+ t.expect(2);
+
+ user1.p('name', 'uniqueDeleteTest');
+ user1.p('email', 'uniqueDeleteTest@test.de');
+ user1.p('country', '');
+ user1.save(function (err) {
+ t.same('invalid', err, 'There was an unexpected problem: ' + util.inspect(err));
+ redis.exists(prefix + ':uniques:UserMockup:name:'+user1.p('name').toLowerCase(), function (err, value) {
+ t.equals(value, 0, 'The unique was locked although there were errors in the non-unique checks.');
+ t.done();
+ });
+ });
+};
+
exports.uniqueOnlyCheckSpecified = function (t) {
var user1 = new UserMockup();
t.expect(2);
@@ -786,3 +811,34 @@ exports.factory = function (t) {
});
t.ok(user2, 'Using the factory with an id and callback returned false');
};
+
+exports.purgeDB = function (t) {
+ var expected = 1;
+ var countKeys = function (prefix, callback) {
+ redis.keys(prefix+'*', function (err, orig_num) {
+ callback(err, orig_num.length);
+ });
+ };
+
+ var tests = [];
+ Object.keys(nohm.prefix).forEach(function (key) {
+ expected += 2;
+ tests.push(async.apply(countKeys, nohm.prefix[key]));
+ });
+
+ async.series(tests, function (err, num_arr) {
+ t.ok(!err, 'Unexpected redis error');
+ var count = num_arr.reduce(function (num, add) { return num + add; }, 0);
+ t.ok(count > 0, 'Database did not have any keys');
+ nohm.purgeDb(function (err) {
+ t.ok(!err, 'Unexpected redis error');
+ async.series(tests, function (err, num_arr) {
+ t.ok(!err, 'Unexpected redis error');
+ var count = num_arr.reduce(function (num, add) { return num + add; }, 0);
+ t.same(count, 0, 'Database did have keys left after purging.');
+ t.done();
+ });
+ });
+ });
+};
+
View
1,249 test/findTests.js
@@ -1,10 +1,9 @@
-var util = require('util');
+var async = require('async');
+var nohm = require(__dirname + '/../lib/nohm').Nohm;
+var h = require(__dirname + '/helper.js');
+var args = require(__dirname + '/testArgs.js');
+var redis = args.redis;
-// please do not prepend saving tests before the findAll tests
-
-var nohm = require(__dirname+'/../lib/nohm').Nohm;
-var redis = nohm.client;
-var relationsprefix = nohm.prefix.relations;
var UserFindMockup = nohm.model('UserFindMockup', {
properties: {
name: {
@@ -13,7 +12,7 @@ var UserFindMockup = nohm.model('UserFindMockup', {
index: true,
validations: [
'notEmpty'
- ]
+ ]
},
email: {
type: 'string',
@@ -42,167 +41,183 @@ var UserFindMockup = nohm.model('UserFindMockup', {
idGenerator: 'increment'
});
-var RoleFindMockup = nohm.model('RoleFindMockup', {
+var UserFindNoIncrementMockup = nohm.model('UserFindNoIncrementMockup', {
properties: {
name: {
type: 'string',
- value: 'user'
+ defaultValue: 'testName',
+ index: true,
+ validations: [
+ 'notEmpty'
+ ]
+ },
+ number: {
+ type: 'integer',
+ defaultValue: 1,
+ index: true
}
- },
- idGenerator: 'increment'
+ }
});
-var errLogger = function (err) {
+var errLogger = function(err) {
if (err) {
console.dir(err);
}
};
-var userNumeric = new UserFindMockup(),
-userNumeric2 = new UserFindMockup(),
-userNumeric3 = new UserFindMockup(),
-all = [];
-
-userNumeric.p({
- name: 'numericindextest',
- email: 'numericindextest@hurgel.de',
- number: 3
-});
-userNumeric.save(function (err) {
- errLogger(err);
- all.push(userNumeric.id);
-});
-
-userNumeric2.p({
- name: 'numericindextest',
- email: 'numericindextest2@hurgel.de',
- number: 4,
- number2: 33
-});
-userNumeric2.save(function (err) {
- errLogger(err);
- all.push(userNumeric2.id);
-});
-
-userNumeric3.p({
- name: 'numericindextest',
- email: 'numericindextest3@hurgel.de',
- number: 4,
- number2: 1
-});
-userNumeric3.save(function (err) {
- errLogger(err);
- all.push(userNumeric3.id);
-});
-
-var userUnique = new UserFindMockup();
-userUnique.p({
- name: 'uniquefind',
- email: 'uniquefind@hurgel.de'
-});
-userUnique.save(function (err) {
- errLogger(err);
- all.push(userUnique.id);
-});
-
-var userString = new UserFindMockup();
-userString.p({
- name: 'indextest',
- email: 'indextest@hurgel.de'
-});
-userString.save(function (err) {
- errLogger(err);
- all.push(userString.id);
-});
+var createUsers = function(props, modelName, callback) {
+ if (typeof(modelName) === 'function') {
+ callback = modelName;
+ modelName = 'UserFindMockup';
+ }
+ var makeSeries = function(prop) {
+ return function(next) {
+ var user = nohm.factory(modelName);
+ user.p(prop);
+ user.save(function (err) {
+ next(err, user);
+ });
+ };
+ };
-var userString2 = new UserFindMockup();
-userString2.p({
- name: 'indextest',
- email: 'indextest2@hurgel.de'
-});
-userString2.save(function (err) {
- errLogger(err);
- all.push(userString2.id);
-});
+ var series = props.map(function(prop) {
+ return makeSeries(prop);
+ });
+ async.series(series, function(err, users) {
+ var ids = users.map(function (user) {
+ return user.id;
+ });
+ callback(users, ids);
+ });
+};
-exports.loadInvalid = function (t) {
- var user = new UserFindMockup();
- t.expect(1);
+exports.find = {
+
+ setUp: function(next) {
+ if (!nohm.client) {
+ nohm.setClient(redis);
+ }
+ var t = this;
+ h.cleanUp(redis, args.prefix, function() {
+ createUsers([{
+ name: 'numericindextest',
+ email: 'numericindextest@hurgel.de',
+ number: 3
+ }, {
+ name: 'numericindextest',
+ email: 'numericindextest2@hurgel.de',
+ number: 4,
+ number2: 33
+ }, {
+ name: 'numericindextest',
+ email: 'numericindextest3@hurgel.de',
+ number: 4,
+ number2: 1
+ }, {
+ name: 'uniquefind',
+ email: 'uniquefind@hurgel.de'
+ }, {
+ name: 'indextest',
+ email: 'indextest@hurgel.de'
+ }, {
+ name: 'indextest',
+ email: 'indextest2@hurgel.de'
+ }, {
+ name: 'a_sort_first',
+ email: 'a_sort_first@hurgel.de',
+ number: 1
+ }, {
+ name: 'z_sort_last',
+ email: 'z_sort_last@hurgel.de',
+ number: 100000
+ }], function(users, ids) {
+ t.users = users;
+ t.userIds = ids;
+ next();
+ });
+ });
+ },
+ tearDown: function(next) {
+ h.cleanUp(redis, args.prefix, next);
+ },
- user.load(1, function (err) {
- t.equals(err, 'not found', 'Load() did not return "not true" for id 1 even though there should not be a user yet.');
- t.done();
- });
-};
+ loadInvalid: function(t) {
+ var user = new UserFindMockup();
+ t.expect(1);
+ h.cleanUp(redis, args.prefix, function () {
+ user.load(1, function(err) {
+ t.equals(err, 'not found', 'Load() did not return "not found" for id 1 even though there should not be a user yet.');
+ t.done();
+ });
+ });
+ },
-// please do not prepend saving tests before the findAll tests
-exports.load = function (t) {
- var user = new UserFindMockup(),
- findUser = new UserFindMockup();
- t.expect(5);
+ load: function(t) {
+ var user = new UserFindMockup(),
+ findUser = new UserFindMockup();
+ t.expect(5);
- user.p({
- name: 'hurgelwurz',
- email: 'hurgelwurz@hurgel.de',
- json: { test: 1 }
- });
+ user.p({
+ name: 'hurgelwurz',
+ email: 'hurgelwurz@hurgel.de',
+ json: {
+ test: 1
+ }
+ });
- user.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- all.push(user.id); // this is for findAll. we can't do findAll before this one, because this way it kinda ensures that findAll is called after all objects were saved.
- findUser.load(user.id, function (err) {
+ user.save(function(err) {
if (err) {
console.dir(err);
t.done();
}
- t.equals(user.p('name'), findUser.p('name'), 'The loaded version of the name was not the same as a set one.');
- t.equals(user.p('email'), findUser.p('email'), 'The loaded version of the email was not the same as a set one.');
- t.equals(findUser.p('json').test, 1, 'The loaded version of the json was not the same as the set one.');
- t.equals(user.id, findUser.id, 'The loaded version of the email was not the same as a set one.');
- t.equals(user.p('bool'), false, 'The loaded version of the boolean was not the same as a set one.');
- t.done();
+ findUser.load(user.id, function(err) {
+ if (err) {
+ console.dir(err);
+ t.done();
+ }
+ t.equals(user.p('name'), findUser.p('name'), 'The loaded version of the name was not the same as a set one.');
+ t.equals(user.p('email'), findUser.p('email'), 'The loaded version of the email was not the same as a set one.');
+ t.equals(findUser.p('json').test, 1, 'The loaded version of the json was not the same as the set one.');
+ t.equals(user.id, findUser.id, 'The loaded version of the email was not the same as a set one.');
+ t.equals(user.p('bool'), false, 'The loaded version of the boolean was not the same as a set one.');
+ t.done();
+ });
});
- });
-};
+ },
-// please do not prepend saving tests before the findAll tests
+ findAll: function(t) {
+ var self = this;
+ var findUser = new UserFindMockup();
+ t.expect(1);
-exports.findAll = function (t) {
- // this is a fuckup and heavily relies upon the rest of this file. (the stuff above this test, not below)
- var findUser = new UserFindMockup();
- t.expect(1);
-
- findUser.find(function (err, ids) {
- ids.sort(); // usually redis returns them first-in-first-out, but not always
- t.same(all, ids, 'find() did not return all users when not given any search parameters.');
- t.done();
- });
-};
+ findUser.find(function(err, ids) {
+ ids.sort(); // usually redis returns them first-in-first-out, but not always
+ t.same(self.userIds, ids, 'find() did not return all users when not given any search parameters.');
+ t.done();
+ });
+ },
-exports.exists = function (t) {
- var existsUser = new UserFindMockup();
- t.expect(2);
+ exists: function(t) {
+ var existsUser = new UserFindMockup();
+ t.expect(2);
- existsUser.exists(1, function (exists) {
- t.equals(exists, true, 'Exists() did not return true for id 1.');
-
- existsUser.exists(9999999, function (exists) {
- t.equals(exists, false, 'Exists() did not return false for id 9999999.');
- t.done();
+ existsUser.exists(1, function(exists) {
+ t.equals(exists, true, 'Exists() did not return true for id 1.');
+
+ existsUser.exists(9999999, function(exists) {
+ t.equals(exists, false, 'Exists() did not return false for id 9999999.');
+ t.done();
+ });
});
- });
-};
+ },
- /* I don't know how to do this right now.
-exports.loadArray = function (t) {
+/* I don't know how to do this right now.
+loadArray: function (t) {
var findUser = new UserFindMockup();
t.expect(2);
@@ -211,428 +226,666 @@ exports.loadArray = function (t) {
t.ok(Array.isArray(users), 'load()ing an array of ids did not return an array');
t.same(all.length, users.length, 'load()ing an array of ids did not return an array with the coorect length');
});
-};*/
+},*/
-exports.findByUnique = function (t) {
- var findUser = new UserFindMockup();
- t.expect(1);
+ findByUnique: function(t) {
+ var findUser = new UserFindMockup();
+ var userUnique = this.users.filter(function (user) {
+ return user.p('name') ==='uniquefind';
+ })[0];
+ t.expect(1);
- findUser.find({
- email: userUnique.p('email')
- }, function (err, ids) {
- if (err) {
- console.dir(err);
- }
- t.same(ids, [userUnique.id], 'The found id did not match the id of the saved object.');
- t.done();
- });
-};
+ findUser.find({
+ email: userUnique.p('email')
+ }, function(err, ids) {
+ if (err) {
+ console.dir(err);
+ }
+ t.same(ids, [userUnique.id], 'The found id did not match the id of the saved object.');
+ t.done();
+ });
+ },
-exports.findByUniqueOtherCase = function (t) {
- var findUser = new UserFindMockup();
- t.expect(1);
+ findByUniqueOtherCase: function(t) {
+ var findUser = new UserFindMockup();
+ var userUnique = this.users.filter(function (user) {
+ return user.p('name') ==='uniquefind';
+ })[0];
+ t.expect(1);
- findUser.find({
- email: userUnique.p('email').toUpperCase()
- }, function (err, ids) {
- if (err) {
- console.dir(err);
- }
- t.same(ids, [userUnique.id], 'The found id did not match the id of the saved object.');
- t.done();
- });
-};
+ findUser.find({
+ email: userUnique.p('email').toUpperCase()
+ }, function(err, ids) {
+ if (err) {
+ console.dir(err);
+ }
+ t.same(ids, [userUnique.id], 'The found id did not match the id of the saved object.');
+ t.done();
+ });
+ },
-exports.findByUniqueInvalidSearch = function (t) {
- var findUser = new UserFindMockup();
- t.expect(1);
-
- console.log('There should be an error in the next line');
- findUser.find({
- email: {}
- }, function (err, ids) {
- t.same(0, err.indexOf('Invalid search parameters'), 'The found id did not match the id of the saved object.');
- t.done();
- });
-};
+ findByUniqueInvalidSearch: function(t) {
+ var findUser = new UserFindMockup();
+ t.expect(1);
-exports.findByStringIndex = function (t) {
- var findUser = new UserFindMockup();
- t.expect(1);
+ console.log('There should be an error in the next line');
+ findUser.find({
+ email: {}
+ }, function(err) {
+ t.same(0, err.indexOf('Invalid search parameters'), 'The found id did not match the id of the saved object.');
+ t.done();
+ });
+ },
- findUser.find({
- name: 'indextest'
- }, function (err, ids) {
- if (err) {
- console.dir(err);
- }
- t.same(ids, [userString.id, userString2.id], 'The found id did not match the id of the saved object.');
- t.done();
- });
-};
+ findByStringIndex: function(t) {
+ var findUser = new UserFindMockup();
+ var users = this.users.filter(function (user) {
+ return user.p('name') ==='indextest';
+ });
+ t.expect(1);
-exports.findByNumericIndex = function (t) {
- var findUser = new UserFindMockup();
- t.expect(1);
-
- findUser.find({
- number: {
- min: 2
- },
- number2: {
- max: 100,
- limit: 2
- }
- }, function (err, ids) {
- errLogger(err);
- t.same(ids, [userNumeric2.id, userNumeric3.id], 'The found id did not match the id of the saved object.');
- t.done();
- });
-};
+ findUser.find({
+ name: 'indextest'
+ }, function(err, ids) {
+ if (err) {
+ console.dir(err);
+ }
+ t.same(ids, [users[0].id, users[1].id], 'The found id did not match the id of the saved object.');
+ t.done();
+ });
+ },
-exports.findByMixedIndex = function (t) {
- var user = new UserFindMockup(),
- user2 = new UserFindMockup(),
- user3 = new UserFindMockup(),
- user4 = new UserFindMockup(),
- findUser = new UserFindMockup();
- t.expect(1);
-
- user.p({
- name: 'mixedindextest',
- email: 'mixedindextest@hurgel.de',
- number: 3,
- number2: 33
- });
+ findByNumericIndex: function(t) {
+ var findUser = new UserFindMockup();
+ var users = this.users.filter(function (user) {
+ return user.p('number') > 2 && user.p('number2') < 100;
+ });
+ t.expect(1);
- user2.p({
- name: 'mixedindextest',
- email: 'mixedindextest2@hurgel.de',
- number: 4,
- number2: 33
- });
+ findUser.find({
+ number: {
+ min: 2
+ },
+ number2: {
+ max: 100,
+ limit: 2
+ }
+ }, function(err, ids) {
+ errLogger(err);
+ t.same(ids.sort(), [users[0].id, users[1].id].sort(), 'The found id did not match the id of the saved object.');
+ t.done();
+ });
+ },
- user3.p({
- name: 'mixedindextestNOT',
- email: 'mixedindextest3@hurgel.de',
- number: 4,
- number2: 1
- });
-
- user4.p({
- name: 'mixedindextest',
- email: 'mixedindextest4@hurgel.de',
- number: 1,
- number2: 33
- });
+ findByMixedIndex: function(t) {
+ var findUser = new UserFindMockup();
+ t.expect(1);
+
+ createUsers([{
+ name: 'mixedindextest',
+ email: 'mixedindextest@hurgel.de',
+ number: 3,
+ number2: 33
+ }, {
+ name: 'mixedindextest',
+ email: 'mixedindextest2@hurgel.de',
+ number: 4,
+ number2: 33
+ }, {
+ name: 'mixedindextestNOT',
+ email: 'mixedindextest3@hurgel.de',
+ number: 4,
+ number2: 1
+ }, {
+ name: 'mixedindextest',
+ email: 'mixedindextest4@hurgel.de',
+ number: 1,
+ number2: 33
+ }], function (users, inserted_ids) {
- user.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- user2.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- user3.save(function (err) {
+ findUser.find({
+ number: {
+ min: 2
+ },
+ number2: {
+ max: 100
+ },
+ name: 'mixedindextest'
+ }, function(err, ids) {
if (err) {
console.dir(err);
- t.done();
}
- user4.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- findUser.find({
- number: {
- min: 2
- },
- number2: {
- max: 100
- },
- name: 'mixedindextest'
- }, function (err, ids) {
- if (err) {
- console.dir(err);
- }
- t.same(ids, [user.id, user2.id], 'The found id did not match the id of the saved object.');
- t.done();
- });
- });
+ t.same(ids.sort(), [users[0].id, users[1].id].sort(), 'The found id did not match the id of the saved object.');
+ t.done();
});
});
- });
-};
-
-exports.findSameNumericTwice = function (t) {
- var user = new UserFindMockup(),
- user2 = new UserFindMockup(),
- user3 = new UserFindMockup(),
- findUser = new UserFindMockup();
- t.expect(1);
-
- user.p({
- name: 'SameNumericTwice',
- email: 'SameNumericTwice@hurgel.de',
- number: 3000
- });
-
- user2.p({
- name: 'SameNumericTwice2',
- email: 'SameNumericTwice2@hurgel.de',
- number: 3000
- });
+ },
- user.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- user2.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
+ findSameNumericTwice: function(t) {
+ var self = this;
+ var findUser = new UserFindMockup();
+ t.expect(2);
+
+
+ createUsers([{
+ name: 'SameNumericTwice',
+ email: 'SameNumericTwice@hurgel.de',
+ number: 3000
+ }, {
+ name: 'SameNumericTwice2',
+ email: 'SameNumericTwice2@hurgel.de',
+ number: 3000
+ }], function (users, userIds) {
findUser.find({
number: {
min: 3000
}
- }, function (err, ids) {
+ }, function(err, ids) {
if (err) {
console.dir(err);
}
- t.same(ids, [user.id, user2.id], 'The found id did not match the id of the saved objects.');
+ userIds.push(self.userIds[self.userIds.length-1]);
+ t.same(userIds.length, 3, 'Didn\'t create 2 users, instead: '+userIds.length);
+ t.same(ids.sort(), userIds.sort(), 'The found id did not match the id of the saved objects.');
t.done();
});
});
- });
-};
-
-exports.findByMixedIndexMissing = function (t) {
- var user = new UserFindMockup(),
- user2 = new UserFindMockup(),
- user3 = new UserFindMockup(),
- findUser = new UserFindMockup();
- t.expect(1);
-
- user2.p({
- name: 'mixedindextestMissing',
- email: 'mixedindextestMissing@hurgel.de',
- number: 4
- });
-
- user3.p({
- name: 'mixedindextestMissing2',
- email: 'mixedindextestMissing2@hurgel.de',
- number: 4
- });
+ },
- user.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- user2.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- user3.save(function (err) {
+ findByMixedIndexMissing: function(t) {
+ var findUser = new UserFindMockup();
+ t.expect(1);
+
+ createUsers([{
+ name: 'mixedindextestMissing',
+ email: 'mixedindextestMissing@hurgel.de',
+ number: 4
+ }, {
+ name: 'mixedindextestMissing2',
+ email: 'mixedindextestMissing2@hurgel.de',
+ number: 4
+ }], function () {
+ findUser.find({
+ number: {
+ min: 2
+ },
+ name: 'mixedindextASDASDestMISSING'
+ }, function(err, ids) {
if (err) {
console.dir(err);
- t.done();
}
- findUser.find({
- number: {
- min: 2
- },
- name: 'mixedindextASDASDestMISSING'
- }, function (err, ids) {
- if (err) {
- console.dir(err);
- }
- t.same(ids, [], 'Ids were found even though the name should not be findable.');
- t.done();
- });
+ t.same(ids, [], 'Ids were found even though the name should not be findable.');
+ t.done();
});
});
- });
-};
+ },
-exports.findNumericWithoutLimit = function (t) {
- var findUser = new UserFindMockup()
- , usersLooped = 0
- , loopUserCreation = function () {
- usersLooped++;
- if (usersLooped === 55) {
- findUser.find({
- number: {
- min: 1,
- limit: 0
+ findNumericWithoutLimit: function(t) {
+ var findUser = new UserFindMockup(),
+ usersLooped = 0,
+ loopUserCreation = function() {
+ usersLooped++;
+ if (usersLooped === 55) {
+ findUser.find({
+ number: {
+ min: 1,
+ limit: 0
+ }
+ }, function(err, ids) {
+ errLogger(err);
+ t.ok(ids.length > 54, 'The limit: 0 option did not return more than 50 ids.');
+ t.done();
+ });
}
- }, function (err, ids) {
- errLogger(err);
- t.ok(ids.length > 54, 'The limit: 0 option did not return more than 50 ids.');
- t.done();
+ };
+ t.expect(1);
+
+ for (var i = 0, len = 55; i < len; i++) {
+ var user = new UserFindMockup();
+ user.p({
+ name: 'findNumericWithoutLimit' + i,
+ email: 'findNumericWithoutLimit' + i + '@hurgel.de',
+ number: i
});
- }
- };
- t.expect(1);
-
- for (var i = 0, len = 55; i < len; i++) {
- var user = new UserFindMockup();
- user.p({
- name: 'findNumericWithoutLimit'+i,
- email: 'findNumericWithoutLimit'+i+'@hurgel.de',
- number: i
- });
-
- user.save(loopUserCreation);
- }
-};
-exports.findExactNumeric = function (t) {
- var user = new UserFindMockup(),
- findUser = new UserFindMockup(),
- num = 999876543;
- t.expect(2);
-
- user.p({
- name: 'findExactNumeric',
- email: 'findExactNumeric@hurgel.de',
- number: num
- });
- user.save(function (err) {
- if (err) {
- console.dir(err);
+ user.save(loopUserCreation);
}
- findUser.find({
+ },
+
+ findExactNumeric: function(t) {
+ var user = new UserFindMockup(),
+ findUser = new UserFindMockup(),
+ num = 999876543;
+ t.expect(2);
+
+ user.p({
+ name: 'findExactNumeric',
+ email: 'findExactNumeric@hurgel.de',
number: num
- }, function (err, ids) {
- t.same(ids, [user.id], 'Did not find an exact number match');
+ });
+ user.save(function(err) {
+ if (err) {
+ console.dir(err);
+ }
findUser.find({
- number: (num-1)
- }, function (err, ids) {
- t.same(ids, [], 'Searching for a nonexistant number did not return an empty array.');
- t.done();
+ number: num
+ }, function(err, ids) {
+ t.same(ids, [user.id], 'Did not find an exact number match');
+ findUser.find({
+ number: (num - 1)
+ }, function(err, ids) {
+ t.same(ids, [], 'Searching for a nonexistant number did not return an empty array.');
+ t.done();
+ });
});
});
- });
-};
+ },
-exports.loadReturnsProps = function (t) {
- var user = new UserFindMockup(),
- findUser = new UserFindMockup();
- t.expect(1);
+ loadReturnsProps: function(t) {
+ var user = new UserFindMockup(),
+ findUser = new UserFindMockup();
+ t.expect(1);
- user.p({
- name: 'loadReturnsProps',
- email: 'loadReturnsProps@hurgel.de',
- json: { test: 1 }
- });
+ user.p({
+ name: 'loadReturnsProps',
+ email: 'loadReturnsProps@hurgel.de',
+ json: {
+ test: 1
+ }
+ });
- user.save(function (err) {
- if (err) {
- console.dir(err);
- t.done();
- }
- findUser.load(user.id, function (err, props) {
+ user.save(function(err) {
if (err) {
console.dir(err);
t.done();
}
- var testProps = user.allProperties();
- delete testProps.id;
- t.same(props, testProps, 'The loaded properties are not the same as allProperties() (without id).');
- t.done();
+ findUser.load(user.id, function(err, props) {
+ if (err) {
+ console.dir(err);
+ t.done();
+ }
+ var testProps = user.allProperties();
+ delete testProps.id;
+ t.same(props, testProps, 'The loaded properties are not the same as allProperties() (without id).');
+ t.done();
+ });
});
- });
-};
+ },
-exports.shortForms = function (t) {
- t.expect(11);
- var shortFormMockup = nohm.model('shortFormMockup', {
- properties: {
- name: {
- type: 'string',
- defaultValue: 'testName',
- index: true,
- validations: [
- 'notEmpty'
- ]
- }
- },
- idGenerator: 'increment'
- });
-
- shortFormMockup.save(function (err) {
- var id = this.id;
- t.ok(!err, 'There was an error while saving');
- t.ok(this instanceof shortFormMockup, '´this´ was not set to an instance of UserFindMockup');
- t.ok(id > 0, 'The id was not set properly');
- this.p('name', 'shortForm');
- this.save(function () {
- this.p('name', 'asdasd'); // make sure our comparisons in load aren't bogus
- shortFormMockup.load(id, function (err, props) {
- t.ok(!err, 'There was an error while loading.');
- t.ok(props.hasOwnProperty('name') && props.name === 'shortForm', 'The props argument was not properly passed in load.');
- t.same(this.p('name'), 'shortForm', 'The `this` instance has some property issues.');
- shortFormMockup.find({name: 'shortForm'}, function (err, ids) {
- t.ok(!err, 'There was an error while finding');
- t.same(ids, [id], 'The found ids do not match [id]');
- shortFormMockup.remove(id, function (err) {
- t.ok(!err, 'There was an error while removing');
- shortFormMockup.find({name: 'shortForm'}, function (err, ids) {
- t.ok(!err, 'There was en error while finding the second time');
- t.same(ids, [], 'Remove did not remove the correct instance. Uh-Oh.... :D ');
- t.done();
+ shortForms: function(t) {
+ t.expect(11);
+ var shortFormMockup = nohm.model('shortFormMockup', {
+ properties: {
+ name: {
+ type: 'string',
+ defaultValue: 'testName',
+ index: true,
+ validations: [
+ 'notEmpty'
+ ]
+ }
+ },
+ idGenerator: 'increment'
+ });
+
+ shortFormMockup.save(function(err) {
+ var id = this.id;
+ t.ok(!err, 'There was an error while saving');
+ t.ok(this instanceof shortFormMockup, '´this´ was not set to an instance of UserFindMockup');
+ t.ok(id > 0, 'The id was not set properly');
+ this.p('name', 'shortForm');
+ this.save(function() {
+ this.p('name', 'asdasd'); // make sure our comparisons in load aren't bogus
+ shortFormMockup.load(id, function(err, props) {
+ t.ok(!err, 'There was an error while loading.');
+ t.ok(props.hasOwnProperty('name') && props.name === 'shortForm', 'The props argument was not properly passed in load.');
+ t.same(this.p('name'), 'shortForm', 'The `this` instance has some property issues.');
+ shortFormMockup.find({
+ name: 'shortForm'
+ }, function(err, ids) {
+ t.ok(!err, 'There was an error while finding');
+ t.same(ids, [id], 'The found ids do not match [id]');
+ shortFormMockup.remove(id, function(err) {
+ t.ok(!err, 'There was an error while removing');
+ shortFormMockup.find({
+ name: 'shortForm'
+ }, function(err, ids) {
+ t.ok(!err, 'There was en error while finding the second time');
+ t.same(ids, [], 'Remove did not remove the correct instance. Uh-Oh.... :D ');
+ t.done();
+ });
});
});
});
});
});
- });
-}
-
-exports.uuidLoadFind = function (t) {
- t.expect(6);
- var uuidMockup = nohm.model('uuidMockup', {
- properties: {
- name: {
- type: 'string',
- defaultValue: 'testName',
- index: true,
- validations: [