🌳
pt0/serverF/ethpayF/testsF/ethpayTestsAI.mts
7import { EtherscanProvider } from 'ethers'
11const assertEq = <T,>(actual: T, expected: T, label?: string) => {
12 if (actual !== expected) throw new Error(`${label ? label + ': ' : ''}Expected ${expected}, got ${actual}`)
15// Known payment address that received test txs on Arbitrum (getEtherTxs filters for incoming txs)
16const knownArbPaymentAddr = peatTestfundAddr
17// Known tx to this address on Arbitrum at block 454580315
18const knownArbTxBlock = 454580315
19const knownArbTxHash = '0xccaadf2ed1268dcbe2294f5eb082e0cc96f787bbbb9af2e4cdb512d0ec33da0c'
21export const runEthpaySuite = async (ctx: SuiteRunnerCtx) => {
22 const {tests, test} = createTestCollector()
24 test('seededShuffle: deterministic for same seed', async () => {
25 const addrs = ['0xAAA', '0xBBB', '0xCCC', '0xDDD']
26 assertEq(seededShuffle('test@example.com', addrs)[0], seededShuffle('test@example.com', addrs)[0])
27 return {passed: true, msg: ''}
28 })
29 test('seededShuffle: single element always returns that element', async () => {
30 for (const email of ['a@b.com', 'x@y.com', 'foo@bar.com']) {
31 assertEq(seededShuffle(email, ['0xONLYONE'])[0], '0xONLYONE', email)
32 }
33 return {passed: true, msg: ''}
34 })
35 test('findFreeGwei: returns target when no collisions', async () => {
36 assertEq(findFreeGwei({tgtGwei: 1000000, pendingGweis: []}), 1000000)
37 return {passed: true, msg: ''}
38 })
39 test('findFreeGwei: avoids collisions', async () => {
40 const pendingGweis = [1000000, 999000, 1001000]
41 const result = findFreeGwei({tgtGwei: 1000000, pendingGweis})
42 if (pendingGweis.includes(result)) return {passed: false, msg: `Result ${result} collides with pending`}
43 if (result !== 998000 && result !== 1002000) return {passed: false, msg: `Expected 998000 or 1002000, got ${result}`}
44 return {passed: true, msg: ''}
45 })
46 test('usdToTargetGwei: converts correctly', async () => {
47 assertEq(usdToTargetGwei({totalUsd: 10, ethPrice: 2000}), Math.round(10 / 2000 * 1e9 / gweiStep) * gweiStep)
48 return {passed: true, msg: ''}
49 })
50 test('roundedGwei: rounds bigint to gwei', async () => {
51 assertEq(roundedGwei({value: BigInt('1000000000000000')}), 1000000)
52 return {passed: true, msg: ''}
53 })
55 // Live integration tests for getEtherTxs - verifies provider wrappers work end-to-end
56 // These would have caught: Etherscan V2 migration, endblock bugs, normalization issues
57 const getArbProv = () => {
58 const apiKey = getPlainSecOpt('etherscan_api' as any)
59 return apiKey ? new EtherscanProvider('arbitrum', apiKey) : null
60 }
62 test('getEtherTxs: Etherscan finds txs with startBlock on arbitrum', async () => {
63 const prov = getArbProv()
64 if (!prov) return skipTest('no etherscan_api secret')
65 const txs = await getEtherTxs({address: knownArbPaymentAddr, startBlock: knownArbTxBlock - 100, endBlock: undefined, ethersProvider: prov})
66 if (!txs.length) return {passed: false, msg: 'No txs returned - startBlock filtering may be broken'}
67 const found = txs.find((tx: {hash?: string}) => tx.hash === knownArbTxHash)
68 if (!found) return {passed: false, msg: `Known tx ${knownArbTxHash.slice(0, 16)}... not found in ${txs.length} results`}
69 if (!found.eth_gwei) return {passed: false, msg: 'tx missing eth_gwei - normalization broken'}
70 return {passed: true, msg: `found ${txs.length} txs incl ${knownArbTxHash.slice(0, 10)}...`}
71 }, {extDep: true})
73 test('getEtherTxs: returns empty array for unused address', async () => {
74 const prov = getArbProv()
75 if (!prov) return skipTest('no etherscan_api secret')
76 const randomAddr = '0x' + 'ab'.repeat(20)
77 const txs = await getEtherTxs({address: randomAddr, startBlock: 445000000, endBlock: undefined, ethersProvider: prov})
78 if (!Array.isArray(txs)) return {passed: false, msg: `Expected array, got ${typeof txs}`}
79 return {passed: true, msg: `${txs.length} txs (expected 0)`}
80 }, {extDep: true})
82 return runTestsWithProgress({tests, suiteName: 'ethpay', singleTestName: ctx.singleTestName})
85export const ethpaySuiteConfig = { name: 'ethpay', runner: runEthpaySuite, deps: [] } as const