How can I change a laika test from testing successful insert to testing for failed insert? - meteor

I've updated my allow and deny rules from the client. No inserts, updates or removes should work on the client side. Previously this test (listed below) passed because it tested to see if the client could insert into the collection. Now I want to switch this to test make sure the test only passes when the client can't insert into the collection.
How is this done?
//tests/tests.js
var assert = require('assert');
suite('Donate', function() {
test('in the server', function(done, server) {
server.eval(function() {
Donate.insert({fname: 'George'});
var docs = Donate.find().fetch();
emit('docs', docs);
});
server.once('docs', function(docs) {
assert.equal(docs.length, 1);
done();
});
});
});
test('using both client and the server', function(done, server, client) {
server.eval(function() {
Donate.find().observe({
added: addedNewDonate
});
function addedNewDonate(donate) {
emit('donate', donate);
}
}).once('donate', function(donate) {
assert.equal(donate.fname, 'George');
done();
});
client.eval(function() {
Donate.insert({fname: 'George'});
});
});

You might be going about this in the wrong way. Testing to see if an insert is denied as expected is actually testing the Meteor core which is already tested. Put another way, you should be testing the method that returns false for the insert deny property. If all you are doing is:
Donate.deny({
insert: function(){
return false;
}
)};
then you don't need to test for this since the Meteor core is tested enough for you to know that this will work.
On the other hand if you have something like
function complexDenyFunction(){
//perform complex actions
//if all complex conditions are satisfied
//return true
//else return false
return result;
}
Donate.deny({
insert: complexDenyFunction
});
Then what you want to do is create the scenarios where complexDenyFunction would return true and false and test complexDenyFunction to see if if returns the expected results

Related

Meteor wrapAsync

I'm trying to implement the following scenario:
1. Client calls a meteor-method.
2. Inside the meteor-method i make an HTTP-Post to a different server.
3. When the HTTP-Call is responded, the meteor method should return true and in the case an error occurs it should return false.
Here is what my meteor method looks like:
uploadUserImage: function(data_url,userid) {
asyncfnc =function(data,uid){
HTTP.post("http://localhost:2000/upload", {
data: {
"data_url": data,
"user_id": uid
}
},function(err,res){
console.log(res);
if (err){
console.log("error");
throw new Error(err.message);
}
else{
console.log("return true");
return true;
}
});
};
var waitForResult = Meteor.wrapAsync(asyncfnc);
var result = waitForResult(data_url,userid);
return result;
}
The HTTP-Call works and I also get into the Callback of the HTTP.post-function.
But on the clientside where I called the meteor-method i don't get into my callback-function. It looks like this:
Meteor.call("uploadUserImage",data_url,Session.get("newUserID"),function (err, res) {
if(err){
console.log(err);
} else {
console.log('response: ', res);
}
});
What am I doing wrong? Why is my meteor-method not returning anything?
Is everything correct with my Meteor.wrapAsync()?
Thanks for your help!
I found a solution, which does not require Meteor.wrapAsync().
var url = "http://localhost:2000/upload";
//synchronous GET
var result = Meteor.http.post(url,{
data: {
"title": "i want to upload a picture",
"data_url": data_url,
"user_id": userid
},timeout:30000});
if(result.statusCode==200) {
console.log(result);
console.log("response received.");
return result;
} else {
console.log("Response issue: ", result.statusCode);
var errorJson = JSON.parse(result.content);
throw new Meteor.Error(result.statusCode, errorJson.error);
}
This makes the HTTP-Post-Call synchronous, so there is no need to wrap async.
You are asking too much in this situation.
Meteor methods can be called synchronously, but it's not advisable if the method is doing a remote call like this.
My feeling is that you are hanging on to a procedural programming model where you want a synchronous result to 1) a call to your server, and 2) a request sent to another remote server. And you want to get a return value from your call. It doesn't work like that.
Meteor protects you to a large degree from dealing with asynchronicity, but sometimes you have to accept that a little more work is required to deal with it correctly.
So my recommendation is to use callbacks for notification.

How to unit test collection methods with velocity/jasmine

I'm completely new to javascript testing and I am trying to get a grasp on how to approach testing methods that touch the database
For example, I have this method that returns true if there are any documents in the db matching the query
Payments = new Mongo.Collection('payments');
_.extend(Payments, {
hasAnyPayments: function(userId) {
var payments = Payments.find({ userId: userId });
return payments.count() > 0;
}
});
So far I have only written the structure that I think is correct, but I am pretty lost
describe('Payments', function() {
describe('#hasAnyPayments', function() {
it('should return true when user has any payments', function() {
});
});
});
Are such tests even supposed to touch the database? Any advice is much appreciated
Unless you are manually inputting data into Mongo manually (or outside of Meteor) then you don't need to test the database.
What you should be testing, are the execution paths in your code.
So for the case above, hasAnyPayments is a unit that finds all user payments and returns true if there are more than 0. So your test would look something like this:
describe('Payments', function() {
describe('#hasAnyPayments', function() {
it('should return true when user has any payments', function() {
// SETUP
Payments.find = function() { return 1; } // stub to return a positive value
// EXECUTE
var actualValue = Payments.hasAnyPayments(); // you don't really care about the suer
// VERIFY
expect(actualValue).toBe(true);
});
});
});

Get Meteor Method Return Value Inside Helper Function

