move the remaining RippleAPI tests to the new test runner

This commit is contained in:
Fred K. Schott
2019-11-26 16:35:40 -08:00
parent 8ba36b2588
commit 84097a3179
55 changed files with 4976 additions and 4336 deletions

File diff suppressed because it is too large Load Diff

29
test/api/combine/index.ts Normal file
View File

@@ -0,0 +1,29 @@
import assert from 'assert-diff'
import binary from 'ripple-binary-codec'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const { combine: REQUEST_FIXTURES } = requests
const { combine: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'combine': async (api, address) => {
const combined = api.combine(REQUEST_FIXTURES.setDomain)
assertResultMatch(combined, RESPONSE_FIXTURES.single, 'sign')
},
'combine - different transactions': async (api, address) => {
const request = [REQUEST_FIXTURES.setDomain[0]]
const tx = binary.decode(REQUEST_FIXTURES.setDomain[0])
tx.Flags = 0
request.push(binary.encode(tx))
assert.throws(() => {
api.combine(request)
}, /txJSON is not the same for all signedTransactions/)
}
}

View File

@@ -0,0 +1,191 @@
import assert from 'assert-diff'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const { computeLedgerHash: REQUEST_FIXTURES } = requests
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'given corrupt data - should fail': async (api, address) => {
const request = {
includeTransactions: true,
includeState: true,
includeAllData: true,
ledgerVersion: 38129
}
const ledger = await api.getLedger(request)
assert.strictEqual(
// @ts-ignore
ledger.transactions[0].rawTransaction,
'{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Amount":"10000000000","Destination":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Fee":"10","Flags":0,"Sequence":62,"SigningPubKey":"034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E","TransactionType":"Payment","TxnSignature":"3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639","hash":"3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF","meta":{"AffectedNodes":[{"CreatedNode":{"LedgerEntryType":"AccountRoot","LedgerIndex":"4C6ACBD635B0F07101F7FA25871B0925F8836155462152172755845CE691C49E","NewFields":{"Account":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Balance":"10000000000","Sequence":1}}},{"ModifiedNode":{"FinalFields":{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Balance":"981481999380","Flags":0,"OwnerCount":0,"Sequence":63},"LedgerEntryType":"AccountRoot","LedgerIndex":"B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A","PreviousFields":{"Balance":"991481999390","Sequence":62},"PreviousTxnID":"2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F","PreviousTxnLgrSeq":31317}}],"TransactionIndex":0,"TransactionResult":"tesSUCCESS"},"ledger_index":38129}'
)
// @ts-ignore - Change Amount to 12000000000
ledger.transactions[0].rawTransaction =
'{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Amount":"12000000000","Destination":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Fee":"10","Flags":0,"Sequence":62,"SigningPubKey":"034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E","TransactionType":"Payment","TxnSignature":"3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639","hash":"3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF","meta":{"AffectedNodes":[{"CreatedNode":{"LedgerEntryType":"AccountRoot","LedgerIndex":"4C6ACBD635B0F07101F7FA25871B0925F8836155462152172755845CE691C49E","NewFields":{"Account":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Balance":"10000000000","Sequence":1}}},{"ModifiedNode":{"FinalFields":{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Balance":"981481999380","Flags":0,"OwnerCount":0,"Sequence":63},"LedgerEntryType":"AccountRoot","LedgerIndex":"B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A","PreviousFields":{"Balance":"991481999390","Sequence":62},"PreviousTxnID":"2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F","PreviousTxnLgrSeq":31317}}],"TransactionIndex":0,"TransactionResult":"tesSUCCESS"},"ledger_index":38129}'
ledger.parentCloseTime = ledger.closeTime
let hash
try {
hash = api.computeLedgerHash(ledger, { computeTreeHashes: true })
} catch (error) {
assert(error instanceof api.errors.ValidationError)
assert.strictEqual(
error.message,
'transactionHash in header does not match computed hash of transactions'
)
assert.deepStrictEqual(error.data, {
transactionHashInHeader:
'DB83BF807416C5B3499A73130F843CF615AB8E797D79FE7D330ADF1BFA93951A',
computedHashOfTransactions:
'EAA1ADF4D627339450F0E95EA88B7069186DD64230BAEBDCF3EEC4D616A9FC68'
})
return
}
assert(
false,
'Should throw ValidationError instead of producing hash: ' + hash
)
},
'given ledger without raw transactions - should throw': async (
api,
address
) => {
const request = {
includeTransactions: true,
includeState: true,
includeAllData: true,
ledgerVersion: 38129
}
const ledger = await api.getLedger(request)
assert.strictEqual(
// @ts-ignore
ledger.transactions[0].rawTransaction,
'{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Amount":"10000000000","Destination":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Fee":"10","Flags":0,"Sequence":62,"SigningPubKey":"034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E","TransactionType":"Payment","TxnSignature":"3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639","hash":"3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF","meta":{"AffectedNodes":[{"CreatedNode":{"LedgerEntryType":"AccountRoot","LedgerIndex":"4C6ACBD635B0F07101F7FA25871B0925F8836155462152172755845CE691C49E","NewFields":{"Account":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Balance":"10000000000","Sequence":1}}},{"ModifiedNode":{"FinalFields":{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Balance":"981481999380","Flags":0,"OwnerCount":0,"Sequence":63},"LedgerEntryType":"AccountRoot","LedgerIndex":"B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A","PreviousFields":{"Balance":"991481999390","Sequence":62},"PreviousTxnID":"2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F","PreviousTxnLgrSeq":31317}}],"TransactionIndex":0,"TransactionResult":"tesSUCCESS"},"ledger_index":38129}'
)
// Delete rawTransaction
// @ts-ignore - Delete rawTransaction
delete ledger.transactions[0].rawTransaction
ledger.parentCloseTime = ledger.closeTime
let hash
try {
hash = api.computeLedgerHash(ledger, { computeTreeHashes: true })
} catch (error) {
assert(error instanceof api.errors.ValidationError)
assert.strictEqual(
error.message,
'ledger' + ' is missing raw transactions'
)
return
}
assert(
false,
'Should throw ValidationError instead of producing hash: ' + hash
)
},
'given ledger without state or transactions - only compute ledger hash': async (
api,
address
) => {
const request = {
includeTransactions: true,
includeState: true,
includeAllData: true,
ledgerVersion: 38129
}
const ledger = await api.getLedger(request)
assert.strictEqual(
// @ts-ignore
ledger.transactions[0].rawTransaction,
'{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Amount":"10000000000","Destination":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Fee":"10","Flags":0,"Sequence":62,"SigningPubKey":"034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E","TransactionType":"Payment","TxnSignature":"3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639","hash":"3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF","meta":{"AffectedNodes":[{"CreatedNode":{"LedgerEntryType":"AccountRoot","LedgerIndex":"4C6ACBD635B0F07101F7FA25871B0925F8836155462152172755845CE691C49E","NewFields":{"Account":"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj","Balance":"10000000000","Sequence":1}}},{"ModifiedNode":{"FinalFields":{"Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV","Balance":"981481999380","Flags":0,"OwnerCount":0,"Sequence":63},"LedgerEntryType":"AccountRoot","LedgerIndex":"B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A","PreviousFields":{"Balance":"991481999390","Sequence":62},"PreviousTxnID":"2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F","PreviousTxnLgrSeq":31317}}],"TransactionIndex":0,"TransactionResult":"tesSUCCESS"},"ledger_index":38129}'
)
ledger.parentCloseTime = ledger.closeTime
const computeLedgerHash = api.computeLedgerHash
const ValidationError = api.errors.ValidationError
function testCompute(ledger, expectedError) {
let hash = computeLedgerHash(ledger)
assert.strictEqual(
hash,
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E'
)
// fail if required to compute tree hashes
try {
hash = computeLedgerHash(ledger, { computeTreeHashes: true })
} catch (error) {
assert(error instanceof ValidationError)
assert.strictEqual(error.message, expectedError)
return
}
assert(
false,
'Should throw ValidationError instead of producing hash: ' + hash
)
}
const transactions = ledger.transactions
delete ledger.transactions
testCompute(ledger, 'transactions property is missing from the ledger')
delete ledger.rawState
testCompute(ledger, 'transactions property is missing from the ledger')
ledger.transactions = transactions
testCompute(ledger, 'rawState property is missing from the ledger')
},
'wrong hash': async (api, address) => {
const request = {
includeTransactions: true,
includeState: true,
includeAllData: true,
ledgerVersion: 38129
}
const ledger = await api.getLedger(request)
assertResultMatch(ledger, responses.getLedger.full, 'getLedger')
const newLedger = {
...ledger,
parentCloseTime: ledger.closeTime,
stateHash:
'D9ABF622DA26EEEE48203085D4BC23B0F77DC6F8724AC33D975DA3CA492D2E44'
}
assert.throws(() => {
api.computeLedgerHash(newLedger)
}, /does not match computed hash of state/)
},
'computeLedgerHash': async (api, address) => {
// const api = new RippleAPI()
const header = REQUEST_FIXTURES.header
const ledgerHash = api.computeLedgerHash(header)
assert.strictEqual(
ledgerHash,
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349'
)
},
'computeLedgerHash - with transactions': async (api, address) => {
// const api = new RippleAPI()
const header = {
...REQUEST_FIXTURES.header,
transactionHash: undefined,
rawTransactions: JSON.stringify(REQUEST_FIXTURES.transactions)
}
const ledgerHash = api.computeLedgerHash(header)
assert.strictEqual(
ledgerHash,
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349'
)
},
'computeLedgerHash - incorrent transaction_hash': async (api, address) => {
// const api = new RippleAPI()
const header = Object.assign({}, REQUEST_FIXTURES.header, {
transactionHash:
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9'
})
header.rawTransactions = JSON.stringify(REQUEST_FIXTURES.transactions)
assert.throws(() => api.computeLedgerHash(header))
}
}

View File

@@ -0,0 +1,29 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
import { RippleAPI } from 'ripple-api'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'RippleAPI - implicit server port': () => {
new RippleAPI({ server: 'wss://s1.ripple.com' })
},
'RippleAPI invalid options': () => {
// @ts-ignore - This is intentionally invalid
assert.throws(() => new RippleAPI({ invalid: true }))
},
'RippleAPI valid options': () => {
const api = new RippleAPI({ server: 'wss://s:1' })
const privateConnectionUrl = (api.connection as any)._url
assert.deepEqual(privateConnectionUrl, 'wss://s:1')
},
'RippleAPI invalid server uri': () => {
assert.throws(() => new RippleAPI({ server: 'wss//s:1' }))
}
}

View File

@@ -0,0 +1,16 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'returns address for public key': async (api, address) => {
var address = api.deriveAddress(
'035332FBA71D705BD5D97014A833BE2BBB25BEFCD3506198E14AFEA241B98C2D06'
)
assert.equal(address, 'rLczgQHxPhWtjkaQqn3Q6UM8AbRbbRvs5K')
}
}

View File

