|
1 | 1 | // ─── Env vars MUST be set before any app imports ───────────────────────────── |
| 2 | + |
2 | 3 | process.env.JWT_SECRET = 'test-secret'; |
3 | 4 | process.env.INTERNAL_API_KEY = 'test-api-key'; |
4 | 5 | process.env.NODE_ENV = 'test'; |
5 | 6 |
|
6 | | -// ─── Mock the DB before app loads ──────────────────────────────────────────── |
7 | 7 | import { jest } from '@jest/globals'; |
8 | 8 |
|
9 | | -jest.mock('../db/connection.js', () => ({ |
10 | | - query: jest.fn(), |
11 | | - default: { |
12 | | - query: jest.fn(), |
13 | | - connect: jest.fn(), |
14 | | - end: jest.fn(), |
15 | | - }, |
| 9 | +// ESM-compatible mocking |
| 10 | +const mockQuery: any = jest.fn(); |
| 11 | +jest.unstable_mockModule('../db/connection.js', () => ({ |
| 12 | + query: mockQuery, |
| 13 | + default: { query: mockQuery, connect: jest.fn(), end: jest.fn() }, |
16 | 14 | })); |
17 | | - |
18 | | -jest.mock('../db/transaction.js', () => ({ |
| 15 | +jest.unstable_mockModule('../db/transaction.js', () => ({ |
19 | 16 | withTransaction: jest.fn(), |
20 | 17 | withStellarAndDbTransaction: jest.fn(), |
21 | 18 | })); |
22 | 19 |
|
23 | | -// ─── Imports (after mocks) ──────────────────────────────────────────────────── |
24 | | -import request from 'supertest'; |
25 | | -import jwt from 'jsonwebtoken'; |
26 | | -import app from '../app.js'; |
27 | | -import { query } from '../db/connection.js'; |
28 | | - |
29 | | -const mockQuery = query as jest.MockedFunction<typeof query>; |
| 20 | +let request: typeof import('supertest'); |
| 21 | +let jwt: typeof import('jsonwebtoken'); |
| 22 | +let app: any; |
| 23 | +// Dynamic imports after mocks |
| 24 | +beforeAll(async () => { |
| 25 | + ({ default: request } = await import('supertest')); |
| 26 | + ({ default: jwt } = await import('jsonwebtoken')); |
| 27 | + ({ default: app } = await import('../app.js')); |
| 28 | +}); |
30 | 29 |
|
31 | 30 | // ─── Constants ──────────────────────────────────────────────────────────────── |
32 | 31 | // Real Stellar-format public key so any key-format validation passes |
@@ -55,30 +54,33 @@ function dbOk(command = 'INSERT') { |
55 | 54 | } |
56 | 55 |
|
57 | 56 | // ─── Test state ─────────────────────────────────────────────────────────────── |
| 57 | + |
58 | 58 | let authToken: string; |
59 | 59 | let defaultedLoanId = LOAN_ID; |
60 | | -let disputeId = DISPUTE_ID; |
| 60 | +let disputeId = DISPUTE_ID; |
61 | 61 |
|
62 | | -// ─── Setup ──────────────────────────────────────────────────────────────────── |
| 62 | +// Setup test loan and defaulted state before tests |
63 | 63 | beforeAll(async () => { |
| 64 | + // Wait for dynamic imports |
| 65 | + if (!request || !jwt || !app) { |
| 66 | + ({ default: request } = await import('supertest')); |
| 67 | + ({ default: jwt } = await import('jsonwebtoken')); |
| 68 | + ({ default: app } = await import('../app.js')); |
| 69 | + } |
64 | 70 | authToken = mintToken(); |
65 | 71 |
|
66 | | - /** |
67 | | - * POST /api/loans → createTestLoan |
68 | | - * [1] INSERT loan_events LoanRequested RETURNING loan_id |
69 | | - * [2] INSERT loan_events LoanApproved |
70 | | - * |
71 | | - * POST /api/loans/:id/mark-defaulted → requireLoanBorrowerAccess + markLoanDefaulted |
72 | | - * [3] requireLoanBorrowerAccess: SELECT borrower FROM loan_events WHERE loan_id = 42 |
73 | | - * [4] markLoanDefaulted: SELECT loan_id FROM loan_events (existence check) |
74 | | - * [5] markLoanDefaulted: INSERT loan_events LoanDefaulted |
75 | | - */ |
| 72 | + mockQuery.mockReset(); |
| 73 | + // [1] INSERT loan_events LoanRequested RETURNING loan_id |
| 74 | + // [2] INSERT loan_events LoanApproved |
| 75 | + // [3] requireLoanBorrowerAccess: SELECT borrower FROM loan_events WHERE loan_id = 42 |
| 76 | + // [4] markLoanDefaulted: SELECT loan_id FROM loan_events (existence check) |
| 77 | + // [5] markLoanDefaulted: INSERT loan_events LoanDefaulted |
76 | 78 | mockQuery |
77 | | - .mockResolvedValueOnce(dbRows([{ loan_id: LOAN_ID }])) // [1] |
78 | | - .mockResolvedValueOnce(dbOk()) // [2] |
79 | | - .mockResolvedValueOnce(dbRows([{ borrower: TEST_PUBLIC_KEY }])) // [3] loanAccess |
80 | | - .mockResolvedValueOnce(dbRows([{ loan_id: LOAN_ID }])) // [4] existence check |
81 | | - .mockResolvedValueOnce(dbOk()); // [5] LoanDefaulted |
| 79 | + .mockResolvedValueOnce(dbRows([{ loan_id: LOAN_ID }])) |
| 80 | + .mockResolvedValueOnce(dbOk()) |
| 81 | + .mockResolvedValueOnce(dbRows([{ borrower: TEST_PUBLIC_KEY }])) |
| 82 | + .mockResolvedValueOnce(dbRows([{ loan_id: LOAN_ID }])) |
| 83 | + .mockResolvedValueOnce(dbOk()); |
82 | 84 |
|
83 | 85 | const loanRes = await request(app) |
84 | 86 | .post('/api/loans') |
|
0 commit comments