mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 02:25:52 +00:00
Merge branch 'develop' of github.com:ripple/rippled into develop
This commit is contained in:
@@ -13,14 +13,16 @@
|
||||
"ripple-lib": "0.7.25",
|
||||
"async": "~0.2.9",
|
||||
"extend": "~1.2.0",
|
||||
"simple-jsonrpc": "~0.0.2"
|
||||
"simple-jsonrpc": "~0.0.2",
|
||||
"deep-equal": "0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coffee-script": "~1.6.3",
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
|
||||
"scripts": {
|
||||
"test": "mocha --reporter spec --ui tdd --timeout 10000 --slow 600 test/*-test.js"
|
||||
"test": "mocha test/*-test.{js,coffee}"
|
||||
},
|
||||
|
||||
"repository": {
|
||||
|
||||
389
test/ledger-state.coffee
Normal file
389
test/ledger-state.coffee
Normal file
@@ -0,0 +1,389 @@
|
||||
################################### REQUIRES ###################################
|
||||
|
||||
# This gives coffee-script proper file/lines in the exceptions
|
||||
|
||||
async = require("async")
|
||||
assert = require 'assert'
|
||||
Amount = require("ripple-lib").Amount
|
||||
Remote = require("ripple-lib").Remote
|
||||
Seed = require("ripple-lib").Seed
|
||||
Base = require("ripple-lib").Base
|
||||
Transaction = require("ripple-lib").Transaction
|
||||
sjcl = require("ripple-lib").sjcl
|
||||
Server = require("./server").Server
|
||||
testutils = require("./testutils")
|
||||
|
||||
#################################### HELPERS ###################################
|
||||
|
||||
pretty_json = (v) -> JSON.stringify(v, undefined, 2)
|
||||
|
||||
exports.TestAccount = class TestAccount
|
||||
SHA256_RIPEMD160: (bits) ->
|
||||
sjcl.hash.ripemd160.hash sjcl.hash.sha256.hash(bits)
|
||||
|
||||
get_address: (passphrase) ->
|
||||
key_pair = Seed.from_json(passphrase).get_key()
|
||||
pubKey = sjcl.codec.hex.toBits key_pair.to_hex_pub()
|
||||
Base.encode_check(0,sjcl.codec.bytes.fromBits(@SHA256_RIPEMD160 pubKey))
|
||||
|
||||
constructor: (passphrase) ->
|
||||
@passphrase = passphrase
|
||||
@address = @get_address(passphrase)
|
||||
|
||||
############################# LEDGER STATE COMPILER ############################
|
||||
|
||||
exports.LedgerState = class LedgerState
|
||||
setup_issuer_realiaser: ->
|
||||
users = @config.accounts
|
||||
lookup = {}
|
||||
accounts = []
|
||||
|
||||
for name, user of users
|
||||
accounts.push user.account
|
||||
lookup[user.account] = name
|
||||
|
||||
realias = new RegExp(accounts.join("|"), "g")
|
||||
@realias_issuer = (str) -> str.replace(realias, (match) ->lookup[match])
|
||||
|
||||
parse_amount: (amt_val) ->
|
||||
try
|
||||
amt = Amount.from_json(amt_val)
|
||||
if not amt.is_valid()
|
||||
throw new Error()
|
||||
catch e
|
||||
try
|
||||
amt = Amount.from_human(amt_val)
|
||||
if not amt.is_valid()
|
||||
throw new Error()
|
||||
catch e
|
||||
amt = null
|
||||
amt
|
||||
|
||||
amount_key: (amt) ->
|
||||
currency = amt.currency().to_json()
|
||||
issuer = @realias_issuer amt.issuer().to_json()
|
||||
key = "#{currency}/#{issuer}"
|
||||
key.issuer = issuer
|
||||
key
|
||||
|
||||
apply: (context)->
|
||||
@create_accounts_by_issuing_xrp_from_root(context)
|
||||
@create_trust_limits(context)
|
||||
@deliver_ious(context)
|
||||
|
||||
record_iou: (account_id, amt)->
|
||||
key = @amount_key amt
|
||||
@assert @declaration.accounts[key.split('/')[1]]?,
|
||||
"Account for #{key} does not exist"
|
||||
|
||||
a_ious = @ensure account_id, @ious
|
||||
@assert !a_ious[key]?,
|
||||
"Account #{account_id} has more than one amount for #{key}"
|
||||
a_ious[key] = amt
|
||||
|
||||
ensure: (account_id, obj, val) ->
|
||||
if not obj[account_id]?
|
||||
obj[account_id] = val ? {}
|
||||
obj[account_id]
|
||||
|
||||
record_xrp: (account_id, amt)->
|
||||
@assert !@accounts[account_id]?,
|
||||
"Already declared XRP for #{account_id}"
|
||||
@accounts[account_id] = amt
|
||||
|
||||
record_trust: (account_id, amt, is_balance) ->
|
||||
key = @amount_key amt
|
||||
a_trusts = @ensure account_id, @trusts_by_ci
|
||||
|
||||
if a_trusts[key]? and !is_balance
|
||||
cmp = amt.compareTo a_trusts[key]
|
||||
@assert cmp != - 1,
|
||||
"Account #{account_id} trust is less than balance for #{key}"
|
||||
a_trusts[key] = amt
|
||||
|
||||
compile_explicit_trusts: ->
|
||||
for account_id, account of @declaration.accounts
|
||||
if not account.trusts?
|
||||
continue
|
||||
|
||||
for amt_val in account.trusts
|
||||
amt = @parse_amount amt_val
|
||||
@assert amt != null and !amt.is_native(),
|
||||
"Trust amount #{amt_val} specified for #{account_id} is not valid"
|
||||
@record_trust(account_id, amt, false)
|
||||
|
||||
compile_accounts_balances_and_implicit_trusts: ->
|
||||
for account_id, account of @declaration.accounts
|
||||
xrp_balance = null
|
||||
|
||||
@assert account.balance?,
|
||||
"No balance declared for #{account_id}"
|
||||
|
||||
for amt_val in account.balance
|
||||
amt = @parse_amount amt_val
|
||||
@assert amt != null,
|
||||
"Balance amount #{amt_val} specified for #{account_id} is not valid"
|
||||
|
||||
if amt.is_native()
|
||||
xrp_balance = @record_xrp(account_id, amt)
|
||||
else
|
||||
@record_iou(account_id, amt)
|
||||
@record_trust(account_id, amt, true)
|
||||
|
||||
@assert xrp_balance,
|
||||
"No XRP balanced declared for #{account_id}"
|
||||
|
||||
compile_offers: ->
|
||||
for account_id, account of @declaration.accounts
|
||||
if not account.offers?
|
||||
continue
|
||||
for offer in account.offers
|
||||
[pays, gets, splat...] = offer
|
||||
gets_amt = @parse_amount gets
|
||||
@assert gets_amt != null,
|
||||
"For account #{account_id} taker_gets amount #{gets} is invalid"
|
||||
|
||||
pays_amt = @parse_amount pays
|
||||
@assert pays_amt != null,
|
||||
"For account #{account_id} taker_pays amount #{pays} is invalid"
|
||||
|
||||
a_offers = @ensure(account_id, @offers_by_ci)
|
||||
a_offers = @ensure(account_id, @offers_by_ci)
|
||||
offers_all = @ensure('offers', a_offers, [])
|
||||
|
||||
if gets_amt.is_native()
|
||||
total = a_offers.xrp_total ?= new Amount.from_json('0')
|
||||
new_total = total.add(gets_amt)
|
||||
@assert @accounts[account_id].compareTo(new_total) != - 1,
|
||||
"Account #{account_id}s doesn't have enough xrp to place #{offer}"
|
||||
else
|
||||
key = @amount_key gets_amt
|
||||
key_offers = @ensure(key, a_offers, {})
|
||||
|
||||
total = key_offers.total ?= Amount.from_json("0/#{key}")
|
||||
new_total = total.add(gets_amt)
|
||||
a_ious = @ensure(account_id, @ious)
|
||||
@assert a_ious[key]?,
|
||||
"Account #{account_id} doesn't hold any #{key}"
|
||||
@assert a_ious[key].compareTo(new_total) != - 1,
|
||||
"Account #{account_id} doesn't have enough #{key} to place #{offer}"
|
||||
|
||||
key_offers.total = new_total
|
||||
|
||||
offers_all.push [pays_amt, gets_amt, splat...]
|
||||
|
||||
@offers = []
|
||||
for account_id, obj of @offers_by_ci
|
||||
for offer in obj.offers
|
||||
sliced = offer[0..]
|
||||
sliced.unshift account_id
|
||||
@offers.push sliced
|
||||
# @offers[account_id] = obj.offers
|
||||
|
||||
base_reserve: ->
|
||||
@declaration.reserve?.base ? "50.0"
|
||||
|
||||
incr_reserve: ->
|
||||
@declaration.reserve?.base ? "12.5"
|
||||
|
||||
check_reserves: ->
|
||||
base_reserve_amt = @base_reserve()
|
||||
incr_reserve_amt = @incr_reserve()
|
||||
|
||||
base_reserve = @parse_amount base_reserve_amt
|
||||
inc_reserve = @parse_amount incr_reserve_amt
|
||||
|
||||
@assert base_reserve != null,
|
||||
"Base reserve amount #{base_reserve_amt} is invalid"
|
||||
|
||||
@assert base_reserve != null,
|
||||
"incremental amount #{incr_reserve_amt} is invalid"
|
||||
|
||||
for account_id, account of @declaration.accounts
|
||||
total_needed = base_reserve.clone()
|
||||
owner_count = 0
|
||||
|
||||
a_offers = @offers_by_ci[account_id]
|
||||
if a_offers?
|
||||
if a_offers.xrp_total?
|
||||
total_needed = total_needed.add a_offers.xrp_total
|
||||
if a_offers.offers?
|
||||
owner_count += @offers_by_ci[account_id].offers.length
|
||||
|
||||
if @trusts_by_ci[account_id]?
|
||||
owner_count += Object.keys(@trusts_by_ci[account_id]).length
|
||||
|
||||
owner_count_amount = Amount.from_json(String(owner_count))
|
||||
inc_reserve_n = owner_count_amount.multiply(inc_reserve)
|
||||
total_needed = total_needed.add(inc_reserve_n)
|
||||
|
||||
@assert @accounts[account_id].compareTo total_needed != - 1,
|
||||
"Account #{account_id} needs more XRP for reserve"
|
||||
|
||||
@reserves[account_id] = total_needed
|
||||
|
||||
format_payments: ->
|
||||
# We do these first as the following @ious need xrp to issue ious ;0
|
||||
for account_id, xrps of @accounts
|
||||
@xrp_payments.push ['root', account_id, xrps]
|
||||
|
||||
for account_id, ious of @ious
|
||||
for curr_issuer, amt of ious
|
||||
src = @realias_issuer amt.issuer().to_json()
|
||||
dst = account_id
|
||||
@iou_payments.push [src, dst, amt]
|
||||
|
||||
format_trusts: ->
|
||||
for account_id, trusts of @trusts_by_ci
|
||||
for curr_issuer, amt of trusts
|
||||
@trusts.push [account_id, amt]
|
||||
|
||||
transactor: (fn, args_list, on_each, callback) ->
|
||||
if args_list.length == 0
|
||||
return callback()
|
||||
|
||||
if not callback?
|
||||
callback = on_each
|
||||
on_each = null
|
||||
|
||||
@assert callback?, "Must supply a callback"
|
||||
finalized = {
|
||||
n: args_list.length
|
||||
one: ->
|
||||
if --finalized.n <= 0
|
||||
callback()
|
||||
}
|
||||
|
||||
async.concatSeries(args_list, ((args, callback) =>
|
||||
tx = @remote.transaction()
|
||||
on_each?(args..., tx)
|
||||
fn.apply(tx, args).on("proposed", (m) =>
|
||||
@assert m.engine_result is "tesSUCCESS", "Transactor failure: #{pretty_json m}"
|
||||
callback()
|
||||
).on('final', (m) =>
|
||||
finalized.one()
|
||||
)
|
||||
.on("error", (m) =>
|
||||
assert false, pretty_json m
|
||||
).submit()
|
||||
),
|
||||
=> testutils.ledger_close @remote, ->
|
||||
)
|
||||
|
||||
requester: (fn, args_list, on_each, callback) ->
|
||||
if not callback?
|
||||
callback = on_each
|
||||
on_each = null
|
||||
|
||||
@assert callback?, "Must supply a callback"
|
||||
|
||||
async.concatSeries(args_list, ((args, callback) =>
|
||||
req = fn.apply @remote, args
|
||||
on_each?(args..., req)
|
||||
req.on("success", (m) =>
|
||||
if m.status?
|
||||
@assert m.status is "success", "requester failure: #{pretty_json m}"
|
||||
callback()
|
||||
).on("error", (m) =>
|
||||
@assert false, pretty_json m
|
||||
).request()
|
||||
), -> callback())
|
||||
|
||||
ensure_config_has_test_accounts: ->
|
||||
for account of @declaration.accounts
|
||||
if not @config.accounts[account]?
|
||||
acc = @config.accounts[account] = {}
|
||||
user = new TestAccount(account)
|
||||
acc.account = user.address
|
||||
acc.secret = user.passphrase
|
||||
# Index by nickname ...
|
||||
@remote.set_secret account, acc.secret
|
||||
# ... and by account ID
|
||||
@remote.set_secret acc.account, acc.secret
|
||||
@setup_issuer_realiaser()
|
||||
|
||||
pretty_json: (v) ->
|
||||
@realias_issuer pretty_json(v)
|
||||
|
||||
constructor: (declaration, @assert, @remote, @config) ->
|
||||
@declaration = declaration
|
||||
@accounts = {} # {$account_id : $xrp_amt}
|
||||
@trusts_by_ci = {} # {$account_id : {$currency/$issuer : $iou_amt}}
|
||||
@ious = {} # {$account_id : {$currency/$issuer : $iou_amt}}
|
||||
@offers_by_ci = {} # {$account_id : {offers: [], $currency/$issuer : {total: $iou_amt}}}
|
||||
@reserves = {}
|
||||
|
||||
@xrp_payments = [] # {$account_id: []}
|
||||
@trusts = [] # {$account_id: []}
|
||||
@iou_payments = [] # {$account_id: []}
|
||||
@offers = [] # {$account_id: []}
|
||||
|
||||
@ensure_config_has_test_accounts()
|
||||
@compile_accounts_balances_and_implicit_trusts()
|
||||
@compile_explicit_trusts()
|
||||
@compile_offers()
|
||||
@check_reserves()
|
||||
@format_payments()
|
||||
@format_trusts()
|
||||
|
||||
setup: (log, done) ->
|
||||
LOG = (m) ->
|
||||
self.what = m
|
||||
log(m)
|
||||
|
||||
accounts = (k for k,ac of @accounts).sort()
|
||||
@remote.set_account_seq(seq, 1) for seq in accounts.concat 'root' # <--
|
||||
accounts_apply_arguments = ([ac] for ac in @accounts)
|
||||
self = this
|
||||
|
||||
async.waterfall [
|
||||
(cb) ->
|
||||
self.transactor(
|
||||
Transaction::payment,
|
||||
self.xrp_payments,
|
||||
((src, dest, amt) ->
|
||||
LOG("Account `#{src}` creating account `#{dest}` by
|
||||
making payment of #{amt.to_text_full()}") ),
|
||||
cb)
|
||||
(cb) ->
|
||||
self.transactor(
|
||||
Transaction::ripple_line_set,
|
||||
self.trusts,
|
||||
((src, amt) ->
|
||||
issuer = self.realias_issuer amt.issuer().to_json()
|
||||
currency = amt.currency().to_json()
|
||||
LOG("Account `#{src}` trusts account `#{issuer}` for
|
||||
#{amt.to_text()} #{currency}") ),
|
||||
cb)
|
||||
(cb) ->
|
||||
self.transactor(
|
||||
Transaction::payment,
|
||||
self.iou_payments,
|
||||
((src, dest, amt, tx) ->
|
||||
LOG("Account `#{src}` is making a payment of #{amt.to_text_full()}
|
||||
to `#{dest}`") ),
|
||||
cb)
|
||||
(cb) ->
|
||||
self.transactor(
|
||||
Transaction::offer_create,
|
||||
self.offers,
|
||||
((src, pays, gets) ->
|
||||
LOG("Account `#{src}` is selling #{gets.to_text_full()}
|
||||
for #{pays.to_text_full()}")),
|
||||
cb)
|
||||
(cb) ->
|
||||
testutils.ledger_close self.remote, cb
|
||||
(cb) ->
|
||||
self.requester(Remote::request_account_lines, accounts_apply_arguments,
|
||||
((acc) ->
|
||||
LOG("Checking account_lines for #{acc}")),
|
||||
cb)
|
||||
(cb) ->
|
||||
self.requester(Remote::request_account_info, accounts_apply_arguments,
|
||||
((acc) ->
|
||||
LOG("Checking account_info for #{acc}")),
|
||||
cb)
|
||||
], (error) ->
|
||||
assert !error,
|
||||
"There was an error @ #{self.what}"
|
||||
done()
|
||||
@@ -1 +1,5 @@
|
||||
--reporter spec --ui tdd --timeout 10000 --slow 600
|
||||
--reporter spec
|
||||
--compilers coffee:coffee-script
|
||||
--ui tdd
|
||||
--timeout 10000
|
||||
--slow 600
|
||||
673
test/new-path-test.coffee
Normal file
673
test/new-path-test.coffee
Normal file
@@ -0,0 +1,673 @@
|
||||
################################### REQUIRES ###################################
|
||||
|
||||
extend = require 'extend'
|
||||
fs = require 'fs'
|
||||
async = require 'async'
|
||||
deep_eq = require 'deep-equal'
|
||||
|
||||
{Amount
|
||||
Remote
|
||||
Seed
|
||||
Base
|
||||
Transaction
|
||||
PathFind
|
||||
sjcl
|
||||
UInt160} = require 'ripple-lib'
|
||||
|
||||
testutils = require './testutils'
|
||||
{Server} = require './server'
|
||||
{LedgerState, TestAccount} = require './ledger-state'
|
||||
{test_accounts} = require './random-test-addresses'
|
||||
|
||||
simple_assert = require 'assert'
|
||||
|
||||
#################################### README ####################################
|
||||
"""
|
||||
The tests are written in a declarative style:
|
||||
|
||||
Each case has an entry in the `path_finding_cases` object
|
||||
The key translates to a `suite(key, {...})`
|
||||
The `{...}` passed in is compiled into a setup/teardown for the `ledger` and
|
||||
into a bunch of `test` invokations for the `paths_expected`
|
||||
|
||||
- aliases are used throughout for easier reading
|
||||
|
||||
- test account addresses will be created `on the fly`
|
||||
|
||||
no need to declare in testconfig.js
|
||||
debugged responses from the server substitute addresses for aliases
|
||||
|
||||
- The fixtures are setup just once for each ledger, multiple path finding
|
||||
tests can be executed
|
||||
|
||||
- `paths_expected` top level keys are group names
|
||||
2nd level keys are path test declarations
|
||||
|
||||
test declaration keys can be suffixed meaningfully with
|
||||
|
||||
`_skip`
|
||||
`_only`
|
||||
|
||||
test declaration values can set
|
||||
|
||||
debug: true
|
||||
|
||||
Will dump the path declaration and
|
||||
translated request and subsequent response
|
||||
|
||||
- hops in `alternatives[*][paths][*]` can be written in shorthand
|
||||
eg.
|
||||
ABC/G3|G3
|
||||
get `ABC/G3` through `G3`
|
||||
|
||||
ABC/M1|M1
|
||||
get `ABC/M1` through `M1`
|
||||
|
||||
XRP|$
|
||||
get `XRP` through `$`
|
||||
$ signifies an order book rather than account
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Tests can be written in the 'path-tests.json' file in same directory # <--
|
||||
------------------------------------------------------------------------------
|
||||
"""
|
||||
#################################### HELPERS ###################################
|
||||
|
||||
assert = simple_assert
|
||||
refute = (cond, msg) -> assert(!cond, msg)
|
||||
prettyj = pretty_json = (v) -> JSON.stringify(v, undefined, 2)
|
||||
|
||||
propagater = (done) ->
|
||||
(f) ->
|
||||
->
|
||||
return if done.aborted
|
||||
try
|
||||
f(arguments...)
|
||||
catch e
|
||||
done.aborted = true
|
||||
throw e
|
||||
|
||||
assert_match = (o, key_vals, message) ->
|
||||
"""
|
||||
assert_match path[i], matcher,
|
||||
"alternative[#{ai}].paths[#{pi}]"
|
||||
"""
|
||||
|
||||
for k,v of key_vals
|
||||
assert.equal o[k], v, message
|
||||
|
||||
#################################### CONFIG ####################################
|
||||
|
||||
config = testutils.init_config()
|
||||
|
||||
############################### ALTERNATIVES TEST ##############################
|
||||
|
||||
expand_alternative = (alt) ->
|
||||
"""
|
||||
|
||||
Make explicit the currency and issuer in each hop in paths_computed
|
||||
|
||||
"""
|
||||
amt = Amount.from_json(alt.source_amount)
|
||||
|
||||
for path in alt.paths_computed
|
||||
prev_issuer = amt.issuer().to_json()
|
||||
prev_currency = amt.currency().to_json()
|
||||
|
||||
for hop, hop_i in path
|
||||
if not hop.currency?
|
||||
hop.currency = prev_currency
|
||||
|
||||
if not hop.issuer? and hop.currency != 'XRP'
|
||||
if hop.account?
|
||||
hop.issuer = hop.account
|
||||
else
|
||||
hop.issuer = prev_issuer
|
||||
|
||||
if hop.type & 0x10
|
||||
prev_currency = hop.currency
|
||||
|
||||
if hop.type & 0x20
|
||||
prev_issuer = hop.issuer
|
||||
else if hop.account?
|
||||
prev_issuer = hop.account
|
||||
|
||||
return alt
|
||||
|
||||
create_shorthand = (alternatives) ->
|
||||
"""
|
||||
|
||||
Convert explicit paths_computed into the format used by `paths_expected`
|
||||
These can be pasted in as the basis of tests.
|
||||
|
||||
"""
|
||||
shorthand = []
|
||||
|
||||
for alt in alternatives
|
||||
short_alt = {}
|
||||
shorthand.push short_alt
|
||||
|
||||
amt = Amount.from_json alt.source_amount
|
||||
if amt.is_native()
|
||||
short_alt.amount = amt.to_human()
|
||||
if not (~short_alt.amount.search('.'))
|
||||
short_alt.amount = short_alt.amount + '.0'
|
||||
else
|
||||
short_alt.amount = amt.to_text_full()
|
||||
|
||||
short_alt.paths = []
|
||||
|
||||
for path in alt.paths_computed
|
||||
short_path = []
|
||||
short_alt.paths.push short_path
|
||||
|
||||
for node in path
|
||||
hop = node.currency
|
||||
hop = "#{hop}/#{node.issuer}" if node.issuer?
|
||||
hop = "#{hop}|#{if node.account? then node.account else "$"}"
|
||||
short_path.push hop
|
||||
|
||||
return shorthand
|
||||
|
||||
ensure_list = (v) ->
|
||||
if Array.isArray(v)
|
||||
v
|
||||
else
|
||||
[v]
|
||||
|
||||
test_alternatives_factory = (realias_pp, realias_text) ->
|
||||
"""
|
||||
|
||||
We are using a factory to create `test_alternatives` because it needs the
|
||||
per ledger `realias_*` functions
|
||||
|
||||
"""
|
||||
hop_matcher = (decl_hop) ->
|
||||
[ci, f] = decl_hop.split('|')
|
||||
if not f?
|
||||
throw new Error("No `|` in #{decl_hop}")
|
||||
|
||||
[c, i] = ci.split('/')
|
||||
is_account = if f == '$' then false else true
|
||||
matcher = currency: c
|
||||
matcher.issuer = i if i?
|
||||
matcher.account = f if is_account
|
||||
matcher
|
||||
|
||||
match_path = (test, path, ai, pi) ->
|
||||
test = (hop_matcher(hop) for hop in test)
|
||||
assert.equal path.length, test.length,
|
||||
"alternative[#{ai}] path[#{pi}] expecting #{test.length} hops"
|
||||
|
||||
for matcher, i in test
|
||||
assert_match path[i], matcher,
|
||||
"alternative[#{ai}].paths[#{pi}]"
|
||||
return
|
||||
|
||||
simple_match_path = (test, path, ai, pi) ->
|
||||
"""
|
||||
|
||||
@test
|
||||
|
||||
A shorthand specified path
|
||||
|
||||
@path
|
||||
|
||||
A path as returned by the server with `expand_alternative` done
|
||||
so issuer and currency are always stated.
|
||||
|
||||
"""
|
||||
test = (hop_matcher(hop) for hop in test)
|
||||
return false if not test.length == path.length
|
||||
|
||||
for matcher, i in test
|
||||
for k, v of matcher
|
||||
return false if not path[i]?
|
||||
if path[i][k] != v
|
||||
return false
|
||||
true
|
||||
|
||||
amounts = ->
|
||||
(Amount.from_json a for a in arguments)
|
||||
|
||||
amounts_text = ->
|
||||
(realias_text a.to_text_full() for a in arguments)
|
||||
|
||||
check_for_no_redundant_paths = (alternatives) ->
|
||||
for alt, i in alternatives
|
||||
existing_paths = []
|
||||
for path in alt.paths_computed
|
||||
for existing in existing_paths
|
||||
assert !(deep_eq path, existing),
|
||||
"Duplicate path in alternatives[#{i}]\n"+
|
||||
"#{realias_pp alternatives[0]}"
|
||||
|
||||
existing_paths.push path
|
||||
return
|
||||
|
||||
test_alternatives = (test, actual, error_context) ->
|
||||
"""
|
||||
|
||||
@test
|
||||
alternatives in shorthand format
|
||||
|
||||
@actual
|
||||
alternatives as returned in a `path_find` response
|
||||
|
||||
@error_context
|
||||
|
||||
a function providing a string with extra context to provide to assertion
|
||||
messages
|
||||
|
||||
"""
|
||||
check_for_no_redundant_paths actual
|
||||
|
||||
for t, ti in ensure_list(test)
|
||||
a = actual[ti]
|
||||
[t_amt, a_amt] = amounts(t.amount, a.source_amount)
|
||||
[t_amt_txt, a_amt_txt] = amounts_text(t_amt, a_amt)
|
||||
|
||||
# console.log typeof t_amt
|
||||
|
||||
assert t_amt.equals(a_amt),
|
||||
"Expecting alternative[#{ti}].amount: "+
|
||||
"#{t_amt_txt} == #{a_amt_txt}"
|
||||
|
||||
t_paths = ensure_list(t.paths)
|
||||
|
||||
tn = t_paths.length
|
||||
an = a.paths_computed.length
|
||||
assert.equal tn, an, "Different number of paths specified for alternative[#{ti}]"+
|
||||
", expected: #{prettyj t_paths}, "+
|
||||
"actual(shorthand): #{prettyj create_shorthand actual}"+
|
||||
"actual(verbose): #{prettyj a.paths_computed}"+
|
||||
error_context()
|
||||
|
||||
for p, i in t_paths
|
||||
matched = false
|
||||
|
||||
for m in a.paths_computed
|
||||
if simple_match_path(p, m, ti, i)
|
||||
matched = true
|
||||
break
|
||||
|
||||
assert matched, "Can't find a match for path[#{i}]: #{prettyj p} "+
|
||||
"amongst #{prettyj create_shorthand [a]}"+
|
||||
error_context()
|
||||
return
|
||||
|
||||
################################################################################
|
||||
|
||||
create_path_test = (pth) ->
|
||||
return (done) ->
|
||||
propagates = propagater done
|
||||
|
||||
self = this
|
||||
WHAT = self.log_what
|
||||
ledger = self.ledger
|
||||
test_alternatives = test_alternatives_factory ledger.pretty_json.bind(ledger),
|
||||
ledger.realias_issuer
|
||||
|
||||
|
||||
WHAT "#{pth.title}: #{pth.src} sending #{pth.dst}, "+
|
||||
"#{pth.send}, via #{pth.via}"
|
||||
|
||||
one_message = (f) ->
|
||||
self.remote._servers[0].once 'before_send_message_for_non_mutators', f
|
||||
|
||||
sent = "TODO: need to patch ripple-lib"
|
||||
one_message (m) -> sent = m
|
||||
|
||||
error_info = (m, more) ->
|
||||
info = path_expected: pth, path_find_request: sent, path_find_updates: m
|
||||
extend(info, more) if more?
|
||||
ledger.pretty_json(info)
|
||||
|
||||
assert Amount.from_json(pth.send).is_valid(),
|
||||
"#{pth.send} is not valid Amount"
|
||||
|
||||
_src = UInt160.json_rewrite(pth.src)
|
||||
_dst = UInt160.json_rewrite(pth.dst)
|
||||
_amt = Amount.from_json(pth.send)
|
||||
|
||||
# self.server.clear_logs() "TODO: need to patch ripple-lib"
|
||||
pf = self.remote.path_find(_src, _dst, _amt, [{currency: pth.via}])
|
||||
|
||||
updates = 0
|
||||
max_seen = 0
|
||||
messages = {}
|
||||
|
||||
pf.on "error", propagates (m) -> # <--
|
||||
assert false, "fail (error): #{error_info(m)}"
|
||||
done()
|
||||
|
||||
pf.on "update", propagates (m) -> # <--
|
||||
# TODO:hack:
|
||||
expand_alternative alt for alt in m.alternatives
|
||||
|
||||
messages[if updates then "update-#{updates}" else 'initial-response'] = m
|
||||
updates++
|
||||
|
||||
assert m.alternatives.length >= max_seen,
|
||||
"Subsequent path_find update' should never have less " +
|
||||
"alternatives:\n#{ledger.pretty_json messages}"
|
||||
|
||||
max_seen = m.alternatives.length
|
||||
|
||||
if updates == 2
|
||||
testutils.ledger_close(self.remote, -> )
|
||||
|
||||
if updates == 3
|
||||
# "TODO: need to patch ripple-lib"
|
||||
# self.log_pre(self.server.get_logs(), "Server Logs")
|
||||
|
||||
if pth.do_send?
|
||||
do_send( (ledger.pretty_json.bind ledger), WHAT, self.remote, pth,
|
||||
messages['update-2'], done )
|
||||
|
||||
if pth.debug
|
||||
console.log ledger.pretty_json(messages)
|
||||
console.log error_info(m)
|
||||
console.log ledger.pretty_json create_shorthand(m.alternatives)
|
||||
|
||||
if pth.alternatives?
|
||||
# We realias before doing any comparisons
|
||||
alts = ledger.realias_issuer(JSON.stringify(m.alternatives))
|
||||
alts = JSON.parse(alts)
|
||||
test = pth.alternatives
|
||||
|
||||
assert test.length == alts.length,
|
||||
"Number of `alternatives` specified is different: "+
|
||||
"#{error_info(m)}"
|
||||
|
||||
if test.length == alts.length
|
||||
test_alternatives(pth.alternatives, alts, -> error_info(m))
|
||||
|
||||
if pth.n_alternatives?
|
||||
assert pth.n_alternatives == m.alternatives.length,
|
||||
"fail (wrong n_alternatives): #{error_info(m)}"
|
||||
|
||||
done() if not pth.do_send?
|
||||
|
||||
################################ SUITE CREATION ################################
|
||||
|
||||
skip_or_only = (title, test_or_suite) ->
|
||||
endsWith = (s, suffix) ->
|
||||
~s.indexOf(suffix, s.length - suffix.length)
|
||||
|
||||
if endsWith title, '_only'
|
||||
test_or_suite.only
|
||||
else if endsWith title, '_skip'
|
||||
test_or_suite.skip
|
||||
else
|
||||
test_or_suite
|
||||
|
||||
gather_path_definers = (path_expected) ->
|
||||
tests = []
|
||||
|
||||
for group, subgroup of path_expected
|
||||
for title, path of subgroup
|
||||
definer_factory = (group, title, path) ->
|
||||
path.title = "#{[group, title].join('.')}"
|
||||
test_func = skip_or_only path.title, test
|
||||
->
|
||||
test_func(path.title, create_path_test(path) )
|
||||
|
||||
tests.push definer_factory(group, title, path)
|
||||
tests
|
||||
|
||||
suite_factory = (declaration) ->
|
||||
->
|
||||
context = null
|
||||
|
||||
suiteSetup (done) ->
|
||||
context = @
|
||||
@log_what = ->
|
||||
|
||||
testutils.build_setup().call @, ->
|
||||
context.ledger = new LedgerState(declaration.ledger,
|
||||
assert,
|
||||
context.remote,
|
||||
config)
|
||||
|
||||
context.ledger.setup(context.log_what, done)
|
||||
|
||||
suiteTeardown (done) ->
|
||||
testutils.build_teardown().call context, done
|
||||
|
||||
for definer in gather_path_definers(declaration.paths_expected)
|
||||
definer()
|
||||
|
||||
define_suites = (path_finding_cases) ->
|
||||
for case_name, declaration of path_finding_cases
|
||||
suite_func = skip_or_only case_name, suite
|
||||
suite_func case_name, suite_factory(declaration)
|
||||
|
||||
############################## PATH FINDING CASES ##############################
|
||||
# Later we reference A0, the `unknown account`, directly embedding the full
|
||||
# address.
|
||||
A0 = (new TestAccount('A0')).address
|
||||
assert A0 == 'rBmhuVAvi372AerwzwERGjhLjqkMmAwxX'
|
||||
|
||||
path_finding_cases_string = fs.readFileSync(__dirname + "/path-tests.json")
|
||||
path_finding_cases = JSON.parse path_finding_cases_string
|
||||
|
||||
# You need two gateways, same currency. A market maker. A source that trusts one
|
||||
# gateway and holds its currency, and a destination that trusts the other.
|
||||
|
||||
extend path_finding_cases,
|
||||
"Path Tests (Bitstamp + SnapSwap account holders | liquidity provider with no offers)":
|
||||
ledger:
|
||||
accounts:
|
||||
G1BS:
|
||||
balance: ["1000.0"]
|
||||
G2SW:
|
||||
balance: ["1000.0"]
|
||||
A1:
|
||||
balance: ["1000.0", "1000/HKD/G1BS"]
|
||||
trusts: ["2000/HKD/G1BS"]
|
||||
A2:
|
||||
balance: ["1000.0", "1000/HKD/G2SW"]
|
||||
trusts: ["2000/HKD/G2SW"]
|
||||
M1:
|
||||
# SnapSwap wants to be able to set trust line quality settings so they
|
||||
# can charge a fee when transactions ripple across. Liquitidy
|
||||
# provider, via trusting/holding both accounts
|
||||
balance: ["11000.0",
|
||||
"1200/HKD/G1BS",
|
||||
"5000/HKD/G2SW"
|
||||
]
|
||||
trusts: ["100000/HKD/G1BS", "100000/HKD/G2SW"]
|
||||
# We haven't got ANY offers
|
||||
|
||||
paths_expected: {
|
||||
BS:
|
||||
P1:
|
||||
debug: false
|
||||
src: "A1", dst: "A2", send: "10/HKD/A2", via: "HKD"
|
||||
n_alternatives: 1
|
||||
P2:
|
||||
debug: false
|
||||
src: "A2", dst: "A1", send: "10/HKD/A1", via: "HKD"
|
||||
n_alternatives: 1
|
||||
P3:
|
||||
debug: false
|
||||
src: "G1BS", dst: "A2", send: "10/HKD/A2", via: "HKD"
|
||||
alternatives: [
|
||||
amount: "10/HKD/G1BS",
|
||||
paths: [["HKD/M1|M1", "HKD/G2SW|G2SW"]]
|
||||
]
|
||||
P5:
|
||||
debug: false
|
||||
src: "M1",
|
||||
send: "10/HKD/M1",
|
||||
dst: "G1BS",
|
||||
via: "HKD"
|
||||
P4:
|
||||
debug: false
|
||||
src: "G2SW", send: "10/HKD/A1", dst: "A1", via: "HKD"
|
||||
alternatives: [
|
||||
amount: "10/HKD/G2SW",
|
||||
paths: [["HKD/M1|M1", "HKD/G1BS|G1BS"]]
|
||||
]
|
||||
}
|
||||
"Path Tests #4 (non-XRP to non-XRP, same currency)": {
|
||||
ledger:
|
||||
accounts:
|
||||
G1: balance: ["1000.0"]
|
||||
G2: balance: ["1000.0"]
|
||||
G3: balance: ["1000.0"]
|
||||
G4: balance: ["1000.0"]
|
||||
A1:
|
||||
balance: ["1000.0", "1000/HKD/G1"]
|
||||
trusts: ["2000/HKD/G1"]
|
||||
A2:
|
||||
balance: ["1000.0", "1000/HKD/G2"]
|
||||
trusts: ["2000/HKD/G2"]
|
||||
A3:
|
||||
balance: ["1000.0", "1000/HKD/G1"]
|
||||
trusts: ["2000/HKD/G1"]
|
||||
A4:
|
||||
balance: ["10000.0"]
|
||||
M1:
|
||||
balance: ["11000.0", "1200/HKD/G1", "5000/HKD/G2"]
|
||||
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
|
||||
offers: [
|
||||
["1000/HKD/G1", "1000/HKD/G2"]
|
||||
]
|
||||
M2:
|
||||
balance: ["11000.0", "1200/HKD/G1", "5000/HKD/G2"]
|
||||
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
|
||||
offers: [
|
||||
["10000.0", "1000/HKD/G2"]
|
||||
["1000/HKD/G1", "10000.0"]
|
||||
]
|
||||
|
||||
paths_expected: {
|
||||
T4:
|
||||
"A) Borrow or repay":
|
||||
comment: 'Source -> Destination (repay source issuer)'
|
||||
src: "A1", send: "10/HKD/G1", dst: "G1", via: "HKD"
|
||||
alternatives: [amount: "10/HKD/A1", paths: []]
|
||||
|
||||
"A2) Borrow or repay":
|
||||
comment: 'Source -> Destination (repay destination issuer)'
|
||||
src: "A1", send: "10/HKD/A1", dst: "G1", via: "HKD"
|
||||
alternatives: [amount: "10/HKD/A1", paths: []]
|
||||
|
||||
"B) Common gateway":
|
||||
comment: 'Source -> AC -> Destination'
|
||||
src: "A1", send: "10/HKD/A3", dst: "A3", via: "HKD"
|
||||
alternatives: [amount: "10/HKD/A1", paths: [["HKD/G1|G1"]]]
|
||||
|
||||
"C) Gateway to gateway":
|
||||
comment: 'Source -> OB -> Destination'
|
||||
src: "G1", send: "10/HKD/G2", dst: "G2", via: "HKD"
|
||||
debug: false
|
||||
alternatives: [
|
||||
amount: "10/HKD/G1"
|
||||
paths: [["HKD/M2|M2"],
|
||||
["HKD/M1|M1"],
|
||||
["HKD/G2|$"]
|
||||
["XRP|$", "HKD/G2|$"]
|
||||
]
|
||||
]
|
||||
|
||||
"D) User to unlinked gateway via order book":
|
||||
comment: 'Source -> AC -> OB -> Destination'
|
||||
src: "A1", send: "10/HKD/G2", dst: "G2", via: "HKD"
|
||||
debug: false
|
||||
alternatives: [
|
||||
amount: "10/HKD/A1"
|
||||
paths: [
|
||||
["HKD/G1|G1", "HKD/G2|$"], # <--
|
||||
["HKD/G1|G1", "HKD/M2|M2"],
|
||||
["HKD/G1|G1", "HKD/M1|M1"],
|
||||
["HKD/G1|G1", "XRP|$", "HKD/G2|$"]
|
||||
]
|
||||
]
|
||||
|
||||
"I4) XRP bridge":
|
||||
comment: 'Source -> AC -> OB to XRP -> OB from XRP -> AC -> Destination'
|
||||
src: "A1", send: "10/HKD/A2", dst: "A2", via: "HKD"
|
||||
debug: false
|
||||
alternatives: [
|
||||
amount: "10/HKD/A1",
|
||||
paths: [
|
||||
# Focus
|
||||
["HKD/G1|G1", "HKD/G2|$", "HKD/G2|G2" ],
|
||||
["HKD/G1|G1", "XRP|$", "HKD/G2|$", "HKD/G2|G2"], # <--
|
||||
# Incidental
|
||||
["HKD/G1|G1", "HKD/M1|M1", "HKD/G2|G2"],
|
||||
["HKD/G1|G1", "HKD/M2|M2", "HKD/G2|G2"]
|
||||
]
|
||||
]
|
||||
|
||||
}
|
||||
},
|
||||
"Path Tests #2 (non-XRP to non-XRP, same currency)": {
|
||||
ledger:
|
||||
accounts:
|
||||
G1: balance: ["1000.0"]
|
||||
G2: balance: ["1000.0"]
|
||||
A1:
|
||||
balance: ["1000.0", "1000/HKD/G1"]
|
||||
trusts: ["2000/HKD/G1"]
|
||||
A2:
|
||||
balance: ["1000.0", "1000/HKD/G2"]
|
||||
trusts: ["2000/HKD/G2"]
|
||||
A3:
|
||||
balance: ["1000.0"]
|
||||
trusts: ["2000/HKD/A2"]
|
||||
M1:
|
||||
balance: ["11000.0", "5000/HKD/G1", "5000/HKD/G2"]
|
||||
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
|
||||
offers: [
|
||||
["1000/HKD/G1", "1000/HKD/G2"]
|
||||
# ["2000/HKD/G2", "2000/HKD/G1"]
|
||||
# ["2000/HKD/M1", "2000/HKD/G1"]
|
||||
# ["100.0", "1000/HKD/G1"]
|
||||
# ["1000/HKD/G1", "100.0"]
|
||||
]
|
||||
|
||||
paths_expected: {
|
||||
T4:
|
||||
"E) Gateway to user":
|
||||
ledger: false
|
||||
comment: 'Source -> OB -> AC -> Destination'
|
||||
# comment: 'Gateway -> OB -> Gateway 2 -> User'
|
||||
src: "G1", send: "10/HKD/A2", dst: "A2", via: "HKD"
|
||||
debug: false
|
||||
alternatives: [
|
||||
amount: "10/HKD/G1"
|
||||
paths: [
|
||||
["HKD/G2|$", "HKD/G2|G2"],
|
||||
["HKD/M1|M1", "HKD/G2|G2"]
|
||||
]
|
||||
]
|
||||
|
||||
"F) Different gateways, ripple _skip":
|
||||
comment: 'Source -> AC -> AC -> Destination'
|
||||
|
||||
"G) Different users of different gateways, ripple _skip":
|
||||
comment: 'Source -> AC -> AC -> AC -> Destination'
|
||||
|
||||
"H) Different gateways, order book _skip":
|
||||
comment: 'Source -> AC -> OB -> AC -> Destination'
|
||||
|
||||
"I1) XRP bridge _skip":
|
||||
comment: 'Source -> OB to XRP -> OB from XRP -> Destination'
|
||||
src: "A4", send: "10/HKD/G2", dst: "G2", via: "XRP"
|
||||
debug: true
|
||||
|
||||
"I2) XRP bridge _skip":
|
||||
comment: 'Source -> AC -> OB to XRP -> OB from XRP -> Destination'
|
||||
|
||||
"I3) XRP bridge _skip":
|
||||
comment: 'Source -> OB to XRP -> OB from XRP -> AC -> Destination'
|
||||
}
|
||||
}
|
||||
|
||||
################################# DEFINE SUITES ################################
|
||||
|
||||
define_suites(path_finding_cases)
|
||||
88
test/path-tests.json
Normal file
88
test/path-tests.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"Path Tests #1 (XRP -> XRP) and #2 (XRP -> IOU)": {
|
||||
|
||||
"ledger": {"accounts": {"A1": {"balance": ["100000.0",
|
||||
"3500/XYZ/G1",
|
||||
"1200/ABC/G3"],
|
||||
"trusts": ["5000/XYZ/G1",
|
||||
"5000/ABC/G3"]},
|
||||
"A2": {"balance": ["10000.0"],
|
||||
"trusts": ["5000/XYZ/G2",
|
||||
"5000/ABC/G3"]},
|
||||
"A3": {"balance": ["1000.0"],
|
||||
"trusts": ["1000/ABC/A2"]},
|
||||
"G1": {"balance": ["1000.0"]},
|
||||
"G2": {"balance": ["1000.0"]},
|
||||
"G3": {"balance": ["1000.0"]},
|
||||
"M1": {"balance": ["1000.0",
|
||||
"25000/XYZ/G2",
|
||||
"25000/ABC/G3"],
|
||||
"offers": [["1000/XYZ/G1",
|
||||
"1000/XYZ/G2"],
|
||||
["10000.0",
|
||||
"1000/ABC/G3"]],
|
||||
"trusts": ["100000/XYZ/G1",
|
||||
"100000/ABC/G3",
|
||||
"100000/XYZ/G2"]}}},
|
||||
|
||||
"paths_expected": {"T1": {"A1": {"n_alternatives": 0,
|
||||
"src": "A1",
|
||||
"send": "10.0",
|
||||
"dst": "A2",
|
||||
"via": "XRP"},
|
||||
"A2": {"comment": "Send to non existing account",
|
||||
"src": "A1",
|
||||
"send_comment": "malformed error not great for 10.0 amount",
|
||||
"send": "200.0",
|
||||
"dst": "rBmhuVAvi372AerwzwERGjhLjqkMmAwxX",
|
||||
"via": "XRP",
|
||||
"n_alternatives": 0}},
|
||||
"T2": {"A": {"alternatives": [{"amount": "100.0",
|
||||
"paths": [
|
||||
["ABC/G3|$"]
|
||||
]}],
|
||||
"src": "A2",
|
||||
"send": "10/ABC/G3",
|
||||
"dst": "G3",
|
||||
"via": "XRP",
|
||||
"debug": 0,
|
||||
"n_alternatives": 1},
|
||||
"B": {"alternatives": [{"amount": "10.0",
|
||||
"paths": [["ABC/G3|$",
|
||||
"ABC/G3|G3"]]}],
|
||||
"src": "A1",
|
||||
"send": "1/ABC/A2",
|
||||
"dst": "A2",
|
||||
"via": "XRP",
|
||||
"n_alternatives": 1},
|
||||
"C": {"alternatives": [{"amount": "10.0",
|
||||
"paths": [["ABC/G3|$",
|
||||
"ABC/G3|G3",
|
||||
"ABC/A2|A2"]]}],
|
||||
"src": "A1",
|
||||
"send": "1/ABC/A3",
|
||||
"dst": "A3",
|
||||
"via": "XRP",
|
||||
"n_alternatives": 1}}}},
|
||||
"Path Tests #3 (non-XRP to XRP)": {
|
||||
|
||||
"ledger": {"accounts": {"A1": {"balance": ["1000.0",
|
||||
"1000/ABC/G3"]},
|
||||
"A2": {"balance": ["1000.0",
|
||||
"1000/ABC/G3"]},
|
||||
"G3": {"balance": ["1000.0"]},
|
||||
"M1": {"balance": ["11000.0",
|
||||
"1200/ABC/G3"],
|
||||
"offers": [["1000/ABC/G3",
|
||||
"10000.0"]],
|
||||
"trusts": ["100000/ABC/G3"]}}},
|
||||
|
||||
"paths_expected": {"T3": {"A": {"alternatives": [{"amount": "1/ABC/A1",
|
||||
"paths": [["ABC/G3|G3",
|
||||
"XRP|$"]]}],
|
||||
"src": "A1",
|
||||
"dst": "A2",
|
||||
"debug":false,
|
||||
"send": "10.0",
|
||||
"via": "ABC"}}}}
|
||||
}
|
||||
153
test/random-test-addresses.coffee
Normal file
153
test/random-test-addresses.coffee
Normal file
@@ -0,0 +1,153 @@
|
||||
exports.test_accounts = [
|
||||
'rBmhuVAvi372AerwzwERGjhLjqkMmAwxX',
|
||||
'r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf',
|
||||
'rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD',
|
||||
'rrnsYgWn13Z28GtRgznrSUsLfMkvsXCZSu',
|
||||
'rJsaPnGdeo7BhMnHjuc3n44Mf7Ra1qkSVJ',
|
||||
'rnYDWQaRdMb5neCGgvFfhw3MBoxmv5LtfH',
|
||||
'r31PEiKfa3y6xTi7uBcSp7F3nDLvVMmqyi',
|
||||
'rfM5xD2CY6XB8o1WsWoJ3ZHGkbHU4NYXr',
|
||||
'r9MB1RNWZChfV3YdLrB3Rm5AoMULewDtiu',
|
||||
'rH15iZg9KFSi7d1usvcsPerUtg7dhpMbk4',
|
||||
'rGWYwGaczQWiduWkccFZKXfp5nDRPqNBNS',
|
||||
'rBU1EP5oMwKxWr1gZnNe7K8GouQTBhzUKs',
|
||||
'rwiTxuknPNeLDYHHLgajRVetKEEwkYhTaQ',
|
||||
'rEbq9pWn2knXFTjjuoNNrKgQeGxhmispMi',
|
||||
'rJfBCsnwSHXjTJ4GH5Ax6Kyw48X977hqyq',
|
||||
'rado7qRcvPpS8ZL8SNg4SG8kBNksHyqoRa',
|
||||
'rNgfurDhqvfsVzLr5ZGB3dJysJhRkvJ79F',
|
||||
'rBtVTnNgX3uR3kyfVyaQ6hjZTdk42ay9Z3',
|
||||
'rND1XyLAU9G2ydhUgmRo4i2kdrSKgYZc31',
|
||||
'rHd21p9Gb834Ri4pzRzGFJ7PjRzymWuBWu',
|
||||
'rhfvFTgpPzCvFPgbA9Nvz42mX92U5ak92m',
|
||||
'rByT8y4BUX1Kcc6xotEabCgwc5PGsbTfSv',
|
||||
'rPBrMbL7uGuteU49b2ibcEBSztoWPX4srr',
|
||||
'rQGRcZn2RyXJL3s7Dfqqzc96Juc7j9j6i9',
|
||||
'rwZrLewGghBMj29kFq7TPw9h9p5eAQ1LUp',
|
||||
'rM9qHXk5uifboWrpu9Wte6Gjzbe974nZ4z',
|
||||
'rBt69hdwMeBgmAtM9YwuFAKxjMqgaBLf3F',
|
||||
'rHpcrpggafr5NNn7mafPhaeB8PMYKXGahp',
|
||||
'rsMKP5MSoyve54o7LgwnZzFfGKzAK8SE5F',
|
||||
'rfN3ccsNxPt41MjHNZRk7ek7q4TpPLqUzL',
|
||||
'rDRWocPjBdhKZSWZezbsnwNpALcJ6GqSGf',
|
||||
'rsmJ4tEMWpcK2qcEMp9uoUv4Ht5Nd6cGTV',
|
||||
'rsxWsnMVRnFozrKJV2VZ1SG6UNbEeHYu16',
|
||||
'r4KoiD6MpaQNPzBka3FRLREkx6EZFwynY4',
|
||||
'rUdovjorVqxyemu5jbpfqA6DYDLdD4eYcj',
|
||||
'r4MrNttmbdiJ7DjWh1MCKW3Kh7kfML46TA',
|
||||
'rndYw73Btcm9Pv9gssZY1S9UcDUPLnpip7',
|
||||
'rh8MnoZmAeWyLx7X8bJZqyjZ48mv1og5PS',
|
||||
'rBoJvU7pcvoy5hjDMMTDNVG4YG85Ed3MEq',
|
||||
'rs4f1BwdNgXAHWLT8rZgW2T1RKSBNY4iDz',
|
||||
'rEmhxShqw42EPm7bY7df5uQySZBkQWnqae',
|
||||
'rNerRdGnbZP6wej22zBdoTUfQKWoMDTH7d',
|
||||
'rDyXvd2WFALJovh76uLe5kUrJ7QLpgmQYE',
|
||||
'rUVi1L28AsCvieXP5pMqPHA9WAfsvCDUjU',
|
||||
'rscuoJN9um2VM4xVv386X5T9APtExFKsbB',
|
||||
'raeeyPs6g5xQn5jyNQbCZ6QeLrqu3FrFvb',
|
||||
'r9UqovJD979WTfNEWXxDU2CVj3K1yo2mqG',
|
||||
'rfRjsAqM1MEuSbWzuLcD6EhSZazgqvZSjy',
|
||||
'rUL4CAxmfpNqDXsQTPCK9ZJ8zHqhUvDWfw',
|
||||
'rP6ZRDFZxjQqeAgdBh1YQSQjWNSASpCL7N',
|
||||
'rsV4AtAqsdyRyZ8s4kaWbM21EPwY5fonx5',
|
||||
'rHaKEMyJErGY6VaKuTj16fSheTp4BRpWG1',
|
||||
'rELHJtahsRpSiSj1nfkY5yKRHCCyRgynw4',
|
||||
'rLYtaGnw4xK86J6mTsLfayyREoYaPPr8Cj',
|
||||
'rD5pAYUfZypmJRSrJnBy3pYo5ApHqw5Jt5',
|
||||
'rfYQrqwNXoA8e2gBDmiHAJAMYrASdQvqDm',
|
||||
'rESt1CB9Sqaj8PYj8SV9x76iwMGBFPzLHb',
|
||||
'rHZWAXh3NdQbyksKzDRLeP9ui32TcqssHZ',
|
||||
'rK9iNjw5SozqKj5zNervwQQTLAgu8V813j',
|
||||
'rUjpFBSmZ8F6cP16VxqpdAXCVCW3rBSZyn',
|
||||
'raPib2vNQAjhh47fVQ7PswKaX1daNBSs2G',
|
||||
'rwhuqz7FppLNvLWdxs7TLLW9UDVztFbw9z',
|
||||
'rJYRe27KXWTjs4P3uu1d4x58Pk5Y13DbUg',
|
||||
'rLFxCuE2GHq38wFUHpswgHJAcz6EUhPimC',
|
||||
'rAaQrzi5satsth174EogwdtdxLZRW5n1h',
|
||||
'rB18Rxdv1aPYtf9nDFpNPJ2HA5BBAqmyoG',
|
||||
'rDSaTM6nCSrc1vH8pPcTAwQpmvb9Y6M2gw',
|
||||
'rpmeCBJUpp9ij1nRM23tRGesWjY7chSHqs',
|
||||
'rwQz7ZkCGdQt7iiuWC3EpbbKwWdL7uLT9C',
|
||||
'rULwRizwxjBwDjcaA44Tbh9MjM5TZFTcLj',
|
||||
'rGMZBEGHbSoegfvqXC2ajXe7RM2Z1LK65N',
|
||||
'rGyFSppE8G7cELAdBU5yL7kWodbw29YdpN',
|
||||
'rJYN3qZsjWhH6bzXX6ZHMZewkMPHeEyGNb',
|
||||
'rEgZVpVSs75eEh6KM4HvcvG64p2TZBL4DC',
|
||||
'rBRfZaqSAkXZYQWBfoy4sN2Q7zZHVGiGwU',
|
||||
'rwg1DRGXLTzQpoWtS35mDowN4PSFQ732eQ',
|
||||
'rKZvkY3T6ahhqkWLTQDSdB1MTKFbbnkqBX',
|
||||
'rGXgpwvaAZ7rBmoSKFUrd83N7WN3Lm4vuX',
|
||||
'rhCMsQ3SJa5Wb4AvBe27hxBQaQQjDG1LG4',
|
||||
'rNtvcU3ePYpZnuYKG77pjRxtKJJ1yrutbm',
|
||||
'rsfK2cTikveAeyvSG8F62c4VFUvZBsH5Rf',
|
||||
'rJT2LrXe7hH1pMEnhEkCMznwtgKuYJS7uz',
|
||||
'rE4Fi4GVjo9NY2g6MtMbitjenUZ21zFoSG',
|
||||
'rp39zV6AFPRJ7yrrL1PSPC1s3oKM2uv2iW',
|
||||
'raCoTW3mhdK6WGUZUSEuvbFM34CSGRRHut',
|
||||
'rKZD9yCV7XAgKq3Rj3a5DmqHHkHBd3ZYao',
|
||||
'rfKtpLEQz8bGCVtQsEzD8cJKeo6AW6J2pD',
|
||||
'rJqNyWJ3rovwkWFdwPhCc6m3jpFogGzRr9',
|
||||
'r41fiShunXNNgJjTjqU9whsjnSYU1BXsjY',
|
||||
'r3uHcWgsNwowCBGF5rhCP5dfdjpYmByBbJ',
|
||||
'r4GZm8WnwX5E9cr8uGiLX42y7KN2NaLqYn',
|
||||
'rKBVsMWErdH443FUkaT799CRVyY9XnVyCK',
|
||||
'razu52accAWWHjxhWEHXNLHWCDhhs1L79p',
|
||||
'rHaL5e3niiikv6KJG4UARqpcjyD5tKNgyV',
|
||||
'rMfcPdGcB5y9zEYw91t3QG2Qj1Z7tqms3S',
|
||||
'r3mqtPNiwkLKisvbnFjM9eC8Rm9JUEXLMD',
|
||||
'rKJGLaJr5SFjZ43BkDeqWKWAtGy1qAAkPf',
|
||||
'rM2zQeTDrt6sMM936Gxh263t1qx3hEb7XK',
|
||||
'rGtqoQJGe4zWenC3yWH9pByTAsGPcjmTU2',
|
||||
'raNUsR5m8jsmb9o9mpNZGGyV86aZUYPunr',
|
||||
'rManQrKu85ezooZ11UmXbxejw5EYHqiUZm',
|
||||
'rKrjCBtwnhQeYkJKrVjDA5CaR5W9NPN2Bb',
|
||||
'rcwyaWqsvGXMRyVW2KHnGUV2MhJVhVx2p',
|
||||
'rLozhAmzcwtEgr1bA28GUh2kbf5kFBMhph',
|
||||
'rJqt8XVcGdpfjZmujoTc6ArHQdZZVhADgM',
|
||||
'rPthXZ93cGAtHJrnAF58vZz6E1js8o6fVf',
|
||||
'rL1LH68PG93BuAeLsiM4cxeBtFmxsoGhRB',
|
||||
'rndqHX6xLknzbgmQPXL4DvhNLRANYf8cDA',
|
||||
'rNEnd2tV3Z1aL5kQmShmHGM3uciSJ4jakF',
|
||||
'rGqPPWnLVWDwWXf495qdxp4um1BTw8TBh5',
|
||||
'rhEDG4mzWGXjjfQp4WMCpZPBDyug5ays9e',
|
||||
'raR6MJ2dYxj1PECUKnLbeamM1k8FnYsY34',
|
||||
'rDcC1S6TRNgTMTgpuSzhAZdTdQJ5EHVf8X',
|
||||
'rLHtg3bWtMKNDYXydd7frGB5pvFbtqzTjg',
|
||||
'rnHhh7qXu2trXuC8aD3Zm7gvM9YetRkEDB',
|
||||
'rfwwUrJztrR7PeKzELEoC1cjcBpsqHmxws',
|
||||
'rw4KzQgZwPJDYX4wHc3NgsLjdR12JGAKsF',
|
||||
'rPCbN5kfEjNgP7TBN3VDJf8eUhv1zCreRL',
|
||||
'r32W6FTsguE3hMv3h79eoNVSJutsDDw6rH',
|
||||
'rBSDvMzyxsea8Zgy8EfryZ7cc1QGrVp9Do',
|
||||
'rfz1TPVJPQYbpSxi6oTiWyhNqy4J1Vz7VL',
|
||||
'rKCyiG5sKxqmKoT2NGT9zS86gS26m9HrQa',
|
||||
'rK7WRdHd6aNqCu2tNbebxYrQkP5u2DouS3',
|
||||
'rLC3xWV4N3ohGxxzcueDrxPLHNqbXrmpUK',
|
||||
'rDE2MwprS1vFvknSKLZJfmxKmbVuE2F85',
|
||||
'r91o1Huejw2qqz37b22Ev8igM4V2Rog1jv',
|
||||
'rEhGRU4P7pVjuxco5BgoXz1974QESKQ1Wy',
|
||||
'raVZ3Uk3qmKqe5aV1ifQsRFL6gU7m25Y2L',
|
||||
'r9p5buUwdMmZ5Um5uK5gC9piRd1fBzMPBN',
|
||||
'r4ruPrbfwnkp5aDSNzQXnWcEK64hJGRnDG',
|
||||
'rwsHCfEwi3PzsfcpcaWLbHXZ2zG5FtNX1J',
|
||||
'r4DkAh7hbTX4E8JPp4kAWkQYazW6236dBB',
|
||||
'rGZSqs5KHQKEJMCaDS3iyWagymixa9zuCv',
|
||||
'r9Nn5Le1bourefaZJ79K2h5VnREFpTqjUw',
|
||||
'rh7NSNpXR9mwWFF8uXvaWjcLVRPfxqQfM9',
|
||||
'rpdxPDQ8mV8gadRcDBh7kNFj1mxYjfddh5',
|
||||
'rMHxgnXgo68vDLUj327YtsHjQyiHGjeccj',
|
||||
'raDd7xZ3RYvKcGiojy9h5UqFV7WULv626i',
|
||||
'rEHzCw3nAuHj66C3xKnGFxV6zDBAssNdSY',
|
||||
'rfvWW9qgyNKYVuJ212himJ7mt6fvkUaS7E',
|
||||
'r9ctoegJfquoj1RHo1bxuW9MjWhbjHyZSA',
|
||||
'rsNPHLq4CgxGoenX67jpuabKe8hx1QvLZk',
|
||||
'rLZouhBz2CpT3ULfJjt193gsLNR4TxjdUS',
|
||||
'rKuUjCThS3DBqfDAupxTvyhkWaskxHoWSP',
|
||||
'rKwSstBmYbKFM3CFw8hvBqsU8pfcZMgNwA',
|
||||
'rBN8iGgpbCc6KYNE36D67TeS6t1YXwmTQc',
|
||||
'rGvBvCyqwn5kPuRUErGD7idtKRb7LtptrS',
|
||||
'r3h89HbdaYJBVd1wfHV6GxDj6Pf2s5iXnF',
|
||||
'rfbUVWFke9JzbK6BhkuayCgX2MVoJvgxBk',
|
||||
'rwTXRBQvPKPMskjp9By1kLJ1pPdRAg77Rz',
|
||||
'rw4ve76xnh8cPwVpE58i46s39MbQ7x1m5W',
|
||||
'r32XzGdUP3GuNJXDGxFM2cYkw7K1KrwXdt' ]
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
mocha --ui tdd --reporter spec --timeout 10000 test/*-test.js
|
||||
|
||||
# flags set in mocha.opts
|
||||
mocha test/*-test.{js,coffee}
|
||||
@@ -28,7 +28,7 @@ suite('WebSocket connection', function() {
|
||||
}
|
||||
});
|
||||
|
||||
test('WebSocket connect and disconnect', function() {
|
||||
test('WebSocket connect and disconnect', function(done) {
|
||||
var alpha = Remote.from_config("alpha");
|
||||
|
||||
alpha.on('connected', function () {
|
||||
|
||||
Reference in New Issue
Block a user