@@ -0,0 +1,39 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'returns keypair for secret': async (api, address) => {
var keypair = api.deriveKeypair('snsakdSrZSLkYpCXxfRkS4Sh96PMK')
assert.equal(
keypair.privateKey,
'008850736302221AFD59FF9CA1A29D4975F491D726249302EE48A3078A8934D335'
)
assert.equal(
keypair.publicKey,
'035332FBA71D705BD5D97014A833BE2BBB25BEFCD3506198E14AFEA241B98C2D06'
)
},
'returns keypair for ed25519 secret': async (api, address) => {
var keypair = api.deriveKeypair('sEdV9eHWbibBnTj7b1H5kHfPfv7gudx')
assert.equal(
keypair.privateKey,
'ED5C2EF6C2E3200DFA6B72F47935C7F64D35453646EA34919192538F458C7BC30F'
)
assert.equal(
keypair.publicKey,
'ED0805EC4E728DB87C0CA6C420751F296C57A5F42D02E9E6150CE60694A44593E5'
)
},
'throws with an invalid secret': async (api, address) => {
assert.throws(() => {
api.deriveKeypair('...')
}, /^Error: Non-base58 character$/)
}
}

View File

@@ -0,0 +1,31 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
import { RippleAPI } from '../../../src'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'returns address for public key': async (api, address) => {
assert.equal(
RippleAPI.deriveXAddress({
publicKey:
'035332FBA71D705BD5D97014A833BE2BBB25BEFCD3506198E14AFEA241B98C2D06',
tag: false,
test: false
}),
'XVZVpQj8YSVpNyiwXYSqvQoQqgBttTxAZwMcuJd4xteQHyt'
)
assert.equal(
RippleAPI.deriveXAddress({
publicKey:
'035332FBA71D705BD5D97014A833BE2BBB25BEFCD3506198E14AFEA241B98C2D06',
tag: false,
test: true
}),
'TVVrSWtmQQssgVcmoMBcFQZKKf56QscyWLKnUyiuZW8ALU4'
)
}
}

View File

@@ -0,0 +1,119 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
import BigNumber from 'bignumber.js'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'works with a typical amount': async api => {
const xrp = api.dropsToXrp('2000000')
assert.strictEqual(xrp, '2', '2 million drops equals 2 XRP')
},
'works with fractions': async api => {
let xrp = api.dropsToXrp('3456789')
assert.strictEqual(xrp, '3.456789', '3,456,789 drops equals 3.456789 XRP')
xrp = api.dropsToXrp('3400000')
assert.strictEqual(xrp, '3.4', '3,400,000 drops equals 3.4 XRP')
xrp = api.dropsToXrp('1')
assert.strictEqual(xrp, '0.000001', '1 drop equals 0.000001 XRP')
xrp = api.dropsToXrp('1.0')
assert.strictEqual(xrp, '0.000001', '1.0 drops equals 0.000001 XRP')
xrp = api.dropsToXrp('1.00')
assert.strictEqual(xrp, '0.000001', '1.00 drops equals 0.000001 XRP')
},
'works with zero': async api => {
let xrp = api.dropsToXrp('0')
assert.strictEqual(xrp, '0', '0 drops equals 0 XRP')
// negative zero is equivalent to zero
xrp = api.dropsToXrp('-0')
assert.strictEqual(xrp, '0', '-0 drops equals 0 XRP')
xrp = api.dropsToXrp('0.00')
assert.strictEqual(xrp, '0', '0.00 drops equals 0 XRP')
xrp = api.dropsToXrp('000000000')
assert.strictEqual(xrp, '0', '000000000 drops equals 0 XRP')
},
'works with a negative value': async api => {
const xrp = api.dropsToXrp('-2000000')
assert.strictEqual(xrp, '-2', '-2 million drops equals -2 XRP')
},
'works with a value ending with a decimal point': async api => {
let xrp = api.dropsToXrp('2000000.')
assert.strictEqual(xrp, '2', '2000000. drops equals 2 XRP')
xrp = api.dropsToXrp('-2000000.')
assert.strictEqual(xrp, '-2', '-2000000. drops equals -2 XRP')
},
'works with BigNumber objects': async api => {
let xrp = api.dropsToXrp(new BigNumber(2000000))
assert.strictEqual(xrp, '2', '(BigNumber) 2 million drops equals 2 XRP')
xrp = api.dropsToXrp(new BigNumber(-2000000))
assert.strictEqual(xrp, '-2', '(BigNumber) -2 million drops equals -2 XRP')
xrp = api.dropsToXrp(new BigNumber(2345678))
assert.strictEqual(
xrp,
'2.345678',
'(BigNumber) 2,345,678 drops equals 2.345678 XRP'
)
xrp = api.dropsToXrp(new BigNumber(-2345678))
assert.strictEqual(
xrp,
'-2.345678',
'(BigNumber) -2,345,678 drops equals -2.345678 XRP'
)
},
'works with a number': async api => {
// This is not recommended. Use strings or BigNumber objects to avoid precision errors.
let xrp = api.dropsToXrp(2000000)
assert.strictEqual(xrp, '2', '(number) 2 million drops equals 2 XRP')
xrp = api.dropsToXrp(-2000000)
assert.strictEqual(xrp, '-2', '(number) -2 million drops equals -2 XRP')
},
'throws with an amount with too many decimal places': async api => {
assert.throws(() => {
api.dropsToXrp('1.2')
}, /has too many decimal places/)
assert.throws(() => {
api.dropsToXrp('0.10')
}, /has too many decimal places/)
},
'throws with an invalid value': async api => {
assert.throws(() => {
api.dropsToXrp('FOO')
}, /invalid value/)
assert.throws(() => {
api.dropsToXrp('1e-7')
}, /invalid value/)
assert.throws(() => {
api.dropsToXrp('2,0')
}, /invalid value/)
assert.throws(() => {
api.dropsToXrp('.')
}, /dropsToXrp: invalid value '\.', should be a BigNumber or string-encoded number\./)
},
'throws with an amount more than one decimal point': async api => {
assert.throws(() => {
api.dropsToXrp('1.0.0')
}, /dropsToXrp: invalid value '1\.0\.0'/)
assert.throws(() => {
api.dropsToXrp('...')
}, /dropsToXrp: invalid value '\.\.\.'/)
}
}

19
test/api/errors/index.ts Normal file
View File

@@ -0,0 +1,19 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'RippleError with data': async (api, address) => {
const error = new api.errors.RippleError('_message_', '_data_')
assert.strictEqual(error.toString(), "[RippleError(_message_, '_data_')]")
},
'NotFoundError default message': async (api, address) => {
const error = new api.errors.NotFoundError()
assert.strictEqual(error.toString(), '[NotFoundError(Not found)]')
}
}

View File

@@ -0,0 +1,384 @@
import BigNumber from 'bignumber.js'
import assert from 'assert-diff'
import { RippleAPI } from 'ripple-api'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { TestSuite } from '../../utils'
function checkSortingOfOrders(orders) {
let previousRate = '0'
for (var i = 0; i < orders.length; i++) {
const order = orders[i]
let rate
// We calculate the quality of output/input here as a test.
// This won't hold in general because when output and input amounts get tiny,
// the quality can differ significantly. However, the offer stays in the
// order book where it was originally placed. It would be more consistent
// to check the quality from the offer book, but for the test data set,
// this calculation holds.
if (order.specification.direction === 'buy') {
rate = new BigNumber(order.specification.quantity.value)
.dividedBy(order.specification.totalPrice.value)
.toString()
} else {
rate = new BigNumber(order.specification.totalPrice.value)
.dividedBy(order.specification.quantity.value)
.toString()
}
assert(
new BigNumber(rate).isGreaterThanOrEqualTo(previousRate),
'Rates must be sorted from least to greatest: ' +
rate +
' should be >= ' +
previousRate
)
previousRate = rate
}
return true
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'normal': async (api, address) => {
const orderbookInfo = {
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
counter: {
currency: 'BTC',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 20,
taker: address
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 20,
taker: address
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
assert.deepEqual(orderbook, responses.getOrderbook.normal)
})
},
'with XRP': async (api, address) => {
const orderbookInfo = {
base: {
currency: 'USD',
counterparty: 'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw'
},
counter: {
currency: 'XRP'
}
}
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 20,
taker: address
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 20,
taker: address
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
assert.deepEqual(orderbook, responses.getOrderbook.withXRP)
})
},
'sample XRP/JPY book has orders sorted correctly': async (api, address) => {
const orderbookInfo = {
base: {
// the first currency in pair
currency: 'XRP'
},
counter: {
currency: 'JPY',
counterparty: 'rB3gZey7VWHYRqJHLoHDEJXJ2pEPNieKiS'
}
}
const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 400, // must match `test/fixtures/rippled/requests/1-taker_gets-XRP-taker_pays-JPY.json`
taker: myAddress
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 400, // must match `test/fixtures/rippled/requests/2-taker_gets-JPY-taker_pays-XRP.json`
taker: myAddress
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
assert.deepStrictEqual([], orderbook.bids)
return checkSortingOfOrders(orderbook.asks)
})
},
'sample USD/XRP book has orders sorted correctly': async (api, address) => {
const orderbookInfo = {
counter: { currency: 'XRP' },
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 400, // must match `test/fixtures/rippled/requests/1-taker_gets-XRP-taker_pays-JPY.json`
taker: myAddress
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 400, // must match `test/fixtures/rippled/requests/2-taker_gets-JPY-taker_pays-XRP.json`
taker: myAddress
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
return (
checkSortingOfOrders(orderbook.bids) &&
checkSortingOfOrders(orderbook.asks)
)
})
},
'sorted so that best deals come first': async (api, address) => {
const orderbookInfo = {
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
counter: {
currency: 'BTC',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 20,
taker: address
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 20,
taker: address
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
const bidRates = orderbook.bids.map(
bid => bid.properties.makerExchangeRate
)
const askRates = orderbook.asks.map(
ask => ask.properties.makerExchangeRate
)
// makerExchangeRate = quality = takerPays.value/takerGets.value
// so the best deal for the taker is the lowest makerExchangeRate
// bids and asks should be sorted so that the best deals come first
assert.deepEqual(bidRates.map(x => Number(x)).sort(), bidRates)
assert.deepEqual(askRates.map(x => Number(x)).sort(), askRates)
})
},
'currency & counterparty are correct': async (api, address) => {
const orderbookInfo = {
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
counter: {
currency: 'BTC',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 20,
taker: address
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 20,
taker: address
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
const orders = [...orderbook.bids, ...orderbook.asks]
orders.forEach(order => {
const quantity = order.specification.quantity
const totalPrice = order.specification.totalPrice
const { base, counter } = requests.getOrderbook.normal
assert.strictEqual(quantity.currency, base.currency)
assert.strictEqual(quantity.counterparty, base.counterparty)
assert.strictEqual(totalPrice.currency, counter.currency)
assert.strictEqual(totalPrice.counterparty, counter.counterparty)
})
})
},
'direction is correct for bids and asks': async (api, address) => {
const orderbookInfo = {
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
},
counter: {
currency: 'BTC',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
await Promise.all([
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
ledger_index: 'validated',
limit: 20,
taker: address
}),
api.request('book_offers', {
taker_gets: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter),
taker_pays: RippleAPI.renameCounterpartyToIssuer(orderbookInfo.base),
ledger_index: 'validated',
limit: 20,
taker: address
})
]).then(([directOfferResults, reverseOfferResults]) => {
const directOffers = (directOfferResults
? directOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const reverseOffers = (reverseOfferResults
? reverseOfferResults.offers
: []
).reduce((acc, res) => acc.concat(res), [])
const orderbook = RippleAPI.formatBidsAndAsks(orderbookInfo, [
...directOffers,
...reverseOffers
])
assert(orderbook.bids.every(bid => bid.specification.direction === 'buy'))
assert(
orderbook.asks.every(ask => ask.specification.direction === 'sell')
)
})
}
}

