diff --git a/js/amount.js b/js/amount.js index 36d57889b5..ea8d24095a 100644 --- a/js/amount.js +++ b/js/amount.js @@ -4,14 +4,11 @@ var utils = require('./utils.js'); var jsbn = require('./jsbn.js'); +// Don't include in browser context. +var config = require('../test/config.js'); + var BigInteger = jsbn.BigInteger; -var accounts = {}; - -var setAccounts = function (accounts_new) { - accounts = accounts_new; -}; - var UInt160 = function () { // Internal form: // 0, 1, 'iXXXXX', 20 byte string, or NaN. @@ -44,8 +41,8 @@ UInt160.prototype.copyTo = function(d) { // value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate - if (j in accounts) - j = accounts[j].account; + if (config.accounts && j in config.accounts) + j = config.accounts[j].account; switch (j) { case undefined: @@ -445,7 +442,6 @@ Amount.prototype.parse_issuer = function (issuer) { return this; }; -exports.setAccounts = setAccounts; exports.Amount = Amount; exports.Currency = Currency; exports.UInt160 = UInt160; diff --git a/js/remote.js b/js/remote.js index 4f57c5c3a6..f3393d9a8b 100644 --- a/js/remote.js +++ b/js/remote.js @@ -3,7 +3,15 @@ // - We use the W3C interface for node and browser compatibility: // http://www.w3.org/TR/websockets/#the-websocket-interface // -// YYY Will later provide a network access which use multiple instances of this. +// This class is intended for both browser and node.js use. +// +// This class is designed to work via peer protocol via either the public or +// private websocket interfaces. The JavaScript class for the peer protocol +// has not yet been implemented. However, this class has been designed for it +// to be a very simple drop option. +// +// YYY Will later provide js/network.js which will transparently use multiple +// instances of this class for network access. // // Node @@ -16,6 +24,9 @@ var WebSocket = require('ws'); var Amount = require('./amount.js').Amount; var UInt160 = require('./amount.js').UInt160; +// Don't include in browser context. +var config = require('../test/config.js'); + // Request events emmitted: // 'success' : Request successful. // 'error' : Request failed. @@ -117,8 +128,8 @@ Request.prototype.transaction = function (t) { Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { 'accounts' : [ - UInt160.from_json(account).to_json(), - UInt160.from_json(issuer).to_json() + UInt160.json_rewrite(account), + UInt160.json_rewrite(issuer) ], 'currency' : currency }; @@ -140,12 +151,11 @@ Request.prototype.ripple_state = function (account, issuer, currency) { // // --> trusted: truthy, if remote is trusted -var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { +var Remote = function (trusted, websocket_ip, websocket_port, trace) { this.trusted = trusted; this.websocket_ip = websocket_ip; this.websocket_port = websocket_port; this.id = 0; - this.config = config; this.trace = trace; this.ledger_closed = undefined; this.ledger_current_index = undefined; @@ -155,6 +165,7 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { this.state = 'offline'; // 'online', 'offline' this.retry_timer = undefined; this.retry = undefined; + this.config = config || { 'accounts' : {}}; // Cache information for accounts. this.accounts = { @@ -176,10 +187,10 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { Remote.prototype = new EventEmitter; -var remoteConfig = function (config, server, trace) { - var serverConfig = config.servers[server]; +Remote.from_config = function (name, trace) { + var serverConfig = config.servers[name]; - return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, config, trace); + return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, trace); }; var isTemMalformed = function (engine_result_code) { @@ -190,7 +201,7 @@ var isTefFailure = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; -var flags = { +Remote.flags = { 'OfferCreate' : { 'Passive' : 0x00010000, }, @@ -204,7 +215,7 @@ var flags = { }; // XXX This needs to be determined from the network. -var fees = { +Remote.fees = { 'default' : Amount.from_json("100"), 'account_create' : Amount.from_json("1000"), 'nickname_create' : Amount.from_json("1000"), @@ -233,6 +244,12 @@ Remote.prototype._set_state = function (state) { } }; +Remote.prototype.trace = function () { + this.trace = true; + + return this; +}; + // Set the target online state. Defaults to false. Remote.prototype.connect = function (online) { var target = undefined === online || online; @@ -703,7 +720,9 @@ Remote.prototype.account_seq_cache = function (account, current) { // Mark an account's root node as dirty. Remote.prototype.dirty_account_root = function (account) { - delete this.ledgers.current.account_root[UInt160.json_rewrite(account)]; + var account = UInt160.json_rewrite(account); + + delete this.ledgers.current.account_root[account]; }; // Return a request to get a ripple balance. @@ -713,7 +732,6 @@ Remote.prototype.dirty_account_root = function (account) { // --> currency: String // --> current: bool : true = current ledger Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { - var account_u = UInt160.from_json(account); var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. return request @@ -724,7 +742,7 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c var lowLimit = Amount.from_json(node.LowLimit); var highLimit = Amount.from_json(node.HighLimit); var balance = Amount.from_json(node.Balance); - var flip = account_u == highLimit.issuer; + var flip = UInt160.from_json(account) == highLimit.issuer; var issuerLimit = flip ? lowLimit : highLimit; var accountLimit = flip ? highLimit : lowLimit; var issuerBalance = (flip ? balance.negate() : balance).parse_issuer(issuer); @@ -749,7 +767,7 @@ Remote.prototype.transaction = function () { // Construction: // remote.transaction() // Build a transaction object. // .offer_create(...) // Set major parameters. -// .flags() // Set optional parameters. +// .set_flags() // Set optional parameters. // .on() // Register for events. // .submit(); // Send to network. // @@ -797,8 +815,6 @@ var SUBMIT_LOST = 8; // Give up tracking. var Transaction = function (remote) { var self = this; - this.prototype = EventEmitter; // XXX Node specific. - this.remote = remote; this.secret = undefined; this.transaction = { // Transaction data. @@ -893,12 +909,12 @@ Transaction.prototype.submit = function () { if (undefined === transaction.Fee) { if ('Payment' === transaction.TransactionType - && transaction.Flags & exports.flags.Payment.CreateAccount) { + && transaction.Flags & Remote.flags.Payment.CreateAccount) { - transaction.Fee = fees.account_create.to_json(); + transaction.Fee = Remote.fees.account_create.to_json(); } else { - transaction.Fee = fees['default'].to_json(); + transaction.Fee = Remote.fees['default'].to_json(); } } @@ -971,9 +987,9 @@ Transaction.prototype.send_max = function (send_max) { // Add flags to a transaction. // --> flags: undefined, _flag_, or [ _flags_ ] -Transaction.prototype.flags = function (flags) { +Transaction.prototype.set_flags = function (flags) { if (flags) { - var transaction_flags = exports.flags[this.transaction.TransactionType]; + var transaction_flags = Remote.flags[this.transaction.TransactionType]; if (undefined == this.transaction.Flags) // We plan to not define this field on new Transaction. this.transaction.Flags = 0; @@ -992,8 +1008,8 @@ Transaction.prototype.flags = function (flags) { } } - if (this.transaction.Flags & exports.flags.Payment.CreateAccount) - this.transaction.Fee = fees.account_create.to_json(); + if (this.transaction.Flags & Remote.flags.Payment.CreateAccount) + this.transaction.Fee = Remote.fees.account_create.to_json(); } return this; @@ -1003,40 +1019,78 @@ Transaction.prototype.flags = function (flags) { // Transactions // -// Allow config account defaults to be used. -Transaction.prototype.account_default = function (account) { - return this.remote.config.accounts[account] ? this.remote.config.accounts[account].account : account; -}; - -Transaction.prototype.account_secret = function (account) { +Transaction.prototype._account_secret = function (account) { // Fill in secret from config, if needed. return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined; }; +// .wallet_locator() +// .message_key() +// .domain() +// .transfer_rate() +// .publish() +Transaction.prototype.account_set = function (src) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'AccountSet'; + + return this; +}; + +Transaction.prototype.claim = function (src, generator, public_key, signature) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'Claim'; + this.transaction.Generator = generator; + this.transaction.PublicKey = public_key; + this.transaction.Signature = signature; + + return this; +}; + Transaction.prototype.offer_cancel = function (src, sequence) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'OfferCancel'; - this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.Account = UInt160.json_rewrite(src); this.transaction.OfferSequence = Number(sequence); return this; }; -// XXX Expiration should use a time. +// --> expiration : Date or Number Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'OfferCreate'; - this.transaction.Account = UInt160.from_json(src).to_json(); - this.transaction.Fee = fees.offer.to_json(); + this.transaction.Account = UInt160.json_rewrite(src); + this.transaction.Fee = Remote.fees.offer.to_json(); this.transaction.TakerPays = Amount.json_rewrite(taker_pays); this.transaction.TakerGets = Amount.json_rewrite(taker_gets); if (expiration) - this.transaction.Expiration = expiration; + this.transaction.Expiration = Date === expiration.constructor + ? expiration.getTime() + : Number(expiration); return this; }; +Transaction.prototype.password_fund = function (src, dst) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'PasswordFund'; + this.transaction.Destination = UInt160.json_rewrite(dst); + + return this; +} + +Transaction.prototype.password_set = function (src, authorized_key, generator, public_key, signature) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'PasswordSet'; + this.transaction.AuthorizedKey = authorized_key; + this.transaction.Generator = generator; + this.transaction.PublicKey = public_key; + this.transaction.Signature = signature; + + return this; +} + // Construct a 'payment' transaction. // // When a transaction is submitted: @@ -1045,19 +1099,19 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi // --> dst : UInt160 or String // --> deliver_amount : Amount or String. Transaction.prototype.payment = function (src, dst, deliver_amount) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'Payment'; - this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.Account = UInt160.json_rewrite(src); this.transaction.Amount = Amount.json_rewrite(deliver_amount); - this.transaction.Destination = UInt160.from_json(dst).to_json(); + this.transaction.Destination = UInt160.json_rewrite(dst); return this; } Transaction.prototype.ripple_line_set = function (src, limit, quality_in, quality_out) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'CreditSet'; - this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.Account = UInt160.json_rewrite(src); // Allow limit of 0 through. if (undefined !== limit) @@ -1074,9 +1128,17 @@ Transaction.prototype.ripple_line_set = function (src, limit, quality_in, qualit return this; }; +Transaction.prototype.wallet_add = function (src, amount, authorized_key, public_key, signature) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'WalletAdd'; + this.transaction.Amount = Amount.json_rewrite(amount); + this.transaction.AuthorizedKey = authorized_key; + this.transaction.PublicKey = public_key; + this.transaction.Signature = signature; + + return this; +}; + exports.Remote = Remote; -exports.remoteConfig = remoteConfig; -exports.fees = fees; -exports.flags = flags; // vim:sw=2:sts=2:ts=8 diff --git a/test/config.js b/test/config.js index 6d448ae172..d9e6eb5846 100644 --- a/test/config.js +++ b/test/config.js @@ -7,10 +7,12 @@ var path = require("path"); // Where to find the binary. exports.rippled = path.join(process.cwd(), "rippled"); +exports.server_default = "alpha"; + // Configuration for servers. exports.servers = { // A local test server. - 'alpha' : { + "alpha" : { 'trusted' : true, // "peer_ip" : "0.0.0.0", // "peer_port" : 51235, @@ -26,39 +28,39 @@ exports.servers = { // Configuration for test accounts. exports.accounts = { // Users - 'alice' : { - 'account' : 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - 'secret' : 'alice', + "alice" : { + 'account' : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn", + 'secret' : "alice", }, - 'bob' : { - 'account' : 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK', - 'secret' : 'bob', + "bob" : { + 'account' : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK", + 'secret' : "bob", }, - 'carol' : { - 'account' : 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW', - 'secret' : 'carol', + "carol" : { + 'account' : "rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW", + 'secret' : "carol", }, // Nexuses - 'bitstamp' : { - 'account' : 'r4jKmc2nQb5yEU6eycefiNKGHTU5NQJASx', - 'secret' : 'bitstamp', + "bitstamp" : { + 'account' : "r4jKmc2nQb5yEU6eycefiNKGHTU5NQJASx", + 'secret' : "bitstamp", }, - 'mtgox' : { - 'account' : 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - 'secret' : 'mtgox', + "mtgox" : { + 'account' : "rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v", + 'secret' : "mtgox", }, // Merchants - 'amazon' : { - 'account' : 'rhheXqX7bDnXePJeMHhubDDvw2uUTtenPd', - 'secret' : 'amazon', + "amazon" : { + 'account' : "rhheXqX7bDnXePJeMHhubDDvw2uUTtenPd", + 'secret' : "amazon", }, // Master account - 'root' : { - 'account' : 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', - 'secret' : 'masterpassphrase', + "root" : { + 'account' : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + 'secret' : "masterpassphrase", }, }; diff --git a/test/offer-test.js b/test/offer-test.js index bae3b947ff..18867ea25f 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -1,63 +1,34 @@ var async = require("async"); var buster = require("buster"); -var fs = require("fs"); - -var server = require("./server.js"); -var remote = require("../js/remote.js"); -var config = require("./config.js"); var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; -require("../js/amount.js").setAccounts(config.accounts); +var testutils = require("./testutils.js"); buster.testRunner.timeout = 5000; -var alpha; - -buster.testCase("Work in progress", { - 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - - alpha = remote.remoteConfig(config, "alpha", 'TRACE'); - - alpha - .once('ledger_closed', done) - .connect(); - } -// , 'MOCK' - ); - }, - - 'tearDown' : - function (done) { - alpha - .on('disconnected', function () { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }) - .connect(false); - }, +buster.testCase("Offer tests", { + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_teardown, "offer create then cancel in one ledger" : function (done) { + var self = this; var final_create; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .offer_create("root", "500", "100/USD/root") .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS', m); }) .on("final", function (m) { - console.log("FINAL: offer_create: %s", JSON.stringify(m)); + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -66,14 +37,14 @@ buster.testCase("Work in progress", { .submit(); }, function (m, callback) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence) .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS', m); }) .on("final", function (m) { - console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -82,14 +53,14 @@ buster.testCase("Work in progress", { .submit(); }, function (m, callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); }) .ledger_accept(); } ], function (error) { - console.log("result: error=%s", error); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); @@ -98,22 +69,23 @@ buster.testCase("Work in progress", { "offer_create then ledger_accept then offer_cancel then ledger_accept." : function (done) { + var self = this; var final_create; var offer_seq; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .offer_create("root", "500", "100/USD/root") .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); offer_seq = m.transaction.Sequence; callback(m.result != 'tesSUCCESS'); }) .on("final", function (m) { - console.log("FINAL: offer_create: %s", JSON.stringify(m)); + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -125,9 +97,9 @@ buster.testCase("Work in progress", { }, function (callback) { if (!final_create) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); }) .ledger_accept(); @@ -137,16 +109,16 @@ buster.testCase("Work in progress", { } }, function (callback) { - console.log("CANCEL: offer_cancel: %d", offer_seq); + // console.log("CANCEL: offer_cancel: %d", offer_seq); - alpha.transaction() + self.remote.transaction() .offer_cancel("root", offer_seq) .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS'); }) .on("final", function (m) { - console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -157,23 +129,23 @@ buster.testCase("Work in progress", { }, // See if ledger_accept will crash. function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); }, function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); }, ], function (error) { - console.log("result: error=%s", error); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); @@ -183,33 +155,34 @@ buster.testCase("Work in progress", { "new user offer_create then ledger_accept then offer_cancel then ledger_accept." : function (done) { + var self = this; var final_create; var offer_seq; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .payment('root', 'alice', "1000") - .flags('CreateAccount') + .set_flags('CreateAccount') .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); buster.assert.equals(m.result, 'tesSUCCESS'); callback(); }) .submit() }, function (callback) { - alpha.transaction() + self.remote.transaction() .offer_create("alice", "500", "100/USD/alice") .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); offer_seq = m.transaction.Sequence; callback(m.result != 'tesSUCCESS'); }) .on("final", function (m) { - console.log("FINAL: offer_create: %s", JSON.stringify(m)); + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -221,9 +194,9 @@ buster.testCase("Work in progress", { }, function (callback) { if (!final_create) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); }) .ledger_accept(); @@ -233,16 +206,16 @@ buster.testCase("Work in progress", { } }, function (callback) { - console.log("CANCEL: offer_cancel: %d", offer_seq); + // console.log("CANCEL: offer_cancel: %d", offer_seq); - alpha.transaction() + self.remote.transaction() .offer_cancel("alice", offer_seq) .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS'); }) .on("final", function (m) { - console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -253,23 +226,23 @@ buster.testCase("Work in progress", { }, // See if ledger_accept will crash. function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); }, function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); }, ], function (error) { - console.log("result: error=%s", error); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); }); @@ -277,19 +250,20 @@ buster.testCase("Work in progress", { "offer cancel past and future sequence" : function (done) { + var self = this; var final_create; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') + .set_flags('CreateAccount') .on("proposed", function (m) { - console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m)); + // console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS', m); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); callback(m); @@ -298,20 +272,20 @@ buster.testCase("Work in progress", { }, // Past sequence but wrong function (m, callback) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence) .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m)); callback(m.result != 'tesSUCCESS', m); }) .submit(); }, // Same sequence function (m, callback) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence+1) .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m)); + // console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m)); callback(m.result != 'temBAD_SEQUENCE', m); }) .submit(); @@ -319,29 +293,29 @@ buster.testCase("Work in progress", { // Future sequence function (m, callback) { // After a malformed transaction, need to recover correct sequence. - alpha.set_account_seq("root", alpha.account_seq("root")-1); + self.remote.set_account_seq("root", self.remote.account_seq("root")-1); - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence+2) .on("proposed", function (m) { - console.log("ERROR: offer_cancel future: %s", JSON.stringify(m)); + // console.log("ERROR: offer_cancel future: %s", JSON.stringify(m)); callback(m.result != 'temBAD_SEQUENCE'); }) .submit(); }, // See if ledger_accept will crash. function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); }, function (callback) { - alpha + self.remote .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); callback(); }) .ledger_accept(); @@ -350,7 +324,7 @@ buster.testCase("Work in progress", { callback(); } ], function (error) { - console.log("result: error=%s", error); + // console.log("result: error=%s", error); buster.refute(error); done(); diff --git a/test/remote-test.js b/test/remote-test.js index 86f31554eb..a22230af09 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -1,12 +1,10 @@ var buster = require("buster"); -var config = require("./config.js"); -var server = require("./server.js"); -var remote = require("../js/remote.js"); - var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; -require("../js/amount.js").setAccounts(config.accounts); +var testutils = require("./testutils.js"); var fastTearDown = true; @@ -16,42 +14,19 @@ var serverDelay = 1500; // XXX Not implemented. buster.testRunner.timeout = 5000; buster.testCase("Remote functions", { - '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); - }, + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_teardown, 'request_ledger_current' : function (done) { - alpha.request_ledger_current().on('success', function (m) { - console.log(m); + this.remote.request_ledger_current().on('success', function (m) { + // console.log(m); buster.assert.equals(m.ledger_current_index, 3); done(); }) .on('error', function(m) { - console.log(m); + // console.log(m); buster.assert(false); }) @@ -60,14 +35,14 @@ buster.testCase("Remote functions", { 'request_ledger_closed' : function (done) { - alpha.request_ledger_closed().on('success', function (m) { - console.log("result: %s", JSON.stringify(m)); + this.remote.request_ledger_closed().on('success', function (m) { + // console.log("result: %s", JSON.stringify(m)); buster.assert.equals(m.ledger_closed_index, 2); done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -76,10 +51,12 @@ buster.testCase("Remote functions", { 'manual account_root success' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { + var self = this; + + this.remote.request_ledger_closed().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); - alpha + self.remote .request_ledger_entry('account_root') .ledger_closed(r.ledger_closed) .account_root("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") @@ -90,14 +67,14 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -107,10 +84,12 @@ buster.testCase("Remote functions", { // XXX This should be detected locally. 'account_root remote malformedAddress' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('account_root') .ledger_closed(r.ledger_closed) .account_root("zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") @@ -120,7 +99,7 @@ buster.testCase("Remote functions", { buster.assert(false); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert.equals(m.error, 'remoteError'); buster.assert.equals(m.remote.error, 'malformedAddress'); @@ -129,7 +108,7 @@ buster.testCase("Remote functions", { .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -138,20 +117,22 @@ buster.testCase("Remote functions", { 'account_root entryNotFound' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('account_root') .ledger_closed(r.ledger_closed) - .account_root(config.accounts.alice.account) + .account_root("alice") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); buster.assert(false); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert.equals(m.error, 'remoteError'); buster.assert.equals(m.remote.error, 'entryNotFound'); @@ -160,7 +141,7 @@ buster.testCase("Remote functions", { .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }).request(); @@ -168,13 +149,15 @@ buster.testCase("Remote functions", { 'ledger_entry index' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('index') .ledger_closed(r.ledger_closed) - .account_root(config.accounts.alice.account) + .account_root("alice") .index("2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -183,14 +166,14 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }). request(); }) .on('error', function(m) { - console.log(m); + // console.log(m); buster.assert(false); }) @@ -199,9 +182,9 @@ buster.testCase("Remote functions", { 'create account' : function (done) { - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') + .set_flags('CreateAccount') .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -209,7 +192,7 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -218,40 +201,42 @@ buster.testCase("Remote functions", { "create account final" : function (done) { + var self = this; + var got_proposed; var got_success; - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') + .set_flags('CreateAccount') .on('success', function (r) { - console.log("create_account: %s", JSON.stringify(r)); + // console.log("create_account: %s", JSON.stringify(r)); got_success = true; }) .on('error', function (m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .on('final', function (m) { - console.log("final: %s", JSON.stringify(m)); + // console.log("final: %s", JSON.stringify(m)); buster.assert(got_success && got_proposed); done(); }) .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); // buster.assert.equals(m.result, 'terNO_DST'); buster.assert.equals(m.result, 'tesSUCCESS'); got_proposed = true; - alpha.ledger_accept(); + self.remote.ledger_accept(); }) .on('status', function (s) { - console.log("status: %s", JSON.stringify(s)); + // console.log("status: %s", JSON.stringify(s)); }) .submit(); }, diff --git a/test/send-test.js b/test/send-test.js index 145ce34105..2376b4f42e 100644 --- a/test/send-test.js +++ b/test/send-test.js @@ -1,11 +1,10 @@ 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 = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; -var Amount = amount.Amount; +var testutils = require("./testutils.js"); // How long to wait for server to start. var serverDelay = 1500; @@ -13,51 +12,29 @@ 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); - }, + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_teardown, "send to non-existant account without create." : function (done) { + var self = this; + var ledgers = 20; var got_proposed; - var ledgers = 20; - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) .on('success', function (r) { // Transaction sent. - console.log("success: %s", JSON.stringify(r)); + // console.log("success: %s", JSON.stringify(r)); }) .on('pending', function() { // Moving ledgers along. - console.log("missing: %d", ledgers); + // console.log("missing: %d", ledgers); ledgers -= 1; if (ledgers) { - alpha.ledger_accept(); + self.remote.ledger_accept(); } else { buster.assert(false, "Final never received."); @@ -66,29 +43,29 @@ buster.testCase("Sending", { }) .on('lost', function () { // Transaction did not make it in. - console.log("lost"); + // console.log("lost"); buster.assert(true); done(); }) .on('proposed', function (m) { // Transaction got an error. - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); buster.assert.equals(m.result, 'terNO_DST'); got_proposed = true; - alpha.ledger_accept(); // Move it along. + self.remote.ledger_accept(); // Move it along. }) .on('final', function (m) { - console.log("final: %s", JSON.stringify(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); + // console.log("error: %s", m); buster.assert(false); }) diff --git a/test/server-test.js b/test/server-test.js index 817b5f9fd6..24496537ef 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -1,22 +1,27 @@ var buster = require("buster"); -var server = require("./server.js"); +var Server = require("./server.js").Server; // How long to wait for server to start. -var serverDelay = 1500; +// var serverDelay = 1500; -buster.testRunner.timeout = 5000; +var alpha; buster.testCase("Standalone server startup", { "server start and stop" : function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }); + alpha = Server.from_config("alpha"); + + alpha + .on('started', function () { + alpha + .on('stopped', function () { + buster.assert(true); + + done(); + }) + .stop(); + }) + .start(); } }); diff --git a/test/server.js b/test/server.js index 85efe695b9..aca9b3984b 100644 --- a/test/server.js +++ b/test/server.js @@ -1,6 +1,13 @@ -// Manage test servers +// Create and stop test servers. // -// YYY Would be nice to be able to hide server output. +// Usage: +// s = new Server(name, config) +// s.verbose() : optional +// .start() +// 'started' +// +// s.stop() : stops server is started. +// 'stopped' // // Provide servers @@ -8,29 +15,40 @@ // Servers are created in tmp/server/$server // -var config = require("./config.js"); -var nodeutils = require("../js/nodeutils.js"); +var buster = require("buster"); +var child = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var util = require("util"); +var EventEmitter = require('events').EventEmitter; -var fs = require("fs"); -var path = require("path"); -var util = require("util"); -var child = require("child_process"); - -var servers = {}; +var config = require("./config.js"); +var nodeutils = require("../js/nodeutils.js"); // Create a server object -var Server = function (name, mock) { - this.name = name; - this.mock = mock; +var Server = function (name, config, verbose) { + this.name = name; + this.config = config; + this.started = false; + this.quiet = !verbose; }; -// Return a server's rippled.cfg as string. -Server.prototype.configContent = function() { - var cfg = config.servers[this.name]; +Server.prototype = new EventEmitter; - return Object.keys(cfg).map(function(o) { - return util.format("[%s]\n%s\n", o, cfg[o]); - }).join(""); +Server.from_config = function (name, verbose) { + return new Server(name, config.servers[name], verbose); +}; + +Server.prototype.on = function (e, c) { + EventEmitter.prototype.on.call(this, e, c); + + return this; +}; + +Server.prototype.once = function (e, c) { + EventEmitter.prototype.once.call(this, e, c); + + return this; }; Server.prototype.serverPath = function() { @@ -42,38 +60,51 @@ Server.prototype.configPath = function() { }; // Write a server's rippled.cfg. -Server.prototype.writeConfig = function(done) { - fs.writeFile(this.configPath(), this.configContent(), 'utf8', done); +Server.prototype._writeConfig = function(done) { + var self = this; + + fs.writeFile( + this.configPath(), + Object.keys(this.config).map(function(o) { + return util.format("[%s]\n%s\n", o, self.config[o]); + }).join(""), + 'utf8', done); }; // Spawn the server. -Server.prototype.serverSpawnSync = function() { - // Spawn in standalone mode for now. - this.child = child.spawn( - config.rippled, - [ +Server.prototype._serverSpawnSync = function() { + var self = this; + + var args = [ "-a", "-v", "--conf=rippled.cfg" - ], + ]; + + // Spawn in standalone mode for now. + this.child = child.spawn( + config.rippled, + args, { cwd: this.serverPath(), env: process.env, - stdio: 'inherit' + stdio: this.quiet ? 'ignore' : 'inherit' }); - console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.rippled, this.configPath()); + if (!this.quiet) + console.log("server: start %s: %s --conf=%s", + this.child.pid, config.rippled, args.join(" "), this.configPath()); // By default, just log exits. this.child.on('exit', function(code, signal) { // If could not exec: code=127, signal=null // If regular exit: code=0, signal=null - console.log("server: spawn: server exited code=%s: signal=%s", code, signal); + if (!self.quiet) console.log("server: spawn: server exited code=%s: signal=%s", code, signal); }); }; // Prepare server's working directory. -Server.prototype.makeBase = function (done) { +Server.prototype._makeBase = function (done) { var path = this.serverPath(); var self = this; @@ -83,80 +114,59 @@ Server.prototype.makeBase = function (done) { throw e; } else { - self.writeConfig(done); + self._writeConfig(done); } }); }; +Server.prototype.verbose = function () { + this.quiet = false; + + return this; +}; + // Create a standalone server. // Prepare the working directory and spawn the server. -Server.prototype.start = function (done) { +Server.prototype.start = function () { var self = this; - if (this.mock) { - done(); - } - else { - this.makeBase(function (e) { - if (e) { - throw e; - } - else { - self.serverSpawnSync(); - done(); - } - }); - } + if (!this.quiet) console.log("server: start: %s: %s", this.name, JSON.stringify(this.config)); + + this._makeBase(function (e) { + if (e) { + throw e; + } + else { + self._serverSpawnSync(); + self.emit('started'); + } + }); + + return this; }; // Stop a standalone server. -Server.prototype.stop = function (done) { - if (this.mock) { - console.log("server: stop: mock"); - done(); - } - else if (this.child) { +Server.prototype.stop = function () { + var self = this; + + if (this.child) { // Update the on exit to invoke done. this.child.on('exit', function (code, signal) { - console.log("server: stop: server exited"); - done(); + + if (!self.quiet) console.log("server: stop: server exited"); + + self.emit('stopped'); + delete this.child; }); + this.child.kill(); } else { - console.log("server: stop: no such server"); - done('noSuchServer'); + buster.log("server: stop: can't stop"); } -}; -// Start the named server. -exports.start = function (name, done, mock) { - if (servers[name]) - { - console.log("server: start: server already started."); - } - else - { - var server = new Server(name, mock); - - servers[name] = server; - - console.log("server: start: %s", JSON.stringify(server)); - - server.start(done); - } -}; - -// Delete the named server. -exports.stop = function (name, done) { - console.log("server: stop: %s of %s", name, Object.keys(servers).toString()); - - var server = servers[name]; - if (server) { - server.stop(done); - delete servers[name]; - } + return this; }; exports.Server = Server; diff --git a/test/testutils.js b/test/testutils.js new file mode 100644 index 0000000000..f2f0111275 --- /dev/null +++ b/test/testutils.js @@ -0,0 +1,34 @@ +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; + +var config = require("./config.js"); + +var test_setup = function (done, host) { + var self = this; + var host = host || config.server_default; + + this.store = this.store || {}; + + var data = this.store[host] = this.store[host] || {}; + + data.server = Server.from_config(host).on('started', function () { + self.remote = data.remote = Remote.from_config(host).once('ledger_closed', done).connect(); + }).start(); +}; + +var test_teardown = function (done, host) { + var host = host || config.server_default; + + var data = this.store[host]; + + data.remote + .on('disconnected', function () { + data.server.on('stopped', done).stop(); + }) + .connect(false); +}; + +exports.test_setup = test_setup; +exports.test_teardown = test_teardown; + +// vim:sw=2:sts=2:ts=8 diff --git a/test/websocket-test.js b/test/websocket-test.js index 055a0aab23..dcfc25d5b5 100644 --- a/test/websocket-test.js +++ b/test/websocket-test.js @@ -1,8 +1,7 @@ var buster = require("buster"); -var config = require("./config.js"); -var server = require("./server.js"); -var remote = require("../js/remote.js"); +var Server = require("./server.js").Server; +var Remote = require("../js/remote.js").Remote; // How long to wait for server to start. var serverDelay = 1500; @@ -11,26 +10,14 @@ buster.testRunner.timeout = 5000; buster.testCase("WebSocket connection", { 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - done(); - } - ); - }, + function (done) { server = Server.from_config("alpha").on('started', done).start(); }, 'tearDown' : - function (done) { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }, + function (done) { server.on('stopped', done).stop(); }, "websocket connect and disconnect" : function (done) { - var alpha = remote.remoteConfig(config, "alpha", 'TRACE'); + var alpha = Remote.from_config("alpha"); alpha .on('connected', function () {