Stagenet wallet rehearsal passed

This commit is contained in:
2026-10-21 16:53:59 -04:00
parent 0534cde1a9
commit f00ea55dfd
5 changed files with 243 additions and 26 deletions
+26 -7
View File
@@ -9,6 +9,10 @@ const config = { network: 'mainnet' as const, url: 'http://127.0.0.1:18083/json_
function response(id: string, result: Record<string, unknown>, status = 200, type = 'application/json') {
return new Response(JSON.stringify({ jsonrpc: '2.0', id, result }), { status, headers: { 'content-type': type } });
}
function rpcErrorResponse(id: string, code: number, message: string) {
return new Response(JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } }),
{ headers: { 'content-type': 'application/json' } });
}
function fakeTransport(incoming: Record<string, unknown>[] = []): { transport: MoneroFixtureTransport; calls: { method: string; params: unknown; auth: string | null }[] } {
const calls: { method: string; params: unknown; auth: string | null }[] = [];
const transport: MoneroFixtureTransport = async (url, init) => {
@@ -63,8 +67,8 @@ test('deposit-address RPC client is narrowly allow-listed and creates only one v
const transport: MoneroFixtureTransport = async (_url, init) => {
const body = JSON.parse(init.body as string) as { id: string; method: string; params: unknown };
calls.push({ method: body.method, params: body.params });
if (body.method === 'get_wallet_info') return response(body.id, { address: ADDITIONAL_MARKET_FEE.address,
network_type: 'Mainnet', wallet_type: 'Watch only' });
if (body.method === 'validate_address') return response(body.id, { valid: true, integrated: false,
nettype: 'mainnet', subaddress: false, openalias_address: '' });
if (body.method === 'get_address') {
const params = body.params as { address_index?: number[] };
return response(body.id, { address: ADDITIONAL_MARKET_FEE.address,
@@ -80,8 +84,8 @@ test('deposit-address RPC client is narrowly allow-listed and creates only one v
assert.deepEqual(await rpc.getSubaddress(0, 7), { address: subaddress, label: 'wikideck-deposit-v1:fixture' });
assert.deepEqual(await rpc.createSubaddress(0, 'wikideck-deposit-v1:fixture'), { address: subaddress, addressIndex: 7 });
assert.deepEqual(calls, [
{ method: 'get_wallet_info', params: {} },
{ method: 'get_address', params: { account_index: 0 } },
{ method: 'validate_address', params: { address: ADDITIONAL_MARKET_FEE.address } },
{ method: 'get_address', params: { account_index: 0, address_index: [7] } },
{ method: 'create_address', params: { account_index: 0, label: 'wikideck-deposit-v1:fixture', count: 1 } },
]);
@@ -99,11 +103,26 @@ test('deposit-address RPC client is narrowly allow-listed and creates only one v
const spendCapableWallet: MoneroFixtureTransport = async (_url, init) => {
const body = JSON.parse(init.body as string) as { id: string; method: string };
if (body.method === 'get_wallet_info') return response(body.id, { address: ADDITIONAL_MARKET_FEE.address,
network_type: 'Mainnet', wallet_type: 'Normal' });
return response(body.id, { address: ADDITIONAL_MARKET_FEE.address, status: 'OK' });
if (body.method === 'get_address') return response(body.id, { address: ADDITIONAL_MARKET_FEE.address, status: 'OK' });
if (body.method === 'validate_address') return response(body.id, { valid: true, integrated: false,
nettype: 'stagenet', subaddress: false, openalias_address: '' });
throw new Error(`unexpected method ${body.method}`);
};
await assert.rejects(createMoneroDepositAddressRpc(config, spendCapableWallet).verifyWallet(), /expected/);
await assert.rejects(createMoneroDepositAddressRpc(config, spendCapableWallet).verifyWallet());
});
test('official wallet not-yet-created address-index error is recoverable but unrelated RPC errors fail closed', async () => {
const notFound: MoneroFixtureTransport = async (_url, init) => {
const body = JSON.parse(init.body as string) as { id: string };
return rpcErrorResponse(body.id, -15, 'address index is out of bound');
};
assert.equal(await createMoneroDepositAddressRpc(config, notFound).getSubaddress(0, 1), null);
const unrelated: MoneroFixtureTransport = async (_url, init) => {
const body = JSON.parse(init.body as string) as { id: string };
return rpcErrorResponse(body.id, -15, 'account index is out of bound');
};
await assert.rejects(createMoneroDepositAddressRpc(config, unrelated).getSubaddress(0, 1), /method failed \(-15\)/);
});
test('observer refuses non-loopback plaintext, URL credentials and malformed height ranges', async () => {