View File

@@ -0,0 +1,30 @@
import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import { TestSuite } from '../../utils'
const { generateAddress: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'generateAddress': async (api, address) => {
function random(): number[] {
return new Array(16).fill(0)
}
assert.deepEqual(
api.generateAddress({ entropy: random() }),
RESPONSE_FIXTURES
)
},
'generateAddress invalid': async (api, address) => {
assert.throws(() => {
function random() {
return new Array(1).fill(0)
}
api.generateAddress({ entropy: random() })
}, api.errors.UnexpectedError)
}
}

View File

@@ -0,0 +1,29 @@
import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'generateXAddress': async (api, address) => {
function random() {
return new Array(16).fill(0)
}
assert.deepEqual(
api.generateXAddress({ entropy: random() }),
responses.generateXAddress
)
},
'generateXAddress invalid': async (api, address) => {
assert.throws(() => {
function random() {
return new Array(1).fill(0)
}
api.generateXAddress({ entropy: random() })
}, api.errors.UnexpectedError)
}
}

View File

@@ -0,0 +1,27 @@
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getAccountInfo': async (api, address) => {
const result = await api.getAccountInfo(address)
assertResultMatch(result, responses.getAccountInfo, 'getAccountInfo')
},
'getAccountInfo - options undefined': async (api, address) => {
const result = await api.getAccountInfo(address, undefined)
assertResultMatch(result, responses.getAccountInfo, 'getAccountInfo')
},
'getAccountInfo - invalid options': async (api, address) => {
await assertRejects(
// @ts-ignore - This is intentionally invalid
api.getAccountInfo(address, { invalid: 'options' }),
api.errors.ValidationError
)
}
}

View File

@@ -0,0 +1,21 @@
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const { getAccountObjects: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getAccountObjects': async (api, address) => {
const result = await api.getAccountObjects(address)
assertResultMatch(result, RESPONSE_FIXTURES, 'AccountObjectsResponse')
},
'getAccountObjects - invalid options': async (api, address) => {
// @ts-ignore - This is intentionally invalid
const result = await api.getAccountObjects(address, { invalid: 'options' })
assertResultMatch(result, RESPONSE_FIXTURES, 'AccountObjectsResponse')
}
}

View File

@@ -0,0 +1,26 @@
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getBalanceSheet': async (api, address) => {
await api.getBalanceSheet(address)
},
'getBalanceSheet - invalid options': async (api, address) => {
await assertRejects(
// @ts-ignore - This is intentionally invalid
api.getBalanceSheet(address, { invalid: 'options' }),
api.errors.ValidationError
)
},
'getBalanceSheet - empty': async (api, address) => {
const options = { ledgerVersion: 123456 }
const result = await api.getBalanceSheet(address, options)
assertResultMatch(result, {}, 'getBalanceSheet')
}
}

View File

@@ -0,0 +1,47 @@
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getBalances': async (api, address) => {
const result = await api.getBalances(address)
assertResultMatch(result, responses.getBalances, 'getBalances')
},
'getBalances - limit': async (api, address) => {
const options = { limit: 3, ledgerVersion: 123456 }
const expectedResponse = responses.getBalances.slice(0, 3)
const result = await api.getBalances(address, options)
assertResultMatch(result, expectedResponse, 'getBalances')
},
'getBalances - limit & currency': async (api, address) => {
const options = { currency: 'USD', limit: 3 }
const expectedResponse = responses.getBalances
.filter(item => item.currency === 'USD')
.slice(0, 3)
const result = await api.getBalances(address, options)
assertResultMatch(result, expectedResponse, 'getBalances')
},
'getBalances - limit & currency & issuer': async (api, address) => {
const options = {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
limit: 3
}
const expectedResponse = responses.getBalances
.filter(
item =>
item.currency === 'USD' &&
item.counterparty === 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
)
.slice(0, 3)
const result = await api.getBalances(address, options)
assertResultMatch(result, expectedResponse, 'getBalances')
}
}

59
test/api/getFee/index.ts Normal file
View File

@@ -0,0 +1,59 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getFee': async (api, address) => {
const fee = await api.getFee()
assert.strictEqual(fee, '0.000012')
},
'getFee default': async (api, address) => {
api._feeCushion = undefined
const fee = await api.getFee()
assert.strictEqual(fee, '0.000012')
},
'getFee - high load_factor': async (api, address) => {
api.connection._send(
JSON.stringify({
command: 'config',
data: { highLoadFactor: true }
})
)
const fee = await api.getFee()
assert.strictEqual(fee, '2')
},
'getFee - high load_factor with custom maxFeeXRP': async (api, address) => {
// Ensure that overriding with high maxFeeXRP of '51540' causes no errors.
// (fee will actually be 51539.607552)
api._maxFeeXRP = '51540'
api.connection._send(
JSON.stringify({
command: 'config',
data: { highLoadFactor: true }
})
)
const fee = await api.getFee()
assert.strictEqual(fee, '51539.607552')
},
'getFee custom cushion': async (api, address) => {
api._feeCushion = 1.4
const fee = await api.getFee()
assert.strictEqual(fee, '0.000014')
},
// This is not recommended since it may result in attempting to pay
// less than the base fee. However, this test verifies the existing behavior.
'getFee cushion less than 1.0': async (api, address) => {
api._feeCushion = 0.9
const fee = await api.getFee()
assert.strictEqual(fee, '0.000009')
}
}

View File

@@ -0,0 +1,14 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (api, address) => {
const fee = await api.connection.getFeeBase()
assert.strictEqual(fee, 10)
}
}

View File

@@ -0,0 +1,14 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (api, address) => {
const fee = await api.connection.getFeeRef()
assert.strictEqual(fee, 10)
}
}

View File

@@ -0,0 +1,14 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default test': async (api, address) => {
const ver = await api.getLedgerVersion()
assert.strictEqual(ver, 8819951)
}
}

View File

@@ -0,0 +1,150 @@
import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import requests from '../../fixtures/requests'
import { TestSuite, assertResultMatch, assertRejects } from '../../utils'
import BigNumber from 'bignumber.js'
function checkSortingOfOrders(orders) {
let previousRate = '0'
for (var i = 0; i < orders.length; i++) {
const order = orders[i]
let rate
// We calculate the quality of output/input here as a test.
// This won't hold in general because when output and input amounts get tiny,
// the quality can differ significantly. However, the offer stays in the
// order book where it was originally placed. It would be more consistent
// to check the quality from the offer book, but for the test data set,
// this calculation holds.
if (order.specification.direction === 'buy') {
rate = new BigNumber(order.specification.quantity.value)
.dividedBy(order.specification.totalPrice.value)
.toString()
} else {
rate = new BigNumber(order.specification.totalPrice.value)
.dividedBy(order.specification.quantity.value)
.toString()
}
assert(
new BigNumber(rate).isGreaterThanOrEqualTo(previousRate),
'Rates must be sorted from least to greatest: ' +
rate +
' should be >= ' +
previousRate
)
previousRate = rate
}
return true
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'normal': async (api, address) => {
const response = await api.getOrderbook(
address,
requests.getOrderbook.normal,
{ limit: 20 }
)
assertResultMatch(response, responses.getOrderbook.normal, 'getOrderbook')
},
'invalid options': async (api, address) => {
assertRejects(
api.getOrderbook(address, requests.getOrderbook.normal, {
// @ts-ignore
invalid: 'options'
}),
api.errors.ValidationError
)
},
'with XRP': async (api, address) => {
const response = await api.getOrderbook(
address,
requests.getOrderbook.withXRP
)
assertResultMatch(response, responses.getOrderbook.withXRP, 'getOrderbook')
},
'sample XRP/JPY book has orders sorted correctly': async (api, address) => {
const orderbookInfo = {
base: {
// the first currency in pair
currency: 'XRP'
},
counter: {
currency: 'JPY',
counterparty: 'rB3gZey7VWHYRqJHLoHDEJXJ2pEPNieKiS'
}
}
const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
const response = await api.getOrderbook(myAddress, orderbookInfo)
assert.deepStrictEqual([], response.bids)
checkSortingOfOrders(response.asks)
},
'sample USD/XRP book has orders sorted correctly': async (api, address) => {
const orderbookInfo = {
counter: { currency: 'XRP' },
base: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
}
}
const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
const response = await api.getOrderbook(myAddress, orderbookInfo)
checkSortingOfOrders(response.bids)
checkSortingOfOrders(response.asks)
},
// WARNING: This test fails to catch the sorting bug, issue #766
'sorted so that best deals come first [bad test]': async (api, address) => {
const response = await api.getOrderbook(
address,
requests.getOrderbook.normal
)
const bidRates = response.bids.map(bid => bid.properties.makerExchangeRate)
const askRates = response.asks.map(ask => ask.properties.makerExchangeRate)
// makerExchangeRate = quality = takerPays.value/takerGets.value
// so the best deal for the taker is the lowest makerExchangeRate
// bids and asks should be sorted so that the best deals come first
assert.deepEqual(
bidRates.sort(x => Number(x)),
bidRates
)
assert.deepEqual(
askRates.sort(x => Number(x)),
askRates
)
},
'currency & counterparty are correct': async (api, address) => {
const response = await api.getOrderbook(
address,
requests.getOrderbook.normal
)
;[...response.bids, ...response.asks].forEach(order => {
const quantity = order.specification.quantity
const totalPrice = order.specification.totalPrice
const { base, counter } = requests.getOrderbook.normal
assert.strictEqual(quantity.currency, base.currency)
assert.strictEqual(quantity.counterparty, base.counterparty)
assert.strictEqual(totalPrice.currency, counter.currency)
assert.strictEqual(totalPrice.counterparty, counter.counterparty)
})
},
'direction is correct for bids and asks': async (api, address) => {
const response = await api.getOrderbook(
address,
requests.getOrderbook.normal
)
assert(response.bids.every(bid => bid.specification.direction === 'buy'))
assert(response.asks.every(ask => ask.specification.direction === 'sell'))
}
}

View File

@@ -0,0 +1,34 @@
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
export const config = {
// TODO: The mock server right now returns a hard-coded string, no matter
// what "Account" value you pass. We'll need it to support more accurate
// responses before we can turn these tests on.
skipXAddress: true
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getOrders': async (api, address) => {
const result = await api.getOrders(address)
assertResultMatch(result, responses.getOrders, 'getOrders')
},
'getOrders - limit': async (api, address) => {
const result = await api.getOrders(address, { limit: 20 })
assertResultMatch(result, responses.getOrders, 'getOrders')
},
'getOrders - invalid options': async (api, address) => {
await assertRejects(
// @ts-ignore - This is intentionally invalid
api.getOrders(address, { invalid: 'options' }),
api.errors.ValidationError
)
}
}

