New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

wolfpack

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

wolfpack - npm Package Compare versions

Comparing version
0.2.1-rc1
to
0.3.0
+6
-0
CHANGELOG.md
Change Log
==========
## 0.3.0
Adds mocking results conditionally on models
Supports multiple results on setResult operations
[#11](https://github.com/fdvj/wolfpack/issues/11) Removes associatons to allow testing (association support planned for the future)
## 0.2.0-rc1

@@ -5,0 +11,0 @@ [#9](https://github.com/fdvj/wolfpack/issues/9)

+62
-5

@@ -1,2 +0,3 @@

var sinon = require('sinon');
var sinon = require('sinon'),
_ = require('lodash');

@@ -7,2 +8,8 @@ function addOperations(value) {

function conditionalResults(operation, condition, collectionName) {
return _.find(WOLFPACK.classResults[collectionName][operation], function(scenario){
return _.isEqual(scenario.when, condition);
});
}
var find = sinon.spy(function (connection, collectionName, options, cb) {

@@ -17,4 +24,15 @@ if (WOLFPACK.errors) {

// Check if we have a conditional result
if (WOLFPACK.classResults[collectionName].find.length > 0 && options.where) {
var match = conditionalResults('find', options.where, collectionName);
if (match) {
return cb(null, match.result);
}
}
if (WOLFPACK.results.find) {
return cb(null, WOLFPACK.results.find);
if (WOLFPACK.results.find.length > 1 && typeof WOLFPACK.results.find[0] === 'object') {
return cb(null, WOLFPACK.results.find.shift());
}
return cb(null, WOLFPACK.results.find[0]);
}

@@ -29,4 +47,21 @@ return cb(null, []);

// Check if we have a conditional result
if (WOLFPACK.classResults[collectionName].create.length > 0 && values) {
var condition = _.clone(values);
delete condition.createdAt;
delete condition.updatedAt;
var match = conditionalResults('create', condition, collectionName);
if (match) {
return cb(null, (match.result.length > 1) ? match.result : match.result[0]);
}
}
var value;
if (WOLFPACK.results.create) {
return cb(null, WOLFPACK.results.create);
if (WOLFPACK.results.create.length > 1 && typeof WOLFPACK.results.create[0] === 'object') {
value = WOLFPACK.results.create.shift();
return cb(null, (_.isArray(value) && value.length > 1) ? value : value[0]);
}
value = WOLFPACK.results.create[0];
return cb(null, (_.isArray(value) && value.length > 1) ? value : value[0]);
}

@@ -41,5 +76,27 @@ return cb(null, values);

// Check if we have a conditional result
if (WOLFPACK.classResults[collectionName].update.length > 0 && values) {
var match = _.find(WOLFPACK.classResults[collectionName].update, function(scenario){
var keys = _.keys(scenario.when);
return _.every(keys, function(key){
return _.isEqual(scenario.when[key], values[key]);
});
});
debugger;
if (match) {
var value = (match.result.length > 1) ? match.result : match.result[0];
addOperations(value);
return cb(null, value);
}
}
if (WOLFPACK.results.update) {
addOperations(WOLFPACK.results.update);
return cb(null, WOLFPACK.results.update);
if (WOLFPACK.results.update.length > 1 && typeof WOLFPACK.results.update[0] === 'object') {
var op = WOLFPACK.results.update.shift();
addOperations(op);
return cb(null, op);
}
addOperations(WOLFPACK.results.update[0]);
return cb(null, WOLFPACK.results.update[0]);
}

@@ -46,0 +103,0 @@ addOperations(values);

+128
-39

@@ -5,3 +5,4 @@ // Set the Wolfpack global variable

CRUD: {},
operations: []
operations: [],
classResults:{}
};

@@ -22,3 +23,4 @@

adapter = require(__dirname + '/wolfpack_adapter'),
rs = require('random-strings');
rs = require('random-strings'),
_ = require('lodash');

@@ -56,3 +58,3 @@

// Spy on model custom methods
// Spy on model class methods
for (var idx in model) {

@@ -65,10 +67,21 @@ if (typeof model[idx] === 'function') {

var pk = false;
var attrKeys;
// Spy on instance custom methods
// Spy on instance methods
if (model.attributes) {
for (idx in model.attributes) {
if (idx)
if (typeof model.attributes[idx] === 'function') {
model.attributes[idx] = sinon.spy(model.attributes[idx]);
}
// Check for associations and delete them
if (_.isObject(model.attributes[idx])) {
attrKeys = _.keys(model.attributes[idx]);
if (attrKeys.indexOf('via') > -1 || attrKeys.indexOf('collection') > -1 || attrKeys.indexOf('model') > -1) {
delete model.attributes[idx].via;
delete model.attributes[idx].collection;
delete model.attributes[idx].model;
delete model.attributes[idx].dominant;
model.attributes[idx].type = 'json';
}
}
}

@@ -122,3 +135,3 @@ }

// Time to spy
for (var idx in instance) {
for (idx in instance) {
if (typeof instance[idx] === 'function') {

@@ -128,50 +141,73 @@ instance[idx] = sinon.spy(instance[idx]);

}
// Add wolfpack mock result methos
instance.wolfpack = new ClassMocks(instance.identity);
// Only add references if the model does not have a method called that way already
if (!instance.setFindResults) {
instance.setFindResults = instance.wolfpack.setFindResults;
}
if (!instance.setCreateResults) {
instance.setCreateResults = instance.wolfpack.setCreateResults;
}
if (!instance.setUpdateResults) {
instance.setUpdateResults = instance.wolfpack.setUpdateResults;
}
return instance;
}
function methods() {
function setResults(method, results) {
if (typeof results !== 'object') {
function setResults() {
var results = _.map(arguments, function(arg){
if (!_.isObject(arg)) {
throw new Error('Results can only be an object');
}
if (!Array.isArray(results)) {
results = [results];
if (!_.isArray(arg)) {
return [arg];
}
this.results[method] = results;
}
return arg;
});
WOLFPACK.results[this] = results;
}
function resetResults() {
this.results = {};
function resetResults() {
this.results = {};
for (var idx in this.classResults) {
this.classResults[idx] = {
find: [],
create: [],
update: []
};
}
}
function setErrors(error) {
this.errors = error;
}
function setErrors(error) {
this.errors = error;
}
function clearErrors() {
delete this.errors;
}
function clearErrors() {
delete this.errors;
}
function spyCRUD(operation) {
return WOLFPACK.CRUD[operation];
}
function spyCRUD(operation) {
return WOLFPACK.CRUD[operation];
}
function resetSpies(crud_operation) {
if (crud_operation) {
return spyCRUD(crud_operation).reset();
}
for (var idx in WOLFPACK.CRUD) {
WOLFPACK.CRUD[idx].reset();
}
return true;
function resetSpies(crud_operation) {
if (crud_operation) {
return spyCRUD(crud_operation).reset();
}
for (var idx in WOLFPACK.CRUD) {
WOLFPACK.CRUD[idx].reset();
}
return true;
}
function methods() {
return {
setFindResults: setResults.bind(WOLFPACK, 'find'),
setCreateResults: setResults.bind(WOLFPACK, 'create'),
setUpdateResults: setResults.bind(WOLFPACK, 'update'),
setFindResults: setResults.bind('find'),
setCreateResults: setResults.bind('create'),
setUpdateResults: setResults.bind('update'),
clearResults: resetResults.bind(WOLFPACK),

@@ -186,2 +222,55 @@ setErrors: setErrors.bind(WOLFPACK),

/*
* Class mocks
*/
function ClassMocks(className) {
var self = this;
function _setResults(when, result) {
if (!_.isObject(result)) {
throw new Error('Results can only be an object');
}
if (!_.isObject(when)) {
throw new Error('When must be an object');
}
if (!_.isArray(result)) {
result = [result];
}
WOLFPACK.classResults[self._identity][this.toString()].push({
when: when,
result: result
});
}
this._identity = className;
WOLFPACK.classResults[this._identity] = {
find: [],
create: [],
update: []
};
// Methods
this.setFindResults = _setResults.bind('find');
this.setCreateResults = _setResults.bind('create');
this.setUpdateResults = _setResults.bind('update');
}
/*
* Object references
*/
wolfpack.setFindResults = setResults.bind('find');
wolfpack.setCreateResults = setResults.bind('create');
wolfpack.setUpdateResults = setResults.bind('update');
wolfpack.clearResults = resetResults.bind(WOLFPACK);
wolfpack.setErrors = setErrors.bind(WOLFPACK);
wolfpack.clearErrors = clearErrors.bind(WOLFPACK);
wolfpack.spy = spyCRUD;
wolfpack.resetSpy = resetSpies;
wolfpack.resetSpies = resetSpies;
module.exports = wolfpack;
{
"name": "wolfpack",
"version": "0.2.1-rc1",
"version": "0.3.0",
"description": "SailsJS Model Testing Library",

@@ -36,2 +36,3 @@ "main": "index.js",

"dependencies": {
"lodash": "^2.4.1",
"random-strings": "0.0.1",

@@ -38,0 +39,0 @@ "sinon": "^1.9.0",

+378
-146

@@ -8,8 +8,4 @@ Wolfpack

Basically, this is a library that instantiates your Sails models so that you can use them in your tests, without worrying about setting up a db, which introduces latency to your tests. By defaults it spies all Model functions with sinonJS, so you can test if the correct parameters are being sent to the model. It works like this:
Basically, this is a library that instantiates your Sails models so that you can use them in your tests, without worrying about setting up a db, which introduces latency to your tests. By default, it spies all Model methods with sinonJS, so you can test if the correct parameters are being sent to the model on each call. It works like this:
## Release Candidate Notes
Several major changes have occured between waterline's (Sails' ORM) 0.9.x to 0.10.x. One major change you'll notice is that ```done``` is no longer a deferred method. Methods now return promises or a deferred object, or a callback can be used. All the documentation below uses ```done```as example, so I still need to update the documentation. If you find any issues, please let me know.
```javascript

@@ -41,2 +37,3 @@ var wolfpack = require('wolfpack');

// We can test that our controller is calling our Model method with the proper params
it("should add a user to a given chatroom", function(){

@@ -77,28 +74,33 @@ ChatController.addUser(request, response);

The above example might look a little bit intimidating, especially if you are new to asynchronous testing. So in the
next sections we are going to see how each component works separately.
next sections we are going to see how each component works separately. The previous example is also a sample of functional testing. With Wolfpack,
you can do unit testing as well!
## Table of Contents
1. [Installation](#installation)
2. [Usage](#usage)
3. [API](#api)
1. [wolpack](#wolpackpath_to_model)
2. [wolfpack().setFindResults](#swolfpacksetfindresultsobject--array-of-objects)
3. [wolfpack().setCreateResults](#wolfpacksetcreateresultsobject--array-of-objects)
4. [wolfpack().setUpdateResults](#wolfpacksetupdateresultsobject--array-of-objects)
5. [wolfpack().clearResults](#wolfpackclearresults)
6. [wolfpack().setErrors](#wolfpackseterrorserrors)
7. [wolfpack().clearErrors](#wolfpackclearerrors)
8. [wolfpack().spy](#wolfpackspyfind--create-update--destroy)
9. [wolfpack().resetSpy](#wolfpackresetspyfind--create--update--destroy)
10. [wolfpack().resetSpies](#wolfpackresetspies)
4. [Examples](#examples)
1. [Mocking Model Results](#mocking-model-results)
2. [Mocking Errors](#mocking-errors)
3. [Testing Sails Controllers](#testing-sails-controllers)
4. [Asynchronous Testing](#asynchronous-testing)
5. [Testing Sails Models](#testing-sails-models)
1. [Installation](https://github.com/fdvj/wolfpack#installation)
2. [Usage](https://github.com/fdvj/wolfpack#usage)
3. [API](https://github.com/fdvj/wolfpack#api)
1. [wolpack](https://github.com/fdvj/wolfpack#wolfpackpath_to_model)
2. [wolfpack.setFindResults](https://github.com/fdvj/wolfpack#wolfpacksetfindresultsobject1-object2-object3-)
3. [wolfpack.setCreateResults](https://github.com/fdvj/wolfpack#wolfpacksetcreateresultsobject1--object2-object3-)
4. [wolfpack.setUpdateResults](https://github.com/fdvj/wolfpack#wolfpacksetupdateresultsobject1--object2-object3-)
5. [wolfpack.clearResults](https://github.com/fdvj/wolfpack#wolfpackclearresults)
6. [wolfpack.setErrors](https://github.com/fdvj/wolfpack#wolfpackseterrorserrors)
7. [wolfpack.clearErrors](https://github.com/fdvj/wolfpack#wolfpackclearerrors)
8. [wolfpack.spy](https://github.com/fdvj/wolfpack#wolfpackspyfind--create-update--destroy)
9. [wolfpack.resetSpy](https://github.com/fdvj/wolfpack#wolfpackresetspyfind--create--update--destroy)
10. [wolfpack.resetSpies](https://github.com/fdvj/wolfpack#wolfpackresetspies)
4. [Conditional Mocking of the Results](https://github.com/fdvj/wolfpack#conditional-mocking-of-results)
1. [Model.setFindResults](https://github.com/fdvj/wolfpack#modelsetfindresultswhen-result)
2. [Model.setCreateResults](https://github.com/fdvj/wolfpack#modelsetcreateresultswhen-result)
3. [Model.setUpdateResults](https://github.com/fdvj/wolfpack#modelsetupdateresultswhen-result)
5. [Examples](#examples)
1. [Mocking Model Results](https://github.com/fdvj/wolfpack#examples)
2. [Mocking Errors](https://github.com/fdvj/wolfpack#mocking-model-results)
3. [Testing Sails Controllers](https://github.com/fdvj/wolfpack#testing-sails-controllers)
4. [Asynchronous Testing](https://github.com/fdvj/wolfpack#asynchronous-testing)
5. [Testing Sails Models](https://github.com/fdvj/wolfpack#testing-sails-models)
## Installation
To install wolfpack, simply do an `npm install wolfpack`. To use it in your applications, just require it in node as you usually do.
To install wolfpack, simply do an `npm install wolfpack --save-dev`. This will add it to your package.json file to the devDependencies section. To use it in your applications, just require it in node as you usually do.

@@ -111,3 +113,3 @@ ```javascript

As stated in the pitch, Wolfpack lets you instantiate your Sails model so that you can test your model without having to connect to a database.
As stated in the pitch, Wolfpack lets you instantiate your Sails model so that you can test your model without having to connect to a database, by basically mocking and stubbing everything it can.

@@ -132,5 +134,5 @@ If you have Backbone testing backgrounds, this will be familiar to you. When testing a backbone model or collection, you instantiate it and provide mock data to test the methods. Rarely do you need your model or collection to communicate with the server to provide the results. That's because you want to test your model or collection, not how or if backbone is doing what it is supposed to do.

This will in return give you an Instantiated model you can use to test the model itself, or controller behvaiour, with all the model methods originally provided by Sails, such as `findOne`, `create`, `find`, and dynamic finders. You can read more of the methods provided by Sails in their [Models documentation](http://sailsjs.org/#!documentation/models).
This will in return give you an instantiated model you can use to test the model itself, or controller behvaiour, with all the model methods originally provided by Sails, such as `findOne`, `create`, `find`, and dynamic finders. You can read more of the methods provided by Sails in their [Models documentation](http://sailsjs.org/#!documentation/models).
The best part of this is that wolfpack, by default, spies on ALL methods, whether they were provided by Sails, you or your instance. The spies are provided by SinonJS, which therefore lets you know if a given method is called or not, with what it was called, etc. To know what properties and methods are available for your spies, please read the [SinonJS spies documentation](http://sinonjs.org/docs/#spies-api).
The best part of this is that wolfpack, by default, spies on ALL methods, whether they were provided by Sails, you or your instance. The spies are provided by SinonJS, which therefore lets you know if a given method is called or not, with what arguments it was called, etc. To know what properties and methods are available for your spies, please read the [SinonJS spies documentation](http://sinonjs.org/docs/#spies-api).

@@ -141,3 +143,3 @@ ## API

The wolfpack constructor allows you to instantiate a spied upon Sails model. You use by calling `wolfpack('path_to_model' || object)` and pass it either a string with the location of the model, or an object from which to build the model. All class and instance methods are spied on with [SinonJS spies](http://sinonjs.org/docs/#spies-api). Once instantiated, you can make your usual model calls.
The wolfpack constructor allows you to instantiate a spied upon Sails model. You use it by calling `wolfpack('path_to_model' || object)` and pass as an argument either a string with the location of the model, or an object from which to build the model. All class and instance methods are spied on with [SinonJS spies](http://sinonjs.org/docs/#spies-api). Once instantiated, you can make your usual model calls.

@@ -149,3 +151,3 @@ ```javascript

MyModel.find({name: 'test'}).done(function(err, results){
MyModel.find({name: 'test'}).then(function(results){
// ... more code ...

@@ -155,3 +157,3 @@ });

For testing ccontrollers, you instantiate your model globally as Sails does on the background, like this:
For testing controllers, you instantiate your model globally as Sails does on the background, like this:

@@ -167,9 +169,9 @@ ```javascript

For more information and examples on how to test, please go forward and read the examples sections, were we present several samples on how to use wolpack to test controllers, model classes, and instances of models.
For more information and examples on how to test, please go forward and read the examples sections, where we present several samples on how to use wolpack to test controllers, model classes, and instances of models.
### wolfpack().setFindResults(object || array of objects)
### wolfpack.setFindResults(object1 [,object2, object3, ...])
The `wolfpack().setFindResults` allows you to mock/fake data coming from the database. In other words, you can fake data coming from the database, and Sails will treat it as real data and build and instance from it (or not).
The `wolfpack.setFindResults` methods allows you to mock/fake data coming from the database. In other words, you can fake data coming from the database, and Sails will treat it as real data and build and instance from it (or not).
To fake the data, use `wolfpack().setFindResults(results)` where `results` is an object or an array of objects with the response "coming" from the database. Please notice that __no arguments__ are passed to the `wolfpack` function.
To fake the data, use `wolfpack.setFindResults(results)` where `results` is an object or an array of objects with the response "coming" from the database.

@@ -182,6 +184,6 @@ ```javascript

// Lets fake data from the db
wolfpack().setFindResults({name: 'John'});
wolfpack.setFindResults({name: 'John'});
MyModel.find({id:1}).done(function(err, results){
// results will be {name: 'John'}
MyModel.findOne(1).then(function(result){
// result will be {name: 'John'}
});

@@ -191,8 +193,26 @@

Please note, if you set any results with the `wolfpack().setFindResults` method, __all future find calls to any model__ will return those results. If you call it to set other results, then those results will always be returned, and so on. To stop sending those fake results, use the `wolfpack().clearResults` method.
As of version 0.3.0, you can now send a series of results to be mocked by wolfpack by passing each result as an individual argument. This means that serialized find calls will get different results according to what you set. Take a look at the following example:
### wolfpack().setCreateResults(object || array of objects)
```javascript
var wolfpack = require('wolfpack');
Just as the `wolfpack().setFindResults`, the `wolfpack().createResults` method will allow you to set the fake db response for any create operation. As the argument, you pass an object or array of objects for the response you want.
var MyModel = wolfpack('path_to_app/api/models/MyModel');
// Lets fake data from the db
wolfpack.setFindResults({name: 'John'}, {name:'Doe'}, {name: Jane});
MyModel.findOne(1).then(function(result){
// result will be {name: 'John'}
MyModel.findOne(9).then(function(second_result){ // the 9 is just a random number
// second_result will be {name: 'Doe'}
MyModel.findOne(1).then(function(third_result){ // no matter what we send, it will pull from the results given
// third_result will be {name: 'Jane'}
});
});
});
```
When Wolfpack runs out of results (in the example above after the third call), it will simply return the last given result always, meaning `{name: 'Jane'}` will be the result it will give from now on, once the pool of results have been used.
It is also important to note that the serialized results will apply to any model, so you can test actions in multiple model this way:
```javascript

@@ -202,8 +222,31 @@ var wolfpack = require('wolfpack');

var MyModel = wolfpack('path_to_app/api/models/MyModel');
var MyOtherModel = wolfpack('path_to_app/api/models/MyOtherModel');
// Lets fake data from the db
wolfpack().setCreateResults({name: 'John'});
wolfpack.setFindResults({id: 1, name: 'John'}, {ownerId: 1, cash: 300});
MyModel.create({name: 'Doe'}).done(function(err, results){
// Results will be {name: 'John', updatedAt: someDate, createdAt: someDate}
MyModel.findOne(1).then(function(result){
// result will be {id: 1, name: 'John'}
MyOtherModel.findOne(9).then(function(second_result){ // the 9 is just a random number
// second_result will be {ownerId: 1, cash: 300}
});
});
```
Please note, if you set any results with the `wolfpack.setFindResults` method, __all future find calls to any model__ will return those results, with some exceptions explained later. If you call it to set other results, then those results will always be returned, and so on. To stop sending those fake results, use the `wolfpack.clearResults` method.
### wolfpack.setCreateResults(object1 [, object2, object3, ...])
Just as `wolfpack.setFindResults`, the `wolfpack.createResults` method will allow you to set the fake db response for any create operation. You can pass one object, or a series of objects as individual arguments to set the results that will be return on a create operation.
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
// Lets fake data from the db
wolfpack.setCreateResults({name: 'John'});
MyModel.create({name: 'Doe'}).then(function(result){
// Result will be {name: 'John', updatedAt: someDate, createdAt: someDate}
// Notice that even though we sent 'Doe' as name, the setCreateResults returned 'John' as we requested

@@ -215,8 +258,28 @@ });

### wolfpack().setUpdateResults(object || array of objects)
Also, as with `setFindResults`, you can pass a series of results to be return in a series of create operations (useful when you have operations on separate models):
```javascript
var wolfpack = require('wolfpack');
The `wolfpack().setUpdateResults` allows you to set the fake db results for all update operations. You pass an object or array of objects for the results you want to fake.
var MyModel = wolfpack('path_to_app/api/models/MyModel');
var MySecondaryModel = wolfpack('path_to_app/api/models/MySecondaryModel');
It is __important__ that, when faking update actions, the instantiated model has an id set. Otherwise, the update action will throw an error.
// Lets fake data from the db
wolfpack.setCreateResults({id: 1, name: 'John'}, {ownerId: 1, cash: 200});
MyModel.create({name: 'Doe'}).then(function(result){
// Result will be {name: 'John', updatedAt: someDate, createdAt: someDate}
// Notice that even though we sent 'Doe' as name, the setCreateResults returned 'John' as we requested
MySecondaryModel.create({ownerId:1}).then(function(second_result){
// second_result will be {ownerId: 1, cash:200}
});
});
```
### wolfpack.setUpdateResults(object1 [, object2, object3, ...])
The `wolfpack.setUpdateResults` method allows you to set the fake db results for all update operations. You pass an object or array of objects as individual arguments for the results you want to fake. Pass several arguments to mock the results in the order of the arguments.
It is __important__ that, when you are faking update actions, you set an id for the instantiated model. Otherwise, the update action will throw an error.
```javascript

@@ -228,7 +291,7 @@ var wolfpack = require('wolfpack');

// First we need a model to update, so lets fake it
wolfpack().setFindResults({id: 1, name: 'John'});
wolfpack().setUpdateResults({id: 1, name: 'Johnson'});
wolfpack.setFindResults({id: 1, name: 'John'}); // Notice the id <= extremely important
wolfpack.setUpdateResults({id: 1, name: 'Johnson'});
// Now we need an instantiated model to perform the update, so lets "find" one
MyModel.findOne(1).done(function(err, model){
MyModel.findOne(1).then(function(model){
model.name = 'Doe'; // Our fake will not return this because the setUpdateResults says something different

@@ -245,5 +308,5 @@ // Now we can update

### wolfpack().clearResults()
### wolfpack.clearResults()
The `wolfpack().clearResults` method clears any fake db responses that have been previously set by any or all of the `setFindResults`, `setCreateResults`, and/or `setUpdateResults` methods.
The `wolfpack.clearResults` method clears any fake db responses that have been previously set by any or all of the `setFindResults`, `setCreateResults`, and/or `setUpdateResults` methods, no exceptions whatsoever.

@@ -256,13 +319,13 @@ ```javascript

// Lets fake some responses
wolfpack().setFindResults({id: 1, name: 'John'});
wolfpack().setCreateResults({name: 'myself'});
wolfpack.setFindResults({id: 1, name: 'John'});
wolfpack.setCreateResults({name: 'myself'});
// Now let's clear those responses
wolfpack().clearResults();
wolfpack.clearResults();
MyModel.find({id: 1}).done(function(err, results){
MyModel.find({id: 1}).then(function(results){
// results will be []
});
MyModel.create({name: 'Awesome developer'}).done(function(err, results){
MyModel.create({name: 'Awesome developer'}).then(function(results){
// results will be {name: 'Awesome developer', updatedAt: someDate, createdAt: someDate}

@@ -272,5 +335,5 @@ });

### wolfpack().setErrors(errors)
### wolfpack.setErrors(errors)
The `wolfpack().setErrors` method allows you to fake an error or group of errors coming from the database. This way, you can test your failure scenarios.
The `wolfpack.setErrors` method allows you to fake an error or group of errors coming from the database. This way, you can test your failure scenarios.

@@ -285,8 +348,5 @@ To use it, pass as an argument that will be the fake error coming from the database.

// Lets fake a db error now
wolfpack().setErrors('DB_CONNECTION_ERROR');
MyModel.findOne(1).done(function(err, results){
// Now we are getting the err arguments, so we can handle it
if (err) {
// lets do our error handling
}
wolfpack.setErrors('DB_CONNECTION_ERROR');
MyModel.findOne(1).catch(function(err){
// Now we are getting the err so we can handle it
});

@@ -299,4 +359,6 @@ ```

### wolfpack().clearErrors()
Please note that right now it is not possible to send a series of errors just like with the set*Result methods. However it is planned for a future release to allow this as well.
### wolfpack.clearErrors()
When you no longer want the to fake errors, you can call the `clearErrors` method which will stop sending errors to your model calls.

@@ -310,17 +372,19 @@

// Lets fake a db error now
wolfpack().setErrors('DB_CONNECTION_ERROR');
wolfpack.setErrors('DB_CONNECTION_ERROR');
// Now lets reset it
wolfpack().clearErrors();
wolfpack.clearErrors();
MyModel.findOne(1).done(function(err, results){
MyModel.findOne(1).then(function(results){
// Now we should get results again
}).catch(function(err){
// Nope, sorry no errors to catch
});
```
### wolfpack().spy('find | create |update | destroy')
### wolfpack.spy('find | create |update | destroy')
There might be situations in which we need to know if a certain CRUD operation is being performed. For example, when calling the save method of a model, we want to be sure that the proper parameters are being called on save. In those scenarios, it is useful to test what update operation is happening in the adapter.
Wolfpack provides the `spy` method which in allows spying all four CRUD operations in the adapter. As the argument, you send which operation you want to check. The available operations are `find`, `create`, `update`, and `destroy`.
Wolfpack provides the `spy` method which allows for spying all four CRUD operations in the adapter. As the argument, you send which operation you want to check. The available operations are `find`, `create`, `update`, and `destroy`.

@@ -333,5 +397,5 @@ ```javascript

// Lets spy on the create
var spy = wolfpack().spy('create');
var spy = wolfpack.spy('create');
wolfpack().create({name: 'test'}).done(function(err, results){
wolfpack.create({name: 'test'}).then(function(results){
// Lets see if the parameters were sent correctly

@@ -342,3 +406,3 @@ return spy.calledWith({name: 'test'}); // returns true

### wolfpack().resetSpy('find | create | update | destroy')
### wolfpack.resetSpy('find | create | update | destroy')

@@ -353,7 +417,7 @@ Since in wolfpack all operations are spied upon, including CRUDs, there might be some cases in which you need your CRUD spy to be set to its beginning value for easier testing. For those cases you can use the `resetSpy` method.

// Lets spy on the create
var spy = wolfpack().spy('create');
var spy = wolfpack.spy('create');
wolfpack().create({name: 'test'}).done(function(err, results){
MyModel.create({name: 'test'}).then(function(results){
spy.called; // returns true
wolfpack().resetSpy('create');
wolfpack.resetSpy('create');
spy.called; // returns false

@@ -363,5 +427,5 @@ });

### wolfpack().resetSpies()
### wolfpack.resetSpies()
The `resetSpies` methods resets all CRUD spies at once, so you don't have to call them one by one.
The `resetSpies` method resets all CRUD spies at once, so you don't have to call them one by one.

@@ -374,7 +438,7 @@ ```javascript

// Lets spy on the create
var spy = wolfpack().spy('create');
var spy = wolfpack.spy('create');
wolfpack().create({name: 'test'}).done(function(err, results){
MyModel.create({name: 'test'}).then(function(results){
spy.called; // returns true
wolfpack().resetSpies();
wolfpack.resetSpies();
spy.called; // returns false

@@ -384,2 +448,176 @@ });

## Conditional Mocking of results
Version 0.3.0 introduced serialized results for set*Results operations. This works great when our models execute calls in a sequential fashion, but what if we have parallel Model actions performing? Afterall,
javascript is an asynchronous language so there might be many cases in which two or more models are performing calls to the db in parallel.
For this scenario, serialized results won't work because we cannot predict the order the Model requests are gonna be fulfilled. To handle that, Wolfpack introduces __conditional results__. Lets see how they work.
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
// When Im looking for id 3, I want to return this result
MyModel.setFindResults({id: 3}, {id: 3, name: 'John'});
// When Im looking for name John, I want these results
MyModel.setFindResults({name: 'John'}, [{id: 3, name: 'John'}, {id: 5, name: 'John'}]);
MyModel.find({
name: 'John'
}).then(function(results){
// results will be [{id: 3, name: 'John'}, {id: 5, name: 'John'}]
});
MyModel.find({
id: 3
}).then(function(results){
// results will be [{id: 3, name: 'John'}]
});
MyModel.find({
id: 5
}).then(function(results){
// results will be [] as we didn't set up any conditions for id=5
});
```
When Wolfpack first instantiates a Sails model, it adds its own conditional result methods. Therefore, every Sails model created by Wolfpack will have the `Model.setFindResults`, `Model.setCreateResults` and `Model.setUpdateResults` methods available.
Conditional results have precedence over global results (the ones set by `wolfpack.set*Results`) as they tend to specific needs. If no conditional result is found for the params given, it will then
look for results in the global space. If no results have been set in the global space, then it will return empty or the object, depending on the operation.
It is important to mention that conditional results will remain in use until a `wolfpack.clearResults()` is called. This will reset ALL conditional results for ALL models and operations.
### Model.setFindResults(when, result)
The most basic conditional operation is the `setFindResults`. This method expects two arguments, a __when__ argument which is an object describing the conditions the parameters that need to be sent to the find operation in order to return the conditional result, and the __result__ argument
which is the object that will be instantiated and return when the condition is met.
You can set multiple conditions by calling the method several times. __Note__: Chaining is not supported.
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
// When Im looking for id 3, I want to return this result
MyModel.setFindResults({id: 3}, {id: 3, name: 'John'});
// When Im looking for name John, I want these results
MyModel.setFindResults({name: 'John'}, [{id: 3, name: 'John'}, {id: 5, name: 'John'}]);
MyModel.find({
name: 'John'
}).then(function(results){
// results will be [{id: 3, name: 'John'}, {id: 5, name: 'John'}]
});
MyModel.find({
id: 3
}).then(function(results){
// results will be [{id: 3, name: 'John'}]
});
MyModel.find({
id: 5
}).then(function(results){
// results will be [] as we didn't set up any conditions for id=5
});
```
Unlike `wolfpack.set*Results` operations, you can only send one result per expectation, and it can be either an object, or an array of objects.
### Model.setCreateResults(when, result)
`Model.setCreateResults` allow you to conditionally define what result will be given when a create operation is called with the given conditioned arguments. It expects a when argument and a results argument.
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
MyModel.setCreateResults({name: 'John'}, {id: 3, name: 'John', date: new Date('2010-01-01')});
MyModel.setCreateResults({name: 'John', date: new Date('2014-10-10')}, {id: 5, name: 'John', date: new Date('2014-10-10')});
MyModel.create({
name: 'John'
}).then(function(results){
// results will be {id: 3, name: 'John', date: Fri Jan 01 2010 00:00:00 },
});
MyModel.create({
name: 'John'
}).then(function(results){
// results will be {id: 5, name: 'John', date: Fri Oct 10 2014 00:00:00}
});
MyModel.create({
name: 'Jane',
date: new Date('2000-01-01')
}).then(function(results){
// results will be {name: 'Jane', date: Sat Jan 01 00:00:00} as we did not set the result for this condition
});
```
### Model.setUpdateResults(when, result)
`Model.setUpdateResults` allow you to conditionally define what result will be given when an update operation is called with the given conditioned arguments. It expects a when argument and a results argument.
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
wolfpack.setFindResults({id: 1, name: 'John', cash: 200});
MyModel.setUpdateResults({name: 'Jane'}, {id: 1, name: 'Jane', cash: 400});
MyModel.setUpdateResults({name: 'Doe'}, {id: 1, name: 'Doe', cash: 300});
// First we need the model to update
MyModel.findOne(1).then(function(model){
// model will be {id: 1, name: 'John', cash: 200}
model.name = 'Jane';
model.save(function(err, results){
// results will be {id: 1, name: 'Jane', cash: 400}
});
});
MyModel.findOne(1).then(function(model){
// model will be {id: 1, name: 'John', cash: 200}
model.name = 'Doe';
model.save(function(err, results){
// results will be {id: 1, name: 'Doe', cash: 300}
});
});
```
`Model.setUpdateResults` is a little bit more trickier than `setFindResults` and `setCreateResults` in that multiple paramaters sent in the when condition might not yield the expected results. Let's look at the following example:
```javascript
var wolfpack = require('wolfpack');
var MyModel = wolfpack('path_to_app/api/models/MyModel');
wolfpack.setFindResults({id: 1, name: 'John', cash: 200});
MyModel.setUpdateResults({cash: 200}, {id: 5, name: 'Jane', cash: 200});
MyModel.setUpdateResults({cash: 200, name: 'Doe'}, {id: 3, name: 'Doe', cash: 200});
MyModel.findOne(1).then(function(model){
// model will be {id: 1, name: 'John', cash: 200}
model.name = 'Doe';
model.cash = 200;
model.save(function(err, results){
// results will be {id: 5, name: 'Jane', cash: 200}
// which are not the results you expect
});
});
```
Why in the above example did it not pick my condition if it was clearly stated? Unfortunately, the wolfpack adapter does not know what are the fields you are changing. It only
receives the updated arguments. It then goes and lookup in its condition for the first one that meets the criteria, in this case it was `{cash:200}` without the name parameter.
When working with mocking conditional update results, it is best that all conditions have the same number of parameters. Better yet, it is better if you only work with IDs, but
wolfpack gives you the flexibility to work with additional parameters if you like. Just bear in mind these special conditions that can cause unwanted results in your tests.
## Examples

@@ -389,5 +627,5 @@

Wolfpack provides an adapter which mocks a database. This allow us to predetermine the data we are expecting back from the database, without the need of one. In other words, we can tell wolfack to give the model certain results when it performs an operation. We do it by using _result operators_, as shown below.
Wolfpack provides an adapter which mocks a database. This allows us to predetermine the data we are expecting back from the database, without the need of one. In other words, we can tell wolfack to give the model certain results when it performs an operation. We do it by using _result operators_, as shown below.
To set the results for a find operation, we use `wolfpack().setFindResults({results: 'we want'})`. Please note that no arguments are provided to the wolfpack function.
To set the results for a find operation, we use `wolfpack.setFindResults({results: 'we want'})`.

@@ -400,5 +638,5 @@ ```javascript

// Set results for a find operation
wolfpack().setFindResults({id:1, name:'John Doe'});
wolfpack.setFindResults({id:1, name:'John Doe'});
Model.find({id: 1}).done(function(err, result){
Model.find({id: 1}).then(function(result){
return result; // {id: 1, name: 'John Doe'}

@@ -417,5 +655,5 @@ });

// Set results for a create operation
wolfpack().setCreateResults({id:1, name:'John Doe'});
wolfpack.setCreateResults({id:1, name:'John Doe'});
Model.create({name: 'A completely different name'}).done(function(err, result){
Model.create({name: 'A completely different name'}).then(function(result){
return result; // {id: 1, name: 'John Doe'}

@@ -425,7 +663,7 @@ });

// To set the results for an update, we first need to mockup a find operation
wolfpack().setFindResults({id: 2, name: 'Myself'});
wolfpack.setFindResults({id: 2, name: 'Myself'});
// Now we can set the results for the update
wolfpack().setUpdateResults({id: 2, name: 'Grumpy cat'});
Model.findOne(2).done(function(err, model){
wolfpack.setUpdateResults({id: 2, name: 'Grumpy cat'});
Model.findOne(2).then(function(model){
// Model will be {id: 2, name: 'Myself'}

@@ -448,11 +686,11 @@ model.name = 'Not me';

Model.find().done(function(err, results){
Model.find().then(function(results){
return results; // returns []
});
Model.findOne(1).done(function(err, results){
Model.findOne(1).then(function(results){
return results; // returns undefined
});
Model.create({name:'John'}).done(function(err, results){
Model.create({name:'John'}).then(function(results){
return results; // returns {name: 'John', updatedAt: date(), createdAt: date()}

@@ -462,4 +700,4 @@ });

// For updates we need a result first, so we need to mock
wolfpack().setFindResults({id: 1, name: 'Test'});
Model.findOne(1).done(function(err, model){
wolfpack.setFindResults({id: 1, name: 'Test'});
Model.findOne(1).then(function(model){
model.name = 'Another name';

@@ -472,3 +710,3 @@ model.save(function(err, results){

Finally, there might be situations in which we no longer want to mock results, after we've mocked some. This because once we set a mock, wolfpack will always return that mock for that operation, no matter where in our tests we are. In some cases we need to clear those results. Wolfpack therefore provides a method to clear the mocks: `clearResults`.
Finally, there might be situations in which we no longer want to mock results, after we've mocked some. This is because once we set a mock, wolfpack will always return that mock for that operation, no matter where in our tests we are. In some cases we need to clear those results. Wolfpack therefore provides a method to clear the mocks: `clearResults`.

@@ -481,4 +719,4 @@ ```javascript

// Lets set a mock result first
wolfpack().setFindResults({id: 1, name: 'Test'});
Model.findOne(1).done(function(err, result){
wolfpack.setFindResults({id: 1, name: 'Test'});
Model.findOne(1).then(function(result){
return result; // returns {id: 1, name: 'Test'}

@@ -488,4 +726,4 @@ });

// Great! Now I dont want to use any more mock results
wolfpack().clearResults();
Model.findOne(1).done(function(err, results){
wolfpack.clearResults();
Model.findOne(1).then(function(results){
return results; // returns undefined

@@ -507,10 +745,9 @@ });

// I need to test errors, so Im gonna set one
wolfpack().setErrors('MySQL is having an identity crisis right now');
wolfpack.setErrors('MySQL is having an identity crisis right now');
try {
Model.findOne(1).done(function(err, results){
if (err) { // This is true
throw new Error(err + ': Its calling itself Maria'); // It will throw
}
return results;
Model.findOne(1).then(function(results){
// It wont get here
}).catch(function(err){
throw new Error(err + ': Its calling itself Maria'); // It will throw
});

@@ -523,8 +760,7 @@ } catch (e) {

try {
Model.create({name: 'test'}).done(function(err, results){
if (err) { // still true
throw new Error('Still in crisis'); // Will throw as
}
return results;
});
Model.create({name: 'test'}).then(function(results){
// wont even get here
}).catch(function(e){
throw new Error('Still in crisis'); // Will throw as we haven't done anything to remove the errors
});
} catch (e) {

@@ -542,10 +778,9 @@ console.log(e); // Still outputing the error

// I need to test errors, so Im gonna set one
wolfpack().setErrors('MySQL is having an identity crisis right now');
wolfpack.setErrors('MySQL is having an identity crisis right now');
try {
Model.findOne(1).done(function(err, results){
if (err) { // This is true
throw new Error(err + ': Its calling itself Maria'); // It will throw
}
return results;
Model.findOne(1).then(function(results){
// wont get here
}).catch(function(err){
throw new Error(err + ': Its calling itself Maria'); // It will throw
});

@@ -559,7 +794,6 @@ } catch (e) {

try {
Model.create({name: 'test'}).done(function(err, results){
if (err) { // not true anymore
throw new Error('Still in crisis');
}
Model.create({name: 'test'}).then(function(results){
return 'Ok, it just composed itself!'; // It will return
}).catch(function(err){
throw new Error('Still in crisis'); // no errors now so it wont throw
});

@@ -661,3 +895,3 @@ } catch (e) {

The above test will pass because arguments are provided in the correct order to the model. Now, let's notice something here. If you look closely to the tests, I'm only testing exactly what the controller should be doing, and not the model itself. My ChatController#addUser should use the Chatroom model to add the user. The controller does this by calling the Chatroom#addUser method. Right now my only concern is that the controller calls the model's method thats supposed to add the user. I don't care if the model adds it or not; I'm not testing if the model is working. I'm testing that the controllers does what it is supposed to do, and focus only on controller testing. Whethere the model is working or not, that's another test done separately.
The above test will pass because arguments are provided in the correct order to the model. Now, let's notice something here. If you look closely to the tests, I'm only testing exactly what the controller should be doing, and not the model itself. My ChatController#addUser should use the Chatroom model to add the user. The controller does this by calling the Chatroom#addUser method. Right now my only concern is that the controller calls the model's method thats supposed to add the user. I don't care if the model adds it or not; I'm not testing if the model is working. I'm testing that the controller does what it is supposed to do, and focus only on controller testing. Whether the model is working or not, that's another test done separately.

@@ -698,7 +932,7 @@ The beauty of wolfpack for controller testing is that it instantiates an actual sails Model for us, so we can test custom methods and sails methods happening in the controller. It also spies on every method so we can keep track of what's going on in the application. That means we can have a controller that uses a `findOne` model method, and we would only care that it is using the method correctly, for example:

One thing we shouldn't forget is that Sails model operations are asynchronous, therefore if we want our test to behave correctly, we should treat them as asynchronous operations.
One thing we shouldn't forget is that Sails model operations are asynchronous, therefore if we want our tests to behave correctly, we should treat them as asynchronous operations.
In the controller test of the previous section, we have to events that we need to test that occur asynchronously, the 200 OK response, and the 403 Forbidden response. They both happen after we've search through our fake db for results, and called the callback function.
In the controller test of the previous section, we have two events that we need to test that occur asynchronously, the 200 OK response, and the 403 Forbidden response. They both happen after we've searched through our fake db for results, and called the callback function.
Asynchronous operations are dealed differently on test regarding the tools you use. For these examples, I'm using jasmine's async functions and sinonJS as well.
Asynchronous operations are dealed differently on tests regarding the tools you use. For these examples, I'm using jasmine's async functions and sinonJS as well.

@@ -737,3 +971,3 @@ ```javascript

// In one test we'll mock errors, so lets reset error states
wolfpack().clearErrors();
wolfpack.clearErrors();
});

@@ -753,3 +987,3 @@

// Lets mock some results
wolfpack().setFindResults({id: 1, room_name: 'awesome', users:[1,2,3]});
wolfpack.setFindResults({id: 1, room_name: 'awesome', users:[1,2,3]});
// Now test

@@ -773,3 +1007,3 @@ ChatController.addUser(request, response);

// First, lets mock the error
wolfpack().setErrors('You broke the internet');
wolfpack.setErrors('You broke the internet');

@@ -819,7 +1053,3 @@ // Lets run our action.

addUser: function(username, chatroom, callback) {
this.findOne({room_name: chatroom}).done(function(err, room){
if (err) {
return callback(err.message || err);
}
this.findOne({room_name: chatroom}).then(function(room){
if (room === undefined) {

@@ -836,2 +1066,4 @@ return callback('Room not found');

room.addUser(username, callback);
}).catch(function(err){
return callback(err.message || err);
});

@@ -844,3 +1076,3 @@ }

There's a couple of stuff we need to test in our model. We need to test the class method is doing what it is supposed to, and we need to test the instance method is doing what it is supposed to. Lets write first the test for the class.
There's a couple of stuff we need to test in our model. We need to test that the class method is doing what it is supposed to do, and we need to test the instance method is doing what it is supposed to. Lets write first the test for the class.

@@ -858,8 +1090,8 @@ ```javascript

// Lets stub a callback function to see whats happening in the results
var callback = sinon.stub();;
var callback = sinon.stub();
beforeEach(function(){
// Lets clear all mock results and errors
wolfpack().clearResults();
wolfpack().clearErrors();
wolfpack.clearResults();
wolfpack.clearErrors();

@@ -934,3 +1166,3 @@ // And reset our callback spy

// Lets mock so we have at least 4 users
wolfpack().setFindResults({room_name: 'awesome', id: 1, users: [1,2,3,4]});
wolfpack.setFindResults({room_name: 'awesome', id: 1, users: [1,2,3,4]});

@@ -973,3 +1205,3 @@ runs(function(){

Really long test that one, especially the last part. However, are you may soon be noticing, we are achieving 100% test coverage, which was something really hard to do before.
Really long test that one, especially the last part. However, as you may soon be noticing, we are achieving 100% test coverage, which was something really hard to do before.

@@ -996,3 +1228,3 @@ We still need to test the instance method. All instances get the save method. We could try and test the save method, but since no parameters are passed to it, we cannot be actually sure if it is creating or updating a record.

function async(err, results) {
function async(results) {
ready = true;

@@ -1020,3 +1252,3 @@ model = results;

runs(function(){
Chatroom.findOne(1).done(async);
Chatroom.findOne(1).then(async);
});

@@ -1038,3 +1270,3 @@

runs(function(){
var update = wolfpack().spy('update');
var update = wolfpack.spy('update');
expect(update.lastCall.args[2].users).toEqual([1,2,3,4,'test']);

@@ -1045,2 +1277,2 @@ });

});
```
```

@@ -62,3 +62,25 @@ var wolfpack = require(__dirname + '/../../lib/wolfpack');

it("should provide spies for custom model methods", function(){
it("should remove any associations and the associated attribute to json type", function(){
var Assoc1 = wolfpack({
attributes: {
name: {
type: 'string'
},
association: {
via: 'model',
dominant: true,
collection: 'test',
model: 'another'
}
}
});
var attributes = Assoc1._attributes;
expect(attributes.association.dominant).not.toBeDefined();
expect(attributes.association.via).not.toBeDefined();
expect(attributes.association.model).not.toBeDefined();
expect(attributes.association.collection).not.toBeDefined();
expect(attributes.association.type).toEqual('json');
});
it("should provide spies for custom class methods", function(){
var Model = wolfpack(__dirname + '/../fixtures/Model');

@@ -96,3 +118,3 @@ expect(Model.modelMethod.called).toBeDefined();

describe('when called without arguments', function(){
describe('when called the object itself', function(){
var Model = wolfpack(__dirname + '/../fixtures/Model');

@@ -127,14 +149,25 @@ var ready, data, fixture, errors;

beforeEach(function(){
fixture = {name: 'My Name', date: new Date()};
fixtures = [
{
name: 'My Name', date: new Date()
},
{
name: 'Another Name', date: new Date('2011-01-01')
},
{
name: 'Last Name', date: new Date('2012-01-01')
}
];
fixture = fixtures[0];
data = null;
ready = false;
errors = null;
wolfpack().clearErrors();
wolfpack().clearResults();
wolfpack.clearErrors();
wolfpack.clearResults();
});
it("should provide a way to set the results given by the ORM", function(){
expect(wolfpack().setFindResults).toBeDefined();
expect(wolfpack().setCreateResults).toBeDefined();
expect(wolfpack().setUpdateResults).toBeDefined();
expect(wolfpack.setFindResults).toBeDefined();
expect(wolfpack.setCreateResults).toBeDefined();
expect(wolfpack.setUpdateResults).toBeDefined();
});

@@ -144,11 +177,11 @@

var passing_obj = function() {
wolfpack().setFindResults({name: 'test'});
wolfpack.setFindResults({name: 'test'});
};
var passing_arr = function() {
wolfpack().setFindResults([{name: 'test'}]);
wolfpack.setFindResults([{name: 'test'}]);
};
var failing = function() {
wolfpack().setFindResults(123);
wolfpack.setFindResults(123);
};

@@ -164,4 +197,4 @@

runs(function(){
wolfpack().setFindResults(fixture);
Model.findOne(1).then(async).fail(asyncErr);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(async).catch(asyncErr);
});

@@ -179,4 +212,4 @@

runs(function(){
wolfpack().setCreateResults(fixture);
Model.create(fixture).then(async).fail(asyncErr);
wolfpack.setCreateResults(fixture);
Model.create(fixture).then(async).catch(asyncErr);
});

@@ -187,4 +220,4 @@

runs(function(){
expect(data[0].name).toBe(fixture.name);
expect(data[0].date).toBe(fixture.date);
expect(data.name).toBe(fixture.name);
expect(data.date).toBe(fixture.date);
});

@@ -197,4 +230,4 @@ });

fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack().setUpdateResults({id:1, name: 'Test', date: new Date()});
wolfpack.setFindResults(fixture);
wolfpack.setUpdateResults({id:1, name: 'Test', date: new Date()});
Model.findOne(1).then(function(model){

@@ -217,7 +250,210 @@ model.name = 'Test';

describe('when multiple results are given', function(){
// Find
it("should return the results in the order they were given (find)", function(){
runs(function(){
wolfpack.setFindResults.apply(this, fixtures);
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.findOne(2).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.findOne(3).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
it("should return an the last given result when all previous have been returned (find)", function(){
runs(function(){
wolfpack.setFindResults.apply(this, fixtures);
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.findOne(2).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.findOne(3).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
asyncReset();
Model.findOne(3).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
// Create
it("should return the results in the order they were given (create)", function(){
runs(function(){
wolfpack.setCreateResults.apply(this, fixtures);
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
it("should return an the last given result when all previous have been returned (create)", function(){
runs(function(){
wolfpack.setCreateResults.apply(this, fixtures);
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
asyncReset();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
// Update
it("should return the results in the order they were given (update)", function(){
function update(model) {
model.name = 'Test';
model.date = Date('2000-01-01');
model.save(function(err, results){
async(results);
});
}
runs(function(){
fixture.id = 1;
wolfpack.setUpdateResults.apply(this, fixtures);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
it("should return an the last given result when all previous have been returned (update)", function(){
function update(model) {
model.name = 'Test';
model.date = Date('2000-01-01');
model.save(function(err, results){
async(results);
});
}
runs(function(){
fixture.id = 1;
wolfpack.setUpdateResults.apply(this, fixtures);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
Model.findOne(1).then(update);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
});
it("if a result is provided, it should be given as an instance of the Model", function(){
wolfpack().setFindResults(fixture);
wolfpack.setFindResults(fixture);
runs(function(){
Model.findOne(1).then(async).fail(asyncErr);
Model.findOne(1).then(async).catch(asyncErr);
});

@@ -244,4 +480,4 @@

runs(function(){
wolfpack().clearResults();
Model.findOne(1).then(async).fail(asyncErr);
wolfpack.clearResults();
Model.findOne(1).then(async).catch(asyncErr);
});

@@ -256,4 +492,4 @@ waitsFor(asyncReady);

runs(function(){
wolfpack().clearResults();
Model.create(fixture).then(async).fail(asyncErr);
wolfpack.clearResults();
Model.create(fixture).then(async).catch(asyncErr);
});

@@ -271,4 +507,4 @@

runs(function(){
wolfpack().setErrors('errors');
Model.create(fixture).then(async).fail(asyncErr);
wolfpack.setErrors('errors');
Model.create(fixture).then(async).catch(asyncErr);
});

@@ -284,5 +520,5 @@ waitsFor(asyncReady);

runs(function(){
wolfpack().clearResults();
wolfpack.clearResults();
fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(function(model){

@@ -304,5 +540,5 @@ model.name = 'another';

fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(function(model){
wolfpack().setErrors('errors');
wolfpack.setErrors('errors');
model.name = 'different';

@@ -322,5 +558,5 @@ model.save(asyncForDone);

fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(function(model){
wolfpack().setErrors('errors');
wolfpack.setErrors('errors');
model.destroy(asyncErr);

@@ -339,3 +575,3 @@ });

fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack.setFindResults(fixture);
Model.findOne(1).then(function(model){

@@ -353,4 +589,4 @@ model.destroy(async);

runs(function(){
wolfpack().setFindResults(fixture);
wolfpack().clearResults();
wolfpack.setFindResults(fixture);
wolfpack.clearResults();
Model.findOne(1).then(async);

@@ -368,4 +604,4 @@ });

runs(function(){
wolfpack().setErrors('error');
Model.findOne(1).then(async).fail(asyncErr);
wolfpack.setErrors('error');
Model.findOne(1).then(async).catch(asyncErr);
});

@@ -383,4 +619,4 @@

runs(function(){
wolfpack().clearErrors();
Model.findOne(1).then(async).fail(asyncErr);
wolfpack.clearErrors();
Model.findOne(1).then(async).catch(asyncErr);
});

@@ -398,4 +634,4 @@

runs(function(){
wolfpack().resetSpies();
spy = wolfpack().spy('find');
wolfpack.resetSpies();
spy = wolfpack.spy('find');
Model.findOne(1).then(async);

@@ -409,5 +645,5 @@ });

// Test if the other CRUD spies have been set
expect(wolfpack().spy('create').called).toBeDefined();
expect(wolfpack().spy('update').called).toBeDefined();
expect(wolfpack().spy('destroy').called).toBeDefined();
expect(wolfpack.spy('create').called).toBeDefined();
expect(wolfpack.spy('update').called).toBeDefined();
expect(wolfpack.spy('destroy').called).toBeDefined();
});

@@ -419,3 +655,3 @@ });

runs(function(){
spy = wolfpack().spy('find');
spy = wolfpack.spy('find');
Model.findOne(1).then(async);

@@ -428,3 +664,3 @@ });

// Reset the spy and see if it resetted
wolfpack().resetSpy('find');
wolfpack.resetSpy('find');
expect(spy.called).toBeFalsy();

@@ -435,8 +671,8 @@ });

it("should provide a way for resetting all CRUD spies at once", function(){
var findSpy = wolfpack().spy('find');
var createSpy = wolfpack().spy('create');
var findSpy = wolfpack.spy('find');
var createSpy = wolfpack.spy('create');
runs(function(){
findSpy = wolfpack().spy('find');
createSpy = wolfpack().spy('create');
findSpy = wolfpack.spy('find');
createSpy = wolfpack.spy('create');
Model.findOne(1).then(async);

@@ -456,3 +692,3 @@ });

runs(function(){
wolfpack().resetSpies();
wolfpack.resetSpies();
expect(findSpy.called).toBeFalsy();

@@ -463,3 +699,498 @@ expect(createSpy.called).toBeFalsy();

it("should provide class methods to allow mocking specific scenarions", function(){
expect(Model.wolfpack).toBeDefined();
expect(Model.wolfpack.setFindResults).toBeDefined();
expect(Model.wolfpack.setCreateResults).toBeDefined();
expect(Model.wolfpack.setUpdateResults).toBeDefined();
});
it("should provide shortcut methods to class mocking result methods", function(){
expect(Model.setFindResults).toBeDefined();
expect(Model.setCreateResults).toBeDefined();
expect(Model.setUpdateResults).toBeDefined();
expect(Model.setFindResults).toEqual(Model.wolfpack.setFindResults);
expect(Model.setCreateResults).toEqual(Model.wolfpack.setCreateResults);
expect(Model.setUpdateResults).toEqual(Model.wolfpack.setUpdateResults);
});
it("should be able to set find results based in a condition", function(){
runs(function(){
Model.setFindResults({id:1}, fixtures[0]);
Model.setFindResults({name: 'test'}, fixtures[1]);
Model.setFindResults({id:1, name: 'test'}, fixtures[2]);
Model.find({id:1}).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data[0].name).toBe(fixtures[0].name);
expect(data[0].date).toBe(fixtures[0].date);
asyncReset();
Model.find({name: 'test'}).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(data[0].name).toBe(fixtures[1].name);
expect(data[0].date).toBe(fixtures[1].date);
asyncReset();
Model.find({name: 'test', id:1}).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(data[0].name).toBe(fixtures[2].name);
expect(data[0].date).toBe(fixtures[2].date);
});
});
it("should be able to set create results based in a condition", function(){
runs(function(){
Model.setCreateResults({name: 'test'}, fixtures[0]);
Model.setCreateResults({date: new Date('2010-09-09')}, fixtures[1]);
Model.setCreateResults({date: new Date('2010-09-09'), name: 'test'}, fixtures[2]);
Model.create({name: 'test'}).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.create({date: new Date('2010-09-09')}).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.create({date: new Date('2010-09-09'), name: 'test'}).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
it("should be able to set update results based in a condition", function(){
runs(function(){
fixture.id = 1;
wolfpack.setFindResults(fixture);
Model.setUpdateResults({name: 'test'}, fixtures[0]);
Model.setUpdateResults({date: new Date('2010-09-09')}, fixtures[1]);
Model.setUpdateResults({date: new Date('2012-01-01'), name: 'test2'}, fixtures[2]);
Model.findOne(1).then(function(model){
model.name = 'test';
model.save(function(err, results){
async(results);
});
});
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[0].name);
expect(data.date).toBe(fixtures[0].date);
asyncReset();
Model.findOne(1).then(function(model){
model.date = new Date('2010-09-09');
model.save(function(err, results){
async(results);
});
});
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[1].name);
expect(data.date).toBe(fixtures[1].date);
asyncReset();
Model.findOne(1).then(function(model){
model.name = 'test2';
model.date = new Date('2012-01-01');
model.save(function(err, results){
async(results);
});
});
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixtures[2].name);
expect(data.date).toBe(fixtures[2].date);
});
});
it("if when setting results for a condition, no condition is given, it should throw", function(){
var failing = function() {
Model.setFindResults(null, fixture);
};
var failing2 = function() {
Model.setCreateResults(null, fixture);
};
var failing3 = function() {
Model.setUpdateResults(null, fixture);
};
expect(failing).toThrow();
expect(failing2).toThrow();
expect(failing2).toThrow();
});
it("if when setting results for a condition, no results are given, it should throw", function(){
var failing = function() {
Model.setFindResults(fixture);
};
var failing2 = function() {
Model.setCreateResults(fixture);
};
var failing3 = function() {
Model.setUpdateResults(fixture);
};
expect(failing).toThrow();
expect(failing2).toThrow();
expect(failing2).toThrow();
});
});
describe('legacy support', function(){
describe('when called without arguments', function(){
var Model = wolfpack(__dirname + '/../fixtures/Model');
var ready, data, fixture, errors;
function async(results) {
ready = true;
data = results;
}
function asyncErr(err) {
errors = err;
ready = true;
}
function asyncForDone(err, results) {
asyncErr(err);
async(results);
}
function asyncReady() {
return ready === true;
}
function asyncReset() {
ready = false;
data = null;
errors = null;
}
beforeEach(function(){
fixture = {name: 'My Name', date: new Date()};
data = null;
ready = false;
errors = null;
wolfpack().clearErrors();
wolfpack().clearResults();
});
it("should provide a way to set the results given by the ORM", function(){
expect(wolfpack().setFindResults).toBeDefined();
expect(wolfpack().setCreateResults).toBeDefined();
expect(wolfpack().setUpdateResults).toBeDefined();
});
it("results provided can only be an object or an array of objects", function(){
var passing_obj = function() {
wolfpack().setFindResults({name: 'test'});
};
var passing_arr = function() {
wolfpack().setFindResults([{name: 'test'}]);
};
var failing = function() {
wolfpack().setFindResults(123);
};
expect(passing_obj).not.toThrow();
expect(passing_arr).not.toThrow();
expect(failing).toThrow();
});
it("should be able to set the results for a find operation to the given values", function(){
runs(function(){
wolfpack().setFindResults(fixture);
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixture.name);
expect(data.date).toBe(fixture.date);
});
});
it("should be able to set the results for a create operation to the given values", function(){
runs(function(){
wolfpack().setCreateResults(fixture);
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixture.name);
expect(data.date).toBe(fixture.date);
});
});
it("should be able to set the results for an update operation to the given values", function(){
var updateResults;
runs(function(){
fixture.id = 1;
wolfpack().setFindResults(fixture);
wolfpack().setUpdateResults({id:1, name: 'Test', date: new Date()});
Model.findOne(1).then(function(model){
model.name = 'Test';
model.save(function(err, data){
updateResults = data;
});
});
});
waitsFor(function(){
return updateResults;
});
runs(function(){
expect(updateResults.name).toBe('Test');
});
});
it("if a result is provided, it should be given as an instance of the Model", function(){
wolfpack().setFindResults(fixture);
runs(function(){
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.save).toBeDefined();
expect(data.name).toBeDefined();
expect(data.date).toBeDefined();
expect(data.instanceMethod).toBeDefined();
});
});
it("if there is a problem during instantiation, it should throw it", function(){
var failing = function() {
wolfpack([]);
};
expect(failing).toThrow();
});
it("if no results are provided for find operation, it should return emtpy", function(){
runs(function(){
wolfpack().clearResults();
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data).not.toBeDefined();
});
});
it("if no results are provided for create operation, it should return the given create values", function(){
runs(function(){
wolfpack().clearResults();
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe(fixture.name);
expect(data.date).toBe(fixture.date);
});
});
it("if an error occurs in create operation, it should return the error and stack", function(){
runs(function(){
wolfpack().setErrors('errors');
Model.create(fixture).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(errors.originalError).toBe('errors');
expect(errors.rawStack).toBeDefined();
});
});
it("if no results are provided for update operation it should return the model values", function(){
runs(function(){
wolfpack().clearResults();
fixture.id = 1;
wolfpack().setFindResults(fixture);
Model.findOne(1).then(function(model){
model.name = 'another';
model.save(asyncForDone);
});
});
waitsFor(asyncReady);
runs(function(){
expect(data.name).toBe('another');
});
});
it("if an error occurs in update operation, it should return the error", function(){
runs(function(){
fixture.id = 1;
wolfpack().setFindResults(fixture);
Model.findOne(1).then(function(model){
wolfpack().setErrors('errors');
model.name = 'different';
model.save(asyncForDone);
});
});
waitsFor(asyncReady);
runs(function(){
expect(errors.originalError).toBe('errors');
expect(errors.rawStack).toBeDefined();
});
});
it("if an error occurs during a destroy operation, it should return the error", function(){
runs(function(){
fixture.id = 1;
wolfpack().setFindResults(fixture);
Model.findOne(1).then(function(model){
wolfpack().setErrors('errors');
model.destroy(asyncErr);
});
});
waitsFor(asyncReady);
runs(function(){
expect(errors.originalError).toBe('errors');
expect(errors.rawStack).toBeDefined();
});
});
it("if no errors occur, it should return nothing as always", function(){
runs(function(){
fixture.id = 1;
wolfpack().setFindResults(fixture);
Model.findOne(1).then(function(model){
model.destroy(async);
});
});
waitsFor(asyncReady);
runs(function(){
expect(data).toBeNull();
});
});
it("should provide a method for resetting results", function(){
runs(function(){
wolfpack().setFindResults(fixture);
wolfpack().clearResults();
Model.findOne(1).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(data).not.toBeDefined();
});
});
it("should provide a way for setting errors", function(){
runs(function(){
wolfpack().setErrors('error');
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(errors.originalError).toBe('error');
expect(errors.rawStack).toBeDefined();
});
});
it("should provide a way for resetting errors", function(){
runs(function(){
wolfpack().clearErrors();
Model.findOne(1).then(async).catch(asyncErr);
});
waitsFor(asyncReady);
runs(function(){
expect(errors).toBeNull();
});
});
it("should provide a way for testing if a CRUD operation was performed in the adapter", function(){
var spy;
runs(function(){
wolfpack().resetSpies();
spy = wolfpack().spy('find');
Model.findOne(1).then(async);
});
waitsFor(asyncReady);
runs(function(){
expect(spy.called).toBeTruthy();
// Test if the other CRUD spies have been set
expect(wolfpack().spy('create').called).toBeDefined();
expect(wolfpack().spy('update').called).toBeDefined();
expect(wolfpack().spy('destroy').called).toBeDefined();
});
});
it("should provide a way for individually resetting CRUD spies", function(){
var spy;
runs(function(){
spy = wolfpack().spy('find');
Model.findOne(1).then(async);
});
waitsFor(asyncReady);
runs(function(){
// Reset the spy and see if it resetted
wolfpack().resetSpy('find');
expect(spy.called).toBeFalsy();
});
});
it("should provide a way for resetting all CRUD spies at once", function(){
var findSpy = wolfpack().spy('find');
var createSpy = wolfpack().spy('create');
runs(function(){
findSpy = wolfpack().spy('find');
createSpy = wolfpack().spy('create');
Model.findOne(1).then(async);
});
waitsFor(asyncReady);
runs(function(){
// Reset async to execute another call
asyncReset();
Model.create({name: 'test'}).then(async);
});
waitsFor(asyncReady);
runs(function(){
wolfpack().resetSpies();
expect(findSpy.called).toBeFalsy();
expect(createSpy.called).toBeFalsy();
});
});
});
});
});
+31
-6
var wolfpack = require(__dirname + '/index');
var Model = wolfpack('spec/fixtures/Model');
wolfpack().setFindResults({id: 1, name: 'My Name', date: new Date()});
var fixtures = [
{id: 1, name: 'My Name', date: new Date()},
{id: 2, name: 'Other name', date: new Date()},
{id: 3, name: 'Last name', date: new Date()}
];
wolfpack.setFindResults(fixtures[0]);
Model.wolfpack.setUpdateResults({name: 'blah'}, fixtures[0]);
Model.wolfpack.setUpdateResults({date: new Date('2009-09-09')}, fixtures[1]);
Model.wolfpack.setUpdateResults({date: new Date('2009-01-01'), name: 'blah'}, fixtures[2]);
try {
Model.findOne(1).then(function(results){
results.name = "Test";
results.save(function(err, data){
console.log(data);
var Assoc1 = wolfpack({
attributes: {
name: {
type: 'string'
},
association: {
via: 'model',
dominant: true,
collection: 'test'
}
}
});
});
debugger;
Model.findOne(1).then(function(model){
model.name = 'blah';
model.date = new Date('2009-01-01');
model.save(function(err, results){
debugger;
});
});
} catch (e) {
debugger;
}