unit-testingmeteorjasmineintegration-testingmeteor-velocity

Testing meteor - testing allow / deny with a unit test instead of integration test


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?


Solution

  • 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);
      });
    });