View File

@@ -1,7 +1,7 @@
import assert from 'assert-diff'
import { assertResultMatch, assertRejects, TestSuite } from '../../utils'
import responses from '../../fixtures/responses'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import addresses from '../../fixtures/addresses.json'
const { getPaths: REQUEST_FIXTURES } = requests
const { getPaths: RESPONSE_FIXTURES } = responses

View File

@@ -0,0 +1,44 @@
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const { getPaymentChannel: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getPaymentChannel': async (api, address) => {
const channelId =
'E30E709CF009A1F26E0E5C48F7AA1BFB79393764F15FB108BDC6E06D3CBD8415'
const result = await api.getPaymentChannel(channelId)
assertResultMatch(result, RESPONSE_FIXTURES.normal, 'getPaymentChannel')
},
'getPaymentChannel - full': async (api, address) => {
const channelId =
'D77CD4713AA08195E6B6D0E5BC023DA11B052EBFF0B5B22EDA8AE85345BCF661'
const result = await api.getPaymentChannel(channelId)
assertResultMatch(result, RESPONSE_FIXTURES.full, 'getPaymentChannel')
},
'getPaymentChannel - not found': async (api, address) => {
const channelId =
'DFA557EA3497585BFE83F0F97CC8E4530BBB99967736BB95225C7F0C13ACE708'
await assertRejects(
api.getPaymentChannel(channelId),
api.errors.RippledError,
'entryNotFound'
)
},
'getPaymentChannel - wrong type': async (api, address) => {
const channelId =
'8EF9CCB9D85458C8D020B3452848BBB42EAFDDDB69A93DD9D1223741A4CA562B'
await assertRejects(
api.getPaymentChannel(channelId),
api.errors.NotFoundError,
'Payment channel ledger entry not found'
)
}
}

View File

@@ -0,0 +1,48 @@
import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite, assertRejects } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default': async (api, address) => {
const serverInfo = await api.getServerInfo()
assertResultMatch(serverInfo, responses.getServerInfo, 'getServerInfo')
},
'error': async (api, address) => {
api.connection._send(
JSON.stringify({
command: 'config',
data: { returnErrorOnServerInfo: true }
})
)
try {
await api.getServerInfo()
throw new Error('Should throw NetworkError')
} catch (err) {
assert(err instanceof api.errors.RippledError)
assert.equal(err.message, 'You are placing too much load on the server.')
assert.equal(err.data.error, 'slowDown')
}
},
'no validated ledger': async (api, address) => {
api.connection._send(
JSON.stringify({
command: 'config',
data: { serverInfoWithoutValidated: true }
})
)
const serverInfo = await api.getServerInfo()
assert.strictEqual(serverInfo.networkLedger, 'waiting')
},
'getServerInfo - offline': async (api, address) => {
await api.disconnect()
return assertRejects(api.getServerInfo(), api.errors.NotConnectedError)
}
}

View File

@@ -0,0 +1,28 @@
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const { getSettings: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getSettings': async (api, address) => {
const result = await api.getSettings(address)
assertResultMatch(result, RESPONSE_FIXTURES, 'getSettings')
},
'getSettings - options undefined': async (api, address) => {
const result = await api.getSettings(address, undefined)
assertResultMatch(result, RESPONSE_FIXTURES, 'getSettings')
},
'getSettings - invalid options': async (api, address) => {
await assertRejects(
// @ts-ignore - This is intentionally invalid
api.getSettings(address, { invalid: 'options' }),
api.errors.ValidationError
)
}
}

View File

@@ -0,0 +1,389 @@
import assert from 'assert-diff'
import {
MissingLedgerHistoryError,
NotFoundError,
UnexpectedError
} from 'ripple-api/common/errors'
import { PendingLedgerVersionError } from '../../../src/common/errors'
import hashes from '../../fixtures/hashes.json'
import responses from '../../fixtures/responses'
import ledgerClosed from '../../fixtures/rippled/ledger-close-newer.json'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const { getTransaction: RESPONSE_FIXTURES } = responses
function closeLedger(connection) {
connection._ws.emit('message', JSON.stringify(ledgerClosed))
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'payment': async (api, address) => {
const response = await api.getTransaction(hashes.VALID_TRANSACTION_HASH)
assertResultMatch(response, RESPONSE_FIXTURES.payment, 'getTransaction')
},
'payment - include raw transaction': async (api, address) => {
const options = {
includeRawTransaction: true
}
const response = await api.getTransaction(
hashes.VALID_TRANSACTION_HASH,
options
)
assertResultMatch(
response,
RESPONSE_FIXTURES.paymentIncludeRawTransaction,
'getTransaction'
)
},
'settings': async (api, address) => {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.settings, 'getTransaction')
},
'settings - include raw transaction': async (api, address) => {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B'
const options = {
includeRawTransaction: true
}
const expected = Object.assign({}, RESPONSE_FIXTURES.settings) // Avoid mutating test fixture
expected.rawTransaction =
'{"Account":"rLVKsA4F9iJBbA6rX2x4wCmkj6drgtqpQe","Fee":"10","Flags":2147483648,"Sequence":1,"SetFlag":2,"SigningPubKey":"03EA3ADCA632F125EC2CC4F7F6A82DE0DCE2B65290CAC1F22242C5163F0DA9652D","TransactionType":"AccountSet","TxnSignature":"3045022100DE8B666B1A31EA65011B0F32130AB91A5747E32FA49B3054CEE8E8362DBAB98A022040CF0CF254677A8E5CD04C59CA2ED7F6F15F7E184641BAE169C561650967B226","date":460832270,"hash":"4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B","inLedger":8206418,"ledger_index":8206418,"meta":{"AffectedNodes":[{"ModifiedNode":{"FinalFields":{"Account":"rLVKsA4F9iJBbA6rX2x4wCmkj6drgtqpQe","Balance":"29999990","Flags":786432,"OwnerCount":0,"Sequence":2},"LedgerEntryType":"AccountRoot","LedgerIndex":"3F5072C4875F32ED770DAF3610A716600ED7C7BB0348FADC7A98E011BB2CD36F","PreviousFields":{"Balance":"30000000","Flags":4194304,"Sequence":1},"PreviousTxnID":"3FB0350A3742BBCC0D8AA3C5247D1AEC01177D0A24D9C34762BAA2FEA8AD88B3","PreviousTxnLgrSeq":8206397}}],"TransactionIndex":5,"TransactionResult":"tesSUCCESS"},"validated":true}'
const response = await api.getTransaction(hash, options)
assertResultMatch(response, expected, 'getTransaction')
},
'order': async (api, address) => {
const hash =
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51'
closeLedger(api.connection)
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.order, 'getTransaction')
},
'sell order': async (api, address) => {
const hash =
'458101D51051230B1D56E9ACAFAA34451BF65FA000F95DF6F0FF5B3A62D83FC2'
closeLedger(api.connection)
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.orderSell, 'getTransaction')
},
'order cancellation': async (api, address) => {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E'
closeLedger(api.connection)
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.orderCancellation,
'getTransaction'
)
},
'order with expiration cancellation': async (api, address) => {
const hash =
'097B9491CC76B64831F1FEA82EAA93BCD728106D90B65A072C933888E946C40B'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.orderWithExpirationCancellation,
'getTransaction'
)
},
'trustline set': async (api, address) => {
const hash =
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.trustline, 'getTransaction')
},
'trustline frozen off': async (api, address) => {
const hash =
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.trustlineFrozenOff,
'getTransaction'
)
},
'trustline no quality': async (api, address) => {
const hash =
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.trustlineNoQuality,
'getTransaction'
)
},
'trustline add memo': async (api, address) => {
const hash =
'9D6AC5FD6545B2584885B85E36759EB6440CDD41B6C55859F84AFDEE2B428220'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.trustlineAddMemo,
'getTransaction'
)
},
'not validated': async (api, address) => {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10'
await assertRejects(
api.getTransaction(hash),
NotFoundError,
'Transaction not found'
)
},
'tracking on': async (api, address) => {
const hash =
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.trackingOn, 'getTransaction')
},
'tracking off': async (api, address) => {
const hash =
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.trackingOff, 'getTransaction')
},
'set regular key': async (api, address) => {
const hash =
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.setRegularKey,
'getTransaction'
)
},
'not found in range': async (api, address) => {
const hash =
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E'
const options = {
minLedgerVersion: 32570,
maxLedgerVersion: 32571
}
await assertRejects(api.getTransaction(hash, options), NotFoundError)
},
'not found by hash': async (api, address) => {
const hash = hashes.NOTFOUND_TRANSACTION_HASH
await assertRejects(api.getTransaction(hash), NotFoundError)
},
'missing ledger history': async (api, address) => {
const hash = hashes.NOTFOUND_TRANSACTION_HASH
// make gaps in history
closeLedger(api.connection)
await assertRejects(api.getTransaction(hash), MissingLedgerHistoryError)
},
'missing ledger history with ledger range': async (api, address) => {
const hash = hashes.NOTFOUND_TRANSACTION_HASH
const options = {
minLedgerVersion: 32569,
maxLedgerVersion: 32571
}
await assertRejects(
api.getTransaction(hash, options),
MissingLedgerHistoryError
)
},
'not found - future maxLedgerVersion': async (api, address) => {
const hash = hashes.NOTFOUND_TRANSACTION_HASH
const options = {
maxLedgerVersion: 99999999999
}
await assertRejects(
api.getTransaction(hash, options),
PendingLedgerVersionError,
"maxLedgerVersion is greater than server's most recent validated ledger"
)
},
'transaction not validated': async (api, address) => {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11'
await assertRejects(
api.getTransaction(hash),
NotFoundError,
/Transaction has not been validated yet/
)
},
'transaction ledger not found': async (api, address) => {
const hash =
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12'
await assertRejects(
api.getTransaction(hash),
NotFoundError,
/ledger not found/
)
},
'ledger missing close time': async (api, address) => {
const hash =
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04'
closeLedger(api.connection)
await assertRejects(api.getTransaction(hash), UnexpectedError)
},
// Checks
'CheckCreate': async (api, address) => {
const hash =
'605A2E2C8E48AECAF5C56085D1AEAA0348DC838CE122C9188F94EB19DA05C2FE'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.checkCreate, 'getTransaction')
},
'CheckCancel': async (api, address) => {
const hash =
'B4105D1B2D83819647E4692B7C5843D674283F669524BD50C9614182E3A12CD4'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.checkCancel, 'getTransaction')
},
'CheckCash': async (api, address) => {
const hash =
'8321208465F70BA52C28BCC4F646BAF3B012BA13B57576C0336F42D77E3E0749'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.checkCash, 'getTransaction')
},
// Escrows
'EscrowCreation': async (api, address) => {
const hash =
'144F272380BDB4F1BD92329A2178BABB70C20F59042C495E10BF72EBFB408EE1'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.escrowCreation,
'getTransaction'
)
},
'EscrowCancellation': async (api, address) => {
const hash =
'F346E542FFB7A8398C30A87B952668DAB48B7D421094F8B71776DA19775A3B22'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.escrowCancellation,
'getTransaction'
)
},
'EscrowExecution': async (api, address) => {
const options = {
minLedgerVersion: 10,
maxLedgerVersion: 15
}
const hash =
'CC5277137B3F25EE8B86259C83CB0EAADE818505E4E9BCBF19B1AC6FD136993B'
const response = await api.getTransaction(hash, options)
assertResultMatch(
response,
RESPONSE_FIXTURES.escrowExecution,
'getTransaction'
)
},
'EscrowExecution simple': async (api, address) => {
const hash =
'CC5277137B3F25EE8B86259C83CB0EAADE818505E4E9BCBF19B1AC6FD1369931'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.escrowExecutionSimple,
'getTransaction'
)
},
'PaymentChannelCreate': async (api, address) => {
const hash =
'0E9CA3AB1053FC0C1CBAA75F636FE1EC92F118C7056BBEF5D63E4C116458A16D'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.paymentChannelCreate,
'getTransaction'
)
},
'PaymentChannelFund': async (api, address) => {
const hash =
'CD053D8867007A6A4ACB7A432605FE476D088DCB515AFFC886CF2B4EB6D2AE8B'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.paymentChannelFund,
'getTransaction'
)
},
'PaymentChannelClaim': async (api, address) => {
const hash =
'81B9ECAE7195EB6E8034AEDF44D8415A7A803E14513FDBB34FA984AB37D59563'
const response = await api.getTransaction(hash)
assertResultMatch(
response,
RESPONSE_FIXTURES.paymentChannelClaim,
'getTransaction'
)
},
'no Meta': async (api, address) => {
const hash =
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B'
const response = await api.getTransaction(hash)
assert.deepEqual(response, RESPONSE_FIXTURES.noMeta)
},
'Unrecognized transaction type': async (api, address) => {
const hash =
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11'
closeLedger(api.connection)
const response = await api.getTransaction(hash)
assert.strictEqual(
// @ts-ignore
response.specification.UNAVAILABLE,
'Unrecognized transaction type.'
)
},
'amendment': async (api, address) => {
const hash =
'A971B83ABED51D83749B73F3C1AAA627CD965AFF74BE8CD98299512D6FB0658F'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.amendment)
},
'feeUpdate': async (api, address) => {
const hash =
'C6A40F56127436DCD830B1B35FF939FD05B5747D30D6542572B7A835239817AF'
const response = await api.getTransaction(hash)
assertResultMatch(response, RESPONSE_FIXTURES.feeUpdate)
}
}

