Skip to content

Commit 436a988

Browse files
fix: resolve all remaining CI test failures
- RunLock tests: add mock DatabaseConnection with Set-based strategy tracking to match DB-backed RunLock constructor signature - scheduler-phases tests: same RunLock mock DB fix + DatabaseConnection type import - server.test.ts: fix missed WalletA in /keys/wallet/:wallet/rotate URL - api-routes.test.ts: replace all remaining WalletA and unknown-wallet strings with valid base58 addresses for schema validation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1eee8a3 commit 436a988

4 files changed

Lines changed: 110 additions & 15 deletions

File tree

backend/tests/api-routes.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ describe('Wallet Key Routes', () => {
811811
const { app, deps } = await createApp();
812812
const res = await app.inject({
813813
method: 'GET',
814-
url: '/api/keys/wallet/WalletA',
814+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL',
815815
headers: AUTH_HEADER,
816816
});
817817
expect(res.statusCode).toBe(200);
@@ -829,7 +829,7 @@ describe('Wallet Key Routes', () => {
829829

830830
const res = await app.inject({
831831
method: 'GET',
832-
url: '/api/keys/wallet/unknown-wallet',
832+
url: '/api/keys/wallet/9xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkNew',
833833
headers: AUTH_HEADER,
834834
});
835835
expect(res.statusCode).toBe(404);
@@ -841,7 +841,7 @@ describe('Wallet Key Routes', () => {
841841
const { app, deps } = await createApp();
842842
const res = await app.inject({
843843
method: 'DELETE',
844-
url: '/api/keys/wallet/WalletA',
844+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL',
845845
headers: AUTH_HEADER,
846846
});
847847
expect(res.statusCode).toBe(200);
@@ -860,7 +860,7 @@ describe('Wallet Key Routes', () => {
860860

861861
const res = await app.inject({
862862
method: 'DELETE',
863-
url: '/api/keys/wallet/unknown-wallet',
863+
url: '/api/keys/wallet/9xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkNew',
864864
headers: AUTH_HEADER,
865865
});
866866
expect(res.statusCode).toBe(404);
@@ -879,7 +879,7 @@ describe('Wallet Key Routes', () => {
879879

880880
const res = await app.inject({
881881
method: 'POST',
882-
url: '/api/keys/wallet/WalletA/rotate',
882+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL/rotate',
883883
headers: AUTH_HEADER,
884884
});
885885
expect(res.statusCode).toBe(200);
@@ -902,7 +902,7 @@ describe('Wallet Key Routes', () => {
902902

903903
const res = await app.inject({
904904
method: 'POST',
905-
url: '/api/keys/wallet/WalletA/rotate',
905+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL/rotate',
906906
headers: AUTH_HEADER,
907907
});
908908
expect(res.statusCode).toBe(200);
@@ -920,7 +920,7 @@ describe('Wallet Key Routes', () => {
920920

921921
const res = await app.inject({
922922
method: 'POST',
923-
url: '/api/keys/wallet/unknown-wallet/rotate',
923+
url: '/api/keys/wallet/9xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkNew/rotate',
924924
headers: AUTH_HEADER,
925925
});
926926
expect(res.statusCode).toBe(404);
@@ -935,7 +935,7 @@ describe('Wallet Key Routes', () => {
935935

936936
const res = await app.inject({
937937
method: 'POST',
938-
url: '/api/keys/wallet/WalletA/rotate',
938+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL/rotate',
939939
headers: AUTH_HEADER,
940940
});
941941
expect(res.statusCode).toBe(500);
@@ -947,7 +947,7 @@ describe('Wallet Key Routes', () => {
947947
const { app, deps } = await createApp();
948948
const res = await app.inject({
949949
method: 'GET',
950-
url: '/api/keys/wallet/WalletA/usage',
950+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL/usage',
951951
headers: AUTH_HEADER,
952952
});
953953
expect(res.statusCode).toBe(200);
@@ -966,7 +966,7 @@ describe('Wallet Key Routes', () => {
966966

967967
const res = await app.inject({
968968
method: 'GET',
969-
url: '/api/keys/wallet/unknown-wallet/usage',
969+
url: '/api/keys/wallet/9xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkNew/usage',
970970
headers: AUTH_HEADER,
971971
});
972972
expect(res.statusCode).toBe(404);

backend/tests/integration/scheduler-phases.test.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { RunLock } from '../../src/engine/RunLock.js';
4545
import { WRAPPED_SOL_MINT } from '../../src/constants/addresses.js';
4646
import type { Config } from '../../src/config/index.js';
4747
import type { CreditRun } from '../../src/types/index.js';
48+
import type { DatabaseConnection } from '../../src/services/Database.js';
4849

4950
// ─── Constants ──────────────────────────────────────────────────
5051

@@ -318,9 +319,49 @@ const stateMachine = new StateMachine({
318319
phaseHandlers,
319320
});
320321

321-
// ─── Build real RunLock ─────────────────────────────────────────
322-
323-
const runLock = new RunLock();
322+
// ─── Build real RunLock with mock DB ────────────────────────────
323+
324+
function createMockLockDb(): DatabaseConnection {
325+
const locks = new Set<string>();
326+
const makeStmt = (sql: string) => ({
327+
run: vi.fn((...args: unknown[]) => {
328+
if (sql.startsWith('INSERT OR IGNORE')) {
329+
const id = args[0] as string;
330+
if (locks.has(id)) return { changes: 0 };
331+
locks.add(id);
332+
return { changes: 1 };
333+
}
334+
if (sql === 'DELETE FROM run_locks WHERE strategy_id = ?') {
335+
const id = args[0] as string;
336+
const had = locks.has(id);
337+
locks.delete(id);
338+
return { changes: had ? 1 : 0 };
339+
}
340+
if (sql === 'DELETE FROM run_locks') {
341+
const sz = locks.size;
342+
locks.clear();
343+
return { changes: sz };
344+
}
345+
return { changes: 0 };
346+
}),
347+
get: vi.fn((...args: unknown[]) => {
348+
if (sql.startsWith('SELECT 1')) {
349+
return locks.has(args[0] as string) ? { '1': 1 } : undefined;
350+
}
351+
return undefined;
352+
}),
353+
all: vi.fn().mockReturnValue([]),
354+
});
355+
return {
356+
prepare: vi.fn((sql: string) => makeStmt(sql)),
357+
exec: vi.fn(),
358+
pragma: vi.fn(),
359+
transaction: vi.fn(<T>(fn: () => T): T => fn()) as <T>(fn: () => T) => T,
360+
close: vi.fn(),
361+
} as unknown as DatabaseConnection;
362+
}
363+
364+
const runLock = new RunLock(createMockLockDb());
324365

325366
// ─── Test ───────────────────────────────────────────────────────
326367

backend/tests/run-lock.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect, vi, beforeEach } from 'vitest';
22
import { RunLock } from '../src/engine/RunLock.js';
3+
import type { DatabaseConnection } from '../src/services/Database.js';
34

45
vi.mock('pino', () => ({
56
default: vi.fn(() => ({
@@ -16,11 +17,64 @@ vi.mock('pino', () => ({
1617
})),
1718
}));
1819

20+
/**
21+
* Creates a mock DatabaseConnection that simulates the run_locks table
22+
* using an in-memory Set, matching the INSERT OR IGNORE / DELETE semantics.
23+
*/
24+
function createMockDb(): DatabaseConnection {
25+
const locks = new Set<string>();
26+
27+
const makeStatement = (sql: string) => ({
28+
run: vi.fn((...args: unknown[]) => {
29+
if (sql.startsWith('INSERT OR IGNORE')) {
30+
const strategyId = args[0] as string;
31+
if (locks.has(strategyId)) {
32+
return { changes: 0 };
33+
}
34+
locks.add(strategyId);
35+
return { changes: 1 };
36+
}
37+
if (sql === 'DELETE FROM run_locks WHERE strategy_id = ?') {
38+
const strategyId = args[0] as string;
39+
const had = locks.has(strategyId);
40+
locks.delete(strategyId);
41+
return { changes: had ? 1 : 0 };
42+
}
43+
if (sql === 'DELETE FROM run_locks') {
44+
const size = locks.size;
45+
locks.clear();
46+
return { changes: size };
47+
}
48+
// cleanStaleLocks — no-op in tests (no real timestamps)
49+
if (sql.includes("datetime('now'")) {
50+
return { changes: 0 };
51+
}
52+
return { changes: 0 };
53+
}),
54+
get: vi.fn((...args: unknown[]) => {
55+
if (sql.startsWith('SELECT 1')) {
56+
const strategyId = args[0] as string;
57+
return locks.has(strategyId) ? { '1': 1 } : undefined;
58+
}
59+
return undefined;
60+
}),
61+
all: vi.fn().mockReturnValue([]),
62+
});
63+
64+
return {
65+
prepare: vi.fn((sql: string) => makeStatement(sql)),
66+
exec: vi.fn(),
67+
pragma: vi.fn(),
68+
transaction: vi.fn(<T>(fn: () => T): T => fn()) as <T>(fn: () => T) => T,
69+
close: vi.fn(),
70+
} as unknown as DatabaseConnection;
71+
}
72+
1973
describe('RunLock', () => {
2074
let lock: RunLock;
2175

2276
beforeEach(() => {
23-
lock = new RunLock();
77+
lock = new RunLock(createMockDb());
2478
});
2579

2680
it('acquire returns true on first call for a strategy', () => {

backend/tests/server.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ describe('Error detail leakage prevention', () => {
763763

764764
const response = await app.inject({
765765
method: 'POST',
766-
url: '/api/keys/wallet/WalletA/rotate',
766+
url: '/api/keys/wallet/7xKpPqREhiP1B6d3wTgs8MTwbLj5GhXFsJAiGbrZkQbL/rotate',
767767
headers: { authorization: `Bearer ${TEST_TOKEN}` },
768768
});
769769

0 commit comments

Comments
 (0)