TDD of Sailsjs Waterline Models with Vowsjs - sails.js

My problem is trying to do TDD of Waterline models. The tests I present are just boilerplate to get my suite constructed. Nevertheless, they raise valid issues. The primary problem is that I require the model in the Vows.js test. In the test scope the model is defined, but it does not have any properties inherited from the Waterline package. For example, here is some model code for "EducationLevel":
module.exports = {
migrate: 'safe',
tableName: 'education_levels',
attributes: {
id : { type: 'integer', required: true },
description: { type: 'string', required: true },
display_sort: { type: 'integer', required: true}
}
};
And here are some trial tests:
vows = require('vows')
assert = require('assert')
EducationLevel = require('../api/models/EducationLevel')
vows.describe('tac_models').addBatch({
'EducationLevel model' : {
topic: function(){
educationLevel = EducationLevel.create();
return true;
},
'It exists': function (topic) {
assert.equal(EducationLevel.create,undefined);
assert.equal(EducationLevel.migrate,undefined);
}
}
}).export(module)
When I run the test, the first assertion passed, but the second does not:
vows spec/*
✗
EducationLevel model
✗ It exists
» expected undefined,
got 'safe' (==) // tac_models.js:13
✗ Broken » 1 broken (1.545s)
This shows that the test knows only what is explicitly declared in the EducationLevel definition. The 'migrate' property is defined because I explicitly define it in the code. It does not know about the Waterline method 'create'. How can I remedy this in a way that makes conventional TDD practical?

Related

Input definition optional keys

I generated an action with sails generate action task/update-task. I now am trying to create an input parameter that should be an object with optional keys:
inputs: {
fields: {
type: {
body: 'string?',
rruleSetStr: 'string?',
},
required: true,
description: 'All keys are not required, but at least one is'
},
However I keep getting error:
The action `task/update-task` could not be registered. It looks like a machine definition (actions2), but it could not be used to build an action.
Details: ImplementationError: Sorry, could not interpret "task/update-task.js" because its underlying implementation has a problem:
------------------------------------------------------
• Invalid input definition ("fields"). Unrecognized `type`. (Must be 'string', 'number', 'boolean', 'json' or 'ref'. Or set it to a type schema like `[{id:'number', name: {givenName: 'Lisa'}}]`.)
------------------------------------------------------
If you are the maintainer of "task/update-task.js", then you can change its implementation to solve the problem above. Otherwise, please file a bug report with the maintainer, or fork your own copy and fix that.
[?] See https://sailsjs.com/support for help.
at machineAsAction (C:\Users\Mercurius\Documents\GitHub\Homie-Web\node_modules\machine-as-action\lib\machine-as-action.js:271:28)
at helpRegisterAction (C:\Users\Mercurius\Documents\GitHub\Homie-Web\node_modules\sails\lib\app\private\controller\help-register-action.js:63:27)
at C:\Users\Mercurius\Documents\GitHub\Homie-Web\node_modules\sails\lib\app\private\controller\load-action-modules.js:146:13
Does anyone know where the documentation is on how to make optional keys in this? I tried here - http://node-machine.org/spec/machine#inputs - but no luck.
Type must be 'string', 'number', 'boolean', 'json' or 'ref' like error say.
So u need set type to 'ref' (object or array), and u can use custom function for validate.
inputs: {
fields: {
type: 'ref',
custom: function (data) {
// some logic
// example
if (typeof data.body !== "string") {
return false;
// or u can use trow Error('Body is not a string')
}
return true;
},
required: true,
description: 'All keys are not required, but at least one is'
}
Now input is type object and in custom function return false or trow Error('Some problem') break validation.
If u use schema type, just remove ? from your example:
inputs: {
fields: {
type: {
body: 'string',
rruleSetStr: 'string'
},
required: true,
description: 'All keys are not required, but at least one is'
}
This is Runtime (recursive) type-checking for JavaScript., please check documentation for writing rules.

Getting "Invalid exit definition" on Compilation of Sails Helper (Sails v1.0)

I'm getting the error
Invalid exit definition ("success"). Must be a dictionary-- i.e. plain JavaScript object like `{}`.
Invalid exit definition ("error"). Must be a dictionary-- i.e. plain JavaScript object like `{}`.
when doing sails lift. The error is on getRole.js
module.exports = {
friendlyName: 'Get Role',
description: '',
inputs: {
user_id: {
friendlyName: 'User Id',
description: 'The ID of the user to check role',
type: 'string',
required: true
}
},
exits: {
success: function (role){
return role;
},
error: function (message) {
return message;
}
},
fn: function (inputs, exits) {
User.findOne({ id: inputs.user_id } , function (err, user) {
if (err) return exits.err(err);
return exits.success(user.role);
});
}
};
This is a new error, and looking at my git, nothing has changed in my code since it successfully compiled. I understand the Sails version (v1.0) I'm using in beta, so I'm taking that into account.
Exits cannot be defined as functions. There is a special syntax (Machine Spec) to define exits. In your example this should work:
exits: {
error: {
description: 'Unexpected error occurred.',
},
success: {
description: 'Role was succesffuly fetched'
}
},
You can read more info about helper exits here: https://next.sailsjs.com/documentation/concepts/helpers
May changes occur on the last release 1.0.0-38. I've not checked underneath yet, but the way to execute helpers changed: on .exec() I get errors. Now, use .switch();

Can't get sails-hook-validate to work with Sails v1.0?

I am having an issue getting validation error messages to attach to the error object in sails v1.0. I am using the sails-hook-validate module.
User model:
module.exports = {
attributes: {
name: {
type: 'string',
required: true,
}
},
validationMessages: {
name: {
required: 'Name is required'
},
},
};
Running User.create in the sails console:
sails> User.create({}).exec(err => console.log(err.toJSON()));
{ error: 'E_UNKNOWN',
status: 500,
summary: 'Encountered an unexpected error',
Errors: undefined }
It appears sails-hook-validate is modifying the error object in some way, but it doesn't seem to be adding my custom error message in any way. Does anybody know how to get sails-hook-validate to work in Sails v1.0?
Sails v1 dramatically changed how validation errors are formatted and sails-hook-validate hasn't been updated to handle Sails v1 yet.
Sails-hook-validate is a third party hook and I dont think its been updated to work with Sails V1.
As #jeffery mentioned the structure of validation errors did change slightly in Sails V1 but there could be other changes that are effecting this hook.

testing sails/mysql with fixtures primary key issue

I have a sails app working against a legacy database (MySQL) and I would like to perform integration tests. I am using fixtures to load data into a separate test database using barrels. When I run my tests I get an error:
[Error (E_UNKNOWN) Encountered an unexpected error] Details: Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
Interview.js api/models/Interview.js:
module.exports = {
tableName: 'interview',
attributes: {
interviewId: {
type: 'integer',
columnName: 'interview_id',
primaryKey: true,
unique: true
},
...
}
};
interview.json tests/fixtures:
[
{
"interviewId": 1,
"title": "some string",
"createdDate": "2015-11-23T09:09:03.000Z",
"lastModified": "2015-11-23T09:09:03.000Z"
},
{...}
]
test environment config/env/test.js :
models: {
connection: 'test',
migrate: 'drop',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
}
The problem seems to lie in defining a primary key in the schema rather than letting sails create one automatically. If I remove the interviewId field from the model and set autoPK: true it works. But this does not accurately represent my data structure.
App info:
sails#0.11.2 sails-mysql#0.11.2 waterline#0.10.27 barrels#1.6.2 node v0.12.7
Many thanks,
Andy

Sails.JS association validation

I'm using several one to many associations in Sails.JS that look like the following:
User
email: {
type: 'string',
required: true,
unique: true
},
projects: {
collection: 'project',
via: 'user'
}
Project
name: {
type: 'string',
required: true,
minLength: 3,
maxLength: 50
},
user: {
model: 'user',
required: true
},
sites: {
collection: 'site',
via: 'project'
}
Site
project: {
model: 'project',
required: true
},
name: {
type: 'string',
required: true
}
Now when I fire off a POST request to /project it creates the project fine, and specifying the param 'user' (taken from the session) associates the project with that particular user.
The same goes for when I create a new site. However, I appear to be able to specify any number for the param 'project', even if that particular project ID doesn't exist. Really it should fail the validation if the project doesn't exist and not create the site. I thought it'd look up the association with project and check that the project ID specified is valid?
Also, I only want to be able to create a site that is associated with a project that belongs to the current user. How would I go about doing this?
Thanks in advance.
I'm not sure if it's a bug or intended behavior with your non-existent project ID association, but one work-around is to have a beforeCreate hook in your models to verify that the project ID exists:
// In your Site model
beforeCreate: function(values, next) {
...
var projectID = values['project'];
Project.findOne(projectID, function (err, project) {
if (err || !project) return next("some error message");
return next();
});
}
You can also do a check in the beforeCreate hook for your second question:
// In your Site model
beforeCreate: function(values, next) {
...
var projectID = values['project'];
Project.findOne(projectID).populate('user').exec(function (err, project) {
if (err || !project) return next("some error message");
if (project.user.id != values['userID']) return next("some other error message");
return next();
});
}
Note that you'll have to pass 'userID' as a param into the params for creating a Site instance.