View File

@@ -0,0 +1,152 @@
import { RippleAPI } from 'ripple-api'
import assert from 'assert-diff'
import { assertResultMatch, TestSuite, assertRejects } from '../../utils'
import responses from '../../fixtures/responses'
import hashes from '../../fixtures/hashes.json'
import addresses from '../../fixtures/addresses.json'
const utils = RippleAPI._PRIVATE.ledgerUtils
const { getTransactions: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default': async (api, address) => {
const options = { types: ['payment', 'order'], initiated: true, limit: 2 }
const response = await api.getTransactions(address, options)
assertResultMatch(response, RESPONSE_FIXTURES.normal, 'getTransactions')
},
'include raw transactions': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 2,
includeRawTransactions: true
}
const response = await api.getTransactions(address, options)
assertResultMatch(
response,
RESPONSE_FIXTURES.includeRawTransactions,
'getTransactions'
)
},
'earliest first': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 2,
earliestFirst: true
}
const expected = Array.from(RESPONSE_FIXTURES.normal as any[]).sort(
utils.compareTransactions
)
const response = await api.getTransactions(address, options)
assertResultMatch(response, expected, 'getTransactions')
},
'earliest first with start option': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 2,
start: hashes.VALID_TRANSACTION_HASH,
earliestFirst: true
}
const response = await api.getTransactions(address, options)
assert.strictEqual(response.length, 0)
},
'gap': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 2,
maxLedgerVersion: 348858000
}
return assertRejects(
api.getTransactions(address, options),
api.errors.MissingLedgerHistoryError
)
},
'tx not found': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 2,
start: hashes.NOTFOUND_TRANSACTION_HASH,
counterparty: address
}
return assertRejects(
api.getTransactions(address, options),
api.errors.NotFoundError
)
},
'filters': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: true,
limit: 10,
excludeFailures: true,
counterparty: addresses.ISSUER
}
const response = await api.getTransactions(address, options)
assert.strictEqual(response.length, 10)
response.forEach(t => assert(t.type === 'payment' || t.type === 'order'))
response.forEach(t => assert(t.outcome.result === 'tesSUCCESS'))
},
'filters for incoming': async (api, address) => {
const options = {
types: ['payment', 'order'],
initiated: false,
limit: 10,
excludeFailures: true,
counterparty: addresses.ISSUER
}
const response = await api.getTransactions(address, options)
assert.strictEqual(response.length, 10)
response.forEach(t => assert(t.type === 'payment' || t.type === 'order'))
response.forEach(t => assert(t.outcome.result === 'tesSUCCESS'))
},
// this is the case where core.RippleError just falls
// through the api to the user
'error': async (api, address) => {
const options = { types: ['payment', 'order'], initiated: true, limit: 13 }
return assertRejects(
api.getTransactions(address, options),
api.errors.RippleError
)
},
// TODO: this doesn't test much, just that it doesn't crash
'getTransactions with start option': async (api, address) => {
const options = {
start: hashes.VALID_TRANSACTION_HASH,
earliestFirst: false,
limit: 2
}
const response = await api.getTransactions(address, options)
assertResultMatch(response, RESPONSE_FIXTURES.normal, 'getTransactions')
},
'start transaction with zero ledger version': async (api, address) => {
const options = {
start: '4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA13',
limit: 1
}
const response = await api.getTransactions(address, options)
assertResultMatch(response, [], 'getTransactions')
},
'no options': async (api, address) => {
const response = await api.getTransactions(addresses.OTHER_ACCOUNT)
assertResultMatch(response, RESPONSE_FIXTURES.one, 'getTransactions')
}
}

View File

@@ -0,0 +1,31 @@
import addresses from '../../fixtures/addresses.json'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const { getTrustlines: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'getTrustlines - filtered': async (api, address) => {
const options = { currency: 'USD' }
const result = await api.getTrustlines(address, options)
assertResultMatch(result, RESPONSE_FIXTURES.filtered, 'getTrustlines')
},
'getTrustlines - more than 400 items': async (api, address) => {
const options = { limit: 401 }
const result = await api.getTrustlines(addresses.THIRD_ACCOUNT, options)
assertResultMatch(
result,
RESPONSE_FIXTURES.moreThan400Items,
'getTrustlines'
)
},
'getTrustlines - no options': async (api, address) => {
await api.getTrustlines(address)
}
}

View File

@@ -0,0 +1,24 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'returns true when there is another page': async (api, address) => {
const response = await api.request('ledger_data')
assert(api.hasNextPage(response))
},
'returns false when there are no more pages': async (api, address) => {
const response = await api.request('ledger_data')
const responseNextPage = await api.requestNextPage(
'ledger_data',
{},
response
)
assert(!api.hasNextPage(responseNextPage))
}
}

View File

@@ -0,0 +1,15 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'disconnect & isConnected': async (api, address) => {
assert.strictEqual(api.isConnected(), true)
await api.disconnect()
assert.strictEqual(api.isConnected(), false)
}
}

View File

@@ -0,0 +1,17 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
import addresses from '../../fixtures/addresses.json'
export default <TestSuite>{
'returns true for valid address': async (api, address) => {
assert(api.isValidAddress('rLczgQHxPhWtjkaQqn3Q6UM8AbRbbRvs5K'))
assert(api.isValidAddress(addresses.ACCOUNT_X))
assert(api.isValidAddress(addresses.ACCOUNT_T))
},
'returns false for invalid address': async (api, address) => {
assert(!api.isValidAddress('foobar'))
assert(!api.isValidAddress(addresses.ACCOUNT_X.slice(0, -1)))
assert(!api.isValidAddress(addresses.ACCOUNT_T.slice(1)))
}
}

View File

@@ -0,0 +1,12 @@
import assert from 'assert-diff'
import { TestSuite } from '../../utils'
export default <TestSuite>{
'returns true for valid secret': async (api, address) => {
assert(api.isValidSecret('snsakdSrZSLkYpCXxfRkS4Sh96PMK'))
},
'returns false for invalid secret': async (api, address) => {
assert(!api.isValidSecret('foobar'))
}
}

View File

@@ -0,0 +1,18 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
prepareCheckCancel: async (api, address) => {
const result = await api.prepareCheckCancel(
address,
requests.prepareCheckCancel.normal
)
assertResultMatch(result, responses.prepareCheckCancel.normal, 'prepare')
}
}

View File

@@ -0,0 +1,26 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareCheckCash amount': async (api, address) => {
const result = await api.prepareCheckCash(
address,
requests.prepareCheckCash.amount
)
assertResultMatch(result, responses.prepareCheckCash.amount, 'prepare')
},
'prepareCheckCash deliverMin': async (api, address) => {
const result = await api.prepareCheckCash(
address,
requests.prepareCheckCash.deliverMin
)
assertResultMatch(result, responses.prepareCheckCash.deliverMin, 'prepare')
}
}

View File

@@ -0,0 +1,32 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareCheckCreate': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const result = await api.prepareCheckCreate(
address,
requests.prepareCheckCreate.normal,
localInstructions
)
assertResultMatch(result, responses.prepareCheckCreate.normal, 'prepare')
},
'prepareCheckCreate full': async (api, address) => {
const result = await api.prepareCheckCreate(
address,
requests.prepareCheckCreate.full
)
assertResultMatch(result, responses.prepareCheckCreate.full, 'prepare')
}
}

View File

@@ -0,0 +1,36 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareEscrowCancellation': async (api, address) => {
const result = await api.prepareEscrowCancellation(
address,
requests.prepareEscrowCancellation.normal,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(
result,
responses.prepareEscrowCancellation.normal,
'prepare'
)
},
'prepareEscrowCancellation with memos': async (api, address) => {
const result = await api.prepareEscrowCancellation(
address,
requests.prepareEscrowCancellation.memos
)
assertResultMatch(
result,
responses.prepareEscrowCancellation.memos,
'prepare'
)
}
}

View File

@@ -0,0 +1,50 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
export const config = {
// TODO: The mock server right now returns a hard-coded string, no matter
// what "Account" value you pass. We'll need it to support more accurate
// responses before we can turn these tests on.
skipXAddress: true
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareEscrowCreation': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const result = await api.prepareEscrowCreation(
address,
requests.prepareEscrowCreation.normal,
localInstructions
)
assertResultMatch(result, responses.prepareEscrowCreation.normal, 'prepare')
},
'prepareEscrowCreation full': async (api, address) => {
const result = await api.prepareEscrowCreation(
address,
requests.prepareEscrowCreation.full
)
assertResultMatch(result, responses.prepareEscrowCreation.full, 'prepare')
},
'prepareEscrowCreation - invalid': async (api, address) => {
const escrow = Object.assign({}, requests.prepareEscrowCreation.full)
delete escrow.amount // Make invalid
await assertRejects(
api.prepareEscrowCreation(address, escrow),
api.errors.ValidationError,
'instance.escrowCreation requires property "amount"'
)
}
}

