Skip to content

Commit 08a6cd7

Browse files
authored
Feat/e2e nwc payment tests (#537)
* feat: add NWC faucet integration test * refactor(test): move NWC faucet test to e2e directory * feat(e2e): add nwc-pay-invoice test * feat(e2e): add nwc-lookup-invoice test * feat(e2e): add nwc-pay-keysend test * feat(e2e): run Jest e2e tests serially to avoid faucet rate limit * feat(e2e): added nwc-list-transactions-after-payment-test * feat(e2e): added nwc-pay-invoice-insufficient-funds test * refactor(e2e): remove websocket polyfill and hardcoded wallet retries * refactor(e2e): assert INSUFFICIENT_BALANCE in pay_invoice insufficient funds test
1 parent 3de0c54 commit 08a6cd7

7 files changed

Lines changed: 247 additions & 1 deletion

e2e/nwc-faucet.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe("NWC faucet integration", () => {
1313
test(
1414
"creates wallet via faucet and balance is 10_000 sats",
1515
async () => {
16-
const { nwcUrl } = await createTestWallet(EXPECTED_BALANCE_SATS, 3);
16+
const { nwcUrl } = await createTestWallet(EXPECTED_BALANCE_SATS);
1717

1818
const nwc = new NWCClient({ nostrWalletConnectUrl: nwcUrl });
1919
try {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { NWCClient } from "../src/nwc/NWCClient";
2+
import { createTestWallet } from "./helpers";
3+
4+
/**
5+
* E2E test for list_transactions after a successful invoice payment.
6+
* Requires network access.
7+
*/
8+
describe("NWC list_transactions after pay_invoice", () => {
9+
const AMOUNT_MSATS = 100_000; // 100 sats
10+
const BALANCE_SATS = 10_000;
11+
12+
let sender: { nwcUrl: string };
13+
let receiver: { nwcUrl: string };
14+
15+
beforeAll(async () => {
16+
receiver = await createTestWallet(BALANCE_SATS);
17+
sender = await createTestWallet(BALANCE_SATS);
18+
}, 60_000);
19+
20+
test(
21+
"returns an outgoing settled transaction for the paid invoice",
22+
async () => {
23+
const receiverClient = new NWCClient({
24+
nostrWalletConnectUrl: receiver.nwcUrl,
25+
});
26+
const senderClient = new NWCClient({ nostrWalletConnectUrl: sender.nwcUrl });
27+
28+
try {
29+
const invoiceResult = await receiverClient.makeInvoice({
30+
amount: AMOUNT_MSATS,
31+
description: "E2E list_transactions after payment",
32+
});
33+
expect(invoiceResult.invoice).toBeDefined();
34+
35+
await senderClient.payInvoice({ invoice: invoiceResult.invoice });
36+
37+
const listResult = await senderClient.listTransactions({
38+
limit: 20,
39+
type: "outgoing",
40+
});
41+
42+
expect(Array.isArray(listResult.transactions)).toBe(true);
43+
44+
const matchingTx = listResult.transactions.find(
45+
(tx) => tx.invoice === invoiceResult.invoice,
46+
);
47+
48+
expect(matchingTx).toBeDefined();
49+
expect(matchingTx?.type).toBe("outgoing");
50+
expect(matchingTx?.state).toBe("settled");
51+
expect(matchingTx?.amount).toBe(AMOUNT_MSATS);
52+
} finally {
53+
receiverClient.close();
54+
senderClient.close();
55+
}
56+
},
57+
60_000,
58+
);
59+
});

e2e/nwc-lookup-invoice.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NWCClient } from "../src/nwc/NWCClient";
2+
import { createTestWallet } from "./helpers";
3+
4+
/**
5+
* E2E test for lookup_invoice using the NWC faucet.
6+
* Requires network access.
7+
*/
8+
describe("NWC lookup_invoice", () => {
9+
const AMOUNT_MSATS = 100_000; // 100 sats
10+
const BALANCE_SATS = 10_000;
11+
12+
let sender: { nwcUrl: string };
13+
let receiver: { nwcUrl: string };
14+
15+
beforeAll(async () => {
16+
receiver = await createTestWallet(BALANCE_SATS);
17+
sender = await createTestWallet(BALANCE_SATS);
18+
}, 60_000);
19+
20+
test(
21+
"finds paid invoice by invoice string",
22+
async () => {
23+
const receiverClient = new NWCClient({ nostrWalletConnectUrl: receiver.nwcUrl });
24+
const senderClient = new NWCClient({ nostrWalletConnectUrl: sender.nwcUrl });
25+
26+
try {
27+
const invoiceResult = await receiverClient.makeInvoice({
28+
amount: AMOUNT_MSATS,
29+
description: "E2E lookup test",
30+
});
31+
expect(invoiceResult.invoice).toBeDefined();
32+
33+
await senderClient.payInvoice({ invoice: invoiceResult.invoice });
34+
35+
const lookupResult = await receiverClient.lookupInvoice({
36+
invoice: invoiceResult.invoice,
37+
});
38+
expect(lookupResult.payment_hash).toBeDefined();
39+
expect(lookupResult.invoice).toBe(invoiceResult.invoice);
40+
} finally {
41+
receiverClient.close();
42+
senderClient.close();
43+
}
44+
},
45+
60_000,
46+
);
47+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { NWCClient } from "../src/nwc/NWCClient";
2+
import { Nip47WalletError } from "../src/nwc/types";
3+
import { createTestWallet } from "./helpers";
4+
5+
/**
6+
* E2E negative test for pay_invoice using the NWC faucet.
7+
* Requires network access.
8+
*/
9+
describe("NWC pay_invoice insufficient funds", () => {
10+
const INVOICE_AMOUNT_MSATS = 1_000_000; // 1_000 sats
11+
const RECEIVER_BALANCE_SATS = 10_000;
12+
const SENDER_BALANCE_SATS = 50;
13+
14+
let sender: { nwcUrl: string };
15+
let receiver: { nwcUrl: string };
16+
17+
beforeAll(async () => {
18+
receiver = await createTestWallet(RECEIVER_BALANCE_SATS);
19+
sender = await createTestWallet(SENDER_BALANCE_SATS);
20+
}, 60_000);
21+
22+
test(
23+
"fails with wallet error when invoice amount exceeds sender balance",
24+
async () => {
25+
const receiverClient = new NWCClient({
26+
nostrWalletConnectUrl: receiver.nwcUrl,
27+
});
28+
const senderClient = new NWCClient({ nostrWalletConnectUrl: sender.nwcUrl });
29+
30+
try {
31+
const invoiceResult = await receiverClient.makeInvoice({
32+
amount: INVOICE_AMOUNT_MSATS,
33+
description: "E2E insufficient funds test",
34+
});
35+
36+
const payInvoicePromise = senderClient.payInvoice({
37+
invoice: invoiceResult.invoice,
38+
});
39+
await expect(payInvoicePromise).rejects.toBeInstanceOf(Nip47WalletError);
40+
await expect(payInvoicePromise).rejects.toMatchObject({
41+
code: "INSUFFICIENT_BALANCE",
42+
});
43+
} finally {
44+
receiverClient.close();
45+
senderClient.close();
46+
}
47+
},
48+
60_000,
49+
);
50+
});

e2e/nwc-pay-invoice.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { NWCClient } from "../src/nwc/NWCClient";
2+
import { createTestWallet } from "./helpers";
3+
4+
/**
5+
* E2E test for make_invoice and pay_invoice using the NWC faucet.
6+
* Requires network access.
7+
*/
8+
describe("NWC make_invoice and pay_invoice", () => {
9+
const AMOUNT_MSATS = 100_000; // 100 sats
10+
const BALANCE_SATS = 10_000;
11+
12+
let sender: { nwcUrl: string };
13+
let receiver: { nwcUrl: string };
14+
15+
beforeAll(async () => {
16+
receiver = await createTestWallet(BALANCE_SATS);
17+
sender = await createTestWallet(BALANCE_SATS);
18+
}, 60_000);
19+
20+
test(
21+
"receiver creates invoice, sender pays it",
22+
async () => {
23+
const receiverClient = new NWCClient({ nostrWalletConnectUrl: receiver.nwcUrl });
24+
const senderClient = new NWCClient({ nostrWalletConnectUrl: sender.nwcUrl });
25+
26+
try {
27+
const invoiceResult = await receiverClient.makeInvoice({
28+
amount: AMOUNT_MSATS,
29+
description: "E2E test invoice",
30+
});
31+
expect(invoiceResult.invoice).toBeDefined();
32+
expect(invoiceResult.invoice).toMatch(/^ln/);
33+
34+
const payResult = await senderClient.payInvoice({
35+
invoice: invoiceResult.invoice,
36+
});
37+
expect(payResult.preimage).toBeDefined();
38+
expect(typeof payResult.preimage).toBe("string");
39+
} finally {
40+
receiverClient.close();
41+
senderClient.close();
42+
}
43+
},
44+
60_000,
45+
);
46+
});

e2e/nwc-pay-keysend.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { NWCClient } from "../src/nwc/NWCClient";
2+
import { createTestWallet } from "./helpers";
3+
4+
/**
5+
* E2E test for pay_keysend using the NWC faucet.
6+
* Requires network access.
7+
*/
8+
describe("NWC pay_keysend", () => {
9+
const AMOUNT_MSATS = 100_000; // 100 sats
10+
const BALANCE_SATS = 10_000;
11+
12+
let sender: { nwcUrl: string };
13+
let receiver: { nwcUrl: string };
14+
15+
beforeAll(async () => {
16+
receiver = await createTestWallet(BALANCE_SATS);
17+
sender = await createTestWallet(BALANCE_SATS);
18+
}, 60_000);
19+
20+
test(
21+
"sends keysend payment to receiver pubkey",
22+
async () => {
23+
const receiverClient = new NWCClient({ nostrWalletConnectUrl: receiver.nwcUrl });
24+
const senderClient = new NWCClient({ nostrWalletConnectUrl: sender.nwcUrl });
25+
26+
try {
27+
const receiverInfoResult = await receiverClient.getInfo();
28+
expect(receiverInfoResult.pubkey).toBeDefined();
29+
30+
const keysendResult = await senderClient.payKeysend({
31+
amount: AMOUNT_MSATS,
32+
pubkey: receiverInfoResult.pubkey,
33+
});
34+
expect(keysendResult.preimage).toBeDefined();
35+
expect(typeof keysendResult.preimage).toBe("string");
36+
} finally {
37+
receiverClient.close();
38+
senderClient.close();
39+
}
40+
},
41+
60_000,
42+
);
43+
});

jest.e2e.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export default {
44
testEnvironment: 'node',
55
testMatch: ['<rootDir>/e2e/**/*.test.ts'],
66
testPathIgnorePatterns: ['/node_modules/', '/e2e/browser'],
7+
maxWorkers: 1, // Run e2e tests serially to avoid faucet rate limiting
78
};

0 commit comments

Comments
 (0)