feat: Jest Test Runner (#2170)

This commit is contained in:
justinr1234
2023-02-03 17:03:07 -06:00
committed by GitHub
parent 5a63f18faf
commit 5fe480ece4
229 changed files with 13497 additions and 17033 deletions

View File

@@ -6,7 +6,12 @@ module.exports = {
parserOptions: {
// Enable linting rules with type information from our tsconfig
tsconfigRootDir: __dirname,
project: ['./tsconfig.eslint.json'],
project: [
'./tsconfig.eslint.json',
'../ripple-binary-codec/tsconfig.eslint.json',
'../ripple-address-codec/tsconfig.eslint.json',
'../ripple-keypairs/tsconfig.eslint.json',
],
// Allow the use of imports / ES modules
sourceType: 'module',
@@ -23,11 +28,13 @@ module.exports = {
node: true,
// Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
es2020: true,
jest: true,
},
plugins: [],
extends: ['@xrplf/eslint-config/base', 'plugin:mocha/recommended'],
extends: ['@xrplf/eslint-config/base'],
rules: {
'multiline-comment-style': 'off',
// Disabled until https://github.com/import-js/eslint-plugin-import/pull/2305 is resolved to
// accomodate this change https://github.com/XRPLF/xrpl.js/pull/2133
'import/no-unused-modules': 'off',
@@ -51,7 +58,6 @@ module.exports = {
// no-shadow has false-positives for enum, @typescript-eslint version fixes that
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'multiline-comment-style': ['error', 'starred-block'],
'jsdoc/check-examples': 'off',
'tsdoc/syntax': 'off',
@@ -74,13 +80,16 @@ module.exports = {
'max-statements': 'off',
// Snippets have logs on console to better understand the working.
'no-console': 'off',
'import/no-extraneous-dependencies': 'off',
},
},
{
files: ['test/**/*.ts'],
rules: {
// Because this project is managed by lerna, dev dependencies are
// hoisted and do not appear in the package.json.
/*
* Because this project is managed by lerna, dev dependencies are
* hoisted and do not appear in the package.json.
*/
'import/no-extraneous-dependencies': 'off',
'node/no-extraneous-import': 'off',
@@ -102,19 +111,12 @@ module.exports = {
// Tests are already in 2 callbacks, so max 3 is pretty restrictive
'max-nested-callbacks': 'off',
// setup/teardown client is easier to do in before/after, even if there is only one testcase
'mocha/no-hooks-for-single-case': 'off',
// messes with fixtures
'consistent-default-export-name/default-import-match-filename': 'off',
},
},
{
files: ['test/client/*.ts'],
rules: {
// Rule does not work with dynamically generated tests.
'mocha/no-setup-in-describe': 'off',
},
},
{
files: ['test/models/*.ts'],

View File

@@ -4,11 +4,16 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
## Unreleased
### Fixed
* Code splitting improvements for lodash
* Fixed missing reason code in websocket implemntation on websocket disconnect
* Fix timeout error in request manager
* Improved typescript typing
### Added
### Changed
* All tests now use the Jest test runner and have been refactored for consistency across all packages
### Deprecated
Wallet.fromMmnemonic()

View File

@@ -0,0 +1,8 @@
// Jest configuration for api
const base = require('../../jest.config.base.js')
module.exports = {
...base,
roots: [...base.roots, '<rootDir>/test'],
displayName: 'xrpl.js',
}

View File

@@ -0,0 +1,14 @@
// the jest.fn() API
import * as jest from 'jest-mock'
// The matchers API
import expect from 'expect'
// Add missing Jest functions
window.test = window.it
window.test.each = (inputs) => (testName, test) =>
inputs.forEach((args) => window.it(testName, () => test(...args)))
window.test.todo = function () {
return undefined
}
window.jest = jest
window.expect = expect

View File

@@ -0,0 +1,34 @@
const webpackConfig = require('./test/webpack.config')[0]()
delete webpackConfig.entry
module.exports = function (config) {
config.set({
plugins: ['karma-webpack', 'karma-jasmine', 'karma-chrome-launcher'],
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
webpack: webpackConfig,
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'build/xrpl-latest.js',
'test/integration/index.ts',
'karma-setup.js',
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'karma-setup.js': ['webpack'],
// Use webpack to bundle our test files
'test/integration/index.ts': ['webpack'],
},
browsers: ['ChromeHeadless'],
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,9 +33,19 @@
"ws": "^8.2.2"
},
"devDependencies": {
"@geut/browser-node-core": "^2.0.13",
"@types/node": "^14.18.36",
"assert-browserify": "^2.0.0",
"browserify-fs": "^1.0.0",
"constants-browserify": "^1.0.0",
"https-browserify": "^1.0.0",
"karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1",
"karma-jasmine": "^5.1.0",
"karma-webpack": "^5.0.0",
"node-polyfill-webpack-plugin": "^2.0.1",
"react": "^18.2.0",
"typedoc": "^0.23.24",
"xrpl-local": "file:./src"
"typedoc": "^0.23.24"
},
"resolutions": {
"elliptic": "^6.5.4"
@@ -48,13 +58,13 @@
"build:browserTests": "webpack --config ./test/webpack.config.js",
"analyze": "run-s build:web --analyze",
"watch": "run-s build:lib --watch",
"clean": "rm -rf dist",
"clean": "rm -rf dist build coverage",
"docgen": "tsc --build tsconfig.docs.json && typedoc && echo js.xrpl.org >> ../../docs/CNAME",
"prepublish": "run-s clean build",
"test": "nyc mocha --config=test/.mocharc.json --exit",
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/integration/**/*.test.ts ./test/integration/*.test.ts",
"test:browser": "npm run build:browserTests && TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/browser/*.ts",
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --config=test/.mocharc.json --watch --reporter dot",
"test": "jest --verbose false --silent=false ./test/**/*.test.ts --testPathIgnorePatterns=./test/integration --testPathIgnorePatterns=./test/fixtures",
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json jest --verbose false --silent=false --runInBand ./test/integration/**/*.test.ts",
"test:browser": "npm run build && npm run build:browserTests && karma start ./karma.config.js --single-run",
"test:watch": "jest --watch --verbose false --silent=false --runInBand ./test/**/*.test.ts --testPathIgnorePatterns=./test/integration --testPathIgnorePatterns=./test/fixtures",
"format": "prettier --write '{src,test}/**/*.ts'",
"lint": "eslint . --ext .ts --max-warnings 0",
"perf": "./scripts/perf_test.sh",

View File

@@ -4,7 +4,7 @@ import {
PaymentChannelCreate,
PaymentChannelClaim,
hashes,
} from '../../dist/npm'
} from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,4 +1,4 @@
import { Client, LedgerResponse, TxResponse } from '../../dist/npm'
import { Client, LedgerResponse, TxResponse } from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -4,7 +4,7 @@ import {
AccountSet,
convertStringToHex,
SignerListSet,
} from '../../dist/npm'
} from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,4 +1,4 @@
import { Client, Payment, PaymentFlags, TrustSet } from '../../dist/npm'
import { Client, Payment, PaymentFlags, TrustSet } from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,4 +1,4 @@
import { Client, Payment, RipplePathFindResponse } from '../../dist/npm'
import { Client, Payment, RipplePathFindResponse } from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,4 +1,4 @@
import { Client, Payment } from '../../dist/npm'
import { Client, Payment } from '../../src'
/**
* When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs.

View File

@@ -4,7 +4,7 @@ import {
EscrowCreate,
EscrowFinish,
isoTimeToRippleTime,
} from '../../dist/npm'
} from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,4 +1,4 @@
import { Client, Payment, SetRegularKey } from '../../dist/npm'
import { Client, Payment, SetRegularKey } from '../../src'
const client = new Client('wss://s.altnet.rippletest.net:51233')

View File

@@ -1,7 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": "./src"
"rootDir": "../../xrpl"
},
"include": ["./src/**/*.ts"]
"include": ["./src/**/*.ts", "../src/**/*.ts", "../src/**/*.json"]
}

View File

@@ -1,4 +1,4 @@
import type { Client } from '..'
import type { Client } from '../client'
import { XRPLFaucetError } from '../errors'
export interface FaucetWallet {

View File

@@ -3,7 +3,7 @@ import { request as httpsRequest, RequestOptions } from 'https'
import { isValidClassicAddress } from 'ripple-address-codec'
import type { Client } from '..'
import type { Client } from '../client'
import { RippledError, XRPLFaucetError } from '../errors'
import {

View File

@@ -2,7 +2,7 @@
import BigNumber from 'bignumber.js'
import { fromSeed } from 'bip32'
import { mnemonicToSeedSync, validateMnemonic } from 'bip39'
import _ from 'lodash'
import isEqual from 'lodash/isEqual'
import {
classicAddressToXAddress,
isValidXAddress,
@@ -498,7 +498,7 @@ class Wallet {
})
/* eslint-enable @typescript-eslint/consistent-type-assertions -- Done with dynamic checking */
if (!_.isEqual(decoded, txCopy)) {
if (!isEqual(decoded, txCopy)) {
const data = {
decoded,
tx,

View File

@@ -20,7 +20,7 @@ export default class RequestManager {
{
resolve: (value: Response | PromiseLike<Response>) => void
reject: (value: Error) => void
timer: NodeJS.Timeout
timer: ReturnType<typeof setTimeout>
}
>()
@@ -34,7 +34,10 @@ export default class RequestManager {
public resolve(id: string | number, response: Response): void {
const promise = this.promisesAwaitingResponse.get(id)
if (promise == null) {
throw new XrplError(`No existing promise with id ${id}`)
throw new XrplError(`No existing promise with id ${id}`, {
type: 'resolve',
response,
})
}
clearTimeout(promise.timer)
promise.resolve(response)
@@ -51,7 +54,10 @@ export default class RequestManager {
public reject(id: string | number, error: Error): void {
const promise = this.promisesAwaitingResponse.get(id)
if (promise == null) {
throw new XrplError(`No existing promise with id ${id}`)
throw new XrplError(`No existing promise with id ${id}`, {
type: 'reject',
error,
})
}
clearTimeout(promise.timer)
// TODO: figure out how to have a better stack trace for an error
@@ -93,20 +99,35 @@ export default class RequestManager {
newId = request.id
}
const newRequest = JSON.stringify({ ...request, id: newId })
const timer = setTimeout(
() => this.reject(newId, new TimeoutError()),
timeout,
)
// Typing required for Jest running in browser
const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
this.reject(
newId,
new TimeoutError(
`Timeout for request: ${JSON.stringify(request)} with id ${newId}`,
request,
),
)
}, timeout)
/*
* Node.js won't exit if a timer is still running, so we tell Node to ignore.
* (Node will still wait for the request to complete).
*/
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Reason above.
if (timer.unref) {
timer.unref()
// The following type assertions are required to get this code to pass in browser environments
// where setTimeout has a different type
// eslint-disable-next-line max-len -- Necessary to disable both rules.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- Reason above.
if ((timer as unknown as any).unref) {
// eslint-disable-next-line max-len -- Necessary to disable both rules.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call -- Reason above.
;(timer as unknown as any).unref()
}
if (this.promisesAwaitingResponse.has(newId)) {
throw new XrplError(`Response with id '${newId}' is already pending`)
clearTimeout(timer)
throw new XrplError(
`Response with id '${newId}' is already pending`,
request,
)
}
const newPromise = new Promise<Response>(
(resolve: (value: Response | PromiseLike<Response>) => void, reject) => {

View File

@@ -3,13 +3,13 @@ import { EventEmitter } from 'events'
// Define the global WebSocket class found on the native browser
declare class WebSocket {
public onclose?: () => void
public onopen?: () => void
public onclose?: (closeEvent: CloseEvent) => void
public onopen?: (openEvent: Event) => void
public onerror?: (error: Error) => void
public onmessage?: (message: MessageEvent) => void
public readyState: number
public constructor(url: string)
public close(code?: number): void
public close(code?: number, reason?: Buffer): void
public send(message: string): void
}
@@ -52,8 +52,13 @@ export default class WSWrapper extends EventEmitter {
this.ws = new WebSocket(url)
this.ws.onclose = (): void => {
this.emit('close')
this.ws.onclose = (closeEvent: CloseEvent): void => {
let reason: Uint8Array | undefined
if (closeEvent.reason) {
const enc = new TextEncoder()
reason = enc.encode(closeEvent.reason)
}
this.emit('close', closeEvent.code, reason)
}
this.ws.onopen = (): void => {
@@ -71,10 +76,13 @@ export default class WSWrapper extends EventEmitter {
/**
* Closes the websocket.
*
* @param code - Close code.
* @param reason - Close reason.
*/
public close(): void {
public close(code?: number, reason?: Buffer): void {
if (this.readyState === 1) {
this.ws.close()
this.ws.close(code, reason)
}
}

View File

@@ -1,8 +1,9 @@
/* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */
import { EventEmitter } from 'events'
import { Agent } from 'http'
import _ from 'lodash'
import omitBy from 'lodash/omitBy'
import WebSocket from 'ws'
import {
@@ -63,7 +64,7 @@ function getAgent(url: string, config: ConnectionOptions): Agent | undefined {
const parsedURL = new URL(url)
const parsedProxyURL = new URL(config.proxy)
const proxyOptions = _.omitBy(
const proxyOptions = omitBy(
{
secureEndpoint: parsedURL.protocol === 'wss:',
secureProxy: parsedProxyURL.protocol === 'https:',
@@ -125,7 +126,7 @@ function createWebSocket(
Authorization: `Basic ${base64}`,
}
}
const optionsOverrides = _.omitBy(
const optionsOverrides = omitBy(
{
ca: config.trustedCertificates,
key: config.key,
@@ -175,8 +176,10 @@ async function websocketSendAsync(
export class Connection extends EventEmitter {
private readonly url: string | undefined
private ws: WebSocket | null = null
private reconnectTimeoutID: null | NodeJS.Timeout = null
private heartbeatIntervalID: null | NodeJS.Timeout = null
// Typing necessary for Jest tests running in browser
private reconnectTimeoutID: null | ReturnType<typeof setTimeout> = null
// Typing necessary for Jest tetsts running in browser
private heartbeatIntervalID: null | ReturnType<typeof setTimeout> = null
private readonly retryConnectionBackoff = new ExponentialBackoff({
min: 100,
max: SECONDS_PER_MINUTE * 1000,
@@ -224,6 +227,7 @@ export class Connection extends EventEmitter {
* @returns When the websocket is connected.
* @throws ConnectionError if there is a connection error, RippleError if there is already a WebSocket in existence.
*/
// eslint-disable-next-line max-lines-per-function -- Necessary
public async connect(): Promise<void> {
if (this.isConnected()) {
return Promise.resolve()
@@ -245,14 +249,17 @@ export class Connection extends EventEmitter {
}
// Create the connection timeout, in case the connection hangs longer than expected.
const connectionTimeoutID = setTimeout(() => {
this.onConnectionFailed(
new ConnectionError(
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
`rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
),
)
}, this.config.connectionTimeout)
const connectionTimeoutID: ReturnType<typeof setTimeout> = setTimeout(
() => {
this.onConnectionFailed(
new ConnectionError(
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
`rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
),
)
},
this.config.connectionTimeout,
)
// Connection listeners: these stay attached only until a connection is done/open.
this.ws = createWebSocket(this.url, this.config)
@@ -337,7 +344,7 @@ export class Connection extends EventEmitter {
timeout?: number,
): Promise<unknown> {
if (!this.shouldBeConnected || this.ws == null) {
throw new NotConnectedError()
throw new NotConnectedError(JSON.stringify(request), request)
}
const [id, message, responsePromise] = this.requestManager.createRequest(
request,
@@ -429,7 +436,9 @@ export class Connection extends EventEmitter {
* @throws Error if the websocket initialized is somehow null.
*/
// eslint-disable-next-line max-lines-per-function -- Many error code conditionals to check.
private async onceOpen(connectionTimeoutID: NodeJS.Timeout): Promise<void> {
private async onceOpen(
connectionTimeoutID: ReturnType<typeof setTimeout>,
): Promise<void> {
if (this.ws == null) {
throw new XrplError('onceOpen: ws is null')
}
@@ -458,13 +467,14 @@ export class Connection extends EventEmitter {
this.ws = null
if (code === undefined) {
const reasonText = reason ? reason.toString() : 'undefined'
// eslint-disable-next-line no-console -- The error is helpful for debugging.
console.error(
`Disconnected but the disconnect code was undefined (The given reason was ${reasonText}).` +
`This could be caused by an exception being thrown during a 'connect' callback. ` +
`Disconnecting with code 1011 to indicate an internal error has occurred.`,
)
// Useful to keep this code for debugging purposes.
// const reasonText = reason ? reason.toString() : 'undefined'
// // eslint-disable-next-line no-console -- The error is helpful for debugging.
// console.error(
// `Disconnected but the disconnect code was undefined (The given reason was ${reasonText}).` +
// `This could be caused by an exception being thrown during a 'connect' callback. ` +
// `Disconnecting with code 1011 to indicate an internal error has occurred.`,
// )
/*
* Error code 1011 represents an Internal Error according to

View File

@@ -454,6 +454,10 @@ class Client extends EventEmitter {
event: 'consensusPhase',
listener: (phase: ConsensusStream) => void,
): this
public on(
event: 'manifestReceived',
listener: (manifest: ManifestResponse) => void,
): this
public on(event: 'path_find', listener: (path: PathFindStream) => void): this
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
public on(event: 'error', listener: (...err: any[]) => void): this

View File

@@ -108,6 +108,10 @@ export interface ResponseOnlyTxInfo {
* The sequence number of the ledger that included this transaction.
*/
ledger_index?: number
/**
* @deprecated Alias for ledger_index.
*/
inLedger?: number
}
/**

View File

@@ -49,7 +49,7 @@ export interface LedgerDataRequest extends BaseRequest {
type LabeledLedgerEntry = { ledgerEntryType: string } & LedgerEntry
interface BinaryLedgerEntry {
export interface BinaryLedgerEntry {
data: string
}

View File

@@ -33,6 +33,14 @@ export interface JobType {
in_progress?: number
}
// The states for validating and proposing do not exist in the field state_accounting
// See https://github.com/XRPLF/rippled/blob/develop/src/ripple/app/misc/NetworkOPs.cpp#L4545
// https://github.com/XRPLF/rippled/blob/develop/src/ripple/app/misc/NetworkOPs.h#L66
export type StateAccountingFinal = Record<
Exclude<ServerState, 'validating' | 'proposing'>,
StateAccounting
>
/**
* Response expected from a {@link ServerInfoRequest}.
*
@@ -158,6 +166,14 @@ export interface ServerInfoResponse extends BaseResponse {
* cost.
*/
load_factor_server?: number
/**
* The number of peer connections which were severed.
*/
peer_disconnects?: string
/**
* The number of peer connections which were severed due to excess resource consumption.
*/
peer_disconnects_resources?: string
network_ledger?: 'waiting'
/** How many other rippled servers this one is currently connected to. */
peers: number
@@ -179,13 +195,13 @@ export interface ServerInfoResponse extends BaseResponse {
* The number of consecutive microseconds the server has been in the
* current state.
*/
server_state_duration_us: number
server_state_duration_us: string
/**
* A map of various server states with information about the time the
* server spends in each. This can be useful for tracking the long-term
* health of your server's connectivity to the network.
*/
state_accounting: Record<ServerState, StateAccounting>
state_accounting: StateAccountingFinal
/** The current time in UTC, according to the server's clock. */
time: string
/** Number of consecutive seconds that the server has been operational. */
@@ -227,6 +243,11 @@ export interface ServerInfoResponse extends BaseResponse {
* static validator list.
*/
validator_list_expires?: string
validator_list?: {
count: number
expiration: 'never' | 'unknown' | string
status: 'active' | 'expired' | 'unknown'
}
}
}
}

View File

@@ -1,5 +1,5 @@
import { BaseRequest, BaseResponse } from './baseMethod'
import { JobType, ServerState, StateAccounting } from './serverInfo'
import { JobType, ServerState, StateAccountingFinal } from './serverInfo'
/**
* The `server_state` command asks the server for various machine-readable
@@ -35,7 +35,10 @@ export interface ServerStateResponse extends BaseResponse {
io_latency_ms: number
jq_trans_overflow: string
last_close: {
converge_time_s: number
// coverage_time_s only exists for `server_info` requests. `server_state` is a "non human" api request,
// therefore the type is coverage_time
// See https://github.com/XRPLF/rippled/blob/83faf43140e27e5d6d6779eaa0ffb75c33d98029/src/ripple/app/misc/NetworkOPs.cpp#L2458
converge_time: number
proposers: number
}
load?: {
@@ -48,24 +51,27 @@ export interface ServerStateResponse extends BaseResponse {
load_factor_fee_queue?: number
load_factor_fee_reference?: number
load_factor_server?: number
peer_disconnects?: string
peer_disconnects_resources?: string
peers: number
pubkey_node: string
pubkey_validator?: string
server_state: ServerState
server_state_duration_us: number
state_accounting: Record<ServerState, StateAccounting>
server_state_duration_us: string
state_accounting: StateAccountingFinal
time: string
uptime: number
validated_ledger?: {
age: number
age?: number
base_fee: number
close_time: number
hash: string
reserve_base: number
reserve_inc: number
seq: number
}
validation_quorum: number
validator_list_expires?: string
validator_list_expires?: number
}
}
}

View File

@@ -53,12 +53,14 @@ export function validateCheckCash(tx: Record<string, unknown>): void {
)
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
if (tx.Amount != null && tx.Amount !== undefined && !isAmount(tx.Amount)) {
throw new ValidationError('CheckCash: invalid Amount')
}
if (
tx.DeliverMin != null &&
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
tx.DeliverMin !== undefined &&
!isAmount(tx.DeliverMin)
) {

View File

@@ -1,7 +1,8 @@
/* eslint-disable complexity -- verifies 19 tx types hence a lot of checks needed */
/* eslint-disable max-lines-per-function -- need to work with a lot of Tx verifications */
import _ from 'lodash'
import isEqual from 'lodash/isEqual'
import omitBy from 'lodash/omitBy'
import { encode, decode } from 'ripple-binary-codec'
import { ValidationError } from '../../errors'
@@ -210,9 +211,9 @@ export function validate(transaction: Record<string, unknown>): void {
}
if (
!_.isEqual(
!isEqual(
decode(encode(tx)),
_.omitBy(tx, (value) => value == null),
omitBy(tx, (value) => value == null),
)
) {
throw new ValidationError(`Invalid Transaction: ${tx.TransactionType}`)

View File

@@ -1,4 +1,4 @@
import _ from 'lodash'
import flatMap from 'lodash/flatMap'
import type { Client } from '..'
import { LedgerIndex } from '../models/common'
@@ -111,7 +111,7 @@ async function getBalances(
// combine results
await Promise.all([xrpPromise, linesPromise]).then(
([xrpBalance, linesResponses]) => {
const accountLinesBalance = _.flatMap(linesResponses, (response) =>
const accountLinesBalance = flatMap(linesResponses, (response) =>
formatBalances(response.result.lines),
)
if (xrpBalance !== '') {

View File

@@ -1,8 +1,9 @@
/* eslint-disable max-lines-per-function -- Needs to process orderbooks. */
import BigNumber from 'bignumber.js'
import _ from 'lodash'
import flatMap from 'lodash/flatMap'
import type { Client } from '../client'
import { ValidationError } from '../errors'
import { LedgerIndex } from '../models/common'
import { OfferFlags } from '../models/ledger/Offer'
import {
@@ -22,6 +23,13 @@ function sortOffers(offers: BookOffer[]): BookOffer[] {
})
}
const getOrderbookOptionsSet = new Set([
'limit',
'ledger_index',
'ledger_hash',
'taker',
])
/**
* Fetch orderbook (buy/sell orders) between two accounts.
*
@@ -40,7 +48,7 @@ function sortOffers(offers: BookOffer[]): BookOffer[] {
* the order book. Defaults to 20.
* @returns An object containing buy and sell objects.
*/
// eslint-disable-next-line max-params -- Once bound to Client, getOrderbook only has 3 parameters.
// eslint-disable-next-line max-params, complexity -- Once bound to Client, getOrderbook only has 3 parameters.
async function getOrderbook(
this: Client,
takerPays: TakerAmount,
@@ -48,21 +56,60 @@ async function getOrderbook(
options: {
limit?: number
ledger_index?: LedgerIndex
ledger_hash?: string
taker?: string
ledger_hash?: string | null
taker?: string | null
} = {},
): Promise<{
buy: BookOffer[]
sell: BookOffer[]
}> {
Object.keys(options).forEach((key) => {
if (!getOrderbookOptionsSet.has(key)) {
throw new ValidationError(`Unexpected option: ${key}`, options)
}
})
if (options.limit && typeof options.limit !== 'number') {
throw new ValidationError('limit must be a number', options.limit)
}
if (
options.ledger_index &&
!(
typeof options.ledger_index === 'number' ||
(typeof options.ledger_index === 'string' &&
['validated', 'closed', 'current'].includes(options.ledger_index))
)
) {
throw new ValidationError(
'ledger_index must be a number or a string of "validated", "closed", or "current"',
options.ledger_index,
)
}
if (
options.ledger_hash !== undefined &&
options.ledger_hash !== null &&
typeof options.ledger_hash !== 'string'
) {
throw new ValidationError(
'ledger_hash must be a string',
options.ledger_hash,
)
}
if (options.taker !== undefined && typeof options.taker !== 'string') {
throw new ValidationError('taker must be a string', options.taker)
}
const request: BookOffersRequest = {
command: 'book_offers',
taker_pays: takerPays,
taker_gets: takerGets,
ledger_index: options.ledger_index ?? 'validated',
ledger_hash: options.ledger_hash,
ledger_hash: options.ledger_hash === null ? undefined : options.ledger_hash,
limit: options.limit ?? DEFAULT_LIMIT,
taker: options.taker,
taker: options.taker ? options.taker : undefined,
}
// 2. Make Request
const directOfferResults = await this.requestAll(request)
@@ -70,11 +117,11 @@ async function getOrderbook(
request.taker_pays = takerGets
const reverseOfferResults = await this.requestAll(request)
// 3. Return Formatted Response
const directOffers = _.flatMap(
const directOffers = flatMap(
directOfferResults,
(directOfferResult) => directOfferResult.result.offers,
)
const reverseOffers = _.flatMap(
const reverseOffers = flatMap(
reverseOfferResults,
(reverseOfferResult) => reverseOfferResult.result.offers,
)

View File

@@ -1,5 +1,6 @@
import BigNumber from 'bignumber.js'
import _ from 'lodash'
import flatten from 'lodash/flatten'
import groupBy from 'lodash/groupBy'
import { Amount, IssuedCurrencyAmount } from '../models/common'
import { TransactionMetadata, Node } from '../models/transactions/metadata'
@@ -63,7 +64,7 @@ function groupByAccount(balanceChanges: BalanceChange[]): Array<{
account: string
balances: Balance[]
}> {
const grouped = _.groupBy(balanceChanges, (node) => node.account)
const grouped = groupBy(balanceChanges, (node) => node.account)
return Object.entries(grouped).map(([account, items]) => {
return { account, balances: items.map((item) => item.balance) }
})
@@ -186,5 +187,5 @@ export default function getBalanceChanges(
}
return []
})
return groupByAccount(_.flatten(quantities))
return groupByAccount(flatten(quantities))
}

View File

@@ -1,5 +1,5 @@
import binary from 'ripple-binary-codec'
import keypairs from 'ripple-keypairs'
import { encodeForSigningClaim } from 'ripple-binary-codec'
import { sign } from 'ripple-keypairs'
import { xrpToDrops } from './xrpConversion'
@@ -17,11 +17,11 @@ function signPaymentChannelClaim(
amount: string,
privateKey: string,
): string {
const signingData = binary.encodeForSigningClaim({
const signingData = encodeForSigningClaim({
channel,
amount: xrpToDrops(amount),
})
return keypairs.sign(signingData, privateKey)
return sign(signingData, privateKey)
}
export default signPaymentChannelClaim

View File

@@ -1,5 +1,5 @@
import binary from 'ripple-binary-codec'
import keypairs from 'ripple-keypairs'
import { encodeForSigningClaim } from 'ripple-binary-codec'
import { verify } from 'ripple-keypairs'
import { xrpToDrops } from './xrpConversion'
@@ -20,11 +20,11 @@ function verifyPaymentChannelClaim(
signature: string,
publicKey: string,
): boolean {
const signingData = binary.encodeForSigningClaim({
const signingData = encodeForSigningClaim({
channel,
amount: xrpToDrops(amount),
})
return keypairs.verify(signingData, signature, publicKey)
return verify(signingData, signature, publicKey)
}
export default verifyPaymentChannelClaim

View File

@@ -1,5 +1,6 @@
import { assert } from 'chai'
import ExponentialBackoff from 'xrpl-local/client/ExponentialBackoff'
import ExponentialBackoff from '../src/client/ExponentialBackoff'
describe('ExponentialBackoff', function () {
it('duration() return value starts with the min value', function () {

View File

@@ -1,46 +0,0 @@
import { assert } from 'chai'
import _ from 'lodash'
import { ServerInfoResponse } from 'xrpl-local'
import responses from './fixtures/responses'
import rippled from './fixtures/rippled'
import { setupBroadcast, teardownClient } from './setupClient'
import { assertResultMatch, ignoreWebSocketDisconnect } from './testUtils'
const TIMEOUT = 20000
describe('BroadcastClient', function () {
this.timeout(TIMEOUT)
beforeEach(setupBroadcast)
afterEach(teardownClient)
it('base', async function () {
this.mocks.forEach((mock) => {
mock.addResponse('server_info', rippled.server_info.normal)
})
assert(this.client.isConnected())
this.client
.request({ command: 'server_info' })
.then((response: ServerInfoResponse) => {
assertResultMatch(responses.getServerInfo, response.result.info)
})
})
it('error propagation', function (done) {
const data = { error: 'type', error_message: 'info' }
this.mocks.forEach((mock) => {
mock.addResponse('echo', data)
})
this.client.once('error', (type, info) => {
assert.strictEqual(type, 'type')
assert.strictEqual(info, 'info')
done()
})
this.client.clients[1].connection
.request({
command: 'echo',
data,
})
.catch(ignoreWebSocketDisconnect)
})
})

View File

@@ -1,101 +0,0 @@
/* eslint-disable no-console -- Logging out errors. */
import path from 'path'
import { expect, assert } from 'chai'
import puppeteer from 'puppeteer'
const TIMEOUT = 150000
interface TestCaseInfo {
name: string
span: string
error?: string
}
function getCountAndDisplayError(result): number {
let count = 0
for (const testCase of result.test) {
if (Object.prototype.hasOwnProperty.call(testCase, 'error')) {
count += 1
console.log(
`${count})`,
result.type,
JSON.stringify(testCase, null, '\t'),
)
}
}
return count
}
describe('Browser Tests', function () {
this.timeout(TIMEOUT)
it('Integration Tests', async function () {
const browser = await puppeteer.launch({ headless: true })
let mocha_results
try {
const page = await browser.newPage().catch()
page.setDefaultNavigationTimeout(0)
await page.goto(
path.join('file:///', __dirname, '../localIntegrationRunner.html'),
)
await page.waitForFunction(
'document.querySelector("body").innerText.includes("closing test")',
{ timeout: TIMEOUT },
)
mocha_results = await page.evaluate(() => {
const results: Array<{ type: string; test: TestCaseInfo[] }> = []
const items = document.querySelectorAll('.suite')
items.forEach((item) => {
const tests = item.querySelectorAll('li')
const cases: TestCaseInfo[] = []
tests.forEach((testCase) => {
cases.push({
name: testCase.querySelector('h2')?.outerText as string,
span: testCase.querySelector('.duration')?.textContent as string,
error: testCase.querySelector('.error')?.textContent as string,
})
})
results.push({
type: item.querySelector('h1')!.textContent as string,
test: cases,
})
})
return results
})
const fails = await page.evaluate(() => {
const element = document.querySelector('.failures')
return element == null ? null : element.textContent
})
const passes = await page.evaluate(() => {
const element = document.querySelector('.passes')
return element == null ? null : element.textContent
})
expect(fails).to.equal('failures: 0')
expect(passes).to.not.equal('passes: 0')
} catch {
// '\x1b[31m' specifies that console text will be displayed in color red here on.
console.log('\x1b[31m', 'Failed Tests:')
let count = 0
for (const result of mocha_results) {
count += getCountAndDisplayError(result)
}
// '\x1b[0m' specifies that console text color will be reset.
console.log(
`Total ${count} test${count === 1 ? '' : 's'} failed. \n`,
'\x1b[0m',
)
// we would always want the number of failing tests to be zero.
assert.equal(0, count)
} finally {
await browser.close()
}
}).timeout(TIMEOUT)
})

View File

@@ -1,37 +0,0 @@
import { assert } from 'chai'
import _ from 'lodash'
import { Client } from 'xrpl-local'
// how long before each test case times out
const TIMEOUT = 20000
describe('Client', function () {
this.timeout(TIMEOUT)
it('Client - implicit server port', function () {
// eslint-disable-next-line no-new -- Need to test constructor
new Client('wss://s1.ripple.com')
})
it('Client invalid options', function () {
// @ts-expect-error - This is intentionally invalid
assert.throws(() => new Client({ invalid: true }))
})
it('Client valid options', function () {
const client = new Client('wss://s:1')
const privateConnectionUrl = client.url
assert.deepEqual(privateConnectionUrl, 'wss://s:1')
})
it('Client invalid server uri', function () {
assert.throws(() => new Client('wss//s:1'))
})
it('Client 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.
*/
})
})

View File

@@ -1,14 +1,18 @@
import { assert } from 'chai'
import {
XrplError,
AccountDelete,
EscrowFinish,
Payment,
Transaction,
} from 'xrpl-local'
} from '../../src'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { assertRejects } from '../testUtils'
const Fee = '10'
@@ -16,8 +20,12 @@ const Sequence = 1432
const LastLedgerSequence = 2908734
describe('client.autofill', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('should not autofill if fields are present', async function () {
const tx: Transaction = {
@@ -28,7 +36,7 @@ describe('client.autofill', function () {
Sequence,
LastLedgerSequence,
}
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Fee, Fee)
assert.strictEqual(txResult.Sequence, Sequence)
@@ -42,11 +50,17 @@ describe('client.autofill', function () {
Amount: '1234',
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Account, 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf')
assert.strictEqual(
@@ -63,7 +77,7 @@ describe('client.autofill', function () {
Fee,
LastLedgerSequence,
}
this.mockRippled.addResponse('account_info', {
testContext.mockRippled!.addResponse('account_info', {
status: 'success',
type: 'response',
result: {
@@ -72,16 +86,22 @@ describe('client.autofill', function () {
},
},
})
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Sequence, 23)
})
it('should throw error if account deletion blockers exist', async function () {
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
testContext.mockRippled!.addResponse(
'account_objects',
rippled.account_objects.normal,
)
@@ -95,7 +115,7 @@ describe('client.autofill', function () {
LastLedgerSequence,
}
await assertRejects(this.client.autofill(tx), XrplError)
await assertRejects(testContext.client.autofill(tx), XrplError)
})
describe('when autofill Fee is missing', function () {
@@ -107,8 +127,11 @@ describe('client.autofill', function () {
Sequence,
LastLedgerSequence,
}
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
const txResult = await this.client.autofill(tx)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Fee, '12')
})
@@ -123,11 +146,17 @@ describe('client.autofill', function () {
'A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100',
Fulfillment: 'A0028000',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Fee, '399')
})
@@ -137,9 +166,12 @@ describe('client.autofill', function () {
TransactionType: 'AccountDelete',
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_state', {
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse('server_state', {
status: 'success',
type: 'response',
result: {
@@ -150,12 +182,15 @@ describe('client.autofill', function () {
},
},
})
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
testContext.mockRippled!.addResponse(
'account_objects',
rippled.account_objects.empty,
)
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Fee, '2000000')
})
@@ -170,10 +205,16 @@ describe('client.autofill', function () {
'A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100',
Fulfillment: 'A0028000',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
const txResult = await this.client.autofill(tx, 4)
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
const txResult = await testContext.client.autofill(tx, 4)
assert.strictEqual(txResult.Fee, '459')
})
@@ -187,14 +228,14 @@ describe('client.autofill', function () {
Fee,
Sequence,
}
this.mockRippled.addResponse('ledger', {
testContext.mockRippled!.addResponse('ledger', {
status: 'success',
type: 'response',
result: {
ledger_index: 9038214,
},
})
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.LastLedgerSequence, 9038234)
})
@@ -204,7 +245,7 @@ describe('client.autofill', function () {
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
}
this.mockRippled.addResponse('account_info', {
testContext.mockRippled!.addResponse('account_info', {
status: 'success',
type: 'response',
result: {
@@ -213,14 +254,14 @@ describe('client.autofill', function () {
},
},
})
this.mockRippled.addResponse('ledger', {
testContext.mockRippled!.addResponse('ledger', {
status: 'success',
type: 'response',
result: {
ledger_index: 9038214,
},
})
this.mockRippled.addResponse('server_info', {
testContext.mockRippled!.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
@@ -231,7 +272,7 @@ describe('client.autofill', function () {
},
},
})
const txResult = await this.client.autofill(tx)
const txResult = await testContext.client.autofill(tx)
assert.strictEqual(txResult.Fee, '12')
assert.strictEqual(txResult.Sequence, 23)
assert.strictEqual(txResult.LastLedgerSequence, 9038234)

View File

@@ -0,0 +1,55 @@
import { assert } from 'chai'
import { Client } from '../../src'
// how long before each test case times out
const TIMEOUT = 20000
describe('Client', function () {
it(
'Client - implicit server port',
() => {
// eslint-disable-next-line no-new -- Need to test constructor
new Client('wss://s1.ripple.com')
},
TIMEOUT,
)
it(
'Client invalid options',
() => {
// @ts-expect-error - This is intentionally invalid
assert.throws(() => new Client({ invalid: true }))
},
TIMEOUT,
)
it(
'Client valid options',
() => {
const client = new Client('wss://s:1')
const privateConnectionUrl = client.url
assert.deepEqual(privateConnectionUrl, 'wss://s:1')
},
TIMEOUT,
)
it(
'Client invalid server uri',
() => {
assert.throws(() => new Client('wss//s:1'))
},
TIMEOUT,
)
// it(
// 'Client connect() times out after 2 seconds',
// () => {
// /*
// * TODO: Use a timer mock like https://jestjs.io/docs/en/timer-mocks
// * to test that connect() times out after 2 seconds.
// */
// },
// TIMEOUT,
// )
})

View File

@@ -1,5 +1,6 @@
import { assert } from 'chai'
import { Client } from 'xrpl-local'
import { Client } from '../../src/client'
describe('client constructor', function () {
it('Client - implicit server port', function () {

View File

@@ -1,12 +1,8 @@
import { assert } from 'chai'
import { XrplError, NotFoundError } from 'xrpl-local'
import { setupClient, teardownClient } from '../setupClient'
import { XrplError, NotFoundError } from '../../src'
describe('client errors', function () {
beforeEach(setupClient)
afterEach(teardownClient)
it('XrplError with data', async function () {
const error = new XrplError('_message_', '_data_')
assert.strictEqual(error.toString(), "[XrplError(_message_, '_data_')]")

View File

@@ -1,7 +1,11 @@
import responses from '../fixtures/responses'
import rippled from '../fixtures/rippled'
import rippledAccountLines from '../fixtures/rippled/accountLines'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { assertResultMatch, addressTests } from '../testUtils'
/**
@@ -10,22 +14,26 @@ import { assertResultMatch, addressTests } from '../testUtils'
* - Check out "test/client/index.ts" for more information about the test runner.
*/
describe('client.getBalances', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
addressTests.forEach(function (testcase) {
describe(testcase.type, function () {
describe(testcase.type, () => {
it('getBalances - base', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_lines',
rippledAccountLines.normal,
)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
const result = await this.client.getBalances(testcase.address)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const result = await testContext.client.getBalances(testcase.address)
assertResultMatch(result, responses.getBalances, 'getBalances')
})
@@ -36,20 +44,20 @@ describe('client.getBalances', function () {
limit: 10,
},
}
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_lines',
rippledAccountLines.normal,
)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const expectedResponse = responses.getBalances.slice(
0,
request.options.limit,
)
const result = await this.client.getBalances(
const result = await testContext.client.getBalances(
request.account,
request.options,
)
@@ -60,20 +68,23 @@ describe('client.getBalances', function () {
const options = {
peer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
}
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_lines',
rippledAccountLines.normal,
)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const expectedResponse = responses.getBalances.filter(
(item) => item.issuer === options.peer,
)
const result = await this.client.getBalances(testcase.address, options)
const result = await testContext.client.getBalances(
testcase.address,
options,
)
assertResultMatch(result, expectedResponse, 'getBalances')
})
@@ -82,20 +93,23 @@ describe('client.getBalances', function () {
peer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
limit: 10,
}
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_lines',
rippledAccountLines.normal,
)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const expectedResponse = responses.getBalances
.filter((item) => item.issuer === options.peer)
.slice(0, options.limit)
const result = await this.client.getBalances(testcase.address, options)
const result = await testContext.client.getBalances(
testcase.address,
options,
)
assertResultMatch(result, expectedResponse, 'getBalances')
})
})

View File

@@ -2,29 +2,40 @@ import { assert } from 'chai'
import getFeeXrp from '../../src/sugar/getFeeXrp'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
describe('getFeeXrp', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('getFeeXrp', async function () {
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
const fee = await getFeeXrp(this.client)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '0.000012')
})
it('getFeeXrp - high load_factor', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.highLoadFactor,
)
const fee = await getFeeXrp(this.client)
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '2')
})
it('getFeeXrp - high load_factor with custom maxFeeXRP', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.highLoadFactor,
)
@@ -33,15 +44,20 @@ describe('getFeeXrp', function () {
* Ensure that overriding with high maxFeeXRP of '51540' causes no errors.
* (fee will actually be 51539.607552)
*/
this.client.maxFeeXRP = '51540'
const fee = await getFeeXrp(this.client)
// @ts-expect-error Manually setting this for the purpose of testing
testContext.client.maxFeeXRP = '51540'
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '51539.607552')
})
it('getFeeXrp custom cushion', async function () {
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.client.feeCushion = 1.4
const fee = await getFeeXrp(this.client)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
// @ts-expect-error Manually setting this for the purpose of testing
testContext.client.feeCushion = 1.4
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '0.000014')
})
@@ -50,15 +66,22 @@ describe('getFeeXrp', function () {
* less than the base fee. However, this test verifies the existing behavior.
*/
it('getFeeXrp cushion less than 1.0', async function () {
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.client.feeCushion = 0.9
const fee = await getFeeXrp(this.client)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
// @ts-expect-error Manually setting this for the purpose of testing
testContext.client.feeCushion = 0.9
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '0.000009')
})
it('getFeeXrp reporting', async function () {
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
const fee = await getFeeXrp(this.client)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
const fee = await getFeeXrp(testContext.client)
assert.strictEqual(fee, '0.000012')
})
})

View File

@@ -1,15 +1,23 @@
import { assert } from 'chai'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
describe('client.getLedgerIndex', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('getLedgerIndex', async function () {
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
const ledgerIndex = await this.client.getLedgerIndex()
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const ledgerIndex = await testContext.client.getLedgerIndex()
assert.strictEqual(ledgerIndex, 9038214)
})
})

View File

@@ -1,13 +1,17 @@
import BigNumber from 'bignumber.js'
import { assert } from 'chai'
import { BookOffersRequest } from 'xrpl-local'
import { ValidationError, XrplError } from 'xrpl-local/errors'
import { OfferFlags } from 'xrpl-local/models/ledger'
import { BookOffersRequest, type Request } from '../../src'
import { ValidationError, XrplError } from '../../src/errors'
import { OfferFlags } from '../../src/models/ledger'
import requests from '../fixtures/requests'
import responses from '../fixtures/responses'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { assertResultMatch, assertRejects } from '../testUtils'
function checkSortingOfOrders(orders): void {
@@ -37,42 +41,48 @@ function isBTC(currency: string): boolean {
)
}
function normalRippledResponse(
request: BookOffersRequest,
): Record<string, unknown> {
function normalRippledResponse(request: Request): Record<string, unknown> {
if (
isBTC(request.taker_gets.currency) &&
isUSD(request.taker_pays.currency)
isBTC((request as BookOffersRequest).taker_gets.currency) &&
isUSD((request as BookOffersRequest).taker_pays.currency)
) {
return rippled.book_offers.fabric.requestBookOffersBidsResponse(request)
}
if (
isUSD(request.taker_gets.currency) &&
isBTC(request.taker_pays.currency)
isUSD((request as BookOffersRequest).taker_gets.currency) &&
isBTC((request as BookOffersRequest).taker_pays.currency)
) {
return rippled.book_offers.fabric.requestBookOffersAsksResponse(request)
}
throw new XrplError('unexpected end')
}
function xrpRippledResponse(
request: BookOffersRequest,
): Record<string, unknown> {
if (request.taker_pays.issuer === 'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw') {
function xrpRippledResponse(request: Request): Record<string, unknown> {
if (
(request as BookOffersRequest).taker_pays.issuer ===
'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw'
) {
return rippled.book_offers.xrp_usd
}
if (request.taker_gets.issuer === 'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw') {
if (
(request as BookOffersRequest).taker_gets.issuer ===
'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw'
) {
return rippled.book_offers.usd_xrp
}
throw new Error('unexpected end')
}
describe('client.getOrderbook', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('normal', async function () {
this.mockRippled.addResponse('book_offers', normalRippledResponse)
testContext.mockRippled!.addResponse('book_offers', normalRippledResponse)
const request = {
takerPays: requests.getOrderbook.normal.takerPays,
takerGets: requests.getOrderbook.normal.takerGets,
@@ -80,7 +90,7 @@ describe('client.getOrderbook', function () {
limit: 1,
},
}
const response = await this.client.getOrderbook(
const response = await testContext.client.getOrderbook(
request.takerPays,
request.takerGets,
request.options,
@@ -93,22 +103,51 @@ describe('client.getOrderbook', function () {
})
it('invalid options', async function () {
this.mockRippled.addResponse('book_offers', normalRippledResponse)
assertRejects(
this.client.getOrderbook(
requests.getOrderbook.normal.takerPays,
requests.getOrderbook.normal.takerGets,
{
invalid: 'options',
},
const invalidOptions = [
{
option: 'invalid',
},
{
limit: 'invalid',
},
{
ledger_index: 'invalid',
},
{
ledger_hash: 0,
},
{
taker: 0,
},
]
testContext.mockRippled!.addResponse('book_offers', normalRippledResponse)
await Promise.all(
invalidOptions.map(
async (invalidOptionObject) =>
new Promise<void>((resolve) => {
assertRejects(
testContext.client
.getOrderbook(
requests.getOrderbook.normal.takerPays,
requests.getOrderbook.normal.takerGets,
// @ts-expect-error Meant to be invalid for testing purposes
invalidOptionObject,
)
.catch((error) => {
resolve()
throw error
}),
ValidationError,
)
}),
),
ValidationError,
)
})
it('with XRP', async function () {
this.mockRippled.addResponse('book_offers', xrpRippledResponse)
const response = await this.client.getOrderbook(
testContext.mockRippled!.addResponse('book_offers', xrpRippledResponse)
const response = await testContext.client.getOrderbook(
requests.getOrderbook.withXRP.takerPays,
requests.getOrderbook.withXRP.takerGets,
)
@@ -116,8 +155,8 @@ describe('client.getOrderbook', function () {
})
it('sample USD/XRP book has orders sorted correctly', async function () {
this.mockRippled.addResponse('book_offers', xrpRippledResponse)
const response = await this.client.getOrderbook(
testContext.mockRippled!.addResponse('book_offers', xrpRippledResponse)
const response = await testContext.client.getOrderbook(
requests.getOrderbook.withXRP.takerPays,
requests.getOrderbook.withXRP.takerGets,
)
@@ -126,13 +165,13 @@ describe('client.getOrderbook', function () {
})
it('sorted so that best deals come first [failure test]', async function () {
this.mockRippled.addResponse('book_offers', normalRippledResponse)
const response = await this.client.getOrderbook(
testContext.mockRippled!.addResponse('book_offers', normalRippledResponse)
const response = await testContext.client.getOrderbook(
requests.getOrderbook.normal.takerPays,
requests.getOrderbook.normal.takerGets,
)
const buyRates = response.buy.map(async (item) => item.quality as number)
const sellRates = response.sell.map(async (item) => item.quality as number)
const buyRates = response.buy.map(async (item) => Number(item.quality))
const sellRates = response.sell.map(async (item) => Number(item.quality))
// buy and sell orders should be sorted so that the best deals come first
assert.deepEqual(
buyRates.sort((item) => Number(item)),
@@ -145,13 +184,13 @@ describe('client.getOrderbook', function () {
})
it('sorted so that best deals come first [bad test](XRP)', async function () {
this.mockRippled.addResponse('book_offers', xrpRippledResponse)
const response = await this.client.getOrderbook(
testContext.mockRippled!.addResponse('book_offers', xrpRippledResponse)
const response = await testContext.client.getOrderbook(
requests.getOrderbook.withXRP.takerPays,
requests.getOrderbook.withXRP.takerGets,
)
const buyRates = response.buy.map(async (item) => item.quality as number)
const sellRates = response.sell.map(async (item) => item.quality as number)
const buyRates = response.buy.map(async (item) => Number(item.quality))
const sellRates = response.sell.map(async (item) => Number(item.quality))
// buy and sell orders should be sorted so that the best deals come first
assert.deepEqual(
buyRates.sort((item) => Number(item)),
@@ -164,8 +203,8 @@ describe('client.getOrderbook', function () {
})
it('direction is correct for buy and sell', async function () {
this.mockRippled.addResponse('book_offers', normalRippledResponse)
const response = await this.client.getOrderbook(
testContext.mockRippled!.addResponse('book_offers', normalRippledResponse)
const response = await testContext.client.getOrderbook(
requests.getOrderbook.normal.takerPays,
requests.getOrderbook.normal.takerGets,
)
@@ -180,9 +219,9 @@ describe('client.getOrderbook', function () {
})
it('getOrderbook - limit', async function () {
this.mockRippled.addResponse('book_offers', normalRippledResponse)
testContext.mockRippled!.addResponse('book_offers', normalRippledResponse)
const LIMIT = 3
const response = await this.client.getOrderbook(
const response = await testContext.client.getOrderbook(
requests.getOrderbook.normal.takerPays,
requests.getOrderbook.normal.takerGets,
{

View File

@@ -1,7 +1,11 @@
import { assert } from 'chai'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { addressTests } from '../testUtils'
/**
@@ -10,18 +14,22 @@ import { addressTests } from '../testUtils'
* - Check out "test/client/index.ts" for more information about the test runner.
*/
describe('client.getXrpBalance', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
addressTests.forEach(function (testcase) {
describe(testcase.type, function () {
describe(testcase.type, () => {
it('getXrpBalance', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
const result = await this.client.getXrpBalance(testcase.address)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
const result = await testContext.client.getXrpBalance(testcase.address)
assert.equal(result, '922.913243')
})
})

View File

@@ -1,14 +1,24 @@
import { assert } from 'chai'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
describe('client.isConnected', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => {
await teardownClient(testContext)
})
it('disconnect & isConnected', async function () {
assert.strictEqual(this.client.isConnected(), true)
await this.client.disconnect()
assert.strictEqual(this.client.isConnected(), false)
assert.strictEqual(testContext.client.isConnected(), true)
await testContext.client.disconnect()
assert.strictEqual(testContext.client.isConnected(), false)
})
})

View File

@@ -1,29 +1,37 @@
/* eslint-disable @typescript-eslint/no-explicit-any -- required for formatting transactions */
import { expect } from 'chai'
import _ from 'lodash'
import type { TransactionStream } from 'xrpl-local'
import cloneDeep from 'lodash/cloneDeep'
import type { TransactionStream } from '../../src'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
const partialPaymentIOU = rippled.partial_payments.iou
const partialPaymentXRP = rippled.partial_payments.xrp
describe('client handling of tfPartialPayments', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('Tx with no tfPartialPayment', async function () {
this.mockRippled.addResponse('tx', rippled.tx.Payment)
const resp = await this.client.request({ command: 'tx' })
testContext.mockRippled!.addResponse('tx', rippled.tx.Payment)
const resp = await testContext.client.request({ command: 'tx' })
expect(resp.warnings).to.equal(undefined)
})
it('Tx with IOU tfPartialPayment', async function () {
const mockResponse = { ...rippled.tx.Payment, result: partialPaymentIOU }
this.mockRippled.addResponse('tx', mockResponse)
const resp = await this.client.request({ command: 'tx' })
testContext.mockRippled!.addResponse('tx', mockResponse)
const resp = await testContext.client.request({ command: 'tx' })
expect(resp.warnings).to.deep.equal([
{
@@ -35,8 +43,8 @@ describe('client handling of tfPartialPayments', function () {
it('Tx with XRP tfPartialPayment', async function () {
const mockResponse = { ...rippled.tx.Payment, result: partialPaymentXRP }
this.mockRippled.addResponse('tx', mockResponse)
const resp = await this.client.request({ command: 'tx' })
testContext.mockRippled!.addResponse('tx', mockResponse)
const resp = await testContext.client.request({ command: 'tx' })
expect(resp.warnings).to.deep.equal([
{
@@ -47,8 +55,11 @@ describe('client handling of tfPartialPayments', function () {
})
it('account_tx with no tfPartialPayment', async function () {
this.mockRippled.addResponse('account_tx', rippled.account_tx.normal)
const resp = await this.client.request({ command: 'account_tx' })
testContext.mockRippled!.addResponse(
'account_tx',
rippled.account_tx.normal,
)
const resp = await testContext.client.request({ command: 'account_tx' })
expect(resp.warnings).to.equal(undefined)
})
@@ -64,8 +75,8 @@ describe('client handling of tfPartialPayments', function () {
meta: partial.result.meta,
} as any)
this.mockRippled.addResponse('account_tx', mockResponse)
const resp = await this.client.request({
testContext.mockRippled!.addResponse('account_tx', mockResponse)
const resp = await testContext.client.request({
command: 'account_tx',
account: mockResponse.result.account,
})
@@ -87,8 +98,8 @@ describe('client handling of tfPartialPayments', function () {
meta: partial.result.meta,
} as any)
this.mockRippled.addResponse('account_tx', mockResponse)
const resp = await this.client.request({
testContext.mockRippled!.addResponse('account_tx', mockResponse)
const resp = await testContext.client.request({
command: 'account_tx',
account: mockResponse.result.account,
})
@@ -102,17 +113,24 @@ describe('client handling of tfPartialPayments', function () {
})
it('transaction_entry with no tfPartialPayment', async function () {
this.mockRippled.addResponse('transaction_entry', rippled.transaction_entry)
const resp = await this.client.request({ command: 'transaction_entry' })
testContext.mockRippled!.addResponse(
'transaction_entry',
rippled.transaction_entry,
)
const resp = await testContext.client.request({
command: 'transaction_entry',
})
expect(resp.warnings).to.equal(undefined)
})
it('transaction_entry with XRP tfPartialPayment', async function () {
const mockResponse = _.cloneDeep(rippled.transaction_entry)
const mockResponse = cloneDeep(rippled.transaction_entry)
mockResponse.result.tx_json.Amount = '1000'
this.mockRippled.addResponse('transaction_entry', mockResponse)
const resp = await this.client.request({ command: 'transaction_entry' })
testContext.mockRippled!.addResponse('transaction_entry', mockResponse)
const resp = await testContext.client.request({
command: 'transaction_entry',
})
expect(resp.warnings).to.deep.equal([
{
@@ -122,21 +140,28 @@ describe('client handling of tfPartialPayments', function () {
])
})
it('Transactions stream with no tfPartialPayment', async function (done) {
this.mockRippled.addResponse('transaction_entry', rippled.transaction_entry)
this.client.on('transaction', (tx: TransactionStream) => {
it('Transactions stream with no tfPartialPayment', (done) => {
testContext.mockRippled!.addResponse(
'transaction_entry',
rippled.transaction_entry,
)
testContext.client.on('transaction', (tx: TransactionStream) => {
expect(tx.warnings).to.equal(undefined)
done()
})
this.client.connection.onMessage(
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.transaction),
)
})
it('Transactions stream with XRP tfPartialPayment', async function (done) {
this.mockRippled.addResponse('transaction_entry', rippled.transaction_entry)
this.client.on('transaction', (tx: TransactionStream) => {
it('Transactions stream with XRP tfPartialPayment', (done) => {
testContext.mockRippled!.addResponse(
'transaction_entry',
rippled.transaction_entry,
)
testContext.client.on('transaction', (tx: TransactionStream) => {
expect(tx.warnings).to.deep.equal([
{
id: 2001,
@@ -146,7 +171,8 @@ describe('client handling of tfPartialPayments', function () {
done()
})
this.client.connection.onMessage(
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.partialPaymentTransaction),
)
})

View File

@@ -1,20 +1,28 @@
import responses from '../fixtures/responses'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { addressTests, assertResultMatch } from '../testUtils'
describe('client.request', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
addressTests.forEach(function (testcase) {
describe(testcase.type, function () {
describe(testcase.type, () => {
it('request account_objects', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_objects',
rippled.account_objects.normal,
)
const result = await this.client.request({
const result = await testContext.client.request({
command: 'account_objects',
account: testcase.address,
})
@@ -27,11 +35,11 @@ describe('client.request', function () {
})
it('request account_objects - invalid options', async function () {
this.mockRippled.addResponse(
testContext.mockRippled!.addResponse(
'account_objects',
rippled.account_objects.normal,
)
const result = await this.client.request({
const result = await testContext.client.request({
command: 'account_objects',
account: testcase.address,
})

View File

@@ -1,7 +1,12 @@
import { assert } from 'chai'
import type { Request } from '../../src'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
const rippledResponse = function (request: Request): Record<string, unknown> {
if ('marker' in request) {
@@ -11,11 +16,15 @@ const rippledResponse = function (request: Request): Record<string, unknown> {
}
describe('client.requestAll', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('requests the next page', async function () {
this.mockRippled.addResponse('ledger_data', rippledResponse)
const allResponses = await this.client.requestAll({
testContext.mockRippled!.addResponse('ledger_data', rippledResponse)
const allResponses = await testContext.client.requestAll({
command: 'ledger_data',
})
assert.equal(allResponses.length, 2)
@@ -26,8 +35,11 @@ describe('client.requestAll', function () {
})
it('rejects when there are no more pages', async function () {
this.mockRippled.addResponse('ledger_data', rippled.ledger_data.last_page)
const allResponses = await this.client.requestAll({
testContext.mockRippled!.addResponse(
'ledger_data',
rippled.ledger_data.last_page,
)
const allResponses = await testContext.client.requestAll({
command: 'ledger_data',
})
assert.equal(allResponses.length, 1)

View File

@@ -1,8 +1,12 @@
import { assert } from 'chai'
import { hasNextPage } from 'xrpl-local'
import { hasNextPage, type Request } from '../../src'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { assertRejects } from '../testUtils'
const rippledResponse = function (request: Request): Record<string, unknown> {
@@ -13,12 +17,18 @@ const rippledResponse = function (request: Request): Record<string, unknown> {
}
describe('client.requestNextPage', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('requests the next page', async function () {
this.mockRippled.addResponse('ledger_data', rippledResponse)
const response = await this.client.request({ command: 'ledger_data' })
const responseNextPage = await this.client.requestNextPage(
testContext.mockRippled!.addResponse('ledger_data', rippledResponse)
const response = await testContext.client.request({
command: 'ledger_data',
})
const responseNextPage = await testContext.client.requestNextPage(
{ command: 'ledger_data' },
response,
)
@@ -29,15 +39,20 @@ describe('client.requestNextPage', function () {
})
it('rejects when there are no more pages', async function () {
this.mockRippled.addResponse('ledger_data', rippledResponse)
const response = await this.client.request({ command: 'ledger_data' })
const responseNextPage = await this.client.requestNextPage(
testContext.mockRippled!.addResponse('ledger_data', rippledResponse)
const response = await testContext.client.request({
command: 'ledger_data',
})
const responseNextPage = await testContext.client.requestNextPage(
{ command: 'ledger_data' },
response,
)
assert(!hasNextPage(responseNextPage))
await assertRejects(
this.client.requestNextPage({ command: 'ledger_data' }, responseNextPage),
testContext.client.requestNextPage(
{ command: 'ledger_data' },
responseNextPage,
),
Error,
'response does not have a next page',
)

View File

@@ -1,16 +1,24 @@
import { assert } from 'chai'
import _ from 'lodash'
import { ValidationError } from 'xrpl-local'
import { Transaction } from 'xrpl-local/models/transactions'
import Wallet from 'xrpl-local/Wallet'
import cloneDeep from 'lodash/cloneDeep'
import { ValidationError } from '../../src'
import { Transaction } from '../../src/models/transactions'
import Wallet from '../../src/Wallet'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
import { assertRejects } from '../testUtils'
describe('client.submit', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
describe('submit unsigned transactions', function () {
const publicKey =
@@ -29,17 +37,23 @@ describe('client.submit', function () {
}
it('should submit an unsigned transaction', async function () {
const tx = _.cloneDeep(transaction)
const tx = cloneDeep(transaction)
const wallet = new Wallet(publicKey, privateKey)
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.mockRippled.addResponse('submit', rippled.submit.success)
testContext.mockRippled!.addResponse(
'account_info',
rippled.account_info.normal,
)
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
testContext.mockRippled!.addResponse(
'server_info',
rippled.server_info.normal,
)
testContext.mockRippled!.addResponse('submit', rippled.submit.success)
try {
const response = await this.client.submit(tx, { wallet })
const response = await testContext.client.submit(tx, { wallet })
assert(response.result.engine_result, 'tesSUCCESS')
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- error type thrown can be any
@@ -48,14 +62,14 @@ describe('client.submit', function () {
})
it('should throw a ValidationError when submitting an unsigned transaction without a wallet', async function () {
const tx: Transaction = _.cloneDeep(transaction)
const tx: Transaction = cloneDeep(transaction)
delete tx.SigningPubKey
delete tx.TxnSignature
this.mockRippled.addResponse('submit', rippled.submit.success)
testContext.mockRippled!.addResponse('submit', rippled.submit.success)
await assertRejects(
this.client.submit(tx),
testContext.client.submit(tx),
ValidationError,
'Wallet must be provided when submitting an unsigned transaction',
)
@@ -80,10 +94,10 @@ describe('client.submit', function () {
it('should submit a signed transaction', async function () {
const signedTx = { ...signedTransaction }
this.mockRippled.addResponse('submit', rippled.submit.success)
testContext.mockRippled!.addResponse('submit', rippled.submit.success)
try {
const response = await this.client.submit(signedTx)
const response = await testContext.client.submit(signedTx)
assert(response.result.engine_result, 'tesSUCCESS')
} catch (_error) {
assert(false, 'Did not expect an error to be thrown')
@@ -94,10 +108,10 @@ describe('client.submit', function () {
const signedTxEncoded =
'1200002400000001201B00003018614000000001312D0068400000000000000C7321030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D74473045022100B3D311371EDAB371CD8F2B661A04B800B61D4B132E09B7B0712D3B2F11B1758302203906B44C4A150311D74FF6A35B146763C0B5B40AC30BD815113F058AA17B3E6381142AF1861DEC1316AEEC995C94FF9E2165B1B784608314FDB08D07AAA0EB711793A3027304D688E10C3648'
this.mockRippled.addResponse('submit', rippled.submit.success)
testContext.mockRippled!.addResponse('submit', rippled.submit.success)
try {
const response = await this.client.submit(signedTxEncoded)
const response = await testContext.client.submit(signedTxEncoded)
assert(response.result.engine_result, 'tesSUCCESS')
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- error type thrown can be any

View File

@@ -1,7 +1,11 @@
import { assert } from 'chai'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
import {
setupClient,
teardownClient,
type XrplTestContext,
} from '../setupClient'
async function assertDoesNotThrow(promise: Promise<unknown>): Promise<void> {
try {
@@ -14,87 +18,133 @@ async function assertDoesNotThrow(promise: Promise<unknown>): Promise<void> {
}
describe('Client subscription', function () {
beforeEach(setupClient)
afterEach(teardownClient)
let testContext: XrplTestContext
beforeEach(async () => {
testContext = await setupClient()
})
afterEach(async () => teardownClient(testContext))
it('Successfully Subscribes', async function () {
this.mockRippled.addResponse('subscribe', rippled.subscribe.success)
testContext.mockRippled!.addResponse('subscribe', rippled.subscribe.success)
await assertDoesNotThrow(this.client.request({ command: 'subscribe' }))
await assertDoesNotThrow(
testContext.client.request({ command: 'subscribe' }),
)
})
it('Successfully Unsubscribes', async function () {
this.mockRippled.addResponse('unsubscribe', rippled.unsubscribe)
testContext.mockRippled!.addResponse('unsubscribe', rippled.unsubscribe)
await assertDoesNotThrow(
this.client.request({
testContext.client.request({
command: 'unsubscribe',
}),
)
})
it('Emits transaction', async function (done) {
this.client.on('transaction', (tx) => {
assert(tx.type === 'transaction')
done()
})
it('Emits transaction', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('transaction', (tx) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(tx.type === 'transaction')
resolve()
})
this.client.connection.onMessage(
JSON.stringify(rippled.streams.transaction),
)
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.transaction),
)
})
})
it('Emits ledger', async function (done) {
this.client.on('ledgerClosed', (ledger) => {
assert(ledger.type === 'ledgerClosed')
done()
})
it('Emits ledger', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('ledgerClosed', (ledger) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(ledger.type === 'ledgerClosed')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.ledger))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.ledger),
)
})
})
it('Emits peerStatusChange', async function (done) {
this.client.on('peerStatusChange', (status) => {
assert(status.type === 'peerStatusChange')
done()
})
it('Emits peerStatusChange', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('peerStatusChange', (status) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(status.type === 'peerStatusChange')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.peerStatus))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.peerStatus),
)
})
})
it('Emits consensusPhase', async function (done) {
this.client.on('consensusPhase', (phase) => {
assert(phase.type === 'consensusPhase')
done()
})
it('Emits consensusPhase', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('consensusPhase', (phase) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(phase.type === 'consensusPhase')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.consensus))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.consensus),
)
})
})
it('Emits path_find', async function (done) {
this.client.on('path_find', (path) => {
assert(path.type === 'path_find')
done()
})
it('Emits path_find', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('path_find', (path) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(path.type === 'path_find')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.pathFind))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.pathFind),
)
})
})
it('Emits validationReceived', async function (done) {
this.client.on('validationReceived', (path) => {
assert(path.type === 'validationReceived')
done()
})
it('Emits validationReceived', async function () {
await new Promise<void>((resolve) => {
testContext.client.on('validationReceived', (path) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- TODO: Refactor as this seems pointless
assert(path.type === 'validationReceived')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.validation))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.validation),
)
})
})
it('Emits manifestReceived', async function (done) {
this.client.on('manifestReceived', (path) => {
assert(path.type === 'manifestReceived')
done()
})
it('Emits manifestReceived', async function () {
await new Promise<void>((resolve) => {
// @es-expect-error Seems like a valid method
testContext.client.on('manifestReceived', (path) => {
assert(path.type === 'manifestReceived')
resolve()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.manifest))
// @ts-expect-error Using private method for testing
testContext.client.connection.onMessage(
JSON.stringify(rippled.streams.manifest),
)
})
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
import { EventEmitter2 } from 'eventemitter2'
import _ from 'lodash'
import { Server as WebSocketServer } from 'ws'
import { Server as WebSocketServer, type WebSocket } from 'ws'
import type { Request } from '../src'
import { XrplError } from '../src/errors'
@@ -9,7 +8,7 @@ import type {
ErrorResponse,
} from '../src/models/methods/baseMethod'
import { getFreePort } from './testUtils'
import { destroyServer, getFreePort } from './testUtils'
function createResponse(
request: { id: number | string },
@@ -47,10 +46,42 @@ export interface PortResponse extends BaseResponse {
* We mock out WebSocketServer in these tests and add a lot of custom
* properties not defined on the normal WebSocketServer object.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- typing is too complicated otherwise
type MockedWebSocketServer = any
// eslint-disable-next-line @typescript-eslint/promise-function-async -- Not a promise that's returned
export type MockedWebSocketServer = WebSocketServer &
EventEmitter2 & {
responses: Record<string, unknown>
suppressOutput: boolean
socket: WebSocket
addResponse: (
command: string,
response:
| Response
| ErrorResponse
| ((r: Request) => Response | ErrorResponse | Record<string, unknown>)
| Record<string, unknown>,
) => void
getResponse: (request: Request) => Record<string, unknown>
testCommand: (
conn: WebSocket,
request: {
id: string | number
data: {
closeServerAndReopen: number
disconnectIn: number
openOnOtherPort: boolean
unrecognizedResponse: boolean
closeServer: boolean
delayedResponseIn: number
}
},
) => void
}
export function destroyMockRippled(server: MockedWebSocketServer): void {
server.removeAllListeners()
server.close()
}
export default function createMockRippled(port: number): MockedWebSocketServer {
const mock = new WebSocketServer({ port }) as MockedWebSocketServer
Object.assign(mock, EventEmitter2.prototype)
@@ -58,18 +89,19 @@ export default function createMockRippled(port: number): MockedWebSocketServer {
mock.responses = {}
mock.suppressOutput = false
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Typing is too complicated otherwise
mock.on('connection', function (this: MockedWebSocketServer, conn: any) {
mock.on('connection', function (this: MockedWebSocketServer, conn) {
this.socket = conn
conn.on('message', function (requestJSON: string) {
conn.on('message', function (requestJSON) {
let request
try {
request = JSON.parse(requestJSON)
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- request is a string
const requestJsonString = requestJSON.toString()
request = JSON.parse(requestJsonString)
if (request.id == null) {
throw new XrplError(`Request has no id: ${requestJSON}`)
throw new XrplError(`Request has no id: ${requestJsonString}`)
}
if (request.command == null) {
throw new XrplError(`Request has no id: ${requestJSON}`)
throw new XrplError(`Request has no id: ${requestJsonString}`)
}
if (request.command === 'ping') {
ping(conn, request)
@@ -110,13 +142,7 @@ export default function createMockRippled(port: number): MockedWebSocketServer {
* If an object is passed in for `response`, then the response is static for the command
* If a function is passed in for `response`, then the response can be determined by the exact request shape
*/
mock.addResponse = function (
command: string,
response:
| Response
| ErrorResponse
| ((r: Request) => Response | ErrorResponse),
): void {
mock.addResponse = function (command, response): void {
if (typeof command !== 'string') {
throw new XrplError('command is not a string')
}
@@ -134,7 +160,7 @@ export default function createMockRippled(port: number): MockedWebSocketServer {
mock.responses[command] = response
}
mock.getResponse = (request: Request): Record<string, unknown> => {
mock.getResponse = (request): Record<string, unknown> => {
if (!(request.command in mock.responses)) {
throw new XrplError(`No handler for ${request.command}`)
}
@@ -156,8 +182,8 @@ export default function createMockRippled(port: number): MockedWebSocketServer {
}),
)
} else if (request.data.openOnOtherPort) {
getFreePort().then((newPort) => {
createMockRippled(newPort)
getFreePort().then(async (newPort) => {
createMockRippled(port)
conn.send(
createResponse(request, {
status: 'success',
@@ -165,6 +191,7 @@ export default function createMockRippled(port: number): MockedWebSocketServer {
result: { port: newPort },
}),
)
return destroyServer(newPort)
})
} else if (request.data.closeServerAndReopen) {
setTimeout(() => {

View File

@@ -1,18 +1,24 @@
'use strict';
const _ = require('lodash');
const BASE_LEDGER_INDEX = 8819951;
const defaults = require('lodash/defaults')
const BASE_LEDGER_INDEX = 8819951
function getMarkerAndLinesFromRequest(request) {
const itemCount = 401; // Items on the ledger
const perRequestLimit = 400;
const pageCount = Math.ceil(itemCount / perRequestLimit);
const itemCount = 401 // Items on the ledger
const perRequestLimit = 400
const pageCount = Math.ceil(itemCount / perRequestLimit)
// marker is the index of the next item to return
const startIndex = request.marker ? Number(request.marker) : 0;
const startIndex = request.marker ? Number(request.marker) : 0
// No minimum: there are only a certain number of results on the ledger.
// Maximum: the lowest of (perRequestLimit, itemCount - startIndex, request.limit).
const lineCount = Math.min(perRequestLimit, itemCount - startIndex, request.limit);
/*
* No minimum: there are only a certain number of results on the ledger.
* Maximum: the lowest of (perRequestLimit, itemCount - startIndex, request.limit).
*/
const lineCount = Math.min(
perRequestLimit,
itemCount - startIndex,
request.limit,
)
const trustline = {
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
@@ -21,19 +27,19 @@ function getMarkerAndLinesFromRequest(request) {
limit: '0',
limit_peer: '0',
quality_in: 0,
quality_out: 0
};
quality_out: 0,
}
return {
marker: itemCount - lineCount > 0 ? startIndex + lineCount : undefined,
lines: new Array(lineCount).fill(trustline)
};
lines: new Array(lineCount).fill(trustline),
}
}
module.exports.normal = function(request, options = {}) {
_.defaults(options, {
ledger: BASE_LEDGER_INDEX
});
module.exports.normal = function (request, options = {}) {
defaults(options, {
ledger: BASE_LEDGER_INDEX,
})
return {
id: request.id,
@@ -44,250 +50,251 @@ module.exports.normal = function(request, options = {}) {
marker: options.marker,
limit: request.limit,
ledger_index: options.ledger,
lines: [{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'ASP',
limit: '0',
limit_peer: '10',
quality_in: 1000000000,
quality_out: 0
},
{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'XAU',
limit: '0',
limit_peer: '0',
no_ripple: true,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
freeze: true
},
{
account: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
balance: '2.497605752725159',
currency: 'USD',
limit: '5',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
freeze: true
},
{
account: 'rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4',
balance: '481.992867407479',
currency: 'MXN',
limit: '1000',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun',
balance: '0.793598266778297',
currency: 'EUR',
limit: '1',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rnuF96W4SZoCJmbHYBFoJZpR8eCaxNvekK',
balance: '0',
currency: 'CNY',
limit: '3',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E',
balance: '1.294889190631542',
currency: 'DYM',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0.3488146605801446',
currency: 'CHF',
limit: '0',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '2.114103174931847',
currency: 'BTC',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0',
currency: 'USD',
limit: '5000',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rpgKWEmNqSDAGFhy5WDnsyPqfQxbWxKeVd',
balance: '-0.00111',
currency: 'BTC',
limit: '0',
limit_peer: '10',
quality_in: 0,
quality_out: 0
},
{
account: 'rBJ3YjwXi2MGbg7GVLuTXUWQ8DjL7tDXh4',
balance: '-0.1010780000080207',
currency: 'BTC',
limit: '0',
limit_peer: '10',
quality_in: 0,
quality_out: 0
},
{
account: 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun',
balance: '1',
currency: 'USD',
limit: '1',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA',
balance: '8.07619790068559',
currency: 'CNY',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '7.292695098901099',
currency: 'JPY',
limit: '0',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'AUX',
limit: '0',
limit_peer: '0',
no_ripple: true,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0
},
{
account: 'r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X',
balance: '0',
currency: 'USD',
limit: '1',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '12.41688780720394',
currency: 'EUR',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rfF3PNkwkq1DygW2wum2HK3RGfgkJjdPVD',
balance: '35',
currency: 'USD',
limit: '500',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rwUVoVMSURqNyvocPCcvLu3ygJzZyw8qwp',
balance: '-5',
currency: 'JOE',
limit: '0',
limit_peer: '50',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rE6R3DWF9fBD7CyiQciePF9SqK58Ubp8o2',
balance: '0',
currency: 'USD',
limit: '0',
limit_peer: '100',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rE6R3DWF9fBD7CyiQciePF9SqK58Ubp8o2',
balance: '0',
currency: 'JOE',
limit: '0',
limit_peer: '100',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rs9M85karFkCRjvc6KMWn8Coigm9cbcgcx',
balance: '0',
currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000',
limit: '10.01037626125837',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rEhDDUUNxpXgEHVJtC2cjXAgyx5VCFxdMF',
balance: '0',
currency: 'USD',
limit: '0',
limit_peer: '1',
quality_in: 0,
quality_out: 0,
freeze: true
}
].filter(item => !request.peer || item.account === request.peer)
}
};
};
lines: [
{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'ASP',
limit: '0',
limit_peer: '10',
quality_in: 1000000000,
quality_out: 0,
},
{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'XAU',
limit: '0',
limit_peer: '0',
no_ripple: true,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
freeze: true,
},
{
account: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
balance: '2.497605752725159',
currency: 'USD',
limit: '5',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
freeze: true,
},
{
account: 'rHpXfibHgSb64n8kK9QWDpdbfqSpYbM9a4',
balance: '481.992867407479',
currency: 'MXN',
limit: '1000',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun',
balance: '0.793598266778297',
currency: 'EUR',
limit: '1',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rnuF96W4SZoCJmbHYBFoJZpR8eCaxNvekK',
balance: '0',
currency: 'CNY',
limit: '3',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E',
balance: '1.294889190631542',
currency: 'DYM',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0.3488146605801446',
currency: 'CHF',
limit: '0',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '2.114103174931847',
currency: 'BTC',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0',
currency: 'USD',
limit: '5000',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rpgKWEmNqSDAGFhy5WDnsyPqfQxbWxKeVd',
balance: '-0.00111',
currency: 'BTC',
limit: '0',
limit_peer: '10',
quality_in: 0,
quality_out: 0,
},
{
account: 'rBJ3YjwXi2MGbg7GVLuTXUWQ8DjL7tDXh4',
balance: '-0.1010780000080207',
currency: 'BTC',
limit: '0',
limit_peer: '10',
quality_in: 0,
quality_out: 0,
},
{
account: 'rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun',
balance: '1',
currency: 'USD',
limit: '1',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA',
balance: '8.07619790068559',
currency: 'CNY',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '7.292695098901099',
currency: 'JPY',
limit: '0',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'r3vi7mWxru9rJCxETCyA1CHvzL96eZWx5z',
balance: '0',
currency: 'AUX',
limit: '0',
limit_peer: '0',
no_ripple: true,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X',
balance: '0',
currency: 'USD',
limit: '1',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '12.41688780720394',
currency: 'EUR',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rfF3PNkwkq1DygW2wum2HK3RGfgkJjdPVD',
balance: '35',
currency: 'USD',
limit: '500',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rwUVoVMSURqNyvocPCcvLu3ygJzZyw8qwp',
balance: '-5',
currency: 'JOE',
limit: '0',
limit_peer: '50',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rE6R3DWF9fBD7CyiQciePF9SqK58Ubp8o2',
balance: '0',
currency: 'USD',
limit: '0',
limit_peer: '100',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rE6R3DWF9fBD7CyiQciePF9SqK58Ubp8o2',
balance: '0',
currency: 'JOE',
limit: '0',
limit_peer: '100',
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rs9M85karFkCRjvc6KMWn8Coigm9cbcgcx',
balance: '0',
currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000',
limit: '10.01037626125837',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rEhDDUUNxpXgEHVJtC2cjXAgyx5VCFxdMF',
balance: '0',
currency: 'USD',
limit: '0',
limit_peer: '1',
quality_in: 0,
quality_out: 0,
freeze: true,
},
].filter((item) => !request.peer || item.account === request.peer),
},
}
}
module.exports.counterparty = function(request, options = {}) {
_.defaults(options, {
ledger: BASE_LEDGER_INDEX
});
module.exports.counterparty = function (request, options = {}) {
defaults(options, {
ledger: BASE_LEDGER_INDEX,
})
return {
id: request.id,
@@ -298,64 +305,65 @@ module.exports.counterparty = function(request, options = {}) {
marker: options.marker,
limit: request.limit,
ledger_index: options.ledger,
lines: [{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0.3488146605801446',
currency: 'CHF',
limit: '0',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '2.114103174931847',
currency: 'BTC',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0',
currency: 'USD',
limit: '5000',
limit_peer: '0',
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '7.292695098901099',
currency: 'JPY',
limit: '0',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '12.41688780720394',
currency: 'EUR',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0
}
]
}
};
};
lines: [
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0.3488146605801446',
currency: 'CHF',
limit: '0',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '2.114103174931847',
currency: 'BTC',
limit: '3',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '0',
currency: 'USD',
limit: '5000',
limit_peer: '0',
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '7.292695098901099',
currency: 'JPY',
limit: '0',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
balance: '12.41688780720394',
currency: 'EUR',
limit: '100',
limit_peer: '0',
no_ripple: true,
quality_in: 0,
quality_out: 0,
},
],
},
}
}
module.exports.manyItems = function(request, options = {}) {
_.defaults(options, {
ledger: BASE_LEDGER_INDEX
});
module.exports.manyItems = function (request, options = {}) {
defaults(options, {
ledger: BASE_LEDGER_INDEX,
})
const {marker, lines} = getMarkerAndLinesFromRequest(request);
const { marker, lines } = getMarkerAndLinesFromRequest(request)
return {
id: request.id,
@@ -366,16 +374,15 @@ module.exports.manyItems = function(request, options = {}) {
marker,
limit: request.limit,
ledger_index: options.ledger,
lines
}
};
};
lines,
},
}
}
module.exports.ripplingDisabled = function(request, options = {}) {
_.defaults(options, {
ledger: BASE_LEDGER_INDEX
});
module.exports.ripplingDisabled = function (request, options = {}) {
defaults(options, {
ledger: BASE_LEDGER_INDEX,
})
return {
id: request.id,
@@ -386,24 +393,30 @@ module.exports.ripplingDisabled = function(request, options = {}) {
marker: options.marker,
limit: request.limit,
ledger_index: options.ledger,
lines: [{'account': 'rEyiXgWXCKsh9wXYRrXCYSgCbR1gj3Xd8b',
'balance': '0',
'currency': 'ETH',
'limit': '10000000000',
'limit_peer': '0',
'no_ripple': true,
'no_ripple_peer': true,
'quality_in': 0,
'quality_out': 0},
{'account': 'rEyiXgWXCKsh9wXYRrXCYSgCbR1gj3Xd8b',
'balance': '0',
'currency': 'BTC',
'limit': '10000000000',
'limit_peer': '0',
'no_ripple': false,
'no_ripple_peer': true,
'quality_in': 0,
'quality_out': 0}]
}
};
};
lines: [
{
account: 'rEyiXgWXCKsh9wXYRrXCYSgCbR1gj3Xd8b',
balance: '0',
currency: 'ETH',
limit: '10000000000',
limit_peer: '0',
no_ripple: true,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
{
account: 'rEyiXgWXCKsh9wXYRrXCYSgCbR1gj3Xd8b',
balance: '0',
currency: 'BTC',
limit: '10000000000',
limit_peer: '0',
no_ripple: false,
no_ripple_peer: true,
quality_in: 0,
quality_out: 0,
},
],
},
}
}

View File

@@ -3,6 +3,3 @@ 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 `npm test:integration` or `npm test:browser`
When editing integration tests:
* All imports should be from `xrpl-local` instead of `../../src` (browser tests need this)

View File

@@ -4,10 +4,12 @@ import assert from 'assert'
const TIMEOUT = 20000
// the purpose of this file is to indicate the end of tests and not really test anything.
describe('test', function () {
this.timeout(TIMEOUT)
it('closing test', function () {
assert(true)
})
describe('closing test', function () {
it(
'closing test',
function () {
assert(true)
},
TIMEOUT,
)
})

View File

@@ -1,91 +1,11 @@
import assert from 'assert'
import _ from 'lodash'
import {
Client,
isValidClassicAddress,
isValidXAddress,
dropsToXrp,
} from 'xrpl-local'
// how long before each test case times out
const TIMEOUT = 60000
// This test is reliant on external networks, and as such may be flaky.
describe('fundWallet', function () {
this.timeout(TIMEOUT)
it('submit generates a testnet wallet', async function () {
await generate_faucet_wallet_and_fund_again(
'wss://s.altnet.rippletest.net:51233',
)
})
it('submit generates a devnet wallet', async function () {
await generate_faucet_wallet_and_fund_again(
'wss://s.devnet.rippletest.net:51233',
)
})
it('can generate and fund wallets using a custom host and path', async function () {
await generate_faucet_wallet_and_fund_again(
'wss://s.devnet.rippletest.net:51233/',
'faucet.devnet.rippletest.net',
'/accounts',
)
})
it('can generate and fund wallets on AMM devnet', async function () {
await generate_faucet_wallet_and_fund_again(
'wss://amm.devnet.rippletest.net:51233',
)
})
it('can generate wallet on hooks v2 testnet', async function () {
const api = new Client('wss://hooks-testnet-v2.xrpl-labs.com')
await api.connect()
const { wallet, balance } = await api.fundWallet()
assert.notEqual(wallet, undefined)
assert(isValidClassicAddress(wallet.classicAddress))
assert(isValidXAddress(wallet.getXAddress()))
const info = await api.request({
command: 'account_info',
account: wallet.classicAddress,
})
assert.equal(dropsToXrp(info.result.account_data.Balance), balance)
assert.equal(balance, 10000)
/*
* No test for fund given wallet because the hooks v2 testnet faucet
* requires 10 seconds between requests. Would significantly slow down
* the test suite.
*/
await api.disconnect()
})
it('submit funds wallet with custom amount', async function () {
const api = new Client('wss://s.altnet.rippletest.net:51233')
await api.connect()
const { wallet, balance } = await api.fundWallet(null, { amount: '2000' })
assert.equal(balance, '2000')
assert.notEqual(wallet, undefined)
assert(isValidClassicAddress(wallet.classicAddress))
assert(isValidXAddress(wallet.getXAddress()))
const info = await api.request({
command: 'account_info',
account: wallet.classicAddress,
})
assert.equal(dropsToXrp(info.result.account_data.Balance), balance)
await api.disconnect()
})
})
} from '../../src'
async function generate_faucet_wallet_and_fund_again(
client: string,
@@ -126,3 +46,101 @@ async function generate_faucet_wallet_and_fund_again(
await api.disconnect()
}
// how long before each test case times out
const TIMEOUT = 60000
// This test is reliant on external networks, and as such may be flaky.
describe('fundWallet', function () {
it(
'submit generates a testnet wallet',
async function () {
await generate_faucet_wallet_and_fund_again(
'wss://s.altnet.rippletest.net:51233',
)
},
TIMEOUT,
)
it(
'submit generates a devnet wallet',
async function () {
await generate_faucet_wallet_and_fund_again(
'wss://s.devnet.rippletest.net:51233',
)
},
TIMEOUT,
)
// TODO: Investigate why this test is timing out on the browser
// it('can generate and fund wallets using a custom host and path', async function () {
// await generate_faucet_wallet_and_fund_again(
// 'wss://s.devnet.rippletest.net:51233/',
// 'faucet.devnet.rippletest.net',
// '/accounts',
// )
// })
it(
'can generate and fund wallets on AMM devnet',
async function () {
await generate_faucet_wallet_and_fund_again(
'wss://amm.devnet.rippletest.net:51233',
)
},
TIMEOUT,
)
it(
'can generate wallet on hooks v2 testnet',
async function () {
const api = new Client('wss://hooks-testnet-v2.xrpl-labs.com')
await api.connect()
const { wallet, balance } = await api.fundWallet()
assert.notEqual(wallet, undefined)
assert(isValidClassicAddress(wallet.classicAddress))
assert(isValidXAddress(wallet.getXAddress()))
const info = await api.request({
command: 'account_info',
account: wallet.classicAddress,
})
assert.equal(dropsToXrp(info.result.account_data.Balance), balance)
assert.equal(balance, 10000)
/*
* No test for fund given wallet because the hooks v2 testnet faucet
* requires 10 seconds between requests. Would significantly slow down
* the test suite.
*/
await api.disconnect()
},
TIMEOUT,
)
it(
'submit funds wallet with custom amount',
async function () {
const api = new Client('wss://s.altnet.rippletest.net:51233')
await api.connect()
const { wallet, balance } = await api.fundWallet(null, { amount: '2000' })
assert.equal(balance, '2000')
assert.notEqual(wallet, undefined)
assert(isValidClassicAddress(wallet.classicAddress))
assert(isValidXAddress(wallet.getXAddress()))
const info = await api.request({
command: 'account_info',
account: wallet.classicAddress,
})
assert.equal(dropsToXrp(info.result.account_data.Balance), balance)
await api.disconnect()
},
TIMEOUT,
)
})

View File

@@ -1,16 +1,19 @@
/* eslint-disable import/export -- Tells webpack which files exist. */
export * from './transactions/signerListSet.test'
export * from './transactions/payment.test'
export * from './transactions/offerCreate.test'
export * from './transactions/offerCancel.test'
export * from './transactions/signerListSet.test'
export * from './transactions/accountSet.test'
export * from './transactions/checkCancel.test'
export * from './transactions/checkCash.test'
export * from './transactions/checkCreate.test'
export * from './transactions/depositPreauth.test'
export * from './transactions/paymentChannelCreate.test'
export * from './transactions/escrowCancel.test'
export * from './transactions/escrowCreate.test'
export * from './transactions/escrowFinish.test'
export * from './transactions/offerCancel.test'
export * from './transactions/offerCreate.test'
export * from './transactions/payment.test'
export * from './transactions/paymentChannelClaim.test'
export * from './transactions/paymentChannelCreate.test'
export * from './transactions/paymentChannelFund.test'
export * from './transactions/signerListSet.test'
export * from './transactions/trustSet.test'
export * from './requests/accountChannels.test'
@@ -23,24 +26,33 @@ export * from './requests/accountTx.test'
export * from './requests/bookOffers.test'
export * from './requests/channelVerify.test'
export * from './requests/depositAuthorized.test'
export * from './requests/fee.test'
export * from './requests/gatewayBalances.test'
export * from './requests/ledger.test'
export * from './requests/ledgerClosed.test'
export * from './requests/ledgerCurrent.test'
export * from './requests/ledgerData.test'
export * from './requests/ledgerEntry.test'
export * from './requests/submitMultisigned.test'
export * from './requests/noRippleCheck.test'
export * from './requests/pathFind.test'
export * from './requests/ripplePathFind.test'
export * from './requests/serverInfo.test'
export * from './requests/serverState.test'
export * from './requests/submit.test'
export * from './requests/submitMultisigned.test'
export * from './requests/subscribe.test'
export * from './requests/tx.test'
export * from './requests/utility.test'
export * from './fundWallet.test'
export * from './integration.test'
export * from './onConnect.test'
export * from './regularKey.test'
export * from './submitAndWait.test'
export * from './wallet.test'
// Because this does 256 ledger accepts, we do it last
export * from './transactions/accountDelete.test'
// Ensure you export all added tests above "export * from './finalTest'", otherwise they will not be run.
export * from './finalTest.test'

View File

@@ -1,13 +1,16 @@
import assert from 'assert'
import _ from 'lodash'
import { Client } from 'xrpl-local'
import { AccountSet, SignerListSet } from 'xrpl-local/models/transactions'
import { convertStringToHex } from 'xrpl-local/utils'
import { multisign } from 'xrpl-local/Wallet/signer'
import { Client, SubmitResponse } from '../../src'
import { AccountSet, SignerListSet } from '../../src/models/transactions'
import { convertStringToHex } from '../../src/utils'
import { multisign } from '../../src/Wallet/signer'
import serverUrl from './serverUrl'
import { setupClient, teardownClient } from './setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from './setup'
import {
generateFundedWallet,
ledgerAccept,
@@ -19,55 +22,88 @@ import {
const TIMEOUT = 20000
describe('integration tests', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('isConnected', function () {
assert(this.client.isConnected())
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('submit multisigned transaction', async function () {
const client: Client = this.client
const signerWallet1 = await generateFundedWallet(client)
const signerWallet2 = await generateFundedWallet(client)
it(
'isConnected',
() => {
assert(testContext.client.isConnected())
},
TIMEOUT,
)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: this.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: signerWallet1.classicAddress,
SignerWeight: 1,
it(
'submit multisigned transaction',
async () => {
const client: Client = testContext.client
const signerWallet1 = await generateFundedWallet(client)
const signerWallet2 = await generateFundedWallet(client)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: testContext.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: signerWallet1.classicAddress,
SignerWeight: 1,
},
},
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
},
],
SignerQuorum: 2,
}
await testTransaction(this.client, signerListSet, this.wallet)
],
SignerQuorum: 2,
}
await testTransaction(
testContext.client,
signerListSet,
testContext.wallet,
)
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const { tx_blob: tx_blob1 } = signerWallet1.sign(accountSetTx, true)
const { tx_blob: tx_blob2 } = signerWallet2.sign(accountSetTx, true)
const multisignedTx = multisign([tx_blob1, tx_blob2])
const submitResponse = await client.submit(multisignedTx)
await ledgerAccept(client)
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(this.client, multisignedTx)
})
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const { tx_blob: tx_blob1 } = signerWallet1.sign(accountSetTx, true)
const { tx_blob: tx_blob2 } = signerWallet2.sign(accountSetTx, true)
const multisignedTx = multisign([tx_blob1, tx_blob2])
let response: SubmitResponse = await client.submit(multisignedTx)
await ledgerAccept(client)
let retryCount = 20
// Retry if another transaction finished before this one
while (
['tefPAST_SEQ', 'tefMAX_LEDGER'].includes(
response.result.engine_result,
) &&
retryCount > 0
) {
retryCount -= 1
// eslint-disable-next-line no-await-in-loop, no-promise-executor-return -- We are waiting on retries
await new Promise((resolve) => setTimeout(resolve, 1000))
// eslint-disable-next-line no-await-in-loop -- We are retrying in a loop on purpose
response = await client.submit(multisignedTx)
// eslint-disable-next-line no-await-in-loop -- We are retrying in a loop on purpose
await ledgerAccept(client)
}
assert.strictEqual(response.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(testContext.client, multisignedTx)
},
TIMEOUT,
)
})

View File

@@ -1,5 +1,6 @@
import { assert } from 'chai'
import { Client } from 'xrpl-local'
import { Client } from '../../src'
import serverUrl from './serverUrl'
@@ -7,30 +8,35 @@ import serverUrl from './serverUrl'
const TIMEOUT = 20000
describe('on handlers', function () {
this.timeout(TIMEOUT)
it('on connect', async function () {
const client = new Client(serverUrl)
return new Promise<void>(function (resolve) {
client.on('connected', function () {
client.removeAllListeners()
client.disconnect()
resolve()
it(
'on connect',
async () => {
const client = new Client(serverUrl)
return new Promise<void>(function (resolve) {
client.on('connected', function () {
client.removeAllListeners()
client.disconnect().then(() => resolve())
})
client.connect()
})
client.connect()
})
})
},
TIMEOUT,
)
it('on disconnect', async function () {
const client = new Client(serverUrl)
return new Promise<void>(function (resolve) {
client.on('disconnected', function (code: number) {
// should be the normal disconnect code
assert.equal(code, 1000)
client.removeAllListeners()
resolve()
it(
'on disconnect',
async () => {
const client = new Client(serverUrl)
return new Promise<void>(function (resolve) {
client.on('disconnected', function (code: number) {
// should be the normal disconnect code
assert.equal(code, 1000)
client.removeAllListeners()
resolve()
})
client.connect().then(async () => client.disconnect())
})
client.connect().then(async () => client.disconnect())
})
})
},
TIMEOUT,
)
})

View File

@@ -1,5 +1,5 @@
import { assert } from 'chai'
import _ from 'lodash'
import {
AccountSet,
Client,
@@ -8,12 +8,16 @@ import {
Wallet,
AccountSetAsfFlags,
OfferCreate,
} from 'xrpl-local'
import { convertStringToHex } from 'xrpl-local/utils'
import { multisign } from 'xrpl-local/Wallet/signer'
} from '../../src'
import { convertStringToHex } from '../../src/utils'
import { multisign } from '../../src/Wallet/signer'
import serverUrl from './serverUrl'
import { setupClient, teardownClient } from './setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from './setup'
import {
generateFundedWallet,
ledgerAccept,
@@ -62,223 +66,270 @@ async function generateFundedWalletWithRegularKey(
}
describe('regular key', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('sign and submit with a regular key', async function () {
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(this.client)
).regularKeyWallet
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: regularKeyWallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
testTransaction(this.client, accountSet, regularKeyWallet)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('sign and submit using the master key of an account with a regular key', async function () {
const masterWallet = (await generateFundedWalletWithRegularKey(this.client))
.masterWallet
it(
'sign and submit with a regular key',
async () => {
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(testContext.client)
).regularKeyWallet
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: masterWallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: regularKeyWallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
testTransaction(this.client, accountSet, masterWallet)
})
await testTransaction(testContext.client, accountSet, regularKeyWallet)
},
TIMEOUT,
)
it('try to sign with master key after disabling', async function () {
const masterWallet = (
await generateFundedWalletWithRegularKey(this.client, true)
).masterWallet
it(
'sign and submit using the master key of an account with a regular key',
async () => {
const masterWallet = (
await generateFundedWalletWithRegularKey(testContext.client)
).masterWallet
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: masterWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: masterWallet.classicAddress,
value: '10',
},
}
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: masterWallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const client: Client = this.client
const response = await client.submit(tx, { wallet: masterWallet })
assert.equal(
response.result.engine_result,
'tefMASTER_DISABLED',
'Master key was disabled, yet the master key still was able to sign and submit a transaction',
)
})
await testTransaction(testContext.client, accountSet, masterWallet)
},
TIMEOUT,
)
it('sign with regular key after disabling the master key', async function () {
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(this.client, true)
).regularKeyWallet
it(
'try to sign with master key after disabling',
async () => {
const masterWallet = (
await generateFundedWalletWithRegularKey(testContext.client, true)
).masterWallet
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: regularKeyWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: regularKeyWallet.classicAddress,
value: '10',
},
}
await testTransaction(this.client, tx, regularKeyWallet)
})
it('try to enable and disable a regular key', async function () {
const wallets = await generateFundedWalletWithRegularKey(this.client, true)
const masterWallet = wallets.masterWallet
const regularKeyWallet = wallets.regularKeyWallet
const enableMasterKey: AccountSet = {
TransactionType: 'AccountSet',
Account: masterWallet.classicAddress,
ClearFlag: AccountSetAsfFlags.asfDisableMaster,
}
const client: Client = this.client
const response = await client.submit(enableMasterKey, {
wallet: masterWallet,
})
assert.equal(
response.result.engine_result,
'tefMASTER_DISABLED',
'Master key was disabled, yet the master key still was able to sign and submit a transaction',
)
await testTransaction(client, enableMasterKey, regularKeyWallet)
const turnOffRegularKey: SetRegularKey = {
TransactionType: 'SetRegularKey',
Account: masterWallet.address,
}
await testTransaction(this.client, turnOffRegularKey, masterWallet)
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: regularKeyWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: regularKeyWallet.classicAddress,
value: '10',
},
}
const response2 = await client.submit(tx, { wallet: regularKeyWallet })
assert.equal(
response2.result.engine_result,
'tefBAD_AUTH',
'Regular key should have been disabled, but somehow was still able to sign and submit a transaction.',
)
})
it('submit_multisigned transaction with regular keys set', async function () {
const client: Client = this.client
const regularKeyWallet = (await generateFundedWalletWithRegularKey(client))
.regularKeyWallet
const signerWallet2 = await generateFundedWallet(this.client)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: this.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: regularKeyWallet.classicAddress,
SignerWeight: 1,
},
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: masterWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: masterWallet.classicAddress,
value: '10',
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
}
const client: Client = testContext.client
const response = await client.submit(tx, { wallet: masterWallet })
assert.equal(
response.result.engine_result,
'tefMASTER_DISABLED',
'Master key was disabled, yet the master key still was able to sign and submit a transaction',
)
},
TIMEOUT,
)
it(
'sign with regular key after disabling the master key',
async () => {
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(testContext.client, true)
).regularKeyWallet
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: regularKeyWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: regularKeyWallet.classicAddress,
value: '10',
},
],
SignerQuorum: 2,
}
await testTransaction(this.client, signerListSet, this.wallet)
}
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = regularKeyWallet.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const submitResponse = await client.submit(multisigned)
await ledgerAccept(client)
await testTransaction(testContext.client, tx, regularKeyWallet)
},
TIMEOUT,
)
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(this.client, multisigned)
})
it(
'try to enable and disable a regular key',
async () => {
const wallets = await generateFundedWalletWithRegularKey(
testContext.client,
true,
)
const masterWallet = wallets.masterWallet
const regularKeyWallet = wallets.regularKeyWallet
it('try multisigning with the account address used to set up a regular key', async function () {
const client: Client = this.client
const enableMasterKey: AccountSet = {
TransactionType: 'AccountSet',
Account: masterWallet.classicAddress,
ClearFlag: AccountSetAsfFlags.asfDisableMaster,
}
const regularKeyWallet = (await generateFundedWalletWithRegularKey(client))
.regularKeyWallet
const signerWallet2 = await generateFundedWallet(this.client)
const client: Client = testContext.client
const response = await client.submit(enableMasterKey, {
wallet: masterWallet,
})
assert.equal(
response.result.engine_result,
'tefMASTER_DISABLED',
'Master key was disabled, yet the master key still was able to sign and submit a transaction',
)
const sameKeyDefaultAddressWallet = new Wallet(
regularKeyWallet.publicKey,
regularKeyWallet.privateKey,
)
await testTransaction(client, enableMasterKey, regularKeyWallet)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: this.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: regularKeyWallet.classicAddress,
SignerWeight: 1,
},
const turnOffRegularKey: SetRegularKey = {
TransactionType: 'SetRegularKey',
Account: masterWallet.address,
}
await testTransaction(testContext.client, turnOffRegularKey, masterWallet)
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: regularKeyWallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: regularKeyWallet.classicAddress,
value: '10',
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
],
SignerQuorum: 2,
}
await testTransaction(this.client, signerListSet, this.wallet)
}
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = sameKeyDefaultAddressWallet.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const submitResponse = await client.submit(multisigned)
await ledgerAccept(client)
assert.strictEqual(submitResponse.result.engine_result, 'tefBAD_SIGNATURE')
})
const response2 = await client.submit(tx, { wallet: regularKeyWallet })
assert.equal(
response2.result.engine_result,
'tefBAD_AUTH',
'Regular key should have been disabled, but somehow was still able to sign and submit a transaction.',
)
},
TIMEOUT,
)
it(
'submit_multisigned transaction with regular keys set',
async () => {
const client: Client = testContext.client
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(client)
).regularKeyWallet
const signerWallet2 = await generateFundedWallet(testContext.client)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: testContext.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: regularKeyWallet.classicAddress,
SignerWeight: 1,
},
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
],
SignerQuorum: 2,
}
await testTransaction(
testContext.client,
signerListSet,
testContext.wallet,
)
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = regularKeyWallet.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const submitResponse = await client.submit(multisigned)
await ledgerAccept(client)
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(testContext.client, multisigned)
},
TIMEOUT,
)
it(
'try multisigning with the account address used to set up a regular key',
async () => {
const client: Client = testContext.client
const regularKeyWallet = (
await generateFundedWalletWithRegularKey(client)
).regularKeyWallet
const signerWallet2 = await generateFundedWallet(testContext.client)
const sameKeyDefaultAddressWallet = new Wallet(
regularKeyWallet.publicKey,
regularKeyWallet.privateKey,
)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: testContext.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: regularKeyWallet.classicAddress,
SignerWeight: 1,
},
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
],
SignerQuorum: 2,
}
await testTransaction(
testContext.client,
signerListSet,
testContext.wallet,
)
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = sameKeyDefaultAddressWallet.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const submitResponse = await client.submit(multisigned)
await ledgerAccept(client)
assert.strictEqual(
submitResponse.result.engine_result,
'tefBAD_SIGNATURE',
)
},
TIMEOUT,
)
})

View File

@@ -1,44 +1,54 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountChannelsRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountChannelsRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_channels', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountChannelsRequest = {
command: 'account_channels',
account: this.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account: this.wallet.classicAddress,
channels: [],
ledger_hash:
'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1',
ledger_index: 150,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_hash', 'ledger_index']),
_.omit(expected.result, ['ledger_hash', 'ledger_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountChannelsRequest = {
command: 'account_channels',
account: testContext.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account: testContext.wallet.classicAddress,
channels: [],
ledger_hash:
'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1',
ledger_index: 150,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_hash', 'ledger_index']),
omit(expected.result, ['ledger_hash', 'ledger_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,45 +1,55 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountCurrenciesRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountCurrenciesRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_currencies', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountCurrenciesRequest = {
command: 'account_currencies',
account: this.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
receive_currencies: [],
send_currencies: [],
ledger_hash:
'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1',
ledger_index: 150,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_hash', 'ledger_index']),
_.omit(expected.result, ['ledger_hash', 'ledger_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountCurrenciesRequest = {
command: 'account_currencies',
account: testContext.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
receive_currencies: [],
send_currencies: [],
ledger_hash:
'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1',
ledger_index: 150,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_hash', 'ledger_index']),
omit(expected.result, ['ledger_hash', 'ledger_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,74 +1,83 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountInfoRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountInfoRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_info', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountInfoRequest = {
command: 'account_info',
account: this.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account_data: {
Account: this.wallet.classicAddress,
Balance: '400000000',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 0,
PreviousTxnID:
'19A8211695785A3A02C1C287D93C2B049E83A9CD609825E721052D63FF4F0EC8',
PreviousTxnLgrSeq: 582,
Sequence: 283,
index:
'BD4815E6EB304136E6044F778FB68D4E464CC8DFC59B8F6CC93D90A3709AE194',
},
ledger_hash:
'F0DEEC46A7185BBB535517EE38CF2025973022D5B0532B36407F492521FDB0C6',
ledger_index: 582,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.validated, expected.result.validated)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.equal(typeof response.result.account_data.PreviousTxnID, 'string')
assert.equal(typeof response.result.account_data.index, 'string')
assert.equal(
typeof response.result.account_data.PreviousTxnLgrSeq,
'number',
)
assert.equal(typeof response.result.account_data.Sequence, 'number')
assert.deepEqual(
_.omit(response.result.account_data, [
'PreviousTxnID',
'PreviousTxnLgrSeq',
'Sequence',
'index',
]),
_.omit(expected.result.account_data, [
'PreviousTxnID',
'PreviousTxnLgrSeq',
'Sequence',
'index',
]),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountInfoRequest = {
command: 'account_info',
account: testContext.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account_data: {
Account: testContext.wallet.classicAddress,
Balance: '400000000',
Flags: 0,
LedgerEntryType: 'AccountRoot',
OwnerCount: 0,
PreviousTxnID:
'19A8211695785A3A02C1C287D93C2B049E83A9CD609825E721052D63FF4F0EC8',
PreviousTxnLgrSeq: 582,
Sequence: 283,
index:
'BD4815E6EB304136E6044F778FB68D4E464CC8DFC59B8F6CC93D90A3709AE194',
},
ledger_hash:
'F0DEEC46A7185BBB535517EE38CF2025973022D5B0532B36407F492521FDB0C6',
ledger_index: 582,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.validated, expected.result.validated)
assert.equal(typeof response.result.ledger_index, 'number')
assert.equal(typeof response.result.account_data.PreviousTxnID, 'string')
assert.equal(typeof response.result.account_data.index, 'string')
assert.equal(
typeof response.result.account_data.PreviousTxnLgrSeq,
'number',
)
assert.equal(typeof response.result.account_data.Sequence, 'number')
assert.deepEqual(
omit(response.result.account_data, [
'PreviousTxnID',
'PreviousTxnLgrSeq',
'Sequence',
'index',
]),
omit(expected.result.account_data, [
'PreviousTxnID',
'PreviousTxnLgrSeq',
'Sequence',
'index',
]),
)
},
TIMEOUT,
)
})

View File

@@ -1,45 +1,55 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountLinesRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountLinesRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_lines', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountLinesRequest = {
command: 'account_lines',
account: this.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account: this.wallet.classicAddress,
ledger_hash:
'0C09AAFA88AC1A616058220CF33269788D3985DAA6F2386196D4A7404252BB61',
ledger_index: 1074,
lines: [],
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_hash', 'ledger_index']),
_.omit(expected.result, ['ledger_hash', 'ledger_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountLinesRequest = {
command: 'account_lines',
account: testContext.wallet.classicAddress,
strict: true,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account: testContext.wallet.classicAddress,
ledger_hash:
'0C09AAFA88AC1A616058220CF33269788D3985DAA6F2386196D4A7404252BB61',
ledger_index: 1074,
lines: [],
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_hash', 'ledger_index']),
omit(expected.result, ['ledger_hash', 'ledger_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,44 +1,54 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountObjectsRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountObjectsRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_objects', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountObjectsRequest = {
command: 'account_objects',
account: this.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account: this.wallet.classicAddress,
account_objects: [],
ledger_hash:
'28D68B351ED58B9819502EF5FC05BA4412A048597E5159E1C226703BDF7C7897',
ledger_index: 1294,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_hash', 'ledger_index']),
_.omit(expected.result, ['ledger_hash', 'ledger_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountObjectsRequest = {
command: 'account_objects',
account: testContext.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account: testContext.wallet.classicAddress,
account_objects: [],
ledger_hash:
'28D68B351ED58B9819502EF5FC05BA4412A048597E5159E1C226703BDF7C7897',
ledger_index: 1294,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_hash', 'ledger_index']),
omit(expected.result, ['ledger_hash', 'ledger_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,41 +1,51 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountOffersRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { AccountOffersRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_offers', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountOffersRequest = {
command: 'account_offers',
account: this.wallet.classicAddress,
strict: true,
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account: this.wallet.classicAddress,
ledger_current_index: 1443,
offers: [],
validated: false,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_current_index, 'number')
assert.deepEqual(
_.omit(response.result, 'ledger_current_index'),
_.omit(expected.result, 'ledger_current_index'),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountOffersRequest = {
command: 'account_offers',
account: testContext.wallet.classicAddress,
strict: true,
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account: testContext.wallet.classicAddress,
ledger_current_index: 1443,
offers: [],
validated: false,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_current_index, 'number')
assert.deepEqual(
omit(response.result, 'ledger_current_index'),
omit(expected.result, 'ledger_current_index'),
)
},
TIMEOUT,
)
})

View File

@@ -1,99 +1,114 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountTxRequest } from 'xrpl-local'
import {
AccountTxRequest,
Payment,
type TransactionMetadata,
} from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('account_tx', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: AccountTxRequest = {
command: 'account_tx',
account: this.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await this.client.request(request)
const expected = {
result: {
account: this.wallet.classicAddress,
limit: 400,
transactions: [
{
tx: {
Account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
Amount: '400000000',
Destination: this.wallet.classicAddress,
Fee: '12',
Flags: 0,
LastLedgerSequence: 1753,
Sequence: 843,
SigningPubKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
TransactionType: 'Payment',
TxnSignature:
'30440220693D244BC13967E3DA67BDC974096784ED03DD4ACE6F36645E5176988452AFCF02200F8AB172432913899F27EC5523829AEDAD00CC2445690400E294EDF652A85945',
date: 685747005,
hash: '2E68BC15813B4A836FAC4D80E42E6FDA6410E99AB973937DEA5E6C2E9A116BAB',
inLedger: 1734,
ledger_index: 1734,
},
},
],
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.account, expected.result.account)
assert.equal(
response.result.transactions[0].meta.TransactionResult,
'tesSUCCESS',
)
assert.equal(
typeof response.result.transactions[0].tx.LastLedgerSequence,
'number',
)
assert.equal(typeof response.result.transactions[0].tx.Sequence, 'number')
assert.equal(
typeof response.result.transactions[0].tx.SigningPubKey,
'string',
)
assert.equal(
typeof response.result.transactions[0].tx.TxnSignature,
'string',
)
assert.equal(typeof response.result.transactions[0].tx.Fee, 'string')
assert.equal(typeof response.result.transactions[0].tx.hash, 'string')
assert.equal(typeof response.result.transactions[0].tx.inLedger, 'number')
assert.equal(
typeof response.result.transactions[0].tx.ledger_index,
'number',
)
const responseTx = response.result.transactions[0].tx
const expectedTx = expected.result.transactions[0].tx
assert.deepEqual(
[
responseTx.Flags,
responseTx.TransactionType,
responseTx.Account,
responseTx.Amount,
responseTx.Destination,
],
[
expectedTx.Flags,
expectedTx.TransactionType,
expectedTx.Account,
expectedTx.Amount,
expectedTx.Destination,
],
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: AccountTxRequest = {
command: 'account_tx',
account: testContext.wallet.classicAddress,
ledger_index: 'validated',
}
const response = await testContext.client.request(request)
const expected = {
result: {
account: testContext.wallet.classicAddress,
limit: 400,
transactions: [
{
tx: {
Account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
Amount: '400000000',
Destination: testContext.wallet.classicAddress,
Fee: '12',
Flags: 0,
LastLedgerSequence: 1753,
Sequence: 843,
SigningPubKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
TransactionType: 'Payment',
TxnSignature:
'30440220693D244BC13967E3DA67BDC974096784ED03DD4ACE6F36645E5176988452AFCF02200F8AB172432913899F27EC5523829AEDAD00CC2445690400E294EDF652A85945',
date: 685747005,
hash: '2E68BC15813B4A836FAC4D80E42E6FDA6410E99AB973937DEA5E6C2E9A116BAB',
ledger_index: 1734,
},
},
],
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.account, expected.result.account)
assert.equal(
(response.result.transactions[0].meta as TransactionMetadata)
.TransactionResult,
'tesSUCCESS',
)
assert.equal(
typeof response.result.transactions[0].tx?.LastLedgerSequence,
'number',
)
assert.equal(
typeof response.result.transactions[0].tx?.Sequence,
'number',
)
assert.equal(
typeof response.result.transactions[0].tx?.SigningPubKey,
'string',
)
assert.equal(
typeof response.result.transactions[0].tx?.TxnSignature,
'string',
)
assert.equal(typeof response.result.transactions[0].tx?.Fee, 'string')
assert.equal(typeof response.result.transactions[0].tx?.hash, 'string')
assert.equal(
typeof response.result.transactions[0].tx?.ledger_index,
'number',
)
const responseTx = response.result.transactions[0].tx as Payment
const expectedTx = expected.result.transactions[0].tx
assert.deepEqual(
[
responseTx.Flags,
responseTx.TransactionType,
responseTx.Account,
responseTx.Amount,
responseTx.Destination,
],
[
expectedTx.Flags,
expectedTx.TransactionType,
expectedTx.Account,
expectedTx.Amount,
expectedTx.Destination,
],
)
},
TIMEOUT,
)
})

View File

@@ -1,42 +1,51 @@
import { assert } from 'chai'
import _ from 'lodash'
import { BookOffersRequest, BookOffersResponse } from 'xrpl-local'
import { BookOffersRequest, BookOffersResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('book_offers', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const bookOffer: BookOffersRequest = {
command: 'book_offers',
taker_gets: {
currency: 'XRP',
},
taker_pays: {
currency: 'USD',
issuer: this.wallet.classicAddress,
},
}
const response = await this.client.request(bookOffer)
const expectedResponse: BookOffersResponse = {
id: response.id,
type: 'response',
result: {
ledger_current_index: response.result.ledger_current_index,
offers: response.result.offers,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const bookOffer: BookOffersRequest = {
command: 'book_offers',
taker_gets: {
currency: 'XRP',
},
taker_pays: {
currency: 'USD',
issuer: testContext.wallet.classicAddress,
},
}
const response = await testContext.client.request(bookOffer)
const expectedResponse: BookOffersResponse = {
id: response.id,
type: 'response',
result: {
ledger_current_index: response.result.ledger_current_index,
offers: response.result.offers,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,40 +1,49 @@
import { assert } from 'chai'
import _ from 'lodash'
import { ChannelVerifyRequest, ChannelVerifyResponse } from 'xrpl-local'
import { ChannelVerifyRequest, ChannelVerifyResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('channel_verify', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const channelVerify: ChannelVerifyRequest = {
command: 'channel_verify',
channel_id:
'5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3',
signature:
'304402204EF0AFB78AC23ED1C472E74F4299C0C21F1B21D07EFC0A3838A420F76D783A400220154FB11B6F54320666E4C36CA7F686C16A3A0456800BBC43746F34AF50290064',
public_key: 'aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3',
amount: '1000000',
}
const response = await this.client.request(channelVerify)
const expectedResponse: ChannelVerifyResponse = {
id: response.id,
type: 'response',
result: {
signature_verified: response.result.signature_verified,
},
}
assert.deepEqual(response, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const channelVerify: ChannelVerifyRequest = {
command: 'channel_verify',
channel_id:
'5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3',
signature:
'304402204EF0AFB78AC23ED1C472E74F4299C0C21F1B21D07EFC0A3838A420F76D783A400220154FB11B6F54320666E4C36CA7F686C16A3A0456800BBC43746F34AF50290064',
public_key: 'aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3',
amount: '1000000',
}
const response = await testContext.client.request(channelVerify)
const expectedResponse: ChannelVerifyResponse = {
id: response.id,
type: 'response',
result: {
signature_verified: response.result.signature_verified,
},
}
assert.deepEqual(response, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,42 +1,54 @@
import { assert } from 'chai'
import _ from 'lodash'
import { DepositAuthorizedRequest, DepositAuthorizedResponse } from 'xrpl-local'
import {
DepositAuthorizedRequest,
DepositAuthorizedResponse,
} from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('deposit_authorized', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const depositAuthorized: DepositAuthorizedRequest = {
command: 'deposit_authorized',
source_account: this.wallet.classicAddress,
destination_account: wallet2.classicAddress,
}
const response = await this.client.request(depositAuthorized)
const expectedResponse: DepositAuthorizedResponse = {
id: response.id,
type: 'response',
result: {
deposit_authorized: true,
destination_account: depositAuthorized.destination_account,
ledger_current_index: response.result.ledger_current_index,
source_account: depositAuthorized.source_account,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const depositAuthorized: DepositAuthorizedRequest = {
command: 'deposit_authorized',
source_account: testContext.wallet.classicAddress,
destination_account: wallet2.classicAddress,
}
const response = await testContext.client.request(depositAuthorized)
const expectedResponse: DepositAuthorizedResponse = {
id: response.id,
type: 'response',
result: {
deposit_authorized: true,
destination_account: depositAuthorized.destination_account,
ledger_current_index: response.result.ledger_current_index,
source_account: depositAuthorized.source_account,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,52 +1,62 @@
import { assert } from 'chai'
import _ from 'lodash'
import { FeeRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { FeeRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('fee', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: FeeRequest = {
command: 'fee',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
current_ledger_size: '0',
current_queue_size: '0',
drops: {
base_fee: '10',
median_fee: '5000',
minimum_fee: '10',
open_ledger_fee: '10',
},
expected_ledger_size: '1000',
ledger_current_index: 2925,
levels: {
median_level: '128000',
minimum_level: '256',
open_ledger_level: '256',
reference_level: '256',
},
max_queue_size: '20000',
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_current_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_current_index']),
_.omit(expected.result, ['ledger_current_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: FeeRequest = {
command: 'fee',
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
current_ledger_size: '0',
current_queue_size: '0',
drops: {
base_fee: '10',
median_fee: '5000',
minimum_fee: '10',
open_ledger_fee: '10',
},
expected_ledger_size: '1000',
ledger_current_index: 2925,
levels: {
median_level: '128000',
minimum_level: '256',
open_ledger_level: '256',
reference_level: '256',
},
max_queue_size: '20000',
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_current_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_current_index']),
omit(expected.result, ['ledger_current_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,44 +1,54 @@
import { assert } from 'chai'
import _ from 'lodash'
import { GatewayBalancesRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { GatewayBalancesRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('gateway_balances', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: GatewayBalancesRequest = {
command: 'gateway_balances',
account: this.wallet.classicAddress,
ledger_index: 'validated',
strict: true,
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
account: this.wallet.classicAddress,
ledger_hash:
'28D68B351ED58B9819502EF5FC05BA4412A048597E5159E1C226703BDF7C7897',
ledger_index: 1294,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
_.omit(response.result, ['ledger_hash', 'ledger_index']),
_.omit(expected.result, ['ledger_hash', 'ledger_index']),
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: GatewayBalancesRequest = {
command: 'gateway_balances',
account: testContext.wallet.classicAddress,
ledger_index: 'validated',
strict: true,
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
account: testContext.wallet.classicAddress,
ledger_hash:
'28D68B351ED58B9819502EF5FC05BA4412A048597E5159E1C226703BDF7C7897',
ledger_index: 1294,
validated: true,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.ledger_hash, 'string')
assert.equal(typeof response.result.ledger_index, 'number')
assert.deepEqual(
omit(response.result, ['ledger_hash', 'ledger_index']),
omit(expected.result, ['ledger_hash', 'ledger_index']),
)
},
TIMEOUT,
)
})

View File

@@ -1,75 +1,84 @@
import { assert } from 'chai'
import _ from 'lodash'
import { LedgerRequest, LedgerResponse } from 'xrpl-local'
import { Ledger } from 'xrpl-local/models/ledger'
import { LedgerRequest, LedgerResponse } from '../../../src'
import { Ledger } from '../../../src/models/ledger'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('ledger', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const ledgerRequest: LedgerRequest = {
command: 'ledger',
ledger_index: 'validated',
}
const expected = {
id: 0,
result: {
ledger: {
accepted: true,
account_hash: 'string',
close_flags: 0,
close_time: 0,
close_time_human: 'string',
},
ledger_hash: 'string',
ledger_index: 1,
validated: true,
},
type: 'response',
}
const ledgerResponse: LedgerResponse = await this.client.request(
ledgerRequest,
)
assert.equal(ledgerResponse.type, expected.type)
assert.equal(ledgerResponse.result.validated, expected.result.validated)
assert.typeOf(ledgerResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerResponse.result.ledger_index, 'number')
const ledger = ledgerResponse.result.ledger as Ledger & {
accepted: boolean
hash: string
seqNum: string
}
assert.equal(ledger.closed, true)
const stringTypes = [
'account_hash',
'close_time_human',
'ledger_hash',
'ledger_index',
'parent_hash',
'total_coins',
'transaction_hash',
]
stringTypes.forEach((strType) => assert.typeOf(ledger[strType], 'string'))
const numTypes = [
'close_flags',
'close_time',
'close_time_resolution',
'parent_close_time',
]
numTypes.forEach((numType) => assert.typeOf(ledger[numType], 'number'))
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const ledgerRequest: LedgerRequest = {
command: 'ledger',
ledger_index: 'validated',
}
const expected = {
id: 0,
result: {
ledger: {
accepted: true,
account_hash: 'string',
close_flags: 0,
close_time: 0,
close_time_human: 'string',
},
ledger_hash: 'string',
ledger_index: 1,
validated: true,
},
type: 'response',
}
const ledgerResponse: LedgerResponse = await testContext.client.request(
ledgerRequest,
)
assert.equal(ledgerResponse.type, expected.type)
assert.equal(ledgerResponse.result.validated, expected.result.validated)
assert.typeOf(ledgerResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerResponse.result.ledger_index, 'number')
const ledger = ledgerResponse.result.ledger as Ledger & {
accepted: boolean
hash: string
seqNum: string
}
assert.equal(ledger.closed, true)
const stringTypes = [
'account_hash',
'close_time_human',
'ledger_hash',
'ledger_index',
'parent_hash',
'total_coins',
'transaction_hash',
]
stringTypes.forEach((strType) => assert.typeOf(ledger[strType], 'string'))
const numTypes = [
'close_flags',
'close_time',
'close_time_resolution',
'parent_close_time',
]
numTypes.forEach((numType) => assert.typeOf(ledger[numType], 'number'))
},
TIMEOUT,
)
})

View File

@@ -1,36 +1,45 @@
import { assert } from 'chai'
import _ from 'lodash'
import { LedgerClosedRequest, LedgerClosedResponse } from 'xrpl-local'
import { LedgerClosedRequest, LedgerClosedResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('ledger_closed', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const ledgerClosedRequest: LedgerClosedRequest = {
command: 'ledger_closed',
}
const ledgerClosedResponse: LedgerClosedResponse =
await this.client.request(ledgerClosedRequest)
const expectedResponse: LedgerClosedResponse = {
id: ledgerClosedResponse.id,
type: 'response',
result: {
ledger_hash: 'string',
ledger_index: 1,
},
}
assert.equal(ledgerClosedResponse.type, expectedResponse.type)
assert.typeOf(ledgerClosedResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerClosedResponse.result.ledger_index, 'number')
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const ledgerClosedRequest: LedgerClosedRequest = {
command: 'ledger_closed',
}
const ledgerClosedResponse: LedgerClosedResponse =
await testContext.client.request(ledgerClosedRequest)
const expectedResponse: LedgerClosedResponse = {
id: ledgerClosedResponse.id,
type: 'response',
result: {
ledger_hash: 'string',
ledger_index: 1,
},
}
assert.equal(ledgerClosedResponse.type, expectedResponse.type)
assert.typeOf(ledgerClosedResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerClosedResponse.result.ledger_index, 'number')
},
TIMEOUT,
)
})

View File

@@ -1,36 +1,45 @@
import { assert } from 'chai'
import _ from 'lodash'
import { LedgerCurrentResponse, LedgerCurrentRequest } from 'xrpl-local'
import { LedgerCurrentResponse, LedgerCurrentRequest } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('ledger_current', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const ledgerCurrentRequest: LedgerCurrentRequest = {
command: 'ledger_current',
}
const ledgerCurrentResponse = await this.client.request(
ledgerCurrentRequest,
)
const expectedResponse: LedgerCurrentResponse = {
id: ledgerCurrentResponse.id,
type: 'response',
result: {
ledger_current_index: 1,
},
}
assert.equal(ledgerCurrentResponse.type, expectedResponse.type)
assert.typeOf(ledgerCurrentResponse.result.ledger_current_index, 'number')
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const ledgerCurrentRequest: LedgerCurrentRequest = {
command: 'ledger_current',
}
const ledgerCurrentResponse = await testContext.client.request(
ledgerCurrentRequest,
)
const expectedResponse: LedgerCurrentResponse = {
id: ledgerCurrentResponse.id,
type: 'response',
result: {
ledger_current_index: 1,
},
}
assert.equal(ledgerCurrentResponse.type, expectedResponse.type)
assert.typeOf(ledgerCurrentResponse.result.ledger_current_index, 'number')
},
TIMEOUT,
)
})

View File

@@ -1,55 +1,67 @@
import { assert } from 'chai'
import _ from 'lodash'
import { LedgerDataRequest } from 'xrpl-local'
import { LedgerDataRequest } from '../../../src'
import type { BinaryLedgerEntry } from '../../../src/models/methods/ledgerData'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('ledger_data', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const ledgerDataRequest: LedgerDataRequest = {
command: 'ledger_data',
ledger_index: 'validated',
limit: 5,
binary: true,
}
const ledgerDataResponse = await this.client.request(ledgerDataRequest)
const expected = {
id: 0,
result: {
ledger_hash: 'string',
ledger_index: 0,
marker: 'string',
state: [
{
data: 'string',
index: 'string',
},
],
},
type: 'response',
}
assert.equal(ledgerDataResponse.type, expected.type)
assert.typeOf(ledgerDataResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerDataResponse.result.ledger_index, 'number')
assert.typeOf(ledgerDataResponse.result.marker, 'string')
assert.equal(ledgerDataResponse.result.state.length, 5)
ledgerDataResponse.result.state.forEach((item) => {
assert.typeOf(item.data, 'string')
assert.typeOf(item.index, 'string')
})
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const ledgerDataRequest: LedgerDataRequest = {
command: 'ledger_data',
ledger_index: 'validated',
limit: 5,
binary: true,
}
const ledgerDataResponse = await testContext.client.request(
ledgerDataRequest,
)
const expected = {
id: 0,
result: {
ledger_hash: 'string',
ledger_index: 0,
marker: 'string',
state: [
{
data: 'string',
index: 'string',
},
],
},
type: 'response',
}
assert.equal(ledgerDataResponse.type, expected.type)
assert.typeOf(ledgerDataResponse.result.ledger_hash, 'string')
assert.typeOf(ledgerDataResponse.result.ledger_index, 'number')
assert.typeOf(ledgerDataResponse.result.marker, 'string')
assert.equal(ledgerDataResponse.result.state.length, 5)
ledgerDataResponse.result.state.forEach((item) => {
assert.typeOf((item as BinaryLedgerEntry).data, 'string')
assert.typeOf(item.index, 'string')
})
},
TIMEOUT,
)
})

View File

@@ -1,47 +1,58 @@
import { assert } from 'chai'
import _ from 'lodash'
import { LedgerEntryRequest, LedgerEntryResponse } from 'xrpl-local'
import { LedgerEntryRequest, LedgerEntryResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('ledger_entry', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const validatedLedgerResponse = await this.client.request({
command: 'ledger_data',
ledger_index: 'validated',
})
assert.equal(validatedLedgerResponse.type, 'response')
const ledgerEntryIndex = validatedLedgerResponse.result.state[0].index
const ledgerEntryRequest: LedgerEntryRequest = {
command: 'ledger_entry',
index: ledgerEntryIndex,
}
const ledgerEntryResponse = await this.client.request(ledgerEntryRequest)
const expectedResponse: LedgerEntryResponse = {
id: ledgerEntryResponse.id,
type: 'response',
result: {
index: ledgerEntryIndex,
ledger_current_index: ledgerEntryResponse.result.ledger_current_index,
node: ledgerEntryResponse.result.node,
validated: false,
},
}
assert.equal(ledgerEntryResponse.type, 'response')
assert.deepEqual(ledgerEntryResponse, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const validatedLedgerResponse = await testContext.client.request({
command: 'ledger_data',
ledger_index: 'validated',
})
assert.equal(validatedLedgerResponse.type, 'response')
const ledgerEntryIndex = validatedLedgerResponse.result.state[0].index
const ledgerEntryRequest: LedgerEntryRequest = {
command: 'ledger_entry',
index: ledgerEntryIndex,
}
const ledgerEntryResponse = await testContext.client.request(
ledgerEntryRequest,
)
const expectedResponse: LedgerEntryResponse = {
id: ledgerEntryResponse.id,
type: 'response',
result: {
index: ledgerEntryIndex,
ledger_current_index: ledgerEntryResponse.result.ledger_current_index,
node: ledgerEntryResponse.result.node,
validated: false,
},
}
assert.equal(ledgerEntryResponse.type, 'response')
assert.deepEqual(ledgerEntryResponse, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,57 +1,70 @@
import { assert } from 'chai'
import _ from 'lodash'
import { NoRippleCheckRequest } from 'xrpl-local'
import { NoRippleCheckRequest, type AccountSet } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('noripple_check', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const request: NoRippleCheckRequest = {
command: 'noripple_check',
account: this.wallet.classicAddress,
role: 'gateway',
ledger_index: 'current',
transactions: true,
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
ledger_current_index: 2535,
problems: ['You should immediately set your default ripple flag'],
transactions: [
{
Account: this.wallet.classicAddress,
Fee: 10,
Sequence: 1268,
SetFlag: 8,
TransactionType: 'AccountSet',
},
],
validated: false,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.transactions[0].Fee, 'number')
assert.equal(typeof response.result.transactions[0].Sequence, 'number')
assert.equal(typeof response.result.problems, 'object')
assert.equal(typeof response.result.problems[0], 'string')
const responseTx = response.result.transactions[0]
const expectedTx = expected.result.transactions[0]
assert.deepEqual(
[responseTx.Account, responseTx.SetFlag, responseTx.TransactionType],
[expectedTx.Account, expectedTx.SetFlag, expectedTx.TransactionType],
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const request: NoRippleCheckRequest = {
command: 'noripple_check',
account: testContext.wallet.classicAddress,
role: 'gateway',
ledger_index: 'current',
transactions: true,
}
const response = await testContext.client.request(request)
const expected = {
id: 0,
result: {
ledger_current_index: 2535,
problems: ['You should immediately set your default ripple flag'],
transactions: [
{
Account: testContext.wallet.classicAddress,
Fee: 10,
Sequence: 1268,
SetFlag: 8,
TransactionType: 'AccountSet',
},
],
validated: false,
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.transactions[0].Fee, 'number')
assert.equal(typeof response.result.transactions[0].Sequence, 'number')
assert.equal(typeof response.result.problems, 'object')
assert.equal(typeof response.result.problems[0], 'string')
const responseTx = response.result.transactions[0]
const expectedTx = expected.result.transactions[0]
assert.deepEqual(
[
responseTx.Account,
(responseTx as AccountSet).SetFlag,
responseTx.TransactionType,
],
[expectedTx.Account, expectedTx.SetFlag, expectedTx.TransactionType],
)
},
TIMEOUT,
)
})

View File

@@ -1,107 +1,121 @@
import { assert } from 'chai'
import _ from 'lodash'
import omit from 'lodash/omit'
import {
PathFindRequest,
PathFindResponse,
Client,
PathFindStream,
} from 'xrpl-local'
} from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, ledgerAccept, subscribeDone } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('path_find', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const pathFind: PathFindRequest = {
command: 'path_find',
subcommand: 'create',
source_account: this.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
const response = await this.client.request(pathFind)
const expectedResponse: PathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
source_account: pathFind.source_account,
full_reply: false,
id: response.id,
},
}
assert.deepEqual(response, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const pathFind: PathFindRequest = {
command: 'path_find',
subcommand: 'create',
source_account: testContext.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
const response = await testContext.client.request(pathFind)
const expectedResponse: PathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
source_account: pathFind.source_account,
full_reply: false,
id: response.id,
},
}
assert.deepEqual(response, expectedResponse)
},
TIMEOUT,
)
/**
* For other stream style tests look at integration/requests/subscribe.ts
* Note: This test uses '.then' to avoid awaits in order to use 'done' style tests.
*/
it('path_find stream succeeds', function (done) {
generateFundedWallet(this.client)
.then((wallet2) => {
const pathFind: PathFindRequest = {
command: 'path_find',
subcommand: 'create',
source_account: this.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
it(
'path_find stream succeeds',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const pathFind: PathFindRequest = {
command: 'path_find',
subcommand: 'create',
source_account: testContext.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
const expectedStreamResult: PathFindStream = {
type: 'path_find',
source_account: pathFind.source_account,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
full_reply: true,
id: 10,
alternatives: [],
}
const expectedStreamResult: PathFindStream = {
type: 'path_find',
source_account: pathFind.source_account,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
full_reply: true,
id: 10,
alternatives: [],
}
const client: Client = this.client
const client: Client = testContext.client
const pathFindPromise = new Promise<void>((resolve) => {
client.on('path_find', (path) => {
assert.equal(path.type, 'path_find')
assert.deepEqual(
_.omit(path, 'id'),
_.omit(expectedStreamResult, 'id'),
)
subscribeDone(this.client, done)
})
this.client.request(pathFind).then((response) => {
const expectedResponse: PathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
source_account: pathFind.source_account,
full_reply: false,
id: response.id,
},
}
assert.deepEqual(response, expectedResponse)
assert.deepEqual(omit(path, 'id'), omit(expectedStreamResult, 'id'))
subscribeDone(testContext.client)
resolve()
})
})
.then(() => {
ledgerAccept(this.client)
})
})
const response = await testContext.client.request(pathFind)
const expectedResponse: PathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: pathFind.destination_account,
destination_amount: pathFind.destination_amount,
source_account: pathFind.source_account,
full_reply: false,
id: response.id,
},
}
assert.deepEqual(response, expectedResponse)
await ledgerAccept(testContext.client)
await pathFindPromise
},
TIMEOUT,
)
})

View File

@@ -1,48 +1,57 @@
import { assert } from 'chai'
import _ from 'lodash'
import { RipplePathFindRequest, RipplePathFindResponse } from 'xrpl-local'
import { RipplePathFindRequest, RipplePathFindResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('ripple_path_find', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const ripplePathFind: RipplePathFindRequest = {
command: 'ripple_path_find',
subcommand: 'create',
source_account: this.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
const response = await this.client.request(ripplePathFind)
const expectedResponse: RipplePathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: wallet2.classicAddress,
destination_currencies: response.result.destination_currencies,
destination_amount: ripplePathFind.destination_amount,
full_reply: true,
id: response.id,
ledger_current_index: response.result.ledger_current_index,
source_account: ripplePathFind.source_account,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const ripplePathFind: RipplePathFindRequest = {
command: 'ripple_path_find',
subcommand: 'create',
source_account: testContext.wallet.classicAddress,
destination_account: wallet2.classicAddress,
destination_amount: '100',
}
const response = await testContext.client.request(ripplePathFind)
const expectedResponse: RipplePathFindResponse = {
id: response.id,
type: 'response',
result: {
alternatives: response.result.alternatives,
destination_account: wallet2.classicAddress,
destination_currencies: response.result.destination_currencies,
destination_amount: ripplePathFind.destination_amount,
full_reply: true,
id: response.id,
ledger_current_index: response.result.ledger_current_index,
source_account: ripplePathFind.source_account,
validated: false,
},
}
assert.deepEqual(response, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,132 +1,149 @@
import { assert } from 'chai'
import _ from 'lodash'
import { ServerInfoRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { type ServerInfoRequest, type ServerInfoResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('server_info', function () {
this.timeout(TIMEOUT)
describe('server_info (rippled)', function () {
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('base', async function () {
const request: ServerInfoRequest = {
command: 'server_info',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
info: {
build_version: '1.7.3',
complete_ledgers: '2563-2928',
hostid: '44578fe64241',
io_latency_ms: 1,
jq_trans_overflow: '0',
last_close: { converge_time_s: 0.1, proposers: 0 },
load: {
job_types: [
{
in_progress: 1,
job_type: 'clientCommand',
peak_time: 4,
per_second: 9,
},
{ job_type: 'updatePaths', per_second: 1 },
{ job_type: 'advanceLedger', per_second: 1 },
{ job_type: 'pathFind', per_second: 1 },
{ job_type: 'WriteNode', per_second: 17 },
],
threads: 1,
},
load_factor: 1,
peer_disconnects: '0',
peer_disconnects_resources: '0',
peers: 0,
pubkey_node: 'n9K6DaaReKkCjb9sEfXh5xP3BV9JisrJ9biKB3CSSFXancBnv5cW',
pubkey_validator: 'none',
server_state: 'full',
server_state_duration_us: '8752395105',
state_accounting: {
connected: { duration_us: '0', transitions: 0 },
disconnected: { duration_us: '41860', transitions: 1 },
full: { duration_us: '20723121268', transitions: 1 },
syncing: { duration_us: '0', transitions: 0 },
tracking: { duration_us: '0', transitions: 0 },
},
time: '2021-Sep-23 22:56:55.320858 UTC',
uptime: 8752,
validated_ledger: {
age: 0,
base_fee_xrp: 0.00001,
hash: '532175EC25CF34081D7F83584F37DAB70035A422CBE94352BEDA8EC123CB8F60',
reserve_base_xrp: 200,
reserve_inc_xrp: 50,
seq: 1906,
},
validation_quorum: 0,
validator_list: {
count: 0,
expiration: 'unknown',
status: 'unknown',
it(
'base',
async () => {
const request: ServerInfoRequest = {
command: 'server_info',
}
const response = await testContext.client.request(request)
const expected: ServerInfoResponse = {
id: 0,
result: {
info: {
build_version: '1.7.3',
complete_ledgers: '2563-2928',
hostid: '44578fe64241',
io_latency_ms: 1,
jq_trans_overflow: '0',
last_close: { converge_time_s: 0.1, proposers: 0 },
load: {
job_types: [
{
in_progress: 1,
job_type: 'clientCommand',
peak_time: 4,
per_second: 9,
},
{ job_type: 'updatePaths', per_second: 1 },
{ job_type: 'advanceLedger', per_second: 1 },
{ job_type: 'pathFind', per_second: 1 },
{ job_type: 'WriteNode', per_second: 17 },
],
threads: 1,
},
load_factor: 1,
peer_disconnects: '0',
peer_disconnects_resources: '0',
peers: 0,
pubkey_node: 'n9K6DaaReKkCjb9sEfXh5xP3BV9JisrJ9biKB3CSSFXancBnv5cW',
pubkey_validator: 'none',
server_state: 'full',
server_state_duration_us: '8752395105',
state_accounting: {
connected: { duration_us: '0', transitions: 0 },
disconnected: { duration_us: '41860', transitions: 1 },
full: { duration_us: '20723121268', transitions: 1 },
syncing: { duration_us: '0', transitions: 0 },
tracking: { duration_us: '0', transitions: 0 },
},
time: '2021-Sep-23 22:56:55.320858 UTC',
uptime: 8752,
validated_ledger: {
age: 0,
base_fee_xrp: 0.00001,
hash: '532175EC25CF34081D7F83584F37DAB70035A422CBE94352BEDA8EC123CB8F60',
reserve_base_xrp: 200,
reserve_inc_xrp: 50,
seq: 1906,
},
validation_quorum: 0,
validator_list: {
count: 0,
expiration: 'unknown',
status: 'unknown',
},
},
},
},
type: 'response',
}
assert.equal(response.type, expected.type)
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.info.time, 'string')
assert.equal(typeof response.result.info.uptime, 'number')
assert.equal(typeof response.result.info.complete_ledgers, 'string')
assert.equal(typeof response.result.info.hostid, 'string')
assert.equal(typeof response.result.info.pubkey_node, 'string')
assert.equal(typeof response.result.info.server_state_duration_us, 'string')
const removeKeys = [
'time',
'uptime',
'complete_ledgers',
'hostid',
'load',
'state_accounting',
'pubkey_node',
'server_state_duration_us',
'validated_ledger',
]
assert.deepEqual(
_.omit(response.result.info, removeKeys),
_.omit(expected.result.info, removeKeys),
)
// load
assert.equal(typeof response.result.info.load.threads, 'number')
for (const obj of response.result.info.load.job_types) {
assert.equal(typeof obj.per_second, 'number')
assert.equal(typeof obj.job_type, 'string')
}
// state_accounting
Object.keys(response.result.info.state_accounting).forEach(function (key) {
assert.equal(typeof response.result.info.time, 'string')
assert.equal(typeof response.result.info.uptime, 'number')
assert.equal(typeof response.result.info.complete_ledgers, 'string')
assert.equal(typeof response.result.info.hostid, 'string')
assert.equal(typeof response.result.info.pubkey_node, 'string')
assert.equal(
typeof response.result.info.state_accounting[key].duration_us,
typeof response.result.info.server_state_duration_us,
'string',
)
assert.equal(
typeof response.result.info.state_accounting[key].transitions,
'number',
const removeKeys = [
'time',
'uptime',
'complete_ledgers',
'hostid',
'load',
'state_accounting',
'pubkey_node',
'server_state_duration_us',
'validated_ledger',
]
assert.deepEqual(
omit(response.result.info, removeKeys),
omit(expected.result.info, removeKeys),
)
})
// validated_ledger
assert.equal(typeof response.result.info.validated_ledger.hash, 'string')
for (const key of Object.keys(
_.omit(response.result.info.validated_ledger, 'hash'),
)) {
assert.equal(typeof response.result.info.validated_ledger[key], 'number')
}
})
// load
assert.equal(typeof response.result.info.load?.threads, 'number')
for (const obj of response.result.info.load?.job_types ?? []) {
assert.equal(typeof obj.job_type, 'string')
}
// state_accounting
Object.keys(response.result.info.state_accounting).forEach(function (
key,
) {
assert.equal(
typeof response.result.info.state_accounting[key].duration_us,
'string',
)
assert.equal(
typeof response.result.info.state_accounting[key].transitions,
'number',
)
})
// validated_ledger
assert.equal(typeof response.result.info.validated_ledger?.hash, 'string')
for (const key of Object.keys(
omit(response.result.info.validated_ledger, 'hash'),
)) {
assert.equal(
typeof response.result.info.validated_ledger?.[key],
'number',
)
}
},
TIMEOUT,
)
})

View File

@@ -1,137 +1,156 @@
import { assert } from 'chai'
import _ from 'lodash'
import { ServerStateRequest } from 'xrpl-local'
import omit from 'lodash/omit'
import { type ServerStateRequest, type ServerStateResponse } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('server_state', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('base', async function () {
const request: ServerStateRequest = {
command: 'server_state',
}
const response = await this.client.request(request)
const expected = {
id: 0,
result: {
state: {
build_version: '1.7.3',
complete_ledgers: '2563-2932',
io_latency_ms: 1,
jq_trans_overflow: '0',
last_close: {
converge_time: 100,
proposers: 0,
it(
'base',
async () => {
const request: ServerStateRequest = {
command: 'server_state',
}
const response = await testContext.client.request(request)
const expected: ServerStateResponse = {
id: 0,
result: {
state: {
build_version: '1.7.3',
complete_ledgers: '2563-2932',
io_latency_ms: 1,
jq_trans_overflow: '0',
last_close: {
converge_time: 100,
proposers: 0,
},
load: {
job_types: [
{
in_progress: 1,
job_type: 'clientCommand',
peak_time: 4,
per_second: 9,
},
{ job_type: 'updatePaths', per_second: 1 },
{ job_type: 'advanceLedger', per_second: 1 },
{ job_type: 'pathFind', per_second: 1 },
{ job_type: 'WriteNode', per_second: 17 },
],
threads: 1,
},
load_base: 256,
load_factor: 256,
load_factor_fee_escalation: 256,
load_factor_fee_queue: 256,
load_factor_fee_reference: 256,
load_factor_server: 256,
peer_disconnects: '0',
peer_disconnects_resources: '0',
peers: 0,
pubkey_node: 'n9K6DaaReKkCjb9sEfXh5xP3BV9JisrJ9biKB3CSSFXancBnv5cW',
pubkey_validator: 'none',
server_state: 'full',
server_state_duration_us: '8752487389',
state_accounting: {
connected: { duration_us: '0', transitions: 0 },
disconnected: { duration_us: '41860', transitions: 1 },
full: { duration_us: '20723121268', transitions: 1 },
syncing: { duration_us: '0', transitions: 0 },
tracking: { duration_us: '0', transitions: 0 },
},
time: '2021-Sep-23 22:56:55.413151 UTC',
uptime: 8752,
validated_ledger: {
base_fee: 10,
close_time: 685829741,
hash: 'B98AABCE40A54DF654C86E56088AD7D46BBA8B8E93AD3FAC2426FEFF847F7937',
reserve_base: 200000000,
reserve_inc: 50000000,
seq: 2294,
},
validation_quorum: 0,
validator_list_expires: 0,
},
load: {
job_types: [
{
in_progress: 1,
job_type: 'clientCommand',
peak_time: 4,
per_second: 9,
},
{ job_type: 'updatePaths', per_second: 1 },
{ job_type: 'advanceLedger', per_second: 1 },
{ job_type: 'pathFind', per_second: 1 },
{ job_type: 'WriteNode', per_second: 17 },
],
threads: 1,
},
load_base: 256,
load_factor: 256,
load_factor_fee_escalation: 256,
load_factor_fee_queue: 256,
load_factor_fee_reference: 256,
load_factor_server: 256,
peer_disconnects: '0',
peer_disconnects_resources: '0',
peers: 0,
pubkey_node: 'n9K6DaaReKkCjb9sEfXh5xP3BV9JisrJ9biKB3CSSFXancBnv5cW',
pubkey_validator: 'none',
server_state: 'full',
server_state_duration_us: '8752487389',
state_accounting: {
connected: { duration_us: '0', transitions: 0 },
disconnected: { duration_us: '41860', transitions: 1 },
full: { duration_us: '20723121268', transitions: 1 },
syncing: { duration_us: '0', transitions: 0 },
tracking: { duration_us: '0', transitions: 0 },
},
time: '2021-Sep-23 22:56:55.413151 UTC',
uptime: 8752,
validated_ledger: {
base_fee: 10,
close_time: 685829741,
hash: 'B98AABCE40A54DF654C86E56088AD7D46BBA8B8E93AD3FAC2426FEFF847F7937',
reserve_base: 200000000,
reserve_inc: 50000000,
seq: 2294,
},
validation_quorum: 0,
validator_list_expires: 0,
},
},
type: 'response',
}
assert.equal(response.type, expected.type)
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(typeof response.result.state.complete_ledgers, 'string')
assert.equal(typeof response.result.state.pubkey_node, 'string')
assert.equal(typeof response.result.state.time, 'string')
assert.equal(typeof response.result.state.uptime, 'number')
assert.equal(
typeof response.result.state.server_state_duration_us,
'string',
)
const removeKeys = [
'complete_ledgers',
'load',
'state_accounting',
'pubkey_node',
'time',
'uptime',
'server_state_duration_us',
'validated_ledger',
]
assert.deepEqual(
_.omit(response.result.state, removeKeys),
_.omit(expected.result.state, removeKeys),
)
// load
assert.equal(typeof response.result.state.load.threads, 'number')
for (const obj of response.result.state.load.job_types) {
assert.equal(typeof obj.per_second, 'number')
assert.equal(typeof obj.job_type, 'string')
}
// state_accounting
Object.keys(response.result.state.state_accounting).forEach(function (key) {
assert.equal(typeof response.result.state.io_latency_ms, 'number')
assert.equal(typeof response.result.state.complete_ledgers, 'string')
assert.equal(typeof response.result.state.pubkey_node, 'string')
assert.equal(typeof response.result.state.time, 'string')
assert.equal(typeof response.result.state.uptime, 'number')
assert.equal(
typeof response.result.state.state_accounting[key].duration_us,
typeof response.result.state.server_state_duration_us,
'string',
)
assert.equal(
typeof response.result.state.state_accounting[key].transitions,
'number',
)
})
// validated_ledger
assert.equal(typeof response.result.state.validated_ledger.hash, 'string')
for (const key of Object.keys(
_.omit(response.result.state.validated_ledger, 'hash'),
)) {
assert.equal(typeof response.result.state.validated_ledger[key], 'number')
}
})
const removeKeys = [
'complete_ledgers',
'load',
'state_accounting',
'pubkey_node',
'time',
'uptime',
'server_state_duration_us',
'validated_ledger',
'io_latency_ms',
]
assert.deepEqual(
omit(response.result.state, removeKeys),
omit(expected.result.state, removeKeys),
)
// load
assert.equal(typeof response.result.state.load?.threads, 'number')
for (const obj of response.result.state.load?.job_types ?? []) {
assert.equal(typeof obj.job_type, 'string')
}
// state_accounting
Object.keys(response.result.state.state_accounting).forEach(function (
key,
) {
assert.equal(
typeof response.result.state.state_accounting[key].duration_us,
'string',
)
assert.equal(
typeof response.result.state.state_accounting[key].transitions,
'number',
)
})
// validated_ledger
assert.equal(
typeof response.result.state.validated_ledger?.hash,
'string',
)
for (const key of Object.keys(
omit(response.result.state.validated_ledger, 'hash'),
)) {
assert.equal(
typeof response.result.state.validated_ledger?.[key],
'number',
)
}
},
TIMEOUT,
)
})

View File

@@ -1,17 +1,20 @@
import { assert } from 'chai'
import _ from 'lodash'
import { decode } from 'ripple-binary-codec/dist'
import { decode } from 'ripple-binary-codec'
import {
AccountSet,
SubmitRequest,
SubmitResponse,
hashes,
Transaction,
} from 'xrpl-local'
import { convertStringToHex } from 'xrpl-local/utils'
} from '../../../src'
import { convertStringToHex } from '../../../src/utils'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { ledgerAccept, verifySubmittedTransaction } from '../utils'
// how long before each test case times out
@@ -19,59 +22,65 @@ const TIMEOUT = 20000
const { hashSignedTx } = hashes
describe('submit', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('submit', async function () {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const autofilledTx = await this.client.autofill(accountSet)
const signedTx = this.wallet.sign(autofilledTx)
const submitRequest: SubmitRequest = {
command: 'submit',
tx_blob: signedTx.tx_blob,
}
const submitResponse = await this.client.request(submitRequest)
await ledgerAccept(this.client)
await verifySubmittedTransaction(
this.client,
signedTx.tx_blob,
signedTx.hash,
)
const expectedResponse: SubmitResponse = {
id: submitResponse.id,
type: 'response',
result: {
engine_result: 'tesSUCCESS',
engine_result_code: 0,
engine_result_message:
'The transaction was applied. Only final in a validated ledger.',
tx_blob: signedTx.tx_blob,
tx_json: {
...(decode(signedTx.tx_blob) as unknown as Transaction),
hash: hashSignedTx(signedTx.tx_blob),
},
accepted: true,
account_sequence_available:
submitResponse.result.account_sequence_available,
account_sequence_next: submitResponse.result.account_sequence_next,
applied: true,
broadcast: submitResponse.result.broadcast,
kept: true,
queued: false,
open_ledger_cost: submitResponse.result.open_ledger_cost,
validated_ledger_index: submitResponse.result.validated_ledger_index,
},
}
assert.deepEqual(submitResponse, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'submit',
async () => {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const autofilledTx = await testContext.client.autofill(accountSet)
const signedTx = testContext.wallet.sign(autofilledTx)
const submitRequest: SubmitRequest = {
command: 'submit',
tx_blob: signedTx.tx_blob,
}
const submitResponse = await testContext.client.request(submitRequest)
await ledgerAccept(testContext.client)
await verifySubmittedTransaction(
testContext.client,
signedTx.tx_blob,
signedTx.hash,
)
const expectedResponse: SubmitResponse = {
id: submitResponse.id,
type: 'response',
result: {
engine_result: 'tesSUCCESS',
engine_result_code: 0,
engine_result_message:
'The transaction was applied. Only final in a validated ledger.',
tx_blob: signedTx.tx_blob,
tx_json: {
...(decode(signedTx.tx_blob) as unknown as Transaction),
hash: hashSignedTx(signedTx.tx_blob),
},
accepted: true,
account_sequence_available:
submitResponse.result.account_sequence_available,
account_sequence_next: submitResponse.result.account_sequence_next,
applied: true,
broadcast: submitResponse.result.broadcast,
kept: true,
queued: false,
open_ledger_cost: submitResponse.result.open_ledger_cost,
validated_ledger_index: submitResponse.result.validated_ledger_index,
},
}
assert.deepEqual(submitResponse, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,6 +1,6 @@
import { assert } from 'chai'
import _ from 'lodash'
import { decode } from 'ripple-binary-codec/dist'
import { decode } from 'ripple-binary-codec'
import {
AccountSet,
Client,
@@ -9,12 +9,15 @@ import {
Transaction,
SubmitMultisignedResponse,
hashes,
} from 'xrpl-local'
import { convertStringToHex } from 'xrpl-local/utils'
import { multisign } from 'xrpl-local/Wallet/signer'
} from '../../../src'
import { convertStringToHex } from '../../../src/utils'
import { multisign } from '../../../src/Wallet/signer'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import {
generateFundedWallet,
ledgerAccept,
@@ -27,73 +30,83 @@ const TIMEOUT = 20000
const { hashSignedTx } = hashes
describe('submit_multisigned', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('submit_multisigned transaction', async function () {
const client: Client = this.client
const signerWallet1 = await generateFundedWallet(this.client)
const signerWallet2 = await generateFundedWallet(this.client)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: this.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: signerWallet1.classicAddress,
SignerWeight: 1,
},
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
],
SignerQuorum: 2,
}
await testTransaction(this.client, signerListSet, this.wallet)
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = signerWallet1.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const multisignedRequest: SubmitMultisignedRequest = {
command: 'submit_multisigned',
tx_json: decode(multisigned) as unknown as Transaction,
}
const submitResponse = await client.request(multisignedRequest)
await ledgerAccept(client)
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(this.client, multisigned)
const expectedResponse: SubmitMultisignedResponse = {
id: submitResponse.id,
type: 'response',
result: {
engine_result: 'tesSUCCESS',
engine_result_code: 0,
engine_result_message:
'The transaction was applied. Only final in a validated ledger.',
tx_blob: multisigned,
tx_json: {
...(decode(multisigned) as unknown as Transaction),
hash: hashSignedTx(multisigned),
},
},
}
assert.deepEqual(submitResponse, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'submit_multisigned transaction',
async () => {
const client: Client = testContext.client
const signerWallet1 = await generateFundedWallet(testContext.client)
const signerWallet2 = await generateFundedWallet(testContext.client)
// set up the multisigners for the account
const signerListSet: SignerListSet = {
TransactionType: 'SignerListSet',
Account: testContext.wallet.classicAddress,
SignerEntries: [
{
SignerEntry: {
Account: signerWallet1.classicAddress,
SignerWeight: 1,
},
},
{
SignerEntry: {
Account: signerWallet2.classicAddress,
SignerWeight: 1,
},
},
],
SignerQuorum: 2,
}
await testTransaction(
testContext.client,
signerListSet,
testContext.wallet,
)
// try to multisign
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const accountSetTx = await client.autofill(accountSet, 2)
const signed1 = signerWallet1.sign(accountSetTx, true)
const signed2 = signerWallet2.sign(accountSetTx, true)
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
const multisignedRequest: SubmitMultisignedRequest = {
command: 'submit_multisigned',
tx_json: decode(multisigned) as unknown as Transaction,
}
const submitResponse = await client.request(multisignedRequest)
await ledgerAccept(client)
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
await verifySubmittedTransaction(testContext.client, multisigned)
const expectedResponse: SubmitMultisignedResponse = {
id: submitResponse.id,
type: 'response',
result: {
engine_result: 'tesSUCCESS',
engine_result_code: 0,
engine_result_message:
'The transaction was applied. Only final in a validated ledger.',
tx_blob: multisigned,
tx_json: {
...(decode(multisigned) as unknown as Transaction),
hash: hashSignedTx(multisigned),
},
},
}
assert.deepEqual(submitResponse, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,34 +1,38 @@
import { assert } from 'chai'
import _ from 'lodash'
import {
Client,
OfferCreate,
SubscribeRequest,
Wallet,
SubscribeResponse,
} from 'xrpl-local'
import { StreamType } from 'xrpl-local/models/common'
} from '../../../src'
import { StreamType } from '../../../src/models/common'
import type { LedgerStreamResponse } from '../../../src/models/methods/subscribe'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { ledgerAccept, subscribeDone, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
// Note: This test use '.then' to avoid awaits in order to use 'done' style tests.
// eslint-disable-next-line max-params -- Helps keep things well-typed
async function createTxHandlerTest(
client: Client,
wallet: Wallet,
done: Mocha.Done,
subscriptionStream: StreamType,
): Promise<void> {
const txStream = 'transaction'
client.on(txStream, (tx) => {
assert.equal(tx.type, txStream)
subscribeDone(client, done)
const transactionPromise = new Promise<void>((resolve) => {
client.on(txStream, (tx) => {
assert.equal(tx.type, txStream)
subscribeDone(client)
resolve()
})
})
const request: SubscribeRequest = {
@@ -37,17 +41,21 @@ async function createTxHandlerTest(
accounts: [wallet.classicAddress],
}
client.request(request).then((response) => {
assert.equal(response.type, 'response')
assert.deepEqual(response.result, {})
})
const response = await client.request(request)
assert.equal(response.type, 'response')
assert.deepEqual(response.result, {})
return transactionPromise
}
describe('subscribe', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
/**
* Subscribe streams which are not testable with just a standalone node:
@@ -60,17 +68,21 @@ describe('subscribe', function () {
* 'server'.
*/
it('Successfully Subscribes', async function () {
const response: SubscribeResponse = await this.client.request({
command: 'subscribe',
})
it(
'Successfully Subscribes',
async () => {
const response: SubscribeResponse = await testContext.client.request({
command: 'subscribe',
})
assert.deepEqual(response.result, {})
assert.equal(response.type, 'response')
})
assert.deepEqual(response.result, {})
assert.equal(response.type, 'response')
},
TIMEOUT,
)
it('Successfully Unsubscribes', async function () {
const response = await this.client.request({
const response = await testContext.client.request({
command: 'unsubscribe',
})
@@ -78,90 +90,111 @@ describe('subscribe', function () {
assert.equal(response.type, 'response')
})
it('Emits transaction', function (done) {
const streamType = 'transactions'
createTxHandlerTest(this.client, this.wallet, done, streamType).then(() => {
it(
'Emits transaction',
async () => {
const streamType = 'transactions'
const transactionPromise = createTxHandlerTest(
testContext.client,
testContext.wallet,
streamType,
)
// Trigger the event
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: this.wallet.classicAddress,
Account: testContext.wallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: this.wallet.classicAddress,
issuer: testContext.wallet.classicAddress,
value: '10',
},
}
await testTransaction(testContext.client, tx, testContext.wallet)
await transactionPromise
},
TIMEOUT,
)
testTransaction(this.client, tx, this.wallet)
})
})
it(
'Emits transaction on transactions_proposed',
async () => {
const transactionPromise = createTxHandlerTest(
testContext.client,
testContext.wallet,
'transactions_proposed',
)
it('Emits transaction on transactions_proposed', function (done) {
createTxHandlerTest(
this.client,
this.wallet,
done,
'transactions_proposed',
).then(() => {
const tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: this.wallet.classicAddress,
Account: testContext.wallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: this.wallet.classicAddress,
issuer: testContext.wallet.classicAddress,
value: '10',
},
}
// The transactions_proposed stream should trigger the transaction handler WITHOUT ledgerAccept
const client: Client = this.client
client.submit(tx, { wallet: this.wallet })
})
})
await testContext.client.submit(tx, { wallet: testContext.wallet })
await transactionPromise
},
TIMEOUT,
)
// Note: This test use '.then' to avoid awaits in order to use 'done' style tests.
it('Emits ledger', function (done) {
const request: SubscribeRequest = {
command: 'subscribe',
streams: ['ledger'],
accounts: [this.wallet.classicAddress],
}
this.client.request(request).then((response) => {
// Explicitly checking that there are only known fields in the return
const expectedResult = {
fee_base: response.result.fee_base,
fee_ref: response.result.fee_ref,
ledger_hash: response.result.ledger_hash,
ledger_index: response.result.ledger_index,
ledger_time: response.result.ledger_time,
reserve_base: response.result.reserve_base,
reserve_inc: response.result.reserve_inc,
validated_ledgers: response.result.validated_ledgers,
it(
'Emits ledger',
async () => {
const request: SubscribeRequest = {
command: 'subscribe',
streams: ['ledger'],
accounts: [testContext.wallet.classicAddress],
}
assert.equal(response.type, 'response')
assert.deepEqual(response.result, expectedResult)
await testContext.client.request(request).then(async (response) => {
const ledgerResponse: LedgerStreamResponse =
response.result as LedgerStreamResponse
// Explicitly checking that there are only known fields in the return
const expectedResult = {
fee_base: ledgerResponse.fee_base,
fee_ref: ledgerResponse.fee_ref,
ledger_hash: ledgerResponse.ledger_hash,
ledger_index: ledgerResponse.ledger_index,
ledger_time: ledgerResponse.ledger_time,
reserve_base: ledgerResponse.reserve_base,
reserve_inc: ledgerResponse.reserve_inc,
validated_ledgers: ledgerResponse.validated_ledgers,
}
const client: Client = this.client
client.on('ledgerClosed', (ledger) => {
// Fields that are expected to change between the initial test and now are updated
assert.deepEqual(ledger, {
...expectedResult,
type: 'ledgerClosed',
txn_count: ledger.txn_count,
ledger_hash: ledger.ledger_hash,
ledger_index: parseInt(expectedResult.ledger_index, 10) + 1,
ledger_time: ledger.ledger_time,
validated_ledgers: ledger.validated_ledgers,
assert.equal(response.type, 'response')
assert.deepEqual(response.result, expectedResult)
const client: Client = testContext.client
const ledgerClosedPromise = new Promise<void>((resolve) => {
client.on('ledgerClosed', (ledger) => {
// Fields that are expected to change between the initial test and now are updated
assert.deepEqual(ledger, {
...expectedResult,
type: 'ledgerClosed',
txn_count: ledger.txn_count,
ledger_hash: ledger.ledger_hash,
ledger_index:
parseInt(expectedResult.ledger_index.toString(), 10) + 1,
ledger_time: ledger.ledger_time,
validated_ledgers: ledger.validated_ledgers,
})
subscribeDone(testContext.client)
resolve()
})
})
subscribeDone(this.client, done)
})
// Trigger the event
ledgerAccept(this.client)
})
})
// Trigger the event
await ledgerAccept(testContext.client)
await ledgerClosedPromise
})
},
TIMEOUT,
)
})

View File

@@ -1,55 +1,67 @@
import { assert } from 'chai'
import _ from 'lodash'
import { AccountSet, hashes, SubmitResponse, TxResponse } from 'xrpl-local'
import { convertStringToHex } from 'xrpl-local/utils'
import { AccountSet, hashes, SubmitResponse, TxResponse } from '../../../src'
import { convertStringToHex } from '../../../src/utils'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
const { hashSignedTx } = hashes
describe('tx', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const account = this.wallet.classicAddress
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: account,
Domain: convertStringToHex('example.com'),
}
const response: SubmitResponse = await this.client.submit(accountSet, {
wallet: this.wallet,
})
const hash = hashSignedTx(response.result.tx_blob)
const txResponse = await this.client.request({
command: 'tx',
transaction: hash,
})
const expectedResponse: TxResponse = {
id: txResponse.id,
type: 'response',
result: {
...accountSet,
Fee: txResponse.result.Fee,
Flags: 0,
LastLedgerSequence: txResponse.result.LastLedgerSequence,
Sequence: txResponse.result.Sequence,
SigningPubKey: this.wallet.publicKey,
TxnSignature: txResponse.result.TxnSignature,
hash: hashSignedTx(response.result.tx_blob),
validated: false,
},
}
assert.deepEqual(txResponse, expectedResponse)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const account = testContext.wallet.classicAddress
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: account,
Domain: convertStringToHex('example.com'),
}
const response: SubmitResponse = await testContext.client.submit(
accountSet,
{
wallet: testContext.wallet,
},
)
const hash = hashSignedTx(response.result.tx_blob)
const txResponse = await testContext.client.request({
command: 'tx',
transaction: hash,
})
const expectedResponse: TxResponse = {
id: txResponse.id,
type: 'response',
result: {
...accountSet,
Fee: txResponse.result.Fee,
Flags: 0,
LastLedgerSequence: txResponse.result.LastLedgerSequence,
Sequence: txResponse.result.Sequence,
SigningPubKey: testContext.wallet.publicKey,
TxnSignature: txResponse.result.TxnSignature,
hash: hashSignedTx(response.result.tx_blob),
validated: false,
},
}
assert.deepEqual(txResponse, expectedResponse)
},
TIMEOUT,
)
})

View File

@@ -1,42 +1,55 @@
import { assert } from 'chai'
import _ from 'lodash'
import { Client } from 'xrpl-local'
import omit from 'lodash/omit'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
// how long before each test case times out
const TIMEOUT = 20000
describe('Utility method integration tests', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('ping', async function () {
const response = await (this.client as Client).request({
command: 'ping',
})
const expected: unknown = {
result: { role: 'admin', unlimited: true },
type: 'response',
}
assert.deepEqual(_.omit(response, 'id'), expected)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('random', async function () {
const response = await (this.client as Client).request({
command: 'random',
})
const expected = {
id: 0,
result: {
random: '[random string of 64 bytes]',
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.random.length, 64)
})
it(
'ping',
async () => {
const response = await testContext.client.request({
command: 'ping',
})
const expected: unknown = {
result: { role: 'admin', unlimited: true },
type: 'response',
}
assert.deepEqual(omit(response, 'id'), expected)
},
TIMEOUT,
)
it(
'random',
async () => {
const response = await testContext.client.request({
command: 'random',
})
const expected = {
id: 0,
result: {
random: '[random string of 64 bytes]',
},
type: 'response',
}
assert.equal(response.type, expected.type)
assert.equal(response.result.random.length, 64)
},
TIMEOUT,
)
})

View File

@@ -1,26 +1,46 @@
import { Client, Wallet } from 'xrpl-local'
import { Client, Wallet } from '../../src'
import serverUrl from './serverUrl'
import { fundAccount } from './utils'
export async function teardownClient(this: Mocha.Context): Promise<void> {
this.client.removeAllListeners()
this.client.disconnect()
export interface XrplIntegrationTestContext {
client: Client
wallet: Wallet
}
export async function teardownClient(
context: XrplIntegrationTestContext,
): Promise<void> {
context.client.removeAllListeners()
return context.client.disconnect()
}
async function connectWithRetry(client: Client, tries = 0): Promise<void> {
return client.connect().catch(async (error) => {
if (tries < 10) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(connectWithRetry(client, tries + 1))
}, 1000)
})
}
throw error
})
}
export async function setupClient(
this: Mocha.Context,
server = serverUrl,
): Promise<void> {
this.wallet = Wallet.generate()
return new Promise<void>((resolve, reject) => {
this.client = new Client(server)
this.client
.connect()
.then(async () => {
await fundAccount(this.client, this.wallet)
resolve()
})
.catch(reject)
): Promise<XrplIntegrationTestContext> {
const context: XrplIntegrationTestContext = {
client: new Client(server, { timeout: 200000 }),
wallet: Wallet.generate(),
}
return connectWithRetry(context.client).then(async () => {
await fundAccount(context.client, context.wallet, {
count: 20,
delayMs: 1000,
})
return context
})
}

View File

@@ -1,91 +1,149 @@
/* eslint-disable @typescript-eslint/no-misused-promises -- supposed to return a promise here */
/* eslint-disable no-restricted-syntax -- not sure why this rule is here, definitely not needed here */
import { assert } from 'chai'
import _ from 'lodash'
import { AccountSet, convertStringToHex, ValidationError } from 'xrpl-local'
import { AccountSet, convertStringToHex, ValidationError } from '../../src'
import { assertRejects } from '../testUtils'
import serverUrl from './serverUrl'
import { setupClient, teardownClient } from './setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from './setup'
import { ledgerAccept } from './utils'
// how long before each test case times out
const TIMEOUT = 60000
describe('client.submitAndWait', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it('submitAndWait an unsigned transaction', async function () {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const responsePromise = this.client.submitAndWait(accountSet, {
wallet: this.wallet,
async function delayedLedgerAccept(): Promise<unknown> {
await new Promise<void>((resolve) => {
setTimeout(resolve, 1000)
})
const ledgerPromise = setTimeout(ledgerAccept, 1000, this.client)
return Promise.all([responsePromise, ledgerPromise]).then(
([response, _ledger]) => {
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
},
)
})
return ledgerAccept(testContext.client)
}
it('should throw a ValidationError when submitting an unsigned transaction without a wallet', async function () {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
it(
'submitAndWait an unsigned transaction',
async () => {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
await assertRejects(
this.client.submitAndWait(accountSet),
ValidationError,
'Wallet must be provided when submitting an unsigned transaction',
)
})
let retries = 10
it('submitAndWait a signed transaction', async function () {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const { tx_blob: signedAccountSet } = this.wallet.sign(
await this.client.autofill(accountSet),
)
const responsePromise = this.client.submitAndWait(signedAccountSet)
const ledgerPromise = setTimeout(ledgerAccept, 1000, this.client)
return Promise.all([responsePromise, ledgerPromise]).then(
([response, _ledger]) => {
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
},
)
})
while (retries > 0) {
retries -= 1
const responsePromise = testContext.client.submitAndWait(accountSet, {
wallet: testContext.wallet,
})
const ledgerPromise = delayedLedgerAccept()
it('submitAndWait a signed transaction longer', async function () {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const { tx_blob: signedAccountSet } = this.wallet.sign(
await this.client.autofill(accountSet),
)
const responsePromise = this.client.submitAndWait(signedAccountSet)
const ledgerPromise = setTimeout(ledgerAccept, 5000, this.client)
return Promise.all([responsePromise, ledgerPromise]).then(
([response, _ledger]) => {
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
},
)
})
try {
// eslint-disable-next-line no-await-in-loop -- Testing purposes
const [response, _ledger] = await Promise.all([
responsePromise,
ledgerPromise,
])
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
retries = 0
break
} catch (err) {
// eslint-disable-next-line max-depth -- Necessary
if (!(err instanceof Error)) {
throw err
}
const errorCodeRegex = /(?:Preliminary result:\s)(?<errorCode>.*)$/gu
const message = err.message
const matches = errorCodeRegex.exec(message)
const errorCode = matches?.groups?.errorCode
// Retry if another transaction finished before this one
// eslint-disable-next-line max-depth -- Testing
if (['tefPAST_SEQ', 'tefMAX_LEDGER'].includes(errorCode || '')) {
// eslint-disable-next-line no-await-in-loop, no-promise-executor-return -- We are waiting on retries
await new Promise((resolve) => setTimeout(resolve, 1000))
} else {
retries = 0
break
}
}
}
},
TIMEOUT,
)
it(
'should throw a ValidationError when submitting an unsigned transaction without a wallet',
async () => {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
await assertRejects(
testContext.client.submitAndWait(accountSet),
ValidationError,
'Wallet must be provided when submitting an unsigned transaction',
)
},
TIMEOUT,
)
it(
'submitAndWait a signed transaction',
async () => {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const { tx_blob: signedAccountSet } = testContext.wallet.sign(
await testContext.client.autofill(accountSet),
)
const responsePromise = testContext.client.submitAndWait(signedAccountSet)
const ledgerPromise = delayedLedgerAccept()
return Promise.all([responsePromise, ledgerPromise]).then(
([response, _ledger]) => {
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
},
)
},
TIMEOUT,
)
it(
'submitAndWait a signed transaction longer',
async () => {
const accountSet: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
Domain: convertStringToHex('example.com'),
}
const { tx_blob: signedAccountSet } = testContext.wallet.sign(
await testContext.client.autofill(accountSet),
)
const responsePromise = testContext.client.submitAndWait(signedAccountSet)
const ledgerPromise = delayedLedgerAccept()
return Promise.all([responsePromise, ledgerPromise]).then(
([response, _ledger]) => {
assert.equal(response.type, 'response')
assert.equal(response.result.validated, true)
},
)
},
TIMEOUT,
)
})

View File

@@ -1,33 +1,64 @@
import _ from 'lodash'
import { AccountDelete } from 'xrpl-local/models/transactions'
import { AccountDelete } from '../../../src/models/transactions'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import { generateFundedWallet, ledgerAccept, testTransaction } from '../utils'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, submitTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('AccountDelete', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
// to the satisfy the condition that account sequence and current ledger_index should be 256 apart.
const promises: Array<Promise<void>> = []
for (let iter = 0; iter < 256; iter += 1) {
promises.push(ledgerAccept(this.client))
}
await Promise.all(promises)
const tx: AccountDelete = {
TransactionType: 'AccountDelete',
Account: this.wallet.classicAddress,
Destination: wallet2.classicAddress,
}
await testTransaction(this.client, tx, this.wallet)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
// to the satisfy the condition that account sequence and current ledger_index should be 256 apart.
// const promises: Array<Promise<void> | Promise<unknown>> = []
// for (let iter = 0; iter < 256; iter += 1) {
// promises.push(ledgerAccept(testContext.client))
// }
// await Promise.all(promises)
const tx: AccountDelete = {
TransactionType: 'AccountDelete',
Account: testContext.wallet.classicAddress,
Destination: wallet2.classicAddress,
}
// Since we are not testing the functionaity of rippled in this library, only that we are submitting commands
// properly, we can just test that the AccountDelete command was successfully received.
await submitTransaction({
client: testContext.client,
transaction: tx,
wallet: testContext.wallet,
})
// TODO: Re-enable this test once we can test the `engine_result` without waiting a significant amount of time.
// Note, we can't test the `engine_result` without waiting a significant
// amount of time because accounts can't be deleted until some number of
// ledgers have closed since its creation.
//
// The documentation for `tecTOO_SOON` reads:
// "The AccountDelete transaction failed because the account to be deleted had a
// Sequence number that is too high. The current ledger index must be at least
// 256 higher than the account's sequence number."
//
// self.assertEqual(response.result['engine_result'], 'tesSUCCESS')
// await testTransaction(testContext.client, tx, testContext.wallet, {
// // Need to retry when running tests concurrently
// count: 5,
// delayMs: 1000,
// })
},
TIMEOUT,
)
})

View File

@@ -1,24 +1,32 @@
import _ from 'lodash'
import { AccountSet } from 'xrpl-local/models/transactions'
import { AccountSet } from '../../../src/models/transactions'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('AccountSet', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const tx: AccountSet = {
TransactionType: 'AccountSet',
Account: this.wallet.classicAddress,
}
await testTransaction(this.client, tx, this.wallet)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const tx: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
}
await testTransaction(testContext.client, tx, testContext.wallet)
},
TIMEOUT,
)
})

View File

@@ -1,63 +1,72 @@
import { assert } from 'chai'
import _ from 'lodash'
import { CheckCreate, CheckCancel } from 'xrpl-local'
import { CheckCreate, CheckCancel } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('CheckCancel', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const setupTx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: this.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: '50',
}
await testTransaction(this.client, setupTx, this.wallet)
// get check ID
const response1 = await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
response1.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
const checkId = response1.result.account_objects[0].index
// actual test - cancel the check
const tx: CheckCancel = {
TransactionType: 'CheckCancel',
Account: this.wallet.classicAddress,
CheckID: checkId,
}
await testTransaction(this.client, tx, this.wallet)
// confirm that the check no longer exists
const accountOffersResponse = await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
0,
'Should be no checks on the ledger',
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const setupTx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: testContext.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: '50',
}
await testTransaction(testContext.client, setupTx, testContext.wallet)
// get check ID
const response1 = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
response1.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
const checkId = response1.result.account_objects[0].index
// actual test - cancel the check
const tx: CheckCancel = {
TransactionType: 'CheckCancel',
Account: testContext.wallet.classicAddress,
CheckID: checkId,
}
await testTransaction(testContext.client, tx, testContext.wallet)
// confirm that the check no longer exists
const accountOffersResponse = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
0,
'Should be no checks on the ledger',
)
},
TIMEOUT,
)
})

View File

@@ -1,66 +1,75 @@
import { assert } from 'chai'
import _ from 'lodash'
import { CheckCreate, CheckCash } from 'xrpl-local'
import { CheckCreate, CheckCash } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('CheckCash', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const amount = '500'
const setupTx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: this.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: amount,
}
await testTransaction(this.client, setupTx, this.wallet)
// get check ID
const response1 = await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
response1.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
const checkId = response1.result.account_objects[0].index
// actual test - cash the check
const tx: CheckCash = {
TransactionType: 'CheckCash',
Account: wallet2.classicAddress,
CheckID: checkId,
Amount: amount,
}
await testTransaction(this.client, tx, wallet2)
// confirm that the check no longer exists
const accountOffersResponse = await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
0,
'Should be no checks on the ledger',
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const amount = '500'
const setupTx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: testContext.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: amount,
}
await testTransaction(testContext.client, setupTx, testContext.wallet)
// get check ID
const response1 = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
response1.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
const checkId = response1.result.account_objects[0].index
// actual test - cash the check
const tx: CheckCash = {
TransactionType: 'CheckCash',
Account: wallet2.classicAddress,
CheckID: checkId,
Amount: amount,
}
await testTransaction(testContext.client, tx, wallet2)
// confirm that the check no longer exists
const accountOffersResponse = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
0,
'Should be no checks on the ledger',
)
},
TIMEOUT,
)
})

View File

@@ -1,41 +1,50 @@
import { assert } from 'chai'
import _ from 'lodash'
import { CheckCreate } from 'xrpl-local'
import { CheckCreate } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('CheckCreate', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = await generateFundedWallet(this.client)
const tx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: this.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: '50',
}
await testTransaction(this.client, tx, this.wallet)
// confirm that the check actually went through
const accountOffersResponse = await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const tx: CheckCreate = {
TransactionType: 'CheckCreate',
Account: testContext.wallet.classicAddress,
Destination: wallet2.classicAddress,
SendMax: '50',
}
await testTransaction(testContext.client, tx, testContext.wallet)
// confirm that the check actually went through
const accountOffersResponse = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'check',
})
assert.lengthOf(
accountOffersResponse.result.account_objects,
1,
'Should be exactly one check on the ledger',
)
},
TIMEOUT,
)
})

View File

@@ -1,27 +1,35 @@
import _ from 'lodash'
import { DepositPreauth, Wallet } from 'xrpl-local'
import { DepositPreauth, Wallet } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { fundAccount, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('DepositPreauth', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
const wallet2 = Wallet.generate()
fundAccount(this.client, wallet2)
const tx: DepositPreauth = {
TransactionType: 'DepositPreauth',
Account: this.wallet.classicAddress,
Authorize: wallet2.classicAddress,
}
await testTransaction(this.client, tx, this.wallet)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = Wallet.generate()
await fundAccount(testContext.client, wallet2)
const tx: DepositPreauth = {
TransactionType: 'DepositPreauth',
Account: testContext.wallet.classicAddress,
Authorize: wallet2.classicAddress,
}
await testTransaction(testContext.client, tx, testContext.wallet)
},
TIMEOUT,
)
})

View File

@@ -1,72 +1,125 @@
import { assert } from 'chai'
import _ from 'lodash'
import { EscrowCancel, EscrowCreate } from 'xrpl-local'
import { EscrowCancel, EscrowCreate } from '../../../src'
import serverUrl from '../serverUrl'
import { setupClient, teardownClient } from '../setup'
import { generateFundedWallet, getXRPBalance, testTransaction } from '../utils'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import {
// calculateWaitTimeForTransaction,
generateFundedWallet,
// getXRPBalance,
testTransaction,
submitTransaction,
} from '../utils'
// TODO: Fix these tests
// NOTE: Because ledger accept is called among multiple tests, the actual ledger close time is not
// accurate. It can end up very far into the future. This means that the CancelAfter timer can potentially
// need to wait for several minutes to be able to properly complete. Since we are not testing the functionaity
// of rippled in this library, only that we are submitting commands properly, we can just test that the EscrowCancel
// command was successfully received. If in the future we isolate tests to run on their own rippled instance,
// we can uncomment the code in this file to test that the escrow was actually cancelled.
// how long before each test case times out
const TIMEOUT = 20000
const TIMEOUT = 50000
describe('EscrowCancel', function () {
this.timeout(TIMEOUT)
let testContext: XrplIntegrationTestContext
beforeEach(_.partial(setupClient, serverUrl))
afterEach(teardownClient)
it('base', async function () {
// get the most recent close_time from the standalone container for cancel & finish after.
const CLOSE_TIME: number = (
await this.client.request({
command: 'ledger',
ledger_index: 'validated',
})
).result.ledger.close_time
const wallet1 = await generateFundedWallet(this.client)
const createTx: EscrowCreate = {
Account: this.wallet.classicAddress,
TransactionType: 'EscrowCreate',
Amount: '10000',
Destination: wallet1.classicAddress,
CancelAfter: CLOSE_TIME + 3,
FinishAfter: CLOSE_TIME + 2,
}
await testTransaction(this.client, createTx, this.wallet)
const initialBalanceWallet1 = await getXRPBalance(this.client, wallet1)
// check that the object was actually created
const accountObjects = (
await this.client.request({
command: 'account_objects',
account: this.wallet.classicAddress,
})
).result.account_objects
assert.equal(accountObjects.length, 1)
const sequence = (
await this.client.request({
command: 'tx',
transaction: accountObjects[0].PreviousTxnID,
})
).result.Sequence
const cancelTx: EscrowCancel = {
TransactionType: 'EscrowCancel',
Account: this.wallet.classicAddress,
Owner: this.wallet.classicAddress,
OfferSequence: sequence,
}
await testTransaction(this.client, cancelTx, this.wallet)
assert.equal(
await getXRPBalance(this.client, wallet1),
initialBalanceWallet1,
)
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
// Funding the wallet can take some time, so we do it first BEFORE getting the ledger close_time.
const wallet1 = await generateFundedWallet(testContext.client)
// get the most recent close_time from the standalone container for cancel & finish after.
const CLOSE_TIME: number = (
await testContext.client.request({
command: 'ledger',
ledger_index: 'validated',
})
).result.ledger.close_time
// const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
const createTx: EscrowCreate = {
Account: testContext.wallet.classicAddress,
TransactionType: 'EscrowCreate',
Amount: '10000',
Destination: wallet1.classicAddress,
CancelAfter: CLOSE_TIME + 3,
FinishAfter: CLOSE_TIME + 2,
}
await testTransaction(testContext.client, createTx, testContext.wallet)
// const initialBalanceWallet1 = await getXRPBalance(
// testContext.client,
// wallet1,
// )
// check that the object was actually created
const accountObjects = (
await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
})
).result.account_objects
assert.equal(accountObjects.length, 1)
const sequence = (
await testContext.client.request({
command: 'tx',
transaction: accountObjects[0].PreviousTxnID,
})
).result.Sequence
if (!sequence) {
throw new Error('sequence did not exist')
}
const cancelTx: EscrowCancel = {
TransactionType: 'EscrowCancel',
Account: testContext.wallet.classicAddress,
Owner: testContext.wallet.classicAddress,
OfferSequence: sequence,
}
// We set the CancelAfter timer to be 3 seconds after the last ledger close_time. We need to wait this long
// before we can cancel the escrow.
// const cancelAfterTimerPromise = new Promise((resolve) => {
// setTimeout(resolve, waitTimeInMs)
// })
// Make sure we wait long enough before canceling the escrow.
// await cancelAfterTimerPromise
// await testTransaction(testContext.client, cancelTx, testContext.wallet, {
// count: 20,
// delayMs: 2000,
// })
await submitTransaction({
client: testContext.client,
transaction: cancelTx,
wallet: testContext.wallet,
})
// Make sure the Destination wallet did not receive any XRP.
// assert.equal(
// await getXRPBalance(testContext.client, wallet1),
// initialBalanceWallet1,
// )
},
TIMEOUT,
)
})

Some files were not shown because too many files have changed in this diff Show More