View File

@@ -0,0 +1,60 @@
import { TestSuite, assertRejects, assertResultMatch } from '../../utils'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareEscrowExecution': async (api, address) => {
const result = await api.prepareEscrowExecution(
address,
requests.prepareEscrowExecution.normal,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(
result,
responses.prepareEscrowExecution.normal,
'prepare'
)
},
'prepareEscrowExecution - simple': async (api, address) => {
const result = await api.prepareEscrowExecution(
address,
requests.prepareEscrowExecution.simple
)
assertResultMatch(
result,
responses.prepareEscrowExecution.simple,
'prepare'
)
},
'prepareEscrowExecution - no condition': async (api, address) => {
await assertRejects(
api.prepareEscrowExecution(
address,
requests.prepareEscrowExecution.noCondition,
instructionsWithMaxLedgerVersionOffset
),
api.errors.ValidationError,
'"condition" and "fulfillment" fields on EscrowFinish must only be specified together.'
)
},
'prepareEscrowExecution - no fulfillment': async (api, address) => {
await assertRejects(
api.prepareEscrowExecution(
address,
requests.prepareEscrowExecution.noFulfillment,
instructionsWithMaxLedgerVersionOffset
),
api.errors.ValidationError,
'"condition" and "fulfillment" fields on EscrowFinish must only be specified together.'
)
}
}

View File

@@ -0,0 +1,52 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'buy order': async (api, address) => {
const request = requests.prepareOrder.buy
const result = await api.prepareOrder(address, request)
assertResultMatch(result, responses.prepareOrder.buy, 'prepare')
},
'buy order with expiration': async (api, address) => {
const request = requests.prepareOrder.expiration
const response = responses.prepareOrder.expiration
const result = await api.prepareOrder(
address,
request,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(result, response, 'prepare')
},
'sell order': async (api, address) => {
const request = requests.prepareOrder.sell
const result = await api.prepareOrder(
address,
request,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(result, responses.prepareOrder.sell, 'prepare')
},
'invalid': async (api, address) => {
const request = Object.assign({}, requests.prepareOrder.sell)
delete request.direction // Make invalid
await assertRejects(
api.prepareOrder(
address,
request,
instructionsWithMaxLedgerVersionOffset
),
api.errors.ValidationError,
'instance.order requires property "direction"'
)
}
}

View File

@@ -0,0 +1,59 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'prepareOrderCancellation': async (api, address) => {
const request = requests.prepareOrderCancellation.simple
const result = await api.prepareOrderCancellation(
address,
request,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(
result,
responses.prepareOrderCancellation.normal,
'prepare'
)
},
'no instructions': async (api, address) => {
const request = requests.prepareOrderCancellation.simple
const result = await api.prepareOrderCancellation(address, request)
assertResultMatch(
result,
responses.prepareOrderCancellation.noInstructions,
'prepare'
)
},
'with memos': async (api, address) => {
const request = requests.prepareOrderCancellation.withMemos
const result = await api.prepareOrderCancellation(address, request)
assertResultMatch(
result,
responses.prepareOrderCancellation.withMemos,
'prepare'
)
},
'invalid': async (api, address) => {
const request = Object.assign(
{},
requests.prepareOrderCancellation.withMemos
)
delete request.orderSequence // Make invalid
await assertRejects(
api.prepareOrderCancellation(address, request),
api.errors.ValidationError,
'instance.orderCancellation requires property "orderSequence"'
)
}
}

View File

@@ -0,0 +1,441 @@
import { assertResultMatch, TestSuite, assertRejects } from '../../utils'
import responses from '../../fixtures/responses'
import requests from '../../fixtures/requests'
import { ValidationError } from 'ripple-api/common/errors'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
const { preparePayment: REQUEST_FIXTURES } = requests
const { preparePayment: RESPONSE_FIXTURES } = responses
const RECIPIENT_ADDRESS = 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'normal': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.normal,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.normal, 'prepare')
},
'min amount xrp': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.minAmountXRP,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.minAmountXRP, 'prepare')
},
'min amount xrp2xrp': async (api, address) => {
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.minAmount,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, RESPONSE_FIXTURES.minAmountXRPXRP, 'prepare')
},
'XRP to XRP': async (api, address) => {
const payment = {
source: {
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
maxAmount: { value: '1', currency: 'XRP' }
},
destination: {
address: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
amount: { value: '1', currency: 'XRP' }
}
}
const expected = {
txJSON:
'{"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":"1000000","Flags":2147483648,"LastLedgerSequence":8820051,"Sequence":23,"Fee":"12"}',
instructions: {
fee: '0.000012',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
payment,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expected, 'prepare')
},
'XRP drops to XRP drops': async (api, address) => {
const payment = {
source: {
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
maxAmount: { value: '1000000', currency: 'drops' }
},
destination: {
address: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
amount: { value: '1000000', currency: 'drops' }
}
}
const expected = {
txJSON:
'{"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":"1000000","Flags":2147483648,"LastLedgerSequence":8820051,"Sequence":23,"Fee":"12"}',
instructions: {
fee: '0.000012',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
payment,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expected, 'prepare')
},
'XRP drops to XRP': async (api, address) => {
const payment = {
source: {
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
maxAmount: { value: '1000000', currency: 'drops' }
},
destination: {
address: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
amount: { value: '1', currency: 'XRP' }
}
}
const expected = {
txJSON:
'{"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":"1000000","Flags":2147483648,"LastLedgerSequence":8820051,"Sequence":23,"Fee":"12"}',
instructions: {
fee: '0.000012',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
payment,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expected, 'prepare')
},
'XRP to XRP drops': async (api, address) => {
const payment = {
source: {
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
maxAmount: { value: '1', currency: 'XRP' }
},
destination: {
address: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
amount: { value: '1000000', currency: 'drops' }
}
}
const expected = {
txJSON:
'{"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":"1000000","Flags":2147483648,"LastLedgerSequence":8820051,"Sequence":23,"Fee":"12"}',
instructions: {
fee: '0.000012',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
payment,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expected, 'prepare')
},
// Errors
'rejects promise and does not throw when payment object is invalid': async (
api,
address
) => {
const payment = {
source: {
address: address,
// instead of `maxAmount`
amount: { value: '1000', currency: 'drops' }
},
destination: {
address: RECIPIENT_ADDRESS,
amount: { value: '1000', currency: 'drops' }
}
}
return assertRejects(
api.preparePayment(address, payment),
ValidationError,
'payment must specify either (source.maxAmount and destination.amount) or (source.amount and destination.minAmount)'
)
},
'rejects promise and does not throw when field is missing': async (
api,
address
) => {
// Marking as "any" to get around the fact that TS won't allow this.
const payment: any = {
source: { address: address },
destination: {
address: RECIPIENT_ADDRESS,
amount: { value: '1000', currency: 'drops' }
}
}
return assertRejects(
api.preparePayment(address, payment),
ValidationError,
'instance.payment.source is not exactly one from <sourceExactAdjustment>,<maxAdjustment>'
)
},
'rejects promise and does not throw when fee exceeds maxFeeXRP': async (
api,
address
) => {
const payment = {
source: {
address: address,
maxAmount: { value: '1000', currency: 'drops' }
},
destination: {
address: RECIPIENT_ADDRESS,
amount: { value: '1000', currency: 'drops' }
}
}
return assertRejects(
api.preparePayment(address, payment, { fee: '3' }),
ValidationError,
'Fee of 3 XRP exceeds max of 2 XRP. To use this fee, increase `maxFeeXRP` in the RippleAPI constructor.'
)
},
'XRP to XRP no partial': async (api, address) => {
return assertRejects(
api.preparePayment(address, REQUEST_FIXTURES.wrongPartial),
ValidationError,
'XRP to XRP payments cannot be partial payments'
)
},
'address must match payment.source.address': async (api, address) => {
return assertRejects(
api.preparePayment(address, REQUEST_FIXTURES.wrongAddress),
ValidationError,
'address must match payment.source.address'
)
},
'wrong amount': async (api, address) => {
return assertRejects(
api.preparePayment(address, REQUEST_FIXTURES.wrongAmount),
ValidationError,
'payment must specify either (source.maxAmount and destination.amount) or (source.amount and destination.minAmount)'
)
},
'throws when fee exceeds 2 XRP': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
fee: '2.1'
}
return assertRejects(
api.preparePayment(address, REQUEST_FIXTURES.normal, localInstructions),
ValidationError,
'Fee of 2.1 XRP exceeds max of 2 XRP. To use this fee, increase `maxFeeXRP` in the RippleAPI constructor.'
)
},
'preparePayment with all options specified': async (api, address) => {
const version = await api.getLedgerVersion()
const localInstructions = {
maxLedgerVersion: version + 100,
fee: '0.000012'
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.allOptions,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.allOptions, 'prepare')
},
'preparePayment without counterparty set': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
sequence: 23
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.noCounterparty,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.noCounterparty, 'prepare')
},
'destination.minAmount': async (api, address) => {
const response = await api.preparePayment(
address,
responses.getPaths.sendAll[0],
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, RESPONSE_FIXTURES.minAmount, 'prepare')
},
'caps fee at 2 XRP by default': async (api, address) => {
api._feeCushion = 1000000
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"2000000","Sequence":23}',
instructions: {
fee: '2',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.normal,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expectedResponse, 'prepare')
},
'allows fee exceeding 2 XRP when maxFeeXRP is higher': async (
api,
address
) => {
api._maxFeeXRP = '2.2'
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
fee: '2.1'
}
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"2100000","Sequence":23}',
instructions: {
fee: '2.1',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
REQUEST_FIXTURES.normal,
localInstructions
)
assertResultMatch(response, expectedResponse, 'prepare')
},
'fee - default maxFee of 2 XRP': async (api, address) => {
api._feeCushion = 1000000
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"2000000","Sequence":23}',
instructions: {
fee: '2',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
requests.preparePayment.normal,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expectedResponse, 'prepare')
},
'fee - capped to maxFeeXRP when maxFee exceeds maxFeeXRP': async (
api,
address
) => {
api._feeCushion = 1000000
api._maxFeeXRP = '3'
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '4'
}
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"3000000","Sequence":23}',
instructions: {
fee: '3',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
requests.preparePayment.normal,
localInstructions
)
assertResultMatch(response, expectedResponse, 'prepare')
},
'fee - capped to maxFee': async (api, address) => {
api._feeCushion = 1000000
api._maxFeeXRP = '5'
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '4'
}
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"4000000","Sequence":23}',
instructions: {
fee: '4',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
requests.preparePayment.normal,
localInstructions
)
assertResultMatch(response, expectedResponse, 'prepare')
},
'fee - calculated fee does not use more than 6 decimal places': async (
api,
address
) => {
api.connection._send(
JSON.stringify({
command: 'config',
data: { loadFactor: 5407.96875 }
})
)
const expectedResponse = {
txJSON:
'{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"64896","Sequence":23}',
instructions: {
fee: '0.064896',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const response = await api.preparePayment(
address,
requests.preparePayment.normal,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(response, expectedResponse, 'prepare')
}
}

View File

@@ -0,0 +1,97 @@
import assert from 'assert-diff'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
const { preparePaymentChannelClaim: REQUEST_FIXTURES } = requests
const { preparePaymentChannelClaim: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'default': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const response = await api.preparePaymentChannelClaim(
address,
REQUEST_FIXTURES.normal,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.normal, 'prepare')
},
'with renew': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const response = await api.preparePaymentChannelClaim(
address,
REQUEST_FIXTURES.renew,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.renew, 'prepare')
},
'with close': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const response = await api.preparePaymentChannelClaim(
address,
REQUEST_FIXTURES.close,
localInstructions
)
assertResultMatch(response, RESPONSE_FIXTURES.close, 'prepare')
},
'rejects Promise on preparePaymentChannelClaim with renew and close': async (
api,
address
) => {
try {
const prepared = await api.preparePaymentChannelClaim(
address,
REQUEST_FIXTURES.full
)
throw new Error(
'Expected method to reject. Prepared transaction: ' +
JSON.stringify(prepared)
)
} catch (err) {
assert.strictEqual(err.name, 'ValidationError')
assert.strictEqual(
err.message,
'"renew" and "close" flags on PaymentChannelClaim are mutually exclusive'
)
}
},
'rejects Promise on preparePaymentChannelClaim with no signature': async (
api,
address
) => {
try {
const prepared = await api.preparePaymentChannelClaim(
address,
REQUEST_FIXTURES.noSignature
)
throw new Error(
'Expected method to reject. Prepared transaction: ' +
JSON.stringify(prepared)
)
} catch (err) {
assert.strictEqual(err.name, 'ValidationError')
assert.strictEqual(
err.message,
'"signature" and "publicKey" fields on PaymentChannelClaim must only be specified together.'
)
}
}
}

