mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-15 18:15:49 +00:00
Lint integration test files and browser test files (#1612)
* clean up utils * more cleanup * remove client.sign * remove unneeded tests * remove unneeded infra * move helper functions to separate files * fix linter issues * more cleanup * make helper functions more generally useful * fix test account funding * add import note to README * lint browser tests * run eslint --fix
This commit is contained in:
@@ -68,6 +68,7 @@ export default class RequestManager {
|
||||
throw new Error(`No existing promise with id ${id}`)
|
||||
}
|
||||
clearTimeout(promise.timer)
|
||||
// TODO: figure out how to have a better stack trace for an error
|
||||
promise.reject(error)
|
||||
this.deletePromise(id)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function setTransactionFlagsToNumber(tx: Transaction): void {
|
||||
|
||||
switch (tx.TransactionType) {
|
||||
case 'AccountSet':
|
||||
tx.Flags =convertAccountSetFlagsToNumber(tx.Flags)
|
||||
tx.Flags = convertAccountSetFlagsToNumber(tx.Flags)
|
||||
return
|
||||
case 'OfferCreate':
|
||||
tx.Flags = convertOfferCreateFlagsToNumber(tx.Flags)
|
||||
@@ -77,11 +77,15 @@ export function setTransactionFlagsToNumber(tx: Transaction): void {
|
||||
}
|
||||
}
|
||||
|
||||
function convertAccountSetFlagsToNumber(flags: AccountSetFlagsInterface): number {
|
||||
function convertAccountSetFlagsToNumber(
|
||||
flags: AccountSetFlagsInterface,
|
||||
): number {
|
||||
return reduceFlags(flags, AccountSetTransactionFlags)
|
||||
}
|
||||
|
||||
function convertOfferCreateFlagsToNumber(flags: OfferCreateFlagsInterface): number {
|
||||
function convertOfferCreateFlagsToNumber(
|
||||
flags: OfferCreateFlagsInterface,
|
||||
): number {
|
||||
return reduceFlags(flags, OfferCreateTransactionFlags)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const RIPPLE_EPOCH_DIFF = 0x386d4380
|
||||
* @param rpepoch - (seconds since 1/1/2000 GMT).
|
||||
* @returns Milliseconds since unix epoch.
|
||||
*/
|
||||
function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
return (rpepoch + RIPPLE_EPOCH_DIFF) * 1000
|
||||
}
|
||||
|
||||
@@ -40,7 +40,4 @@ function ISOTimeToRippleTime(iso8601: string): number {
|
||||
return unixToRippleTimestamp(Date.parse(iso8601))
|
||||
}
|
||||
|
||||
export {
|
||||
rippleTimeToISOTime,
|
||||
ISOTimeToRippleTime,
|
||||
}
|
||||
export { rippleTimeToISOTime, ISOTimeToRippleTime }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import path from 'path'
|
||||
|
||||
import { expect, assert } from 'chai'
|
||||
import puppeteer from 'puppeteer'
|
||||
|
||||
@@ -6,7 +8,9 @@ describe('Browser Tests', function () {
|
||||
const browser = await puppeteer.launch({ headless: true })
|
||||
try {
|
||||
const page = await browser.newPage().catch()
|
||||
await page.goto(`file:///${__dirname}/../localIntegrationRunner.html`)
|
||||
await page.goto(
|
||||
path.join('file:///', __dirname, '../localIntegrationRunner.html'),
|
||||
)
|
||||
|
||||
await page.waitForFunction(
|
||||
'document.querySelector("body").innerText.includes("submit multisigned transaction")',
|
||||
@@ -26,6 +30,7 @@ describe('Browser Tests', function () {
|
||||
expect(fails).to.equal('failures: 0')
|
||||
expect(passes).to.not.equal('passes: 0')
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console -- only prints if something goes wrong
|
||||
console.log(err)
|
||||
assert(false)
|
||||
} finally {
|
||||
|
||||
@@ -3,3 +3,6 @@ To run integration tests:
|
||||
* With docker, run `docker run -p 6006:6006 -it natenichols/rippled-standalone:latest`
|
||||
* Or [download and build rippled](https://xrpl.org/install-rippled.html) and run `./rippled -a`
|
||||
2. Run `yarn test:integration` or `yarn test:browser`
|
||||
|
||||
When editing integration tests:
|
||||
* All imports should be from `xrpl-local` instead of `../../src` (browser tests need this)
|
||||
|
||||
@@ -1,439 +1,53 @@
|
||||
import assert from 'assert'
|
||||
|
||||
import _ from 'lodash'
|
||||
import { isValidXAddress } from 'ripple-address-codec'
|
||||
import { encode } from 'ripple-binary-codec'
|
||||
|
||||
import { Client } from 'xrpl-local'
|
||||
import { Client, Wallet } from 'xrpl-local'
|
||||
import { AccountSet, SignerListSet } from 'xrpl-local/models/transactions'
|
||||
import { convertStringToHex } from 'xrpl-local/utils'
|
||||
import { sign, multisign } from 'xrpl-local/wallet/signer'
|
||||
|
||||
import serverUrl from './serverUrl'
|
||||
import { setupClient, suiteClientSetup, teardownClient } from './setup'
|
||||
import {
|
||||
AccountSet,
|
||||
OfferCreate,
|
||||
SignerListSet,
|
||||
TrustSet,
|
||||
} from 'xrpl-local/models/transactions'
|
||||
import {
|
||||
isValidSecret,
|
||||
generateXAddress,
|
||||
xrpToDrops,
|
||||
convertStringToHex,
|
||||
} from 'xrpl-local/utils'
|
||||
|
||||
// import requests from '../fixtures/requests'
|
||||
|
||||
import { payTo, ledgerAccept } from './utils'
|
||||
import wallet from './wallet'
|
||||
ledgerAccept,
|
||||
testTransaction,
|
||||
verifySubmittedTransaction,
|
||||
fundAccount,
|
||||
} from './utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
const INTERVAL = 1000 // how long to wait between checks for validated ledger
|
||||
|
||||
const HOST = process.env.HOST ?? '0.0.0.0'
|
||||
const PORT = process.env.PORT ?? '6006'
|
||||
const serverUrl = `ws://${HOST}:${PORT}`
|
||||
|
||||
console.log(serverUrl)
|
||||
|
||||
function acceptLedger(client) {
|
||||
return client.connection.request({ command: 'ledger_accept' })
|
||||
}
|
||||
|
||||
function verifyTransaction(testcase, hash, type, options, txData, account) {
|
||||
console.log('VERIFY...')
|
||||
return testcase.client
|
||||
.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
min_ledger: options.minLedgerVersion,
|
||||
max_ledger: options.maxLedgerVersion,
|
||||
})
|
||||
.then((data) => {
|
||||
assert(data && data.result)
|
||||
assert.strictEqual(data.result.TransactionType, type)
|
||||
assert.strictEqual(data.result.Account, account)
|
||||
assert.strictEqual(data.result.meta.TransactionResult, 'tesSUCCESS')
|
||||
if (testcase.transactions != null) {
|
||||
testcase.transactions.push(hash)
|
||||
}
|
||||
return { txJSON: JSON.stringify(txData), id: hash, tx: data }
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error.stack)
|
||||
assert(false, `Transaction not successful: ${error.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
function testTransaction(
|
||||
testcase,
|
||||
type,
|
||||
lastClosedLedgerVersion,
|
||||
prepared,
|
||||
address = wallet.getAddress(),
|
||||
secret = wallet.getSecret(),
|
||||
) {
|
||||
const txJSON = prepared.txJSON
|
||||
assert(txJSON, 'missing txJSON')
|
||||
const txData = JSON.parse(txJSON)
|
||||
assert.strictEqual(txData.Account, address)
|
||||
const signedData = testcase.client.sign(txJSON, secret)
|
||||
console.log('PREPARED...')
|
||||
return testcase.client
|
||||
.request({ command: 'submit', tx_blob: signedData.signedTransaction })
|
||||
.then((response) =>
|
||||
testcase.test.title.indexOf('multisign') !== -1
|
||||
? acceptLedger(testcase.client).then(() => response)
|
||||
: response,
|
||||
)
|
||||
.then(async (response) => {
|
||||
console.log('SUBMITTED...')
|
||||
assert.strictEqual(response.result.engine_result, 'tesSUCCESS')
|
||||
const options = {
|
||||
minLedgerVersion: lastClosedLedgerVersion,
|
||||
maxLedgerVersion: txData.LastLedgerSequence,
|
||||
}
|
||||
ledgerAccept(testcase.client)
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(
|
||||
() =>
|
||||
verifyTransaction(
|
||||
testcase,
|
||||
signedData.id,
|
||||
type,
|
||||
options,
|
||||
txData,
|
||||
address,
|
||||
).then(resolve, reject),
|
||||
INTERVAL,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function setup(this: any, server = serverUrl) {
|
||||
this.client = new Client(server)
|
||||
console.log('CONNECTING...')
|
||||
return this.client.connect().then(
|
||||
() => {
|
||||
console.log('CONNECTED...')
|
||||
},
|
||||
(error) => {
|
||||
console.log('ERROR:', error)
|
||||
throw error
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const masterAccount = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
const masterSecret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
|
||||
|
||||
function makeTrustLine(testcase, address, secret) {
|
||||
const client = testcase.client
|
||||
const trustSet: TrustSet = {
|
||||
TransactionType: 'TrustSet',
|
||||
Account: address,
|
||||
LimitAmount: {
|
||||
value: '1341.1',
|
||||
issuer: masterAccount,
|
||||
currency: 'USD',
|
||||
},
|
||||
Flags: 0x00020000,
|
||||
}
|
||||
const trust = client
|
||||
.autofill(trustSet)
|
||||
.then(async (tx) => {
|
||||
const signed = client.sign(JSON.stringify(tx), secret)
|
||||
if (address === wallet.getAddress()) {
|
||||
testcase.transactions.push(signed.id)
|
||||
}
|
||||
return client.request({
|
||||
command: 'submit',
|
||||
tx_blob: signed.signedTransaction,
|
||||
})
|
||||
})
|
||||
.then((response) => {
|
||||
if (
|
||||
response.result.engine_result !== 'tesSUCCESS' &&
|
||||
response.result.engine_result !== 'tecPATH_PARTIAL'
|
||||
) {
|
||||
console.log(response)
|
||||
assert.fail(`Response not successful, ${response.result.engine_result}`)
|
||||
}
|
||||
ledgerAccept(client)
|
||||
})
|
||||
return trust
|
||||
}
|
||||
|
||||
function makeOrder(client, offerCreate, secret) {
|
||||
return client
|
||||
.autofill(offerCreate)
|
||||
.then((tx) => client.sign(JSON.stringify(tx), secret))
|
||||
.then((signed) =>
|
||||
client.request({ command: 'submit', tx_blob: signed.signedTransaction }),
|
||||
)
|
||||
.then((response) => {
|
||||
if (
|
||||
response.result.engine_result !== 'tesSUCCESS' &&
|
||||
response.result.engine_result !== 'tecPATH_PARTIAL'
|
||||
) {
|
||||
console.log(response)
|
||||
assert.fail(`Response not successful, ${response.result.engine_result}`)
|
||||
}
|
||||
ledgerAccept(client)
|
||||
})
|
||||
}
|
||||
|
||||
function setupAccounts(testcase) {
|
||||
const client = testcase.client
|
||||
let fundAmount = '20'
|
||||
|
||||
const promise = client
|
||||
.request({ command: 'server_info' })
|
||||
.then(
|
||||
(response) =>
|
||||
(fundAmount = xrpToDrops(
|
||||
Number(response.result.info.validated_ledger.reserve_base_xrp) * 2,
|
||||
)),
|
||||
)
|
||||
.then(() => payTo(client, 'rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM', fundAmount))
|
||||
.then(() => payTo(client, wallet.getAddress(), fundAmount))
|
||||
.then(() => payTo(client, testcase.newWallet.classicAddress, fundAmount))
|
||||
.then(() => payTo(client, 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc', fundAmount))
|
||||
.then(() => payTo(client, 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q', fundAmount))
|
||||
.then(() => {
|
||||
const accountSet: AccountSet = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: masterAccount,
|
||||
// default ripple
|
||||
SetFlag: 8,
|
||||
}
|
||||
return client
|
||||
.autofill(accountSet)
|
||||
.then((tx) => client.sign(JSON.stringify(tx), masterSecret))
|
||||
.then((signed) =>
|
||||
client.request({
|
||||
command: 'submit',
|
||||
tx_blob: signed.signedTransaction,
|
||||
}),
|
||||
)
|
||||
.then(() => ledgerAccept(client))
|
||||
})
|
||||
.then(() =>
|
||||
makeTrustLine(testcase, wallet.getAddress(), wallet.getSecret()),
|
||||
)
|
||||
.then(() =>
|
||||
makeTrustLine(
|
||||
testcase,
|
||||
testcase.newWallet.xAddress,
|
||||
testcase.newWallet.secret,
|
||||
),
|
||||
)
|
||||
.then(() => payTo(client, wallet.getAddress(), '123', 'USD', masterAccount))
|
||||
.then(() => payTo(client, 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'))
|
||||
.then(() => {
|
||||
const offerCreate: OfferCreate = {
|
||||
TransactionType: 'OfferCreate',
|
||||
Account: testcase.newWallet.xAddress,
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
value: '432',
|
||||
issuer: masterAccount,
|
||||
},
|
||||
TakerGets: xrpToDrops('432'),
|
||||
}
|
||||
return makeOrder(testcase.client, offerCreate, testcase.newWallet.secret)
|
||||
})
|
||||
.then(() => {
|
||||
const offerCreate: OfferCreate = {
|
||||
TransactionType: 'OfferCreate',
|
||||
Account: masterAccount,
|
||||
TakerPays: xrpToDrops('1741'),
|
||||
TakerGets: {
|
||||
currency: 'USD',
|
||||
value: '171',
|
||||
issuer: masterAccount,
|
||||
},
|
||||
}
|
||||
return makeOrder(testcase.client, offerCreate, masterSecret)
|
||||
})
|
||||
return promise
|
||||
}
|
||||
|
||||
function teardown(this: any) {
|
||||
return this.client.disconnect()
|
||||
}
|
||||
|
||||
function suiteSetup(this: any) {
|
||||
this.transactions = []
|
||||
|
||||
return (
|
||||
setup
|
||||
.bind(this)(serverUrl)
|
||||
.then(() => ledgerAccept(this.client))
|
||||
.then(
|
||||
() =>
|
||||
(this.newWallet = generateXAddress({ includeClassicAddress: true })),
|
||||
)
|
||||
// two times to give time to server to send `ledgerClosed` event
|
||||
// so getLedgerVersion will return right value
|
||||
.then(() => ledgerAccept(this.client))
|
||||
.then(() =>
|
||||
this.client
|
||||
.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
.then((response) => response.result.ledger_index),
|
||||
)
|
||||
.then((ledgerVersion) => {
|
||||
this.startLedgerVersion = ledgerVersion
|
||||
})
|
||||
.then(() => setupAccounts(this))
|
||||
.then(() => teardown.bind(this)())
|
||||
)
|
||||
}
|
||||
|
||||
describe('integration tests', function () {
|
||||
const address = wallet.getAddress()
|
||||
this.timeout(TIMEOUT)
|
||||
|
||||
before(suiteSetup)
|
||||
beforeEach(_.partial(setup, serverUrl))
|
||||
afterEach(teardown)
|
||||
before(suiteClientSetup)
|
||||
beforeEach(_.partial(setupClient, serverUrl))
|
||||
afterEach(teardownClient)
|
||||
|
||||
it('isConnected', function () {
|
||||
assert(this.client.isConnected())
|
||||
})
|
||||
|
||||
it('getFee', function () {
|
||||
return this.client.getFee().then((fee) => {
|
||||
assert.strictEqual(typeof fee, 'string')
|
||||
assert(!isNaN(Number(fee)))
|
||||
assert(parseFloat(fee) === Number(fee))
|
||||
it('submit multisigned transaction', async function () {
|
||||
const client: Client = this.client
|
||||
const multisignAccount = 'r5nx8ZkwEbFztnc8Qyi22DE9JYjRzNmvs'
|
||||
const multisignSecret = 'ss6F8381Br6wwpy9p582H8sBt19J3'
|
||||
const signer1address = 'rQDhz2ZNXmhxzCYwxU6qAbdxsHA4HV45Y2'
|
||||
const signer1secret = 'shK6YXzwYfnFVn3YZSaMh5zuAddKx'
|
||||
const signer2address = 'r3RtUvGw9nMoJ5FuHxuoVJvcENhKtuF9ud'
|
||||
const signer2secret = 'shUHQnL4EH27V4EiBrj6EfhWvZngF'
|
||||
await fundAccount(client, multisignAccount)
|
||||
|
||||
const ledgerResponse = await client.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
})
|
||||
const minLedgerVersion = ledgerResponse.result.ledger_index
|
||||
|
||||
// it('getTrustlines', function () {
|
||||
// const fixture = requests.prepareTrustline.simple
|
||||
// const { currency, counterparty } = fixture
|
||||
// const options = { currency, counterparty }
|
||||
// return this.client.getTrustlines(address, options).then((data) => {
|
||||
// assert(data && data.length > 0 && data[0] && data[0].specification)
|
||||
// const specification = data[0].specification
|
||||
// assert.strictEqual(Number(specification.limit), Number(fixture.limit))
|
||||
// assert.strictEqual(specification.currency, fixture.currency)
|
||||
// assert.strictEqual(specification.counterparty, fixture.counterparty)
|
||||
// })
|
||||
// })
|
||||
|
||||
// it('getBalances', function () {
|
||||
// const fixture = requests.prepareTrustline.simple
|
||||
// const { currency, counterparty } = fixture
|
||||
// const options = { currency, counterparty }
|
||||
// return this.client.getBalances(address, options).then((data) => {
|
||||
// assert(data && data.length > 0 && data[0])
|
||||
// assert.strictEqual(data[0].currency, fixture.currency)
|
||||
// assert.strictEqual(data[0].counterparty, fixture.counterparty)
|
||||
// })
|
||||
// })
|
||||
|
||||
it('getOrderbook', function () {
|
||||
const orderbook = {
|
||||
base: {
|
||||
currency: 'XRP',
|
||||
},
|
||||
counter: {
|
||||
currency: 'USD',
|
||||
counterparty: masterAccount,
|
||||
},
|
||||
}
|
||||
return this.client.getOrderbook(address, orderbook).then((book) => {
|
||||
assert(book && book.bids && book.bids.length > 0)
|
||||
assert(book.asks && book.asks.length > 0)
|
||||
const bid = book.bids[0]
|
||||
assert(bid && bid.specification && bid.specification.quantity)
|
||||
assert(bid.specification.totalPrice)
|
||||
assert.strictEqual(bid.specification.direction, 'buy')
|
||||
assert.strictEqual(bid.specification.quantity.currency, 'XRP')
|
||||
assert.strictEqual(bid.specification.totalPrice.currency, 'USD')
|
||||
const ask = book.asks[0]
|
||||
assert(ask && ask.specification && ask.specification.quantity)
|
||||
assert(ask.specification.totalPrice)
|
||||
assert.strictEqual(ask.specification.direction, 'sell')
|
||||
assert.strictEqual(ask.specification.quantity.currency, 'XRP')
|
||||
assert.strictEqual(ask.specification.totalPrice.currency, 'USD')
|
||||
})
|
||||
})
|
||||
|
||||
// it('getPaths', function () {
|
||||
// const pathfind = {
|
||||
// source: {
|
||||
// address: address
|
||||
// },
|
||||
// destination: {
|
||||
// address: this.newWallet.address,
|
||||
// amount: {
|
||||
// value: '1',
|
||||
// currency: 'USD',
|
||||
// counterparty: masterAccount
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return this.client.getPaths(pathfind).then((data) => {
|
||||
// assert(data && data.length > 0)
|
||||
// const path = data[0]
|
||||
// assert(path && path.source)
|
||||
// assert.strictEqual(path.source.address, address)
|
||||
// assert(path.paths && path.paths.length > 0)
|
||||
// })
|
||||
// })
|
||||
|
||||
// it('getPaths - send all', function () {
|
||||
// const pathfind = {
|
||||
// source: {
|
||||
// address: address,
|
||||
// amount: {
|
||||
// currency: 'USD',
|
||||
// value: '0.005'
|
||||
// }
|
||||
// },
|
||||
// destination: {
|
||||
// address: this.newWallet.address,
|
||||
// amount: {
|
||||
// currency: 'USD'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return this.client.getPaths(pathfind).then((data) => {
|
||||
// assert(data && data.length > 0)
|
||||
// assert(
|
||||
// data.every((path) => {
|
||||
// return (
|
||||
// parseFloat(path.source.amount.value) <=
|
||||
// parseFloat(pathfind.source.amount.value)
|
||||
// )
|
||||
// })
|
||||
// )
|
||||
// const path = data[0]
|
||||
// assert(path && path.source)
|
||||
// assert.strictEqual(path.source.address, pathfind.source.address)
|
||||
// assert(path.paths && path.paths.length > 0)
|
||||
// })
|
||||
// })
|
||||
|
||||
it('generateWallet', function () {
|
||||
const newWallet = generateXAddress()
|
||||
assert(newWallet && newWallet.xAddress && newWallet.secret)
|
||||
assert(isValidXAddress(newWallet.xAddress))
|
||||
assert(isValidSecret(newWallet.secret))
|
||||
})
|
||||
|
||||
const multisignAccount = 'r5nx8ZkwEbFztnc8Qyi22DE9JYjRzNmvs'
|
||||
const multisignSecret = 'ss6F8381Br6wwpy9p582H8sBt19J3'
|
||||
const signer1address = 'rQDhz2ZNXmhxzCYwxU6qAbdxsHA4HV45Y2'
|
||||
const signer1secret = 'shK6YXzwYfnFVn3YZSaMh5zuAddKx'
|
||||
const signer2address = 'r3RtUvGw9nMoJ5FuHxuoVJvcENhKtuF9ud'
|
||||
const signer2secret = 'shUHQnL4EH27V4EiBrj6EfhWvZngF'
|
||||
|
||||
it('submit multisigned transaction', function () {
|
||||
// set up the multisigners for the account
|
||||
const signerListSet: SignerListSet = {
|
||||
TransactionType: 'SignerListSet',
|
||||
Account: multisignAccount,
|
||||
@@ -453,94 +67,33 @@ describe('integration tests', function () {
|
||||
],
|
||||
SignerQuorum: 2,
|
||||
}
|
||||
let minLedgerVersion = null
|
||||
let fundAmount = '20'
|
||||
return this.client
|
||||
.request({ command: 'server_info' })
|
||||
.then(
|
||||
(response) =>
|
||||
(fundAmount = xrpToDrops(
|
||||
Number(response.result.info.validated_ledger.reserve_base_xrp) * 2,
|
||||
)),
|
||||
)
|
||||
.then(() =>
|
||||
payTo(this.client, multisignAccount, fundAmount)
|
||||
.then(() => {
|
||||
return this.client
|
||||
.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
.then((response) => response.result.ledger_index)
|
||||
.then((ledgerVersion) => {
|
||||
minLedgerVersion = ledgerVersion
|
||||
})
|
||||
.then(() => this.client.autofill(signerListSet, 2))
|
||||
.then((tx) => {
|
||||
return testTransaction(
|
||||
this,
|
||||
'SignerListSet',
|
||||
minLedgerVersion,
|
||||
{ txJSON: JSON.stringify(tx) },
|
||||
multisignAccount,
|
||||
multisignSecret,
|
||||
)
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
const accountSet: AccountSet = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: multisignAccount,
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
return this.client.autofill(accountSet, 2).then((tx) => {
|
||||
const signed1 = this.client.sign(
|
||||
JSON.stringify(tx),
|
||||
signer1secret,
|
||||
{
|
||||
signAs: signer1address,
|
||||
},
|
||||
)
|
||||
const signed2 = this.client.sign(
|
||||
JSON.stringify(tx),
|
||||
signer2secret,
|
||||
{
|
||||
signAs: signer2address,
|
||||
},
|
||||
)
|
||||
const combined = this.client.combine([
|
||||
signed1.signedTransaction,
|
||||
signed2.signedTransaction,
|
||||
])
|
||||
return this.client
|
||||
.request({
|
||||
command: 'submit',
|
||||
tx_blob: combined.signedTransaction,
|
||||
})
|
||||
.then((response) =>
|
||||
acceptLedger(this.client).then(() => response),
|
||||
)
|
||||
.then((response) => {
|
||||
assert.strictEqual(
|
||||
response.result.engine_result,
|
||||
'tesSUCCESS',
|
||||
)
|
||||
const options = { minLedgerVersion }
|
||||
return verifyTransaction(
|
||||
this,
|
||||
combined.id,
|
||||
'AccountSet',
|
||||
options,
|
||||
{},
|
||||
multisignAccount,
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error.message)
|
||||
throw error
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
const tx = await client.autofill(signerListSet, 2)
|
||||
await testTransaction(
|
||||
this,
|
||||
minLedgerVersion,
|
||||
tx,
|
||||
multisignAccount,
|
||||
multisignSecret,
|
||||
)
|
||||
|
||||
// try to multisign
|
||||
const accountSet: AccountSet = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: multisignAccount,
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
const accountSetTx = await client.autofill(accountSet, 2)
|
||||
const signed1 = sign(Wallet.fromSeed(signer1secret), accountSetTx, true)
|
||||
const signed2 = sign(Wallet.fromSeed(signer2secret), accountSetTx, true)
|
||||
const combined = multisign([signed1, signed2])
|
||||
// TODO: replace with `client.submitSignedTransaction`
|
||||
const submitResponse = await client.request({
|
||||
command: 'submit',
|
||||
tx_blob: encode(combined),
|
||||
})
|
||||
await ledgerAccept(client)
|
||||
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
|
||||
const options = { minLedgerVersion }
|
||||
await verifySubmittedTransaction(this, combined, options)
|
||||
})
|
||||
})
|
||||
|
||||
6
test/integration/serverUrl.ts
Normal file
6
test/integration/serverUrl.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable node/no-process-env -- needed to find standalone connection */
|
||||
const HOST = process.env.HOST ?? '0.0.0.0'
|
||||
const PORT = process.env.PORT ?? '6006'
|
||||
const serverUrl = `ws://${HOST}:${PORT}`
|
||||
|
||||
export default serverUrl
|
||||
36
test/integration/setup.ts
Normal file
36
test/integration/setup.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { generateXAddress, Client } from 'xrpl-local'
|
||||
|
||||
import serverUrl from './serverUrl'
|
||||
import { ledgerAccept } from './utils'
|
||||
|
||||
export async function teardownClient(this: Mocha.Context): Promise<void> {
|
||||
this.client.disconnect()
|
||||
}
|
||||
|
||||
export async function suiteClientSetup(this: Mocha.Context): Promise<void> {
|
||||
this.transactions = []
|
||||
|
||||
await setupClient.bind(this)(serverUrl)
|
||||
ledgerAccept(this.client)
|
||||
this.newWallet = generateXAddress({ includeClassicAddress: true })
|
||||
// two times to give time to server to send `ledgerClosed` event
|
||||
// so getLedgerVersion will return right value
|
||||
await ledgerAccept(this.client)
|
||||
const response = await this.client.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
const ledgerVersion = response.result.ledger_index
|
||||
this.startLedgerVersion = ledgerVersion
|
||||
await teardownClient.bind(this)()
|
||||
}
|
||||
|
||||
export async function setupClient(
|
||||
this: Mocha.Context,
|
||||
server = serverUrl,
|
||||
): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.client = new Client(server)
|
||||
this.client.connect().then(resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
const assert = require('chai').assert
|
||||
|
||||
const models = require('xrpl-local/models/transactions')
|
||||
const utils = require('xrpl-local/utils')
|
||||
|
||||
const masterAccount = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
const masterSecret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
|
||||
|
||||
function ledgerAccept(client) {
|
||||
const request = { command: 'ledger_accept' }
|
||||
return client.connection.request(request)
|
||||
}
|
||||
|
||||
function pay(client, from, to, amount, secret, currency = 'XRP', issuer) {
|
||||
const paymentAmount =
|
||||
currency === 'XRP' ? amount : { value: amount, currency, issuer }
|
||||
|
||||
const payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: from,
|
||||
Destination: to,
|
||||
Amount: paymentAmount,
|
||||
}
|
||||
|
||||
let id = null
|
||||
return (
|
||||
client
|
||||
.autofill(payment, 1)
|
||||
.then((tx) => {
|
||||
models.verifyPayment(payment)
|
||||
return client.sign(JSON.stringify(tx), secret)
|
||||
})
|
||||
.then((signed) => {
|
||||
id = signed.id
|
||||
return client.request({
|
||||
command: 'submit',
|
||||
tx_blob: signed.signedTransaction,
|
||||
})
|
||||
})
|
||||
// TODO: add better error handling here
|
||||
// TODO: fix path issues
|
||||
.then((response) => {
|
||||
if (
|
||||
response.result.engine_result !== 'tesSUCCESS' &&
|
||||
response.result.engine_result !== 'tecPATH_PARTIAL'
|
||||
) {
|
||||
console.log(response)
|
||||
assert.fail(
|
||||
`Response not successful, ${response.result.engine_result}`,
|
||||
)
|
||||
}
|
||||
ledgerAccept(client)
|
||||
})
|
||||
.then(() => id)
|
||||
)
|
||||
}
|
||||
|
||||
function payTo(
|
||||
client,
|
||||
to,
|
||||
amount = '40000000',
|
||||
currency = 'XRP',
|
||||
counterparty,
|
||||
) {
|
||||
return pay(
|
||||
client,
|
||||
masterAccount,
|
||||
to,
|
||||
amount,
|
||||
masterSecret,
|
||||
currency,
|
||||
counterparty,
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
pay,
|
||||
payTo,
|
||||
ledgerAccept,
|
||||
}
|
||||
135
test/integration/utils.ts
Normal file
135
test/integration/utils.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/* eslint-disable max-params -- helper test functions */
|
||||
import { assert } from 'chai'
|
||||
import _ from 'lodash'
|
||||
import { decode } from 'ripple-binary-codec'
|
||||
|
||||
import { Client, SubmitResponse, Wallet } from 'xrpl-local'
|
||||
import { BaseResponse } from 'xrpl-local/models/methods/baseMethod'
|
||||
import {
|
||||
verifyPayment,
|
||||
Payment,
|
||||
Transaction,
|
||||
} from 'xrpl-local/models/transactions'
|
||||
import { computeSignedTransactionHash } from 'xrpl-local/utils/hashes'
|
||||
import { sign } from 'xrpl-local/wallet/signer'
|
||||
|
||||
import { walletAddress, walletSecret } from './wallet'
|
||||
|
||||
const masterAccount = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
const masterSecret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
|
||||
|
||||
interface LedgerAcceptResponse extends BaseResponse {
|
||||
result: {
|
||||
ledger_current_index: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function ledgerAccept(
|
||||
client: Client,
|
||||
): Promise<LedgerAcceptResponse> {
|
||||
const request = { command: 'ledger_accept' }
|
||||
return client.connection.request(request) as Promise<LedgerAcceptResponse>
|
||||
}
|
||||
|
||||
// TODO: replace with `client.submitTransaction` once that has been merged
|
||||
export async function submitTransaction(
|
||||
client: Client,
|
||||
secret: string,
|
||||
transaction: Transaction,
|
||||
): Promise<SubmitResponse> {
|
||||
const wallet = Wallet.fromSeed(secret)
|
||||
const tx = await client.autofill(transaction)
|
||||
const signedTxEncoded: string = sign(wallet, tx)
|
||||
return client.request({ command: 'submit', tx_blob: signedTxEncoded })
|
||||
}
|
||||
|
||||
export async function fundAccount(
|
||||
client: Client,
|
||||
account: string,
|
||||
): Promise<void> {
|
||||
const payment: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: masterAccount,
|
||||
Destination: account,
|
||||
// 2 times the amount needed for a new account (20 XRP)
|
||||
Amount: '400000000',
|
||||
}
|
||||
const paymentTx = await client.autofill(payment)
|
||||
verifyPayment(paymentTx)
|
||||
|
||||
const response = await submitTransaction(client, masterSecret, paymentTx)
|
||||
if (response.result.engine_result !== 'tesSUCCESS') {
|
||||
// eslint-disable-next-line no-console -- happens only when something goes wrong
|
||||
console.log(response)
|
||||
assert.fail(`Response not successful, ${response.result.engine_result}`)
|
||||
}
|
||||
|
||||
await ledgerAccept(client)
|
||||
}
|
||||
|
||||
export async function verifySubmittedTransaction(
|
||||
testcase: Mocha.Context,
|
||||
tx: Transaction,
|
||||
options: { minLedgerVersion: number; maxLedgerVersion?: number },
|
||||
): Promise<void> {
|
||||
const hash = computeSignedTransactionHash(tx)
|
||||
const data = await testcase.client.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
min_ledger: options.minLedgerVersion,
|
||||
max_ledger: options.maxLedgerVersion,
|
||||
})
|
||||
|
||||
assert(data.result)
|
||||
assert.deepEqual(
|
||||
_.omit(data.result, [
|
||||
'date',
|
||||
'hash',
|
||||
'inLedger',
|
||||
'ledger_index',
|
||||
'meta',
|
||||
'validated',
|
||||
]),
|
||||
tx,
|
||||
)
|
||||
if (typeof data.result.meta === 'object') {
|
||||
assert.strictEqual(data.result.meta.TransactionResult, 'tesSUCCESS')
|
||||
} else {
|
||||
assert.strictEqual(data.result.meta, 'tesSUCCESS')
|
||||
}
|
||||
if (testcase.transactions != null) {
|
||||
testcase.transactions.push(hash)
|
||||
}
|
||||
}
|
||||
|
||||
export async function testTransaction(
|
||||
testcase: Mocha.Context,
|
||||
lastClosedLedgerVersion: number,
|
||||
txData: Transaction,
|
||||
address = walletAddress,
|
||||
secret = walletSecret,
|
||||
): Promise<void> {
|
||||
assert.strictEqual(txData.Account, address)
|
||||
const client: Client = testcase.client
|
||||
const signedData = sign(Wallet.fromSeed(secret), txData)
|
||||
|
||||
const attemptedResponse = await client.request({
|
||||
command: 'submit',
|
||||
tx_blob: signedData,
|
||||
})
|
||||
const submittedResponse = testcase.test?.title.includes('multisign')
|
||||
? await ledgerAccept(client).then(() => attemptedResponse)
|
||||
: attemptedResponse
|
||||
|
||||
assert.strictEqual(submittedResponse.result.engine_result, 'tesSUCCESS')
|
||||
const options = {
|
||||
minLedgerVersion: lastClosedLedgerVersion,
|
||||
maxLedgerVersion: txData.LastLedgerSequence,
|
||||
}
|
||||
await ledgerAccept(testcase.client)
|
||||
await verifySubmittedTransaction(
|
||||
testcase,
|
||||
decode(signedData) as unknown as Transaction,
|
||||
options,
|
||||
)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
function getAddress() {
|
||||
return 'rQDhz2ZNXmhxzCYwxU6qAbdxsHA4HV45Y2';
|
||||
}
|
||||
|
||||
function getSecret() {
|
||||
return 'shK6YXzwYfnFVn3YZSaMh5zuAddKx';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAddress,
|
||||
getSecret
|
||||
};
|
||||
2
test/integration/wallet.ts
Normal file
2
test/integration/wallet.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const walletAddress = 'rQDhz2ZNXmhxzCYwxU6qAbdxsHA4HV45Y2'
|
||||
export const walletSecret = 'shK6YXzwYfnFVn3YZSaMh5zuAddKx'
|
||||
@@ -131,7 +131,8 @@ describe('Models Utils', function () {
|
||||
},
|
||||
}
|
||||
|
||||
const { tfSetfAuth, tfClearNoRipple, tfClearFreeze } = TrustSetTransactionFlags
|
||||
const { tfSetfAuth, tfClearNoRipple, tfClearFreeze } =
|
||||
TrustSetTransactionFlags
|
||||
const expected: number = tfSetfAuth | tfClearNoRipple | tfClearFreeze
|
||||
|
||||
setTransactionFlagsToNumber(tx)
|
||||
|
||||
Reference in New Issue
Block a user