mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-19 03:35:49 +00:00
223 lines
7.8 KiB
TypeScript
223 lines
7.8 KiB
TypeScript
/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- required for
|
|
assertions. */
|
|
import net from 'net'
|
|
|
|
import { assert } from 'chai'
|
|
import omit from 'lodash/omit'
|
|
|
|
import { rippleTimeToUnixTime, unixTimeToRippleTime } from '../src'
|
|
|
|
import addresses from './fixtures/addresses.json'
|
|
|
|
/**
|
|
* Setup to run tests on both classic addresses and X-addresses.
|
|
*/
|
|
export const addressTests = [
|
|
{ type: 'Classic Address', address: addresses.ACCOUNT },
|
|
{ type: 'X-Address', address: addresses.ACCOUNT_X },
|
|
]
|
|
|
|
/**
|
|
* Check the response against the expected result. Optionally validate
|
|
* that response against a given schema as well.
|
|
*
|
|
* @param response - Response received from the method.
|
|
* @param expected - Expected response from the method.
|
|
* @param _schemaName - Name of the schema used to validate the shape of the response.
|
|
*/
|
|
export function assertResultMatch(
|
|
response: any,
|
|
expected: any,
|
|
_schemaName?: string,
|
|
): void {
|
|
if (expected.txJSON) {
|
|
assert(response.txJSON)
|
|
assert.deepEqual(
|
|
JSON.parse(response.txJSON),
|
|
JSON.parse(expected.txJSON),
|
|
'checkResult: txJSON must match',
|
|
)
|
|
}
|
|
if (expected.tx_json) {
|
|
assert(response.tx_json)
|
|
assert.deepEqual(
|
|
response.tx_json,
|
|
expected.tx_json,
|
|
'checkResult: tx_json must match',
|
|
)
|
|
}
|
|
assert.deepEqual(
|
|
omit(response, ['txJSON', 'tx_json']),
|
|
omit(expected, ['txJSON', 'tx_json']),
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Check that the promise rejects with an expected error.
|
|
*
|
|
* @param promise - The promise returned by the method.
|
|
* @param instanceOf - Expected error type that the method will throw.
|
|
* @param message - Expected error message/substring of the error message.
|
|
*/
|
|
export async function assertRejects(
|
|
promise: PromiseLike<unknown>,
|
|
instanceOf: any,
|
|
message?: string | RegExp,
|
|
): Promise<void> {
|
|
try {
|
|
await promise
|
|
assert(false, 'Expected an error to be thrown')
|
|
} catch (error) {
|
|
if (!(error instanceof Error)) {
|
|
throw error
|
|
}
|
|
|
|
assert(error instanceof instanceOf, error.message)
|
|
if (typeof message === 'string') {
|
|
assert.strictEqual(error.message, message, 'Messages do not match')
|
|
} else if (message instanceof RegExp) {
|
|
assert(message.test(error.message))
|
|
}
|
|
}
|
|
}
|
|
|
|
const lastSocketKeyMap: { [port: string]: number } = {}
|
|
const socketMap: {
|
|
[port: string]:
|
|
| {
|
|
server: net.Server
|
|
sockets: { [socketKey: string]: net.Socket }
|
|
}
|
|
| undefined
|
|
} = {}
|
|
|
|
export async function destroyServer(port: number): Promise<void> {
|
|
// loop through all sockets and destroy them
|
|
if (socketMap[port]) {
|
|
Object.keys(socketMap[port]!.sockets).forEach(function (socketKey) {
|
|
socketMap[port]!.sockets[socketKey].destroy()
|
|
})
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
if (socketMap[port]) {
|
|
// after all the sockets are destroyed, we may close the server!
|
|
socketMap[port]!.server.close((error) => {
|
|
if (error) {
|
|
reject(error)
|
|
return
|
|
}
|
|
|
|
resolve()
|
|
})
|
|
} else {
|
|
resolve()
|
|
}
|
|
})
|
|
}
|
|
|
|
// using a free port instead of a constant port enables parallelization
|
|
export async function getFreePort(): Promise<number> {
|
|
return new Promise((resolve, reject) => {
|
|
const server = net.createServer()
|
|
let port: number
|
|
server.on('listening', function () {
|
|
port = (server.address() as net.AddressInfo).port
|
|
server.close()
|
|
})
|
|
server.on('close', function () {
|
|
resolve(port)
|
|
})
|
|
server.on('error', function (error) {
|
|
reject(error)
|
|
})
|
|
const listener = server.listen(0)
|
|
// Keep track of all connections so we can destroy them at the end of the test
|
|
// This will prevent Jest from having open handles when all tests are done
|
|
listener.on('connection', (socket) => {
|
|
// generate a new, unique socket-key
|
|
lastSocketKeyMap[port] += 1
|
|
const lastSocketKey = lastSocketKeyMap[port]
|
|
// add socket when it is connected
|
|
if (socketMap[port]) {
|
|
socketMap[port]!.sockets[lastSocketKey] = socket
|
|
} else {
|
|
socketMap[port] = { sockets: { [lastSocketKey]: socket }, server }
|
|
}
|
|
socket.on('close', () => {
|
|
// remove socket when it is closed
|
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- Necessary to delete key
|
|
delete socketMap[port]!.sockets[lastSocketKey]
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Ignore WebSocket DisconnectErrors. Useful for making requests where we don't
|
|
* care about the response and plan to teardown the test before the response
|
|
* has come back.
|
|
*
|
|
* @param error - Thrown error.
|
|
* @throws If error is not websocket disconnect error.
|
|
*/
|
|
export function ignoreWebSocketDisconnect(error: Error): void {
|
|
if (error.message === 'websocket was closed') {
|
|
return
|
|
}
|
|
throw error
|
|
}
|
|
|
|
/**
|
|
* Attempts to log information about how far off the current time is from the last ledger close time.
|
|
* This is useful for debugging ledger close time issues when sending multiple ledgerAccept requests too quickly.
|
|
* If you send multiple requests in the span of a single second, the ledger can end up with a close time well into the future.
|
|
* See https://xrpl.org/ledgers.html#ledger-close-times for more information.
|
|
* The time that a ledger version closed is recorded at the close_time field of the ledger header. To make it easier for
|
|
* the network to reach a consensus on an exact close time, this value is rounded to a number of seconds based on the
|
|
* close time resolution, currently 10 seconds. If rounding would cause a ledger's close time to be the same as (or earlier than)
|
|
* its parent ledger's, the child ledger has its close time set to the parent's close time plus 1. This guarantees that the
|
|
* close times of validated ledgers are strictly increasing.
|
|
*
|
|
* Since new ledger versions usually close about every 3 to 5 seconds,
|
|
* these rules result in a loose pattern where ledgers' close times end in :00, :01, :02, :10, :11, :20, :21, and so on.
|
|
* Times ending in 2 are less common and times ending in 3 are very rare, but both occur randomly when more ledgers randomly
|
|
* happen to close within a 10-second window.
|
|
*
|
|
* Generally speaking, the ledger cannot make any time-based measurements that are more precise than the close time resolution.
|
|
* For example, to check if an object has passed an expiration date, the rule is to compare it to the close time of the parent
|
|
* ledger. (The close time of a ledger is not yet known when executing transactions to go into that ledger.) This means that,
|
|
* for example, an Escrow could successfully finish at a real-world time that is up to about 10 seconds later than the time-based
|
|
* expiration specified in the Escrow object.
|
|
*
|
|
*
|
|
* @param closeTime - ledger close time in xahau time
|
|
* @returns The difference between last ledger close time and current time in seconds
|
|
*/
|
|
export function debugPrintLedgerTime(closeTime: number): number {
|
|
const closeTimeUnix = rippleTimeToUnixTime(closeTime)
|
|
const closeTimeDate = new Date()
|
|
closeTimeDate.setTime(closeTimeUnix * 1000)
|
|
const currentTimeUnix = Math.floor(new Date().getTime())
|
|
const currentTimeRipple = unixTimeToRippleTime(currentTimeUnix)
|
|
const currentTimeDate = new Date()
|
|
currentTimeDate.setTime(currentTimeUnix * 1000)
|
|
// eslint-disable-next-line no-console -- Intentional debugging function
|
|
console.log(
|
|
`closeTime (xahau): ${closeTime}\n`,
|
|
`closeTime (unix): ${closeTimeUnix}\n`,
|
|
`closeTime (date): \n`,
|
|
closeTimeDate,
|
|
`\n`,
|
|
`currentTime (xahau): ${currentTimeRipple}\n`,
|
|
`currentTime (unix): ${currentTimeUnix}\n`,
|
|
`currentTime (date): \n`,
|
|
currentTimeDate,
|
|
`\n`,
|
|
`diff (current - close) (unix): ${currentTimeUnix - closeTimeUnix}`,
|
|
`diff (current - close) (xahau): ${currentTimeRipple - closeTime}`,
|
|
)
|
|
|
|
return currentTimeRipple - closeTime
|
|
}
|