Over the past few days, I have worked through the library of fab.js apps that support my (fab) game. I have gotten testing coverage in place with vows.js and cleaned up some of my code while I was at it (always good sign for a testing framework).
But I have made code changes. Ideally everything is still working, but well... it is not. When I first enter the room everything stops. Specifically, the comet session drops immediately.
Sure enough, when I check the
comet_view resource via curl, it drops right back to the command line prompt rather than blocking for more output:cstrom@whitefall:~/repos/my_fab_game$ curl http://localhost:4011/comet_view?player=foo\&x=250\&y=350In fab.js, HTTP connections are terminated by calling a downstream listener with no arguments. So I work through my
<html><body>
// comet initialization
<script type="text/javascript">window.parent.player_list.new_player({"id":"foo","x":250,"y":350,"uniq_id":"ef41a5af47ae84ba00fe36e602fbef85"})</script>
<script type="text/javascript">window.parent.player_list.new_player({"id":"foo","x":250,"y":350,"uniq_id":"ef41a5af47ae84ba00fe36e602fbef85"})</script>
cstrom@whitefall:~/repos/my_fab_game$
comet_view stack looking for things like out():( /^\/comet_view/ )Both
( broadcast_new )
( store_player )
( init_comet )
( player_from_querystring )
store_player and init_comet have downstream calls with no arguments. To figure out which one is the cause, I add some print STDERR calls to each:function init_comet (app) {
return function () {
var out = this;
return app.call( function listener(obj) {
if (obj && obj.body && obj.body.uniq_id) {
// take the upstream body response and send
// it back down downstream
}
else {
puts("[init_comet] terminating");
out();
}
return listener;
});
};
}Indeed, that does get called:cstrom@whitefall:~/repos/my_fab_game$ ./game.jsDang. How is that possible? Well, it must be coming from downstream because it is in the listener that is sent to the upstream app.
[store_player] adding: foo
[add_player] broadcasting about: foo
...
[init_comet] terminating
There is only one app that is upstream of
init_comet, player_from_querystring:function player_from_querystring() {
var out = this;
return function(head) {
if (head && head.url && head.url.search) {
var uniq_id;
// calculate the uniq_id
var search = head.url.search.substring(1);
var q = require('querystring').parse(search);
var app = out({ body: {id: q.player, x: q.x || 0, y: q.y || 0, uniq_id: uniq_id} });
if ( app ) app();
}
else {
out();
}
};
}Ah, there is an out() call in there. But, print STDERR proves that it is not being called, nor should it—that code is only reached when query strings are not supplied. There is a query string:cstrom@whitefall:~/repos/my_fab_game$ curl http://localhost:4011/comet_view?player=foo\&x=250\&y=350So where is the trouble coming from?
Aw nuts! There is more than one
out() call in there! The other is disguised as app():function player_from_querystring() {
var out = this;
return function(head) {
if (head && head.url && head.url.search) {
var uniq_id;
// calculate the uniq_id
var search = head.url.search.substring(1);
var q = require('querystring').parse(search);
var app = out({ body: {id: q.player, x: q.x || 0, y: q.y || 0, uniq_id: uniq_id} });
if ( app ) app();
}
else {
out();
}
};
}Dang! That is another out() call. In the init_comet middleware / binary app, the last thing that the listener sent to player_from_querystring does is return itself:function init_comet (app) {
return function () {
var out = this;
return app.call( function listener(obj) {
// Lots of cool comet code
return listener;
});
};
}That listener then gets assigned to the app local variable, which is then invoked with no arguments, terminating the HTTP connection.There is nothing wrong with any of that code. Both the middleware app and the upstream app behave in a very typical fab.js way. In fact, I only added that
app() call because it is a typical fab.js thing to do. But I clearly did not add that to make a test pass. My only defense is that I did that before I had any comfort level with vows.js or any Javascript testing. Aw well, lesson learned. Hopefully. And a lesson learned is a good place to stop for the night.
Day #161
All I can say is :D
ReplyDeleteHehe. I couldn't help but think of you when I tracked that one down.
ReplyDeleteDon't look at Firetower then... no tests at all, purely exploratory coding :-P
ReplyDelete