Skip to content

Commit 0db90f0

Browse files
d-csclaude
andcommitted
test(webapp): mock eager-Redis import-time singletons in global test setup
Worker/engine/marqs/pubsub/socket singletons each construct an ioredis client at import time (singleton() + no lazyConnect), so any test importing the service graph opened real Redis connections on import. In CI there is no Redis, so these accumulate infinite-retry clients across a shard and take the suite down (locally they pass only because dev Redis is up). Globally mock the eager-Redis modules to no-op stubs in test/setup.ts: commonWorker, batchTriggerWorker, legacyRunEngineWorker, alertsWorker, the RunEngine and MarQS singletons, devPubSub and the socket.io server. Only these singletons are mocked — never the run store (~/v3/runStore.server, ~/db.server), which store-routing/residency tests need real against testcontainer Postgres. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0ef3a6b commit 0db90f0

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

apps/webapp/test/setup.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,50 @@
22
// EnvironmentSchema.parse(process.env) succeeds in vitest workers.
33
import { config } from "dotenv";
44
import path from "node:path";
5+
import { vi } from "vitest";
56

67
config({ path: path.resolve(__dirname, "../.env") });
8+
9+
// Worker singletons construct a RedisWorker at import time whose ioredis client
10+
// connects eagerly, so any test importing the service graph opens real Redis
11+
// connections on import — which floods and fails in CI (no Redis). Mock them to
12+
// no-op stubs. Only the worker modules are mocked, never the run store
13+
// (~/v3/runStore.server, ~/db.server), which store-routing tests need real.
14+
function createWorkerStub() {
15+
return {
16+
start: vi.fn(),
17+
stop: vi.fn(),
18+
enqueue: vi.fn().mockResolvedValue(undefined),
19+
enqueueOnce: vi.fn().mockResolvedValue(undefined),
20+
reschedule: vi.fn().mockResolvedValue(undefined),
21+
cancel: vi.fn().mockResolvedValue(undefined),
22+
ack: vi.fn().mockResolvedValue(undefined),
23+
};
24+
}
25+
26+
vi.mock("~/v3/commonWorker.server", () => ({ commonWorker: createWorkerStub() }));
27+
vi.mock("~/v3/batchTriggerWorker.server", () => ({ batchTriggerWorker: createWorkerStub() }));
28+
vi.mock("~/v3/legacyRunEngineWorker.server", () => ({
29+
legacyRunEngineWorker: createWorkerStub(),
30+
}));
31+
vi.mock("~/v3/alertsWorker.server", () => ({ alertsWorker: createWorkerStub() }));
32+
33+
// RunEngine, MarQS, devPubSub and the socket.io server are further singletons
34+
// that open eager ioredis connections at import via the same pattern. No test
35+
// uses these app-level singletons directly (store-routing tests build their own
36+
// engine and run store), so stub them to no-op proxies.
37+
const noopProxy = () =>
38+
new Proxy(
39+
{},
40+
{
41+
get: () => vi.fn().mockResolvedValue(undefined),
42+
}
43+
);
44+
45+
vi.mock("~/v3/runEngine.server", () => ({ engine: noopProxy() }));
46+
vi.mock("~/v3/marqs/index.server", () => ({ marqs: noopProxy(), MarQS: class {} }));
47+
vi.mock("~/v3/marqs/devPubSub.server", () => ({ devPubSub: noopProxy() }));
48+
vi.mock("~/v3/handleSocketIo.server", () => ({
49+
socketIo: noopProxy(),
50+
roomFromFriendlyRunId: (id: string) => `room:${id}`,
51+
}));

0 commit comments

Comments
 (0)