Skip to content

Commit c282c2b

Browse files
d-csclaude
andcommitted
fix(run-ops split): green the run-ops read presenter tests
- readRedirectMarker fails open on an unprovisioned marker table (undefined_table): the known-migrated read optimization must never break the run-list read path when the marker table is absent. - expose runOps new/legacy handles through the db.server test seam so the routed-store reads resolve to the real containers. - hoist the runStore ref so the vi.mock factory no longer hits a TDZ. - correct the getActivity bucket expectation: a 6h window buckets into 72 five-minute buckets (chooseBucketSeconds targets ~72), not 6. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b4554e7 commit c282c2b

4 files changed

Lines changed: 22 additions & 16 deletions

File tree

apps/webapp/test/SpanPresenter.readthrough.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ vi.mock("~/db.server", () => ({
1717
$replica: {},
1818
}));
1919

20-
const routingStoreRef: { current: unknown } = { current: undefined };
20+
const routingStoreRef = vi.hoisted(() => ({ current: undefined as unknown }));
2121
vi.mock("~/v3/runStore.server", () => ({
2222
get runStore() {
2323
return routingStoreRef.current;

apps/webapp/test/apiRunListPresenter.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,14 @@ vi.mock("~/db.server", async () => {
4040
}
4141
);
4242
const replicaProxy = lazyProxy(legacyReplicaHolder, "legacyReplicaHolder.client");
43+
const newProxy = lazyProxy(newClientHolder, "newClientHolder.client");
4344
return {
4445
prisma: replicaProxy,
4546
$replica: replicaProxy,
46-
runOpsNewPrisma: lazyProxy(newClientHolder, "newClientHolder.client"),
47+
runOpsNewPrisma: newProxy,
48+
runOpsNewReplica: newProxy,
49+
runOpsLegacyPrisma: replicaProxy,
50+
runOpsLegacyReplica: replicaProxy,
4751
sqlDatabaseSchema: Prisma.sql([`public`]),
4852
};
4953
});

apps/webapp/test/nextRunListPresenter.readthrough.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ vi.mock("~/db.server", async () => {
3030
}
3131
);
3232
const replicaProxy = lazyProxy(legacyReplicaHolder, "legacyReplicaHolder.client");
33+
const newProxy = lazyProxy(newClientHolder, "newClientHolder.client");
3334
return {
3435
prisma: replicaProxy,
3536
$replica: replicaProxy,
36-
runOpsNewPrisma: lazyProxy(newClientHolder, "newClientHolder.client"),
37+
runOpsNewPrisma: newProxy,
38+
runOpsNewReplica: newProxy,
39+
runOpsLegacyPrisma: replicaProxy,
40+
runOpsLegacyReplica: replicaProxy,
3741
sqlDatabaseSchema: Prisma.sql([`public`]),
3842
};
3943
});

apps/webapp/test/presenters/TaskDetailPresenter.getActivity.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,13 @@ describe("TaskDetailPresenter.getActivity (ClickHouse-only)", () => {
6969
settings: { async_insert: 0, enable_json_type: 1, type_json_skip_duplicated_paths: 1 },
7070
});
7171

72-
// 6h window => 1h buckets => 6 buckets.
72+
// 6h window => 300s (5-minute) buckets => 72 buckets (chooseBucketSeconds targets ~72).
7373
const from = new Date("2026-01-01T00:00:00Z");
7474
const to = new Date("2026-01-01T06:00:00Z");
75+
const BUCKET_MS = 5 * 60 * 1000;
7576

76-
// Bucket 0 (00:00–01:00): 1 COMPLETED, 1 FAILED.
77-
// Bucket 2 (02:00–03:00): 1 CANCELED, 1 RUNNING (EXECUTING), 1 unknown-status
78-
// (folds into RUNNING) => RUNNING total = 2.
79-
// Plus a deleted row in bucket 0 that MUST be excluded.
77+
// 00:30 bucket: 1 COMPLETED, 1 FAILED (+ 1 deleted, excluded).
78+
// 02:30 bucket: 1 CANCELED, RUNNING = EXECUTING + unknown-status = 2.
8079
const bucket0 = new Date("2026-01-01T00:30:00Z").getTime();
8180
const bucket2 = new Date("2026-01-01T02:30:00Z").getTime();
8281

@@ -116,8 +115,8 @@ describe("TaskDetailPresenter.getActivity (ClickHouse-only)", () => {
116115
// Stable legend, fixed group order.
117116
expect(activity.statuses).toEqual(["COMPLETED", "FAILED", "CANCELED", "RUNNING"]);
118117

119-
// 6 one-hour buckets, every bucket carries all four group keys.
120-
expect(activity.data).toHaveLength(6);
118+
// 72 five-minute buckets, every bucket carries all four group keys.
119+
expect(activity.data).toHaveLength(72);
121120
for (const point of activity.data) {
122121
expect(typeof point.bucket).toBe("number");
123122
expect(point).toHaveProperty("COMPLETED");
@@ -126,21 +125,20 @@ describe("TaskDetailPresenter.getActivity (ClickHouse-only)", () => {
126125
expect(point).toHaveProperty("RUNNING");
127126
}
128127

129-
// Buckets are epoch MILLISECONDS aligned to the hour.
130-
const expectedStart = Math.floor(from.getTime() / (60 * 60 * 1000)) * (60 * 60 * 1000);
128+
// Buckets are epoch MILLISECONDS aligned to the 5-minute interval.
131129
const byBucket = new Map(activity.data.map((p) => [p.bucket, p]));
132-
const p0 = byBucket.get(expectedStart)!;
133-
const p2 = byBucket.get(expectedStart + 2 * 60 * 60 * 1000)!;
130+
const p0 = byBucket.get(Math.floor(bucket0 / BUCKET_MS) * BUCKET_MS)!;
131+
const p2 = byBucket.get(Math.floor(bucket2 / BUCKET_MS) * BUCKET_MS)!;
134132
expect(p0).toBeDefined();
135133
expect(p2).toBeDefined();
136134

137-
// Bucket 0: 1 COMPLETED, 1 FAILED, deleted row excluded.
135+
// 00:30 bucket: 1 COMPLETED, 1 FAILED, deleted row excluded.
138136
expect(p0.COMPLETED).toBe(1);
139137
expect(p0.FAILED).toBe(1);
140138
expect(p0.CANCELED).toBe(0);
141139
expect(p0.RUNNING).toBe(0);
142140

143-
// Bucket 2: 1 CANCELED, RUNNING = EXECUTING (1) + unknown status (1) = 2.
141+
// 02:30 bucket: 1 CANCELED, RUNNING = EXECUTING (1) + unknown status (1) = 2.
144142
expect(p2.COMPLETED).toBe(0);
145143
expect(p2.FAILED).toBe(0);
146144
expect(p2.CANCELED).toBe(1);

0 commit comments

Comments
 (0)