From 49046de153d2cea04dee96b8a14eac483632c163 Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Fri, 28 Aug 2026 23:01:06 +0100 Subject: [PATCH 1/6] Add E2E test suite using Supertest (#601) --- .../tests/e2e/admin.e2e.test.js | 96 ++++++++++++ .../tests/e2e/federation.e2e.test.js | 102 +++++++++++++ .../tests/e2e/registration.e2e.test.js | 122 +++++++++++++++ .../tests/e2e/webhooks.e2e.test.js | 141 ++++++++++++++++++ 4 files changed, 461 insertions(+) create mode 100644 stellar-payment-platform/tests/e2e/admin.e2e.test.js create mode 100644 stellar-payment-platform/tests/e2e/federation.e2e.test.js create mode 100644 stellar-payment-platform/tests/e2e/registration.e2e.test.js create mode 100644 stellar-payment-platform/tests/e2e/webhooks.e2e.test.js diff --git a/stellar-payment-platform/tests/e2e/admin.e2e.test.js b/stellar-payment-platform/tests/e2e/admin.e2e.test.js new file mode 100644 index 00000000..d22d1dbd --- /dev/null +++ b/stellar-payment-platform/tests/e2e/admin.e2e.test.js @@ -0,0 +1,96 @@ +'use strict'; + +process.env.ADMIN_API_KEY = 'e2e-admin-key'; + +jest.mock('../../src/cleanup-cron', () => ({ scheduleCleanupJob: jest.fn() })); +jest.mock('../../src/soft-delete-purge-cron', () => ({ scheduleSoftDeletePurgeJob: jest.fn() })); +jest.mock('@stellar/stellar-sdk', () => ({ + Horizon: { Server: jest.fn() }, + StrKey: { isValidEd25519PublicKey: jest.fn(() => true) }, +})); +jest.mock('pdfkit', () => jest.fn()); + +const mockDbUsers = new Map(); + +jest.mock('../../prismaClient', () => { + const prisma = { + user: { + findFirst: jest.fn(async ({ where }) => { + let row = null; + if (where.address) { + for (const entry of mockDbUsers.values()) { + if (entry.address === where.address) { + row = entry; + break; + } + } + } + return row ? { ...row } : null; + }), + update: jest.fn(async ({ where, data }) => { + const entry = mockDbUsers.get(where.address); + if (!entry) throw new Error('Not found'); + const updated = { ...entry, ...data }; + mockDbUsers.set(where.address, updated); + return updated; + }), + findMany: jest.fn(async () => { + return Array.from(mockDbUsers.values()); + }), + count: jest.fn(async () => { + return mockDbUsers.size; + }) + }, + $transaction: jest.fn(async (ops) => Promise.all(ops)), + $disconnect: jest.fn().mockResolvedValue(undefined), + }; + return { prisma, isPrismaConnectionError: () => false }; +}); + +const request = require('supertest'); +const { app } = require('../../server'); + +describe('E2E: Admin Flow', () => { + beforeEach(() => { + mockDbUsers.clear(); + mockDbUsers.set('GABC123XYZ456789ADMIN', { + username: 'admin_test_user', + address: 'GABC123XYZ456789ADMIN', + flaggedAt: null, + createdAt: new Date(), + }); + }); + + it('should allow admin to block a user and export users', async () => { + // 1. Block User + let res = await request(app) + .post('/api/v1/admin/block') + .set('x-api-key', 'e2e-admin-key') + .send({ address: 'GABC123XYZ456789ADMIN' }); + + expect(res.status).toBe(200); + expect(res.body.address).toBe('GABC123XYZ456789ADMIN'); + expect(res.body.flaggedAt).toBeDefined(); + + // 2. Export Users + res = await request(app) + .get('/api/v1/admin/export?format=json') + .set('x-api-key', 'e2e-admin-key'); + + expect(res.status).toBe(200); + expect(res.body.data).toBeDefined(); + expect(Array.isArray(res.body.data)).toBe(true); + expect(res.body.data.length).toBe(1); + expect(res.body.data[0].address).toBe('GABC123XYZ456789ADMIN'); + expect(res.body.data[0].flaggedAt).not.toBeNull(); + }); + + it('should return 401 for unauthorized admin access', async () => { + const res = await request(app) + .post('/api/v1/admin/block') + .set('x-api-key', 'wrong-key') + .send({ address: 'GABC123XYZ456789ADMIN' }); + + expect(res.status).toBe(401); + }); +}); diff --git a/stellar-payment-platform/tests/e2e/federation.e2e.test.js b/stellar-payment-platform/tests/e2e/federation.e2e.test.js new file mode 100644 index 00000000..18b2805c --- /dev/null +++ b/stellar-payment-platform/tests/e2e/federation.e2e.test.js @@ -0,0 +1,102 @@ +'use strict'; + +jest.mock('../../src/cleanup-cron', () => ({ scheduleCleanupJob: jest.fn() })); +jest.mock('../../src/soft-delete-purge-cron', () => ({ scheduleSoftDeletePurgeJob: jest.fn() })); +jest.mock('@stellar/stellar-sdk', () => ({ + Horizon: { Server: jest.fn() }, + StrKey: { isValidEd25519PublicKey: jest.fn(() => true) }, +})); +jest.mock('pdfkit', () => jest.fn()); + +const mockDb = new Map(); + +jest.mock('../../prismaClient', () => { + const prisma = { + user: { + findFirst: jest.fn(async ({ where }) => { + let row = null; + if (where.username) { + const u = typeof where.username === 'string' ? where.username : where.username.equals; + for (const entry of mockDb.values()) { + if (entry.username === u) { + row = entry; + break; + } + } + } else if (where.address) { + const a = typeof where.address === 'string' ? where.address : where.address.equals; + for (const entry of mockDb.values()) { + if (entry.address.toLowerCase() === a.toLowerCase()) { + row = entry; + break; + } + } + } + return row ? { ...row } : null; + }), + create: jest.fn(async ({ data }) => { + const row = { + username: data.username, + address: data.address, + memoType: data.memoType || null, + memo: data.memo || null, + createdAt: new Date(), + }; + mockDb.set(data.address, row); + return row; + }), + }, + $transaction: jest.fn(async (ops) => Promise.all(ops)), + $disconnect: jest.fn().mockResolvedValue(undefined), + }; + return { prisma, isPrismaConnectionError: () => false }; +}); + +jest.mock('../../src/multisigner-verifier', () => ({ + verifyMultiSignerThreshold: jest.fn().mockResolvedValue({ success: true }), + isSingleSignerAccount: jest.fn().mockReturnValue(true), +})); + +jest.mock('bad-words', () => { + return jest.fn().mockImplementation(() => ({ + isProfane: jest.fn(() => false), + })); +}); + +const request = require('supertest'); +const { app } = require('../../server'); +const { prisma } = require('../../prismaClient'); + +describe('E2E: Federation Flow', () => { + beforeEach(() => { + mockDb.clear(); + }); + + it('should successfully lookup a user by name and ID', async () => { + const validUser = { + username: 'federation_test*localhost', + address: 'GABC123XYZ456789FEDERATION', + }; + mockDb.set(validUser.address, validUser); + + // 1. Lookup by Name (default behavior) + let res = await request(app) + .get(`/api/v1/federation?q=${encodeURIComponent(validUser.username)}&type=name`); + + expect(res.status).toBe(200); + expect(res.body.account_id).toBe(validUser.address); + + // 2. Lookup by ID + res = await request(app) + .get(`/api/v1/federation?q=${validUser.address}&type=id`); + + expect(res.status).toBe(200); + expect(res.body.stellar_address).toBe(validUser.username); + + // 3. Not Found + res = await request(app) + .get(`/api/v1/federation?q=nonexistent*localhost&type=name`); + + expect(res.status).toBe(404); + }); +}); diff --git a/stellar-payment-platform/tests/e2e/registration.e2e.test.js b/stellar-payment-platform/tests/e2e/registration.e2e.test.js new file mode 100644 index 00000000..3b9c3c91 --- /dev/null +++ b/stellar-payment-platform/tests/e2e/registration.e2e.test.js @@ -0,0 +1,122 @@ +'use strict'; + +jest.mock('../../src/cleanup-cron', () => ({ scheduleCleanupJob: jest.fn() })); +jest.mock('../../src/soft-delete-purge-cron', () => ({ scheduleSoftDeletePurgeJob: jest.fn() })); +jest.mock('@stellar/stellar-sdk', () => ({ + Horizon: { Server: jest.fn() }, + StrKey: { isValidEd25519PublicKey: jest.fn(() => true) }, +})); +jest.mock('pdfkit', () => jest.fn()); + +const mockDb = new Map(); + +jest.mock('../../prismaClient', () => { + const prisma = { + user: { + findUnique: jest.fn(async ({ where }) => { + let row = null; + if (where.username) { + for (const entry of mockDb.values()) { + if (entry.username === where.username) { + row = entry; + break; + } + } + } + return row ? { ...row } : null; + }), + findFirst: jest.fn(async ({ where }) => { + let row = null; + if (where.username) { + for (const entry of mockDb.values()) { + if (entry.username === where.username) { + row = entry; + break; + } + } + } else if (where.address) { + for (const entry of mockDb.values()) { + if (entry.address === where.address) { + row = entry; + break; + } + } + } + return row ? { ...row } : null; + }), + create: jest.fn(async ({ data }) => { + for (const entry of mockDb.values()) { + if (entry.username === data.username || entry.address === data.address) { + const err = new Error('Unique constraint failed'); + err.code = 'P2002'; + throw err; + } + } + const row = { + username: data.username, + address: data.address, + memoType: data.memoType || null, + memo: data.memo || null, + createdAt: new Date(), + }; + mockDb.set(data.address, row); + return row; + }), + }, + $transaction: jest.fn(async (ops) => Promise.all(ops)), + $disconnect: jest.fn().mockResolvedValue(undefined), + }; + return { prisma, isPrismaConnectionError: () => false }; +}); + +jest.mock('../../src/multisigner-verifier', () => ({ + verifyMultiSignerThreshold: jest.fn().mockResolvedValue({ success: true }), + isSingleSignerAccount: jest.fn().mockReturnValue(true), +})); + +jest.mock('bad-words', () => { + return jest.fn().mockImplementation(() => ({ + isProfane: jest.fn(() => false), + })); +}); + +const request = require('supertest'); +const { app } = require('../../server'); + +describe('E2E: Registration Flow', () => { + beforeEach(() => { + mockDb.clear(); + }); + + it('should successfully register a new user and handle duplicate registration gracefully', async () => { + // 1. Successful Registration + const validUser = { + username: 'e2e_register_test', + address: 'GABC123XYZ456789REGISTRATION', + }; + + let res = await request(app) + .post('/api/v1/register') + .send(validUser); + + expect(res.status).toBe(201); + expect(res.body.ok).toBe(true); + expect(res.body.username).toContain(validUser.username); + expect(res.body.address).toBe(validUser.address); + + // 2. Duplicate Registration should return 409 + res = await request(app) + .post('/api/v1/register') + .send(validUser); + + expect(res.status).toBe(409); + expect(res.body.error).toBeDefined(); + + // 3. Invalid input (e.g., empty username) + res = await request(app) + .post('/api/v1/register') + .send({ address: 'GDEF456XYZ' }); + + expect(res.status).toBe(422); // Validation error + }); +}); diff --git a/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js new file mode 100644 index 00000000..635d15fa --- /dev/null +++ b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js @@ -0,0 +1,141 @@ +'use strict'; + +jest.mock('../../src/cleanup-cron', () => ({ scheduleCleanupJob: jest.fn() })); +jest.mock('../../src/soft-delete-purge-cron', () => ({ scheduleSoftDeletePurgeJob: jest.fn() })); +jest.mock('@stellar/stellar-sdk', () => ({ + Horizon: { Server: jest.fn() }, + StrKey: { isValidEd25519PublicKey: jest.fn(() => true) }, + Keypair: { + fromPublicKey: jest.fn(() => ({ + verify: jest.fn(() => true), + })), + }, +})); +jest.mock('pdfkit', () => jest.fn()); + +const mockDbUsers = new Map(); +const mockDbWebhooks = new Map(); + +jest.mock('../../prismaClient', () => { + const prisma = { + user: { + findUnique: jest.fn(async ({ where }) => { + let row = null; + if (where.username) { + for (const entry of mockDbUsers.values()) { + if (entry.username === where.username) { + row = entry; + break; + } + } + } + return row ? { ...row } : null; + }), + }, + webhook: { + create: jest.fn(async ({ data }) => { + const row = { + ...data, + createdAt: data.createdAt || new Date(), + }; + mockDbWebhooks.set(data.id, row); + return row; + }), + findMany: jest.fn(async ({ where, orderBy }) => { + let results = Array.from(mockDbWebhooks.values()); + if (where && where.username) { + results = results.filter(w => w.username === where.username); + } + return results; + }), + deleteMany: jest.fn(async ({ where }) => { + let count = 0; + for (const [id, entry] of mockDbWebhooks.entries()) { + if (entry.id === where.id && entry.username === where.username) { + mockDbWebhooks.delete(id); + count++; + } + } + return { count }; + }), + }, + $transaction: jest.fn(async (ops) => Promise.all(ops)), + $disconnect: jest.fn().mockResolvedValue(undefined), + }; + return { prisma, isPrismaConnectionError: () => false }; +}); + +jest.mock('../../src/multisigner-verifier', () => ({ + verifyMultiSignerThreshold: jest.fn().mockResolvedValue({ success: true }), + isSingleSignerAccount: jest.fn().mockReturnValue(true), +})); + +const request = require('supertest'); +const { app } = require('../../server'); + +describe('E2E: Webhooks Flow', () => { + beforeEach(() => { + mockDbUsers.clear(); + mockDbWebhooks.clear(); + + // Set up a user for the webhooks + mockDbUsers.set('GABC123XYZ456789WEBHOOK', { + username: 'webhook_test_user', + address: 'GABC123XYZ456789WEBHOOK', + }); + }); + + it('should create, list, and delete a webhook', async () => { + // We mock verifyFreighterSignedMessage implicitly by mocking StrKey and Keypair in stellar-sdk. + + // 1. Create Webhook + let res = await request(app) + .post('/api/v1/webhooks') + .send({ + username: 'webhook_test_user', + signature: 'mock_signature', + url: 'https://example.com/webhook', + }); + + expect(res.status).toBe(201); + expect(res.body.ok).toBe(true); + expect(res.body.webhook.url).toBe('https://example.com/webhook'); + expect(res.body.webhook.id).toBeDefined(); + + const webhookId = res.body.webhook.id; + + // 2. List Webhooks + res = await request(app) + .get('/api/v1/webhooks') + .send({ + username: 'webhook_test_user', + signature: 'mock_signature', + }); + + expect(res.status).toBe(200); + expect(res.body.webhooks.length).toBe(1); + expect(res.body.webhooks[0].id).toBe(webhookId); + + // 3. Delete Webhook + res = await request(app) + .delete(`/api/v1/webhooks/${webhookId}`) + .send({ + username: 'webhook_test_user', + signature: 'mock_signature', + }); + + expect(res.status).toBe(200); + expect(res.body.deleted).toBe(true); + + // 4. Verify Delete + res = await request(app) + .get('/api/v1/webhooks') + .send({ + username: 'webhook_test_user', + signature: 'mock_signature', + }); + + expect(res.status).toBe(200); + expect(res.body.webhooks.length).toBe(0); + }); +}); From 5c9605e607a0a7b583727796a9f5c7cc0f95303e Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Tue, 1 Sep 2026 07:02:38 +0100 Subject: [PATCH 2/6] style: run cargo fmt --- payment_router/src/lib.rs | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/payment_router/src/lib.rs b/payment_router/src/lib.rs index cfab1027..e2d2c1a3 100644 --- a/payment_router/src/lib.rs +++ b/payment_router/src/lib.rs @@ -319,9 +319,7 @@ impl PaymentRouter { .get(&DataKey::TimelockNonce) .unwrap_or(0u64); let next = current + 1; - env.storage() - .instance() - .set(&DataKey::TimelockNonce, &next); + env.storage().instance().set(&DataKey::TimelockNonce, &next); next } @@ -509,9 +507,7 @@ impl PaymentRouter { .set(&DataKey::MaxAmount, &max_amount); env.storage().instance().set(&DataKey::Paused, &false); env.storage().instance().set(&DataKey::Frozen, &false); - env.storage() - .instance() - .set(&DataKey::TimelockNonce, &0u64); + env.storage().instance().set(&DataKey::TimelockNonce, &0u64); env.storage().instance().extend_ttl( Self::INSTANCE_LIFETIME_THRESHOLD, Self::INSTANCE_BUMP_AMOUNT, @@ -548,7 +544,10 @@ impl PaymentRouter { let nonce = Self::next_nonce(&env); let queued_at = env.ledger().timestamp(); - let entry = TimelockEntry { queued_at, action: action.clone() }; + let entry = TimelockEntry { + queued_at, + action: action.clone(), + }; let key = DataKey::TimelockEntry(nonce); env.storage().persistent().set(&key, &entry); @@ -626,17 +625,13 @@ impl PaymentRouter { env.storage().instance().set(&DataKey::FeeCap, &fee_cap); } ActionType::SetFeeBps(new_fee_bps) => { - env.storage() - .instance() - .set(&DataKey::FeeBps, &new_fee_bps); + env.storage().instance().set(&DataKey::FeeBps, &new_fee_bps); } ActionType::SetGovernance(gov) => { env.storage().instance().set(&DataKey::Governance, &gov); } ActionType::SetMinLimit(min_limit) => { - env.storage() - .instance() - .set(&DataKey::MinLimit, &min_limit); + env.storage().instance().set(&DataKey::MinLimit, &min_limit); } ActionType::TransferAdmin(new_admin) => { env.storage().instance().set(&DataKey::Admin, &new_admin); @@ -651,10 +646,8 @@ impl PaymentRouter { Self::INSTANCE_BUMP_AMOUNT, ); - env.events().publish( - (Symbol::new(&env, "action_executed"), admin), - nonce, - ); + env.events() + .publish((Symbol::new(&env, "action_executed"), admin), nonce); log!(&env, "Timelock action executed for nonce {}", nonce); Ok(()) @@ -678,10 +671,8 @@ impl PaymentRouter { env.storage().persistent().remove(&key); - env.events().publish( - (Symbol::new(&env, "action_cancelled"), admin), - nonce, - ); + env.events() + .publish((Symbol::new(&env, "action_cancelled"), admin), nonce); log!(&env, "Timelock action cancelled for nonce {}", nonce); Ok(()) From e1620532ccee3f9fedd81d4af58afc7c41aefdae Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Thu, 3 Sep 2026 08:54:03 +0100 Subject: [PATCH 3/6] fix(e2e): Fix E2E tests for federation, registration, webhooks and admin --- stellar-payment-platform/integration.test.js | 2 +- .../src/routes/v1/federationRoutes.js | 9 ++++++--- stellar-payment-platform/src/routes/v1/index.js | 1 + .../src/routes/v1/webhookRoutes.js | 6 ++++-- .../tests/e2e/admin.e2e.test.js | 14 ++++++++------ .../tests/e2e/federation.e2e.test.js | 2 +- .../tests/e2e/registration.e2e.test.js | 2 +- .../tests/e2e/webhooks.e2e.test.js | 1 + 8 files changed, 23 insertions(+), 14 deletions(-) diff --git a/stellar-payment-platform/integration.test.js b/stellar-payment-platform/integration.test.js index f8b8a27a..e85f5d9a 100644 --- a/stellar-payment-platform/integration.test.js +++ b/stellar-payment-platform/integration.test.js @@ -226,7 +226,7 @@ describe('API Integration Lifecycle Suite', () => { expect(fedRes.status).toBe(200); expect(fedRes.body).toMatchObject({ - stellar_address: user1.address, + stellar_address: user1.federationTag, account_id: user1.address, }); diff --git a/stellar-payment-platform/src/routes/v1/federationRoutes.js b/stellar-payment-platform/src/routes/v1/federationRoutes.js index 0e607580..33053aef 100644 --- a/stellar-payment-platform/src/routes/v1/federationRoutes.js +++ b/stellar-payment-platform/src/routes/v1/federationRoutes.js @@ -28,8 +28,10 @@ module.exports = (redisClient) => { if (!row) return null; + const domain = process.env.DOMAIN || 'localhost'; + const stellar_address = row.username.includes('*') ? row.username : `${row.username}*${domain}`; const response = { - stellar_address: `${row.username}*${process.env.DOMAIN || 'localhost'}`, + stellar_address, account_id: row.address, }; if (row.memoType) { @@ -54,14 +56,14 @@ module.exports = (redisClient) => { const cached = await federationLookupCached(cacheKey, async () => { const row = await prisma.user.findFirst({ where: { username: queryName, deletedAt: null }, - select: { address: true, memoType: true, memo: true }, + select: { username: true, address: true, memoType: true, memo: true }, }); const address = row?.address || USER_DATABASE[queryName]; if (!address) return null; const response = { - stellar_address: address, + stellar_address: queryName, account_id: address, }; if (row?.memoType) { @@ -84,6 +86,7 @@ module.exports = (redisClient) => { ); } } catch (error) { + console.log("FEDERATION LOOKUP ERROR:", error); const dbError = new Error('Database lookup failed', { cause: error }); dbError.statusCode = 500; return next(dbError); diff --git a/stellar-payment-platform/src/routes/v1/index.js b/stellar-payment-platform/src/routes/v1/index.js index 49ef861b..68a3fc4b 100644 --- a/stellar-payment-platform/src/routes/v1/index.js +++ b/stellar-payment-platform/src/routes/v1/index.js @@ -14,6 +14,7 @@ module.exports = (redisClient) => { router.use('/', userRoutes); router.use('/', federationRoutes); router.use('/', receiptRoutes); + router.use('/', webhookRoutes); router.use('/', historyRoutes); router.use('/', exportRoutes); router.use('/', statsRoutes(redisClient)); diff --git a/stellar-payment-platform/src/routes/v1/webhookRoutes.js b/stellar-payment-platform/src/routes/v1/webhookRoutes.js index 71e80b25..b036f2fc 100644 --- a/stellar-payment-platform/src/routes/v1/webhookRoutes.js +++ b/stellar-payment-platform/src/routes/v1/webhookRoutes.js @@ -86,17 +86,19 @@ const authenticateWebhookCall = async (req) => { const normalizedUsername = normalizeNameTag(rawUsername).toLowerCase(); + const dbUsername = normalizedUsername.split('*')[0]; + let userRecord = null; try { userRecord = await prisma.user.findUnique({ - where: { username: normalizedUsername }, + where: { username: dbUsername }, select: { username: true, address: true }, }); } catch (err) { if (!shouldFallbackToLocalRegistry(err)) throw err; const localRow = await poolGet( 'SELECT username, address FROM username_registry WHERE username = ? LIMIT 1', - [normalizedUsername], + [dbUsername], ); userRecord = localRow ? { username: localRow.username, address: localRow.address } diff --git a/stellar-payment-platform/tests/e2e/admin.e2e.test.js b/stellar-payment-platform/tests/e2e/admin.e2e.test.js index d22d1dbd..7db818c7 100644 --- a/stellar-payment-platform/tests/e2e/admin.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/admin.e2e.test.js @@ -41,6 +41,9 @@ jest.mock('../../prismaClient', () => { return mockDbUsers.size; }) }, + payment: { + findMany: jest.fn().mockResolvedValue([{ id: 'payment_1' }]) + }, $transaction: jest.fn(async (ops) => Promise.all(ops)), $disconnect: jest.fn().mockResolvedValue(undefined), }; @@ -72,17 +75,16 @@ describe('E2E: Admin Flow', () => { expect(res.body.address).toBe('GABC123XYZ456789ADMIN'); expect(res.body.flaggedAt).toBeDefined(); - // 2. Export Users + // 2. Export Payments res = await request(app) .get('/api/v1/admin/export?format=json') .set('x-api-key', 'e2e-admin-key'); expect(res.status).toBe(200); - expect(res.body.data).toBeDefined(); - expect(Array.isArray(res.body.data)).toBe(true); - expect(res.body.data.length).toBe(1); - expect(res.body.data[0].address).toBe('GABC123XYZ456789ADMIN'); - expect(res.body.data[0].flaggedAt).not.toBeNull(); + const lines = res.text.trim().split('\n').filter(Boolean); + expect(lines.length).toBeGreaterThan(0); + const parsed = JSON.parse(lines[0]); + expect(parsed.id).toBe('payment_1'); }); it('should return 401 for unauthorized admin access', async () => { diff --git a/stellar-payment-platform/tests/e2e/federation.e2e.test.js b/stellar-payment-platform/tests/e2e/federation.e2e.test.js index 18b2805c..e7ecc91c 100644 --- a/stellar-payment-platform/tests/e2e/federation.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/federation.e2e.test.js @@ -74,7 +74,7 @@ describe('E2E: Federation Flow', () => { it('should successfully lookup a user by name and ID', async () => { const validUser = { - username: 'federation_test*localhost', + username: 'federationtest*localhost', address: 'GABC123XYZ456789FEDERATION', }; mockDb.set(validUser.address, validUser); diff --git a/stellar-payment-platform/tests/e2e/registration.e2e.test.js b/stellar-payment-platform/tests/e2e/registration.e2e.test.js index 3b9c3c91..2229a0b3 100644 --- a/stellar-payment-platform/tests/e2e/registration.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/registration.e2e.test.js @@ -91,7 +91,7 @@ describe('E2E: Registration Flow', () => { it('should successfully register a new user and handle duplicate registration gracefully', async () => { // 1. Successful Registration const validUser = { - username: 'e2e_register_test', + username: 'e2eregistertest', address: 'GABC123XYZ456789REGISTRATION', }; diff --git a/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js index 635d15fa..45a93c5f 100644 --- a/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js @@ -97,6 +97,7 @@ describe('E2E: Webhooks Flow', () => { url: 'https://example.com/webhook', }); + if (res.status !== 201) console.log("Webhook Create Error Response:", res.status, res.body); expect(res.status).toBe(201); expect(res.body.ok).toBe(true); expect(res.body.webhook.url).toBe('https://example.com/webhook'); From 3cd67f304db1b513b521aeb8a202afc66a3212c4 Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Thu, 3 Sep 2026 09:31:51 +0100 Subject: [PATCH 4/6] fix(tests): resolve MaxListenersExceededWarning --- stellar-payment-platform/jest.setup.js | 1 + stellar-payment-platform/package.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 stellar-payment-platform/jest.setup.js diff --git a/stellar-payment-platform/jest.setup.js b/stellar-payment-platform/jest.setup.js new file mode 100644 index 00000000..2a0c99aa --- /dev/null +++ b/stellar-payment-platform/jest.setup.js @@ -0,0 +1 @@ +require('events').EventEmitter.defaultMaxListeners = 100; diff --git a/stellar-payment-platform/package.json b/stellar-payment-platform/package.json index f5d65726..cd7c7afb 100644 --- a/stellar-payment-platform/package.json +++ b/stellar-payment-platform/package.json @@ -22,7 +22,8 @@ "contract:upgrade": "node ../scripts/deploy.js upgrade" }, "jest": { - "testEnvironment": "node" + "testEnvironment": "node", + "setupFiles": ["./jest.setup.js"] }, "prisma": { "seed": "node scripts/seed.js" From 572c3d17941db30e53422c6e7e6405f8f4b967a1 Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Thu, 3 Sep 2026 09:41:39 +0100 Subject: [PATCH 5/6] chore: silence console.warn on 500 errors during tests --- stellar-payment-platform/src/middleware/errorHandler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stellar-payment-platform/src/middleware/errorHandler.js b/stellar-payment-platform/src/middleware/errorHandler.js index cd6cd8f6..c1c14ac4 100644 --- a/stellar-payment-platform/src/middleware/errorHandler.js +++ b/stellar-payment-platform/src/middleware/errorHandler.js @@ -77,7 +77,9 @@ const buildErrorHandler = (isPrismaConnectionError) => if (statusCode >= 500) { const referenceId = crypto.randomUUID(); - console.warn(`[Correlation ID: ${req.correlationId}] [Error ID: ${referenceId}]`, err); + if (process.env.NODE_ENV !== 'test') { + console.warn(`[Correlation ID: ${req.correlationId}] [Error ID: ${referenceId}]`, err); + } logger.error(`[Correlation ID: ${req.correlationId}] [Error ID: ${referenceId}]`, err); return res.status(statusCode).json( From 6ddc3baf6dc59ebb3742b8717324037e095b06f5 Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Thu, 3 Sep 2026 09:50:57 +0100 Subject: [PATCH 6/6] test: mock missing prisma methods in e2e tests --- stellar-payment-platform/tests/e2e/admin.e2e.test.js | 10 ++++++++++ .../tests/e2e/registration.e2e.test.js | 7 +++++++ .../tests/e2e/webhooks.e2e.test.js | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/stellar-payment-platform/tests/e2e/admin.e2e.test.js b/stellar-payment-platform/tests/e2e/admin.e2e.test.js index 7db818c7..0fc986a9 100644 --- a/stellar-payment-platform/tests/e2e/admin.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/admin.e2e.test.js @@ -34,6 +34,16 @@ jest.mock('../../prismaClient', () => { mockDbUsers.set(where.address, updated); return updated; }), + updateMany: jest.fn(async ({ where, data }) => { + let count = 0; + for (const [address, entry] of mockDbUsers.entries()) { + if (where.address && entry.address === where.address) { + mockDbUsers.set(address, { ...entry, ...data }); + count++; + } + } + return { count }; + }), findMany: jest.fn(async () => { return Array.from(mockDbUsers.values()); }), diff --git a/stellar-payment-platform/tests/e2e/registration.e2e.test.js b/stellar-payment-platform/tests/e2e/registration.e2e.test.js index 2229a0b3..52f439bb 100644 --- a/stellar-payment-platform/tests/e2e/registration.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/registration.e2e.test.js @@ -62,6 +62,13 @@ jest.mock('../../prismaClient', () => { mockDb.set(data.address, row); return row; }), + count: jest.fn(async ({ where }) => { + let count = 0; + for (const entry of mockDb.values()) { + if (where.address && entry.address === where.address) count++; + } + return count; + }), }, $transaction: jest.fn(async (ops) => Promise.all(ops)), $disconnect: jest.fn().mockResolvedValue(undefined), diff --git a/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js index 45a93c5f..a2cef94e 100644 --- a/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js +++ b/stellar-payment-platform/tests/e2e/webhooks.e2e.test.js @@ -80,7 +80,7 @@ describe('E2E: Webhooks Flow', () => { // Set up a user for the webhooks mockDbUsers.set('GABC123XYZ456789WEBHOOK', { - username: 'webhook_test_user', + username: 'webhook_test_user*localhost', address: 'GABC123XYZ456789WEBHOOK', }); });