diff --git a/js/remote.js b/js/remote.js index 90f04c4b38..ed89987bd7 100644 --- a/js/remote.js +++ b/js/remote.js @@ -26,13 +26,17 @@ var Amount = amount.Amount; // 'remoteUnexpected' // 'remoteDisconnected' var Request = function (remote, command) { + var self = this; + this.message = { 'command' : command, 'id' : undefined, }; this.remote = remote; - this.on('request', this.request_default); + this.on('request', function () { + self.request_default(); + }); }; Request.prototype = new EventEmitter; @@ -44,6 +48,12 @@ Request.prototype.on = function (e, c) { return this; }; +Request.prototype.once = function (e, c) { + EventEmitter.prototype.once.call(this, e, c); + + return this; +}; + // Send the request to a remote. Request.prototype.request = function (remote) { this.emit('request', remote); @@ -103,7 +113,8 @@ Request.prototype.transaction = function (t) { // 'state': // - 'online' : connectted and subscribed // - 'offline' : not subscribed or not connectted. -// 'ledger_closed' +// 'ledger_closed': A good indicate of ready to serve. +// 'subscribed' : This indicates stand-alone is available. // // --> trusted: truthy, if remote is trusted @@ -296,9 +307,10 @@ Remote.prototype._connect_start = function () { }; if (self.online_target) { - self._server_subscribe(); // Automatically subscribe. - self._set_state('online'); + + // Note, we could get disconnected before tis go through. + self._server_subscribe(); // Automatically subscribe. } else { self._connect_stop(); @@ -372,7 +384,7 @@ Remote.prototype._connect_message = function (ws, json, flags) { this.ledger_closed = message.ledger_closed; this.ledger_current_index = message.ledger_closed_index + 1; - this.emit('ledger_closed', this.ledger_closed, this.ledger_closed_index); + this.emit('ledger_closed', message.ledger_closed, message.ledger_closed_index); break; default: @@ -409,13 +421,20 @@ Remote.prototype._connect_message = function (ws, json, flags) { // Send a request. // <-> request: what to send, consumed. Remote.prototype.request = function (request) { - this.ws.response[request.message.id = this.id] = request; - - this.id += 1; // Advance id. - - if (this.trace) console.log("remote: request: %s", JSON.stringify(request.message)); - - this.ws.send(JSON.stringify(request.message)); + if (this.ws) { + // Only bother if we are still connected. + + this.ws.response[request.message.id = this.id] = request; + + this.id += 1; // Advance id. + + if (this.trace) console.log("remote: request: %s", JSON.stringify(request.message)); + + this.ws.send(JSON.stringify(request.message)); + } + else { + if (this.trace) console.log("remote: request: DROPPING: %s", JSON.stringify(request.message)); + } }; Remote.prototype.request_ledger_closed = function () { @@ -480,7 +499,7 @@ Remote.prototype.request_ledger_entry = function (type) { // This type not cached. } - this.request_default(remote); + this.request_default(); } } }); @@ -668,6 +687,8 @@ Remote.prototype.transaction = function () { // - The client should only trust this when talking to a trusted server. // 'final' : Final status of transaction. // - Only expect a final from honest clients after a tesSUCCESS or ter*. +// 'lost' : Gave up looking for on ledger_closed. +// 'pending' : Transaction was not found on ledger_closed. // 'state' : Follow the state of a transaction. // 'clientSubmitted' - Sent to remote // |- 'remoteError' - Remote rejected transaction. @@ -802,8 +823,8 @@ Transaction.prototype.submit = function () { } } - if (this.listeners('final').length) { - // There are listeners for 'final' arrange to emit it. + if (this.listeners('final').length || this.listeners('lost').length || this.listeners('pending').length) { + // There are listeners for 'final', 'lost', or 'pending' arrange to emit them. this.submit_index = this.remote.ledger_current_index; @@ -820,11 +841,16 @@ Transaction.prototype.submit = function () { if ('remoteError' === message.error && 'transactionNotFound' === message.remote.error) { if (self.submit_index + SUBMIT_LOST < ledger_closed_index) { - self.set_state('clientLost'); // Gave up. + self.set_state('client_lost'); // Gave up. + self.emit('lost'); stop = true; } else if (self.submit_index + SUBMIT_MISSING < ledger_closed_index) { - self.set_state('clientMissing'); // We don't know what happened to transaction, still might find. + self.set_state('client_missing'); // We don't know what happened to transaction, still might find. + self.emit('pending'); + } + else { + self.emit('pending'); } } // XXX Could log other unexpectedness. diff --git a/test/remote-test.js b/test/remote-test.js index 5a99b43c27..34eceb1aa7 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -24,7 +24,7 @@ buster.testCase("Remote functions", { alpha = remote.remoteConfig(config, "alpha"); alpha - .on('connected', done) + .once('ledger_closed', done) .connect(); }); }, diff --git a/test/send-test.js b/test/send-test.js new file mode 100644 index 0000000000..145ce34105 --- /dev/null +++ b/test/send-test.js @@ -0,0 +1,99 @@ +var buster = require("buster"); + +var config = require("./config.js"); +var server = require("./server.js"); +var amount = require("../js/amount.js"); +var remote = require("../js/remote.js"); + +var Amount = amount.Amount; + +// How long to wait for server to start. +var serverDelay = 1500; + +buster.testRunner.timeout = 5000; + +buster.testCase("Sending", { + 'setUp' : + function (done) { + server.start("alpha", + function (e) { + buster.refute(e); + + alpha = remote.remoteConfig(config, "alpha"); + + alpha + .once('ledger_closed', done) + .connect(); + }); + }, + + 'tearDown' : + function (done) { + alpha + .on('disconnected', function () { + server.stop("alpha", function (e) { + buster.refute(e); + done(); + }); + }) + .connect(false); + }, + + "send to non-existant account without create." : + function (done) { + var got_proposed; + var ledgers = 20; + + alpha.transaction() + .payment('root', 'alice', Amount.from_json("10000")) + .on('success', function (r) { + // Transaction sent. + + console.log("success: %s", JSON.stringify(r)); + }) + .on('pending', function() { + // Moving ledgers along. + console.log("missing: %d", ledgers); + + ledgers -= 1; + if (ledgers) { + alpha.ledger_accept(); + } + else { + buster.assert(false, "Final never received."); + done(); + } + }) + .on('lost', function () { + // Transaction did not make it in. + console.log("lost"); + + buster.assert(true); + done(); + }) + .on('proposed', function (m) { + // Transaction got an error. + console.log("proposed: %s", JSON.stringify(m)); + + buster.assert.equals(m.result, 'terNO_DST'); + + got_proposed = true; + + alpha.ledger_accept(); // Move it along. + }) + .on('final', function (m) { + console.log("final: %s", JSON.stringify(m)); + + buster.assert(false, "Should not have got a final."); + done(); + }) + .on('error', function(m) { + console.log("error: %s", m); + + buster.assert(false); + }) + .submit(); + }, +}); + +// vim:sw=2:sts=2:ts=8