View File

@@ -0,0 +1,47 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
export const config = {
// TODO: The mock server right now returns a hard-coded string, no matter
// what "Account" value you pass. We'll need it to support more accurate
// responses before we can turn these tests on.
skipXAddress: true
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'preparePaymentChannelCreate': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const result = await api.preparePaymentChannelCreate(
address,
requests.preparePaymentChannelCreate.normal,
localInstructions
)
assertResultMatch(
result,
responses.preparePaymentChannelCreate.normal,
'prepare'
)
},
'preparePaymentChannelCreate full': async (api, address) => {
const result = await api.preparePaymentChannelCreate(
address,
requests.preparePaymentChannelCreate.full
)
assertResultMatch(
result,
responses.preparePaymentChannelCreate.full,
'prepare'
)
}
}

View File

@@ -0,0 +1,40 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'preparePaymentChannelFund': async (api, address) => {
const localInstructions = {
...instructionsWithMaxLedgerVersionOffset,
maxFee: '0.000012'
}
const result = await api.preparePaymentChannelFund(
address,
requests.preparePaymentChannelFund.normal,
localInstructions
)
assertResultMatch(
result,
responses.preparePaymentChannelFund.normal,
'prepare'
)
},
'preparePaymentChannelFund full': async (api, address) => {
const result = await api.preparePaymentChannelFund(
address,
requests.preparePaymentChannelFund.full
)
assertResultMatch(
result,
responses.preparePaymentChannelFund.full,
'prepare'
)
}
}

View File

@@ -234,5 +234,22 @@ export default <TestSuite>{
)
assert.strictEqual(err.name, 'ValidationError')
}
},
'offline': async (api, address) => {
// const api = new RippleAPI()
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const settings = requests.prepareSettings.domain
const instructions = {
sequence: 23,
maxLedgerVersion: 8820051,
fee: '0.000012'
}
const result = await api.prepareSettings(address, settings, instructions)
assertResultMatch(result, responses.prepareSettings.flags, 'prepare')
assert.deepEqual(
api.sign(result.txJSON, secret),
responses.prepareSettings.signed
)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
simple: async (api, address) => {
const result = await api.prepareTrustline(
address,
requests.prepareTrustline.simple,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(result, responses.prepareTrustline.simple, 'prepare')
},
frozen: async (api, address) => {
const result = await api.prepareTrustline(
address,
requests.prepareTrustline.frozen
)
assertResultMatch(result, responses.prepareTrustline.frozen, 'prepare')
},
complex: async (api, address) => {
const result = await api.prepareTrustline(
address,
requests.prepareTrustline.complex,
instructionsWithMaxLedgerVersionOffset
)
assertResultMatch(result, responses.prepareTrustline.complex, 'prepare')
},
invalid: async (api, address) => {
const trustline = Object.assign({}, requests.prepareTrustline.complex)
delete trustline.limit // Make invalid
await assertRejects(
api.prepareTrustline(
address,
trustline,
instructionsWithMaxLedgerVersionOffset
),
api.errors.ValidationError,
'instance.trustline requires property "limit"'
)
}
}

35
test/api/request/index.ts Normal file
View File

@@ -0,0 +1,35 @@
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'request account_objects': async (api, address) => {
const result = await api.request('account_objects', {
account: address
})
assertResultMatch(
result,
responses.getAccountObjects,
'AccountObjectsResponse'
)
},
'request account_objects - invalid options': async (api, address) => {
// Intentionally no local validation of these options
const result = await api.request('account_objects', {
account: address,
invalid: 'options'
})
assertResultMatch(
result,
responses.getAccountObjects,
'AccountObjectsResponse'
)
}
}

View File

@@ -0,0 +1,38 @@
import assert from 'assert-diff'
import { LedgerData } from 'ripple-api/common/types/objects'
import { assertRejects, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'requests the next page': async (api, address) => {
const response = await api.request('ledger_data')
const responseNextPage = await api.requestNextPage<LedgerData>(
'ledger_data',
{},
response
)
assert.equal(
responseNextPage.state[0].index,
'000B714B790C3C79FEE00D17C4DEB436B375466F29679447BA64F265FD63D731'
)
},
'rejects when there are no more pages': async (api, address) => {
const response = await api.request('ledger_data')
const responseNextPage = await api.requestNextPage(
'ledger_data',
{},
response
)
assert(!api.hasNextPage(responseNextPage))
await assertRejects(
api.requestNextPage('ledger_data', {}, responseNextPage),
Error,
'response does not have a next page'
)
}
}

312
test/api/sign/index.ts Normal file
View File

@@ -0,0 +1,312 @@
import assert from 'assert-diff'
import { RippleAPI } from 'ripple-api'
import binary from 'ripple-binary-codec'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { TestSuite } from '../../utils'
const { schemaValidator } = RippleAPI._PRIVATE
const { sign: REQUEST_FIXTURES } = requests
const { sign: RESPONSE_FIXTURES } = responses
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'sign': async (api, address) => {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = api.sign(REQUEST_FIXTURES.normal.txJSON, secret)
assert.deepEqual(result, RESPONSE_FIXTURES.normal)
schemaValidator.schemaValidate('sign', result)
},
'already signed': async (api, address) => {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = api.sign(REQUEST_FIXTURES.normal.txJSON, secret)
assert.throws(() => {
const tx = JSON.stringify(binary.decode(result.signedTransaction))
api.sign(tx, secret)
}, /txJSON must not contain "TxnSignature" or "Signers" properties/)
},
'EscrowExecution': async (api, address) => {
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
const result = api.sign(REQUEST_FIXTURES.escrow.txJSON, secret)
assert.deepEqual(result, RESPONSE_FIXTURES.escrow)
schemaValidator.schemaValidate('sign', result)
},
'signAs': async (api, address) => {
const txJSON = REQUEST_FIXTURES.signAs
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
const signature = api.sign(JSON.stringify(txJSON), secret, {
signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
})
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs)
},
'withKeypair': async (api, address) => {
const keypair = {
privateKey:
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
publicKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
}
const result = api.sign(REQUEST_FIXTURES.normal.txJSON, keypair)
assert.deepEqual(result, RESPONSE_FIXTURES.normal)
schemaValidator.schemaValidate('sign', result)
},
'withKeypair already signed': async (api, address) => {
const keypair = {
privateKey:
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
publicKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
}
const result = api.sign(REQUEST_FIXTURES.normal.txJSON, keypair)
assert.throws(() => {
const tx = JSON.stringify(binary.decode(result.signedTransaction))
api.sign(tx, keypair)
}, /txJSON must not contain "TxnSignature" or "Signers" properties/)
},
'withKeypair EscrowExecution': async (api, address) => {
const keypair = {
privateKey:
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
publicKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'
}
const result = api.sign(REQUEST_FIXTURES.escrow.txJSON, keypair)
assert.deepEqual(result, RESPONSE_FIXTURES.escrow)
schemaValidator.schemaValidate('sign', result)
},
'withKeypair signAs': async (api, address) => {
const txJSON = REQUEST_FIXTURES.signAs
const keypair = {
privateKey:
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
publicKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'
}
const signature = api.sign(JSON.stringify(txJSON), keypair, {
signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
})
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs)
},
'succeeds - prepared payment': async (api, address) => {
const payment = await api.preparePayment(address, {
source: {
address: address,
maxAmount: {
value: '1',
currency: 'drops'
}
},
destination: {
address: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
amount: {
value: '1',
currency: 'drops'
}
}
})
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = api.sign(payment.txJSON, secret)
const expectedResult = {
signedTransaction:
'12000022800000002400000017201B008694F261400000000000000168400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100A9C91D4CFAE45686146EE0B56D4C53A2E7C2D672FB834D43E0BE2D2E9106519A022075DDA2F92DE552B0C45D83D4E6D35889B3FBF51BFBBD9B25EBF70DE3C96D0D6681145E7B112523F68D2F5E879DB4EAC51C6698A693048314FDB08D07AAA0EB711793A3027304D688E10C3648',
id: '88D6B913C66279EA31ADC25C5806C48B2D4E5680261666790A736E1961217700'
}
assert.deepEqual(result, expectedResult)
schemaValidator.schemaValidate('sign', result)
},
'succeeds - no flags': async (api, address) => {
const txJSON =
'{"TransactionType":"Payment","Account":"r45Rev1EXGxy2hAUmJPCne97KUE7qyrD3j","Destination":"rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r","Amount":"20000000","Sequence":1,"Fee":"12"}'
const secret = 'shotKgaEotpcYsshSE39vmSnBDRim'
const result = api.sign(txJSON, secret)
const expectedResult = {
signedTransaction:
'1200002400000001614000000001312D0068400000000000000C7321022B05847086686F9D0499B13136B94AD4323EE1B67D4C429ECC987AB35ACFA34574473045022100C104B7B97C31FACA4597E7D6FCF13BD85BD11375963A62A0AC45B0061236E39802207784F157F6A98DFC85B051CDDF61CC3084C4F5750B82674801C8E9950280D1998114EE3046A5DDF8422C40DDB93F1D522BB4FE6419158314FDB08D07AAA0EB711793A3027304D688E10C3648',
id: '0596925967F541BF332FF6756645B2576A9858414B5B363DC3D34915BE8A70D6'
}
const decoded = binary.decode(result.signedTransaction)
assert(
decoded.Flags === undefined,
`Flags = ${decoded.Flags}, should be undefined`
)
assert.deepEqual(result, expectedResult)
schemaValidator.schemaValidate('sign', result)
},
'throws when encoded tx does not match decoded tx - prepared payment': async (
api,
address
) => {
const payment = await api.preparePayment(address, {
source: {
address: address,
maxAmount: {
value: '1.1234567',
currency: 'drops'
}
},
destination: {
address: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
amount: {
value: '1.1234567',
currency: 'drops'
}
}
})
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
assert.throws(() => {
api.sign(payment.txJSON, secret)
}, /^Error: 1\.1234567 is an illegal amount/)
},
'throws when encoded tx does not match decoded tx - prepared order': async (
api,
address
) => {
const order = {
direction: 'sell',
quantity: {
currency: 'USD',
counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '3.140000'
},
totalPrice: {
currency: 'XRP',
value: '31415'
}
}
const prepared = await api.prepareOrder(address, order, { sequence: 123 })
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
try {
api.sign(prepared.txJSON, secret)
return Promise.reject(new Error('api.sign should have thrown'))
} catch (error) {
assert.equal(error.name, 'ValidationError')
assert.equal(
error.message,
'Serialized transaction does not match original txJSON. See `error.data`'
)
assert.deepEqual(error.data.diff, {
TakerGets: {
value: '3.14'
}
})
}
},
'throws when encoded tx does not match decoded tx - AccountSet': async (
api,
address
) => {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-Address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1.2","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '0.0000012',
sequence: 23,
maxLedgerVersion: 8820051
}
}
assert.throws(() => {
api.sign(request.txJSON, secret)
}, /Error: 1\.2 is an illegal amount/)
},
'throws when encoded tx does not match decoded tx - higher fee': async (
api,
address
) => {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-Address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1123456.7","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '1.1234567',
sequence: 23,
maxLedgerVersion: 8820051
}
}
assert.throws(() => {
api.sign(request.txJSON, secret)
}, /Error: 1123456\.7 is an illegal amount/)
},
'throws when Fee exceeds maxFeeXRP (in drops)': async (api, address) => {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"${address}","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051
}
}
assert.throws(() => {
api.sign(request.txJSON, secret)
}, /Fee" should not exceed "2000000"\. To use a higher fee, set `maxFeeXRP` in the RippleAPI constructor\./)
},
'throws when Fee exceeds maxFeeXRP (in drops) - custom maxFeeXRP': async (
api,
address
) => {
api._maxFeeXRP = '1.9'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"${address}","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051
}
}
assert.throws(() => {
api.sign(request.txJSON, secret)
}, /Fee" should not exceed "1900000"\. To use a higher fee, set `maxFeeXRP` in the RippleAPI constructor\./)
},
'permits fee exceeding 2000000 drops when maxFeeXRP is higher than 2 XRP': async (
api,
address
) => {
api._maxFeeXRP = '2.1'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-Address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"726970706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051
}
}
const result = api.sign(request.txJSON, secret)
const expectedResponse = {
signedTransaction:
'12000322800000002400000017201B008695536840000000001EAB90732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8744630440220384FBB48EEE7B0E58BD89294A609F9407C51FBE8FA08A4B305B22E9A7489D66602200152315EFE752DA381E74493419871550D206AC6503841DA5F8C30E35D9E3892770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304',
id: 'A1586D6AF7B0821E7075E12A0132D9EB50BC1874A0749441201497F7561795FB'
}
assert.deepEqual(result, expectedResponse)
schemaValidator.schemaValidate('sign', result)
}
}