I have the following in server/functions.js file:
Meteor.methods({
checkIfVoted: function (postId) {
if (postId) {
...
if (condition is met) {
return true;
} else {
return false;
}
}
}
});
And then the following in client/showPost.js:
Template.showPost.helpers({
alreadyVotedByUser: function () {
var answer = false;
if(this) {
Meteor.call("checkIfVoted", this._id, function(err, response) {
if (response) { console.log(response); }
answer = response;
});
}
console.log(answer);
return answer;
}
});
When I am doing the console.log for response I get the value to be true when the condition is met, but the answer variable does not take it, and still shows as having false as its value.
(I am aware that I put the Meteor methods in the server directory and not in a common directory to be shared between client and server to provide latency compensation)
Please if some one could help me with this, it will be highly appreciated.
On the client, Meteor.call is asynchronous - it returns undefined and its return value can only be accesses via a callback. Helpers, on the other hand, execute synchronously. See the answers to this question for how you can call methods from helpers. Here's a quick solution:
$ meteor add simple:reactive-method
Template.showPost.helpers({
alreadyVotedByUser: function () {
return ReactiveMethod.call('checkIfVoted', this._id);
}
});
The problem with this package is that it can encourage bad behavior, but it should be fine for method calls which don't change state.
Also see the section about helpers in this post.

wrapAsync + method + session

i'm having issues with wrapAsync + method + sessions.
How do I implement the WrapAsync correctly?
I want, in a template to know if the user has at least one item created by him. And then define whether or not he can create another item.
Now i'm getting this error:
W20141013-15:04:43.237(-3)? (STDERR) Error: Can't wait without a fiber
But, I could not find Fiber at Documentation. And for implementing this, is it really necessary?
 
On the client side I want something like:
//pagina.js
Template.pagina.helpers{
userHasItem: return Session.get('userHasItem');
}
//pagina.js
Meteor.call('userHasItem', Meteor.userId(), function (error,result) {
Session.set('userHasItem', result);
});
//at server side:
if(Meteor.isServer){
Meteor.startup(function () {
var userHasItemAsync = function (userId) {
setTimeout(function () {
if (Items.findOne({'userId': userId})) {
return true;
} else {
return false;
}
}, 4000);
};
Meteor.methods({
userHasItem: function(userId) {
var userHasItemSync = Meteor.wrapAsync(userHasItemAsync),
result;
try {
userHasItemSync(userId);
console.log(result);
return result;
}catch (e) {
console.log('erreur', e.message);
throw new Meteor.Error(500, e);
}
},
}
});
}
Can't get your error to reproduce based on the existing code.
Still, userHasItemAsync is not available because you've defined it locally in the Meteor.startup function. But the error you should get in this case is userHasItemAsync is undefined.
Also the code you've entered here has multiple errors (i guess you typed it in not copy / pasted from your project): template instead of Template, Template it's defined outside of isClient (probably it's in a file available for the client) etc. Because of that it's hard to reproduce your exact case.
There is no need to call a server method to see if the item exists (assuming you have set up the proper publications/subscriptions), nor any need to call wrapAsync. In fact, what you want to achieve doesn't even require a session. All of the code can be ultimately distilled to this:
Template.pagina.helpers{
userHasItem: return Items.find({ userId: Meteor.userId() }).count() > 0;
}
The cursor returned by Items.find is reactive in itself, so there is no need for using a Session.

Accessing this.userId not working when calling from within Meteor.SetTimeout

I've been trying to access the this.userId variable from within a Meteor.methods call, but it doesn't seem to work when I try to call the method via Meteor.setTimeout or Meteor.setInterval.
This is what I've got:
if (Meteor.is_server) {
Meteor.methods({
getAccessToken : function() {
try {
console.log(this.userId);
return Meteor.users.findOne({_id: this.userId}).services.facebook.accessToken;
} catch(e) {
return null;
}
}
});
var fetch_feed = function() {
console.log(Meteor.call("getAccessToken"));
[...] // A bunch of other code
};
Meteor.startup(function() {
Meteor.setInterval(fetch_feed, 60000); // fetch a facebook group feed every minute
Meteor.setTimeout(fetch_feed, 3000); // initially fetch the feed after 3 seconds
});
}
Watching the terminal log, the this.userId always returns a null. But if I try calling the method from the client side, or through the console, it returns the correct ID.
How come this doesn't work from within a Meteor.setInterval? Is it a bug or am I doing something wrong?
Meteor userId's are associated with client connections. The server may interact with many clients and this.userId inside a method will tell you which client has asked for the method to be run.
If the server uses Meteor.call() to run a method then it will not have a userId since it is not running for any client.
The methods allow clients to call for functions to be run on the server. For things the server will trigger itself a javascript function will do.
There is a solution I used - sometimes you do not want to make the method a function but really want it to remain a method. In that case, a hack to make this work:
var uniqueVar_D8kMWHtMMZJRCraiJ = Meteor.userId();
Meteor.setTimeout(function() {
// hack to make Meteor.userId() work on next async
// call to current method
if(! Meteor._userId) Meteor._userId = Meteor.userId;
Meteor.userId = function() {
return Meteor._userId() || uniqueVar_D8kMWHtMMZJRCraiJ
};
Meteor.apply(methodName, args);
}
, 100);
Some brief explanation: we save Meteor.userId in Meteor._userId and overwrite Meteor.userId with a function that returns Meteor._userId() if it is true and otherwise the historic value of Meteor.userId() before any of this happened. That historic value is saved in an impossible to occur twice var name so that no context conflicts can happen.

Resources