I have an application code that restricts documents in the following manner
Docs.allow({
insert: function(userId, doc){
return !!userId
},
update: function(userId, doc){
return userId && doc.owner == userId;
}
})
Currently, I can only run an integration test which makes actual http calls. I am not able to stub the components (Meteor current user) outside the system under test (allow / deny rules).
it("should succeed if user is authenticated", function(done) {
Meteor.loginWithPassword(’shawn@abc.com', ‘hahaha', function(err){
expect(err).toBe(undefined);
Doc = Docs.insert({title: 'abc',
category: 'Finance'},
function(err, id){
expect(err).toBeUndefined();
expect(id).not.toBeUndefined();
done();
});
});
});
it("should fail if user is not authenticated", function(done) {
Meteor.logout(function(){
doc = Docs.insert({title: 'abc',
category: 'Finance',
owner: '1232131'},
function(err, id){
expect(err).not.toBeUndefined();
done();
});
});
});
This makes my test incredibly slow, especially if there are many paths I want to test. Is there a way for me to move this test to a lower level unit test instead?
Building up on The Meteor Test Manual's answer... Stories.allow
mock was defined AFTER the app code has loaded. Therefore, it has no effect.
As documented in https://github.com/Sanjo/meteor-jasmine#stubs,
Files in tests/jasmine folder (or a subfolder of it) that end with -stubs.js or -stub.js are treated as stubs and are loaded before the app code.
So to make The Meteor Test Manual's answer work, we have to define the stub / mock in a -stubs.js file. This is what I've done on z-security-stubs.js
.
Note I prefixed the file name with 'z' because meteor load files in the same level of a subdirectory in alphabetical order. We have to ensure that our self-defined stubs load after the automatically generated package-stubs.js
and packageMocksSpec.js
made by Velocity.
With that in mind, z-security-stubs.js
can contain something like this:
Mongo.Collection.prototype.allow = function(rules){
this._velocityAllow = rules;
}
Mongo.Collection.prototype.deny = function(rules){
this._velocityDeny = rules;
}
This retains a reference to our allow / deny security rules in a property of a collection instance (eg. Docs, Files, or whatever your collection is named);
Afterwards, we can refer to the security functions in this property and make assertions:
describe("Docs security rules", function() {
var allow;
beforeEach(function(){
allow = Docs._velocityAllow;
});
it("insert deny access to non-logged in users", function() {
var response = allow.insert(null, {});
expect(response).toBe(false);
});
it("insert allow access to logged in users", function() {
var response = allow.insert(true, {});
expect(response).toBe(true);
});
it("update allow access to logged in users who are owners", function() {
var response = allow.insert(2, {owner: 2});
expect(response).toBe(true);
});
it("update deny access to non-logged in users", function() {
var response = allow.update(null, {owner: 2});
expect(response).toBe(false);
});
it("update deny access to logged in users who are not owners", function() {
var response = allow.update(1, {owner: 2});
expect(response).toBe(false);
});
});