View File

@@ -0,0 +1,25 @@
import { TestSuite, assertResultMatch } from '../../utils'
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
signPaymentChannelClaim: async (api, address) => {
const privateKey =
'ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A'
const result = api.signPaymentChannelClaim(
requests.signPaymentChannelClaim.channel,
requests.signPaymentChannelClaim.amount,
privateKey
)
assertResultMatch(
result,
responses.signPaymentChannelClaim,
'signPaymentChannelClaim'
)
}
}

19
test/api/submit/index.ts Normal file
View File

@@ -0,0 +1,19 @@
import responses from '../../fixtures/responses'
import { assertRejects, assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'submit': async (api, address) => {
const result = await api.submit(responses.sign.normal.signedTransaction)
assertResultMatch(result, responses.submit, 'submit')
},
'submit - failure': async (api, address) => {
await assertRejects(api.submit('BAD'), api.errors.RippledError)
// assert.strictEqual(error.data.resultCode, 'temBAD_FEE')
}
}

View File

@@ -0,0 +1,34 @@
import requests from '../../fixtures/requests'
import responses from '../../fixtures/responses'
import { assertResultMatch, TestSuite } from '../../utils'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'verifyPaymentChannelClaim': async (api, address) => {
const publicKey =
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
const result = api.verifyPaymentChannelClaim(
requests.signPaymentChannelClaim.channel,
requests.signPaymentChannelClaim.amount,
responses.signPaymentChannelClaim,
publicKey
)
assertResultMatch(result, true, 'verifyPaymentChannelClaim')
},
'verifyPaymentChannelClaim - invalid': async (api, address) => {
const publicKey =
'03A6523FE4281DA48A6FD77FAF3CB77F5C7001ABA0B32BCEDE0369AC009758D7D9'
const result = api.verifyPaymentChannelClaim(
requests.signPaymentChannelClaim.channel,
requests.signPaymentChannelClaim.amount,
responses.signPaymentChannelClaim,
publicKey
)
assertResultMatch(result, false, 'verifyPaymentChannelClaim')
}
}

View File

@@ -0,0 +1,184 @@
import assert from 'assert-diff'
import _ from 'lodash'
import { RippleAPI } from 'ripple-api'
import { RecursiveData } from 'ripple-api/ledger/utils'
import { assertRejects, assertResultMatch } from './utils'
import addresses from './fixtures/addresses.json'
import responses from './fixtures/responses'
import ledgerClosed from './fixtures/rippled/ledger-close-newer.json'
import setupAPI from './setup-api'
const { validate, schemaValidator, ledgerUtils } = RippleAPI._PRIVATE
const address = addresses.ACCOUNT
assert.options.strict = true
// how long before each test case times out
const TIMEOUT = 20000
describe('RippleAPI', function() {
this.timeout(TIMEOUT)
beforeEach(setupAPI.setup)
afterEach(setupAPI.teardown)
it('RippleAPI - implicit server port', function() {
new RippleAPI({ server: 'wss://s1.ripple.com' })
})
it('RippleAPI invalid options', function() {
// @ts-ignore - This is intentionally invalid
assert.throws(() => new RippleAPI({ invalid: true }))
})
it('RippleAPI valid options', function() {
const api = new RippleAPI({ server: 'wss://s:1' })
const privateConnectionUrl = (api.connection as any)._url
assert.deepEqual(privateConnectionUrl, 'wss://s:1')
})
it('RippleAPI invalid server uri', function() {
assert.throws(() => new RippleAPI({ server: 'wss//s:1' }))
})
xit('RippleAPI connect() times out after 2 seconds', function() {
// TODO: Use a timer mock like https://jestjs.io/docs/en/timer-mocks
// to test that connect() times out after 2 seconds.
})
it('ledger closed event', function(done) {
this.api.on('ledger', message => {
assertResultMatch(message, responses.ledgerEvent, 'ledgerEvent')
done()
})
this.api.connection._ws.emit('message', JSON.stringify(ledgerClosed))
})
describe('[private] schema-validator', function() {
it('valid', function() {
assert.doesNotThrow(function() {
schemaValidator.schemaValidate(
'hash256',
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A0F'
)
})
})
it('invalid', function() {
assert.throws(function() {
schemaValidator.schemaValidate('hash256', 'invalid')
}, this.api.errors.ValidationError)
})
it('invalid - empty value', function() {
assert.throws(function() {
schemaValidator.schemaValidate('hash256', '')
}, this.api.errors.ValidationError)
})
it('schema not found error', function() {
assert.throws(function() {
schemaValidator.schemaValidate('unexisting', 'anything')
}, /no schema/)
})
})
describe('[private] validator', function() {
it('validateLedgerRange', function() {
const options = {
minLedgerVersion: 20000,
maxLedgerVersion: 10000
}
const thunk = _.partial(validate.getTransactions, { address, options })
assert.throws(thunk, this.api.errors.ValidationError)
assert.throws(
thunk,
/minLedgerVersion must not be greater than maxLedgerVersion/
)
})
it('secret', function() {
function validateSecret(secret) {
validate.sign({ txJSON: '', secret })
}
assert.doesNotThrow(
_.partial(validateSecret, 'shzjfakiK79YQdMjy4h8cGGfQSV6u')
)
assert.throws(
_.partial(validateSecret, 'shzjfakiK79YQdMjy4h8cGGfQSV6v'),
this.api.errors.ValidationError
)
assert.throws(
_.partial(validateSecret, 1),
this.api.errors.ValidationError
)
assert.throws(
_.partial(validateSecret, ''),
this.api.errors.ValidationError
)
assert.throws(
_.partial(validateSecret, 's!!!'),
this.api.errors.ValidationError
)
assert.throws(
_.partial(validateSecret, 'passphrase'),
this.api.errors.ValidationError
)
// 32 0s is a valid hex repr of seed bytes
const hex = new Array(33).join('0')
assert.throws(
_.partial(validateSecret, hex),
this.api.errors.ValidationError
)
})
})
it('common utils - toRippledAmount', async () => {
const amount = { issuer: 'is', currency: 'c', value: 'v' }
assert.deepEqual(ledgerUtils.common.toRippledAmount(amount), {
issuer: 'is',
currency: 'c',
value: 'v'
})
})
it('ledger utils - renameCounterpartyToIssuerInOrder', async () => {
const order = {
taker_gets: { counterparty: '1', currency: 'XRP' },
taker_pays: { counterparty: '1', currency: 'XRP' }
}
const expected = {
taker_gets: { issuer: '1', currency: 'XRP' },
taker_pays: { issuer: '1', currency: 'XRP' }
}
assert.deepEqual(
ledgerUtils.renameCounterpartyToIssuerInOrder(order),
expected
)
})
it('ledger utils - compareTransactions', async () => {
// @ts-ignore
assert.strictEqual(ledgerUtils.compareTransactions({}, {}), 0)
let first: any = { outcome: { ledgerVersion: 1, indexInLedger: 100 } }
let second: any = { outcome: { ledgerVersion: 1, indexInLedger: 200 } }
assert.strictEqual(ledgerUtils.compareTransactions(first, second), -1)
first = { outcome: { ledgerVersion: 1, indexInLedger: 100 } }
second = { outcome: { ledgerVersion: 1, indexInLedger: 100 } }
assert.strictEqual(ledgerUtils.compareTransactions(first, second), 0)
first = { outcome: { ledgerVersion: 1, indexInLedger: 200 } }
second = { outcome: { ledgerVersion: 1, indexInLedger: 100 } }
assert.strictEqual(ledgerUtils.compareTransactions(first, second), 1)
})
it('ledger utils - getRecursive', async () => {
function getter(marker) {
return new Promise<RecursiveData>((resolve, reject) => {
if (marker !== undefined) {
reject(new Error())
return
}
resolve({ marker: 'A', results: [1] })
})
}
await assertRejects(ledgerUtils.getRecursive(getter, 10), Error)
})
})