もくもくしてるナニのメモ

完成目前、なのかどうか。とりあえず smtp な fakeweb 的ソリューションが無いみたいなので nodemailer な部分については別な手続きにして試験を作ってます。
# そのうち fakesmtp とかってのが出てくるのか作るのか

試験

メソドが無い、ってエラーが出てて ?? 状態だったんですが、vows って

$ vows test/*

で起動したら emacs のバックアップも処理対象にしちゃうんですねorz
あと、intercept してない URL にアクセスするコースを通過してるはずなんですが、何のお咎めも無かったなぁ。もしかして通ってないのかも。

そして

graph API 呼び出すトコが全然試験できてないことが判明orz
セッション周辺がマズそうなのかどうか。https だから? うう、ビンゴらしい orz

掘削および改修

ええと、とりあえず fakeweb.js は http.request を書き換えてるのみ、だったので以下を追加。

var old_https_request = https.request;
https.request = function(options, callback){
    var rule = match_rule(options);
    if(rule){
        var res = new events.EventEmitter();
        res.headers = rule.headers || {'Content-Type': 'text/html'};
        return {end: function(){ 
            callback(res);
            res.emit('data', rule.body || '');
            res.emit('end');
            } 
        };
    } else {
        return old_https_request.call(http, options, callback);
    }
};

動かしてみたら setEncoding というメソドが無い、とお叱りを受けました。とりあえず上記 res に何らかの属性を追加する必要があるんですがどうなのか。

顛末

なんとか動くようになったので情報を纏めておきます。

fakeweb で https な応答をする手続きが未定義

これは上で書いてますね。

FacebookClient の setEncoding 対策

FacebookClient の doRequest という手続きが戻す手続きの中で setEncoding 手続きを呼び出す記述があります。

        var request = protocol.request(options, function(response){
            response.setEncoding("utf8");

            var body = [];

fakeweb の https.request 手続きにて空っぽの手続きをセットしてます。

var old_https_request = https.request;
https.request = function(options, callback){
    var rule = match_rule(options);

    if(rule){
        var res = new events.EventEmitter();
	res.setEncoding = function(encoding) {};

すげぇ無理矢理。

match_rule 手続き

ドキュメントには以下のようにせい、という記述があるんですが

    var fakeweb = require('fakeweb'),
        http = require('http')
    http.register_intercept({
        uri: '/foo', 
        host: 'test.com',
        body: 'I'm the mocked-out body!'
    })
    http.request({uri: "/foo", host: "test.com"}, function(response){
        // ...
    })

FacebookClient 側の options の記述が以下になってます。

        var options = {
            host: host,
            port: port,
            path: path,
            method: method || 'GET'
        };

これが前提になってくるのですが fakeweb の match_rule という手続きは以下な形で定義されておりまして

function match_rule(options){
    var matched_rule;
    intercept_rules.forEach(function(rule){
        var keys = Object.keys(rule),
            match = false;
        // TODO headers matching and regex support
        keys.forEach(function(key){
            if(options[key]){ 
                if(rule[key] instanceof RegExp){
                    match = rule[key].test(options[key]);
                } else {
                    match = options[key] == rule[key];
                }
            }
        });
        if(match){
            matched_rule = rule;
        }
    });
    return matched_rule;
}

マッチングかけたい key が uri と path ってなってて、そこがそもそもマッチしない、というアレ。しかも残念なことに全ての要素についてマッチングしてて、その上最後のマッチングの結果が戻る形になっております。
とりあえず

  • register_intercept 手続きに渡す javascript オブジェクトは uri じゃなくて path という key にする
  • path な値は RegExp オブジェクトにする
  • path のみでマッチング
  • マッチしたらその後のマッチングはスルー

ということにして以下な実装に変更してたりして。

function match_rule(options){
    var matched_rule;
    intercept_rules.forEach(function(rule){
        var keys = Object.keys(rule),
            match = false;

        keys.forEach(
	    function(key){
		if(key == 'path' && !match){
		    if(rule[key] instanceof RegExp){
			match = rule[key].test(options[key]);
		    } else {
			match = options[key] == rule[key];
		    }		    
		}
        });

オレオレ fakeweb ってことでリポジトリにも追加しとこ。とりあえず試験も全てパスしたので再度 commit/push も。
あとは本番で正常動作するか、がアレなんですが、試験用のアカウントもあるみたいなので、そっちで試験をする方向。