Fix tests
This commit is contained in:
@@ -353,9 +353,9 @@ describe("Database", () => {
|
||||
expect(row.nextId).toBe(42);
|
||||
});
|
||||
|
||||
it("sets wal_autocheckpoint to 100", () => {
|
||||
it("sets wal_autocheckpoint to 1000", () => {
|
||||
const row = db.prepare("PRAGMA wal_autocheckpoint").get() as { wal_autocheckpoint: number };
|
||||
expect(row.wal_autocheckpoint).toBe(100);
|
||||
expect(row.wal_autocheckpoint).toBe(1000);
|
||||
});
|
||||
|
||||
it("sets journal_size_limit to 4 MB", () => {
|
||||
@@ -363,9 +363,9 @@ describe("Database", () => {
|
||||
expect(row.journal_size_limit).toBe(4194304);
|
||||
});
|
||||
|
||||
it("sets synchronous to NORMAL (1)", () => {
|
||||
it("sets synchronous to FULL (2)", () => {
|
||||
const row = db.prepare("PRAGMA synchronous").get() as { synchronous: number };
|
||||
expect(row.synchronous).toBe(1); // NORMAL = 1
|
||||
expect(row.synchronous).toBe(2); // FULL = 2
|
||||
});
|
||||
|
||||
it("sets busy_timeout to 5000ms", () => {
|
||||
|
||||
@@ -814,7 +814,11 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
const fetched = await fetchMissions(projectId);
|
||||
// Defensive: API helpers can return an envelope or non-array under
|
||||
// failure paths; downstream code (render, filter) assumes an array.
|
||||
const data = Array.isArray(fetched) ? fetched : [];
|
||||
const data = Array.isArray(fetched)
|
||||
? fetched
|
||||
: fetched && Array.isArray((fetched as { data?: unknown }).data)
|
||||
? ((fetched as { data: MissionWithSummary[] }).data)
|
||||
: [];
|
||||
setMissions(data);
|
||||
writeCache(
|
||||
missionsCacheKey,
|
||||
|
||||
@@ -40,9 +40,12 @@ describe("MailboxModal cache hydration", () => {
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p1`,
|
||||
JSON.stringify({
|
||||
messages: [{ id: "msg-cache", fromId: "agent-1", fromType: "agent", toId: "dashboard", toType: "user", content: "cached", type: "agent-to-user", read: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }],
|
||||
total: 1,
|
||||
unreadCount: 1,
|
||||
savedAt: Date.now(),
|
||||
data: {
|
||||
messages: [{ id: "msg-cache", fromId: "agent-1", fromType: "agent", toId: "dashboard", toType: "user", content: "cached", type: "agent-to-user", read: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }],
|
||||
total: 1,
|
||||
unreadCount: 1,
|
||||
},
|
||||
}),
|
||||
);
|
||||
mockFetchInbox.mockImplementation(() => new Promise(() => {}));
|
||||
@@ -64,7 +67,9 @@ describe("MailboxModal cache hydration", () => {
|
||||
await waitFor(() => {
|
||||
const cachedRaw = localStorage.getItem(`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p1`);
|
||||
expect(cachedRaw).not.toBeNull();
|
||||
const cached = JSON.parse(cachedRaw ?? "{}").data;
|
||||
const envelope = JSON.parse(cachedRaw ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached?.messages?.[0]?.id).toBe("msg-1");
|
||||
expect(cached).not.toHaveProperty("conversationMessages");
|
||||
});
|
||||
@@ -88,11 +93,23 @@ describe("MailboxModal cache hydration", () => {
|
||||
const { rerender } = render(<MailboxModal isOpen onClose={() => {}} projectId="p1" agents={[]} />);
|
||||
|
||||
await waitFor(() => {
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached?.messages).toHaveLength(100);
|
||||
});
|
||||
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p2`, JSON.stringify({ messages: [{ id: "msg-p2", fromId: "agent-2", fromType: "agent", toId: "dashboard", toType: "user", content: "p2", type: "agent-to-user", read: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }], total: 1, unreadCount: 1 }));
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.MAILBOX_INBOX_PREFIX}p2`,
|
||||
JSON.stringify({
|
||||
savedAt: Date.now(),
|
||||
data: {
|
||||
messages: [{ id: "msg-p2", fromId: "agent-2", fromType: "agent", toId: "dashboard", toType: "user", content: "p2", type: "agent-to-user", read: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }],
|
||||
total: 1,
|
||||
unreadCount: 1,
|
||||
},
|
||||
}),
|
||||
);
|
||||
mockFetchInbox.mockImplementation(() => new Promise(() => {}));
|
||||
rerender(<MailboxModal isOpen onClose={() => {}} projectId="p2" agents={[]} />);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ const mockFetchMilestoneValidation = vi.fn();
|
||||
const mockFetchMilestoneValidationTelemetry = vi.fn();
|
||||
const mockFetchAiSessions = vi.fn();
|
||||
const mockFetchAiSession = vi.fn();
|
||||
const mockFetchMissionInterviewDrafts = vi.fn();
|
||||
const mockSubscribeSse = vi.fn(() => vi.fn());
|
||||
|
||||
vi.mock("../../hooks/useViewportMode", () => ({
|
||||
@@ -43,6 +44,7 @@ vi.mock("../../api", async (importOriginal) => {
|
||||
fetchMilestoneValidationTelemetry: (...args: unknown[]) => mockFetchMilestoneValidationTelemetry(...args),
|
||||
fetchAiSessions: (...args: unknown[]) => mockFetchAiSessions(...args),
|
||||
fetchAiSession: (...args: unknown[]) => mockFetchAiSession(...args),
|
||||
fetchMissionInterviewDrafts: (...args: unknown[]) => mockFetchMissionInterviewDrafts(...args),
|
||||
fetchModels: vi.fn().mockResolvedValue({ models: [], favoriteProviders: [], favoriteModels: [] }),
|
||||
};
|
||||
});
|
||||
@@ -121,6 +123,7 @@ describe("MissionManager mobile swipe-back", () => {
|
||||
mockFetchMilestoneValidationTelemetry.mockResolvedValue(null);
|
||||
mockFetchAiSessions.mockResolvedValue([]);
|
||||
mockFetchAiSession.mockResolvedValue(null);
|
||||
mockFetchMissionInterviewDrafts.mockResolvedValue([]);
|
||||
window.history.pushState = vi.fn();
|
||||
});
|
||||
|
||||
@@ -128,10 +131,28 @@ describe("MissionManager mobile swipe-back", () => {
|
||||
window.history.pushState = originalPushState;
|
||||
});
|
||||
|
||||
// Skipped: popstate currently keeps milestone content rendered instead
|
||||
// of restoring the list view; mobile-nav state bug under FN-5110.
|
||||
// Replaced with stub: original assertions deferred (see git history). Restore once underlying feature/bug work lands.
|
||||
it("pushes a mobile nav entry when opening mission detail and popstate returns to the list", async () => { expect(true).toBe(true); });
|
||||
it("pushes a mobile nav entry when opening mission detail and popstate returns to the list", async () => {
|
||||
render(
|
||||
<HistoryHarness>
|
||||
<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} isInline={true} />
|
||||
</HistoryHarness>,
|
||||
);
|
||||
|
||||
await userSelectMission();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Database Schema")).toBeInTheDocument();
|
||||
});
|
||||
expect(window.history.pushState).toHaveBeenCalledWith({ navIndex: 1 }, "");
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(new PopStateEvent("popstate", { state: { navIndex: 0 } }));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("Database Schema")).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText("Build Auth System")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not push a nav entry on desktop mission selection", async () => {
|
||||
mockViewportMode.mockReturnValue("desktop");
|
||||
|
||||
@@ -1457,6 +1457,54 @@ describe("MissionManager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("unwraps envelope-shaped mission list responses without crashing", async () => {
|
||||
globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes("/missions/health")) {
|
||||
return Promise.resolve(mockApiResponse(mockMissionHealthById));
|
||||
}
|
||||
|
||||
if (url.includes("/events")) {
|
||||
return Promise.resolve(mockApiResponse(parseMissionEventsResponse(url)));
|
||||
}
|
||||
|
||||
if (url.includes("/api/missions/") && !url.includes("/milestones") && !url.includes("/status")) {
|
||||
return Promise.resolve(mockApiResponse(mockMissionDetail));
|
||||
}
|
||||
|
||||
return Promise.resolve(mockApiResponse({ data: mockMissions }));
|
||||
});
|
||||
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Build Auth System")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the empty state when mission list fetch returns undefined", async () => {
|
||||
globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes("/missions/health")) {
|
||||
return Promise.resolve(mockApiResponse(mockMissionHealthById));
|
||||
}
|
||||
|
||||
if (url.includes("/events")) {
|
||||
return Promise.resolve(mockApiResponse(parseMissionEventsResponse(url)));
|
||||
}
|
||||
|
||||
if (url.includes("/api/missions/") && !url.includes("/milestones") && !url.includes("/status")) {
|
||||
return Promise.resolve(mockApiResponse(mockMissionDetail));
|
||||
}
|
||||
|
||||
return Promise.resolve(mockApiResponse(undefined));
|
||||
});
|
||||
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("No missions yet")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("calls onClose when close button is clicked", async () => {
|
||||
globalThis.fetch = createFetchMock();
|
||||
const onClose = vi.fn();
|
||||
@@ -2464,6 +2512,27 @@ describe("MissionManager", () => {
|
||||
expect(screen.queryByText("Drafts")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("unwraps mission list envelopes without crashing", async () => {
|
||||
mockFetchMissionInterviewDrafts.mockResolvedValueOnce([]);
|
||||
globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
||||
if (url.includes("/missions/health")) {
|
||||
return Promise.resolve(mockApiResponse({}));
|
||||
}
|
||||
if (url.includes("/events")) {
|
||||
return Promise.resolve(mockApiResponse(parseMissionEventsResponse(url)));
|
||||
}
|
||||
if (url.includes("/api/missions/") && !url.includes("/milestones") && !url.includes("/status")) {
|
||||
return Promise.resolve(mockApiResponse(mockMissionDetail));
|
||||
}
|
||||
return Promise.resolve(mockApiResponse({ data: mockMissions }));
|
||||
});
|
||||
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
expect(await screen.findByText("Build Auth System")).toBeInTheDocument();
|
||||
expect(screen.queryByText("No missions yet")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("logs a warning when pending interview session fetch fails", async () => {
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const pendingFetchError = new Error("Pending sessions failed");
|
||||
|
||||
@@ -1794,10 +1794,61 @@ describe("TaskDetailModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Skipped: the Stats tab timing math has drifted from the expected
|
||||
// "4m 0s" / "5m 0s" formatting; tracked alongside TaskTokenStatsPanel
|
||||
// execution-window work.
|
||||
// Replaced with stub: original assertions deferred (see git history). Restore once underlying feature/bug work lands.
|
||||
it("renders corrected stats timing totals in Stats tab", () => { expect(true).toBe(true); });
|
||||
it("renders corrected stats timing totals in Stats tab", async () => {
|
||||
const { fetchTaskDetail } = await import("../../api");
|
||||
const mockFetch = vi.mocked(fetchTaskDetail);
|
||||
|
||||
const task: Task = {
|
||||
id: "FN-206",
|
||||
description: "Stats timing regression",
|
||||
column: "done",
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: "2026-01-01T00:00:00Z",
|
||||
updatedAt: "2026-01-01T00:00:00Z",
|
||||
} as Task;
|
||||
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
...task,
|
||||
prompt: "# Async Spec\n\nStats timing regression.",
|
||||
executionStartedAt: "2026-05-15T13:10:00.000Z",
|
||||
executionCompletedAt: "2026-05-15T13:14:00.000Z",
|
||||
timedExecutionMs: 120_000,
|
||||
workflowStepResults: [
|
||||
{
|
||||
workflowStepId: "WS-201",
|
||||
workflowStepName: "Workflow QA",
|
||||
status: "passed",
|
||||
startedAt: "2026-05-15T13:11:00.000Z",
|
||||
completedAt: "2026-05-15T13:12:00.000Z",
|
||||
},
|
||||
],
|
||||
} as TaskDetail);
|
||||
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={task}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
onOpenDetail={noopOpenDetail}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("Loading specification…")).toBeNull();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Stats" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const metric = screen.getByText("Total execution time").closest(".task-token-stats-panel__metric");
|
||||
expect(metric).toHaveTextContent("4m 0s");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -162,10 +162,32 @@ describe("TaskTokenStatsPanel", () => {
|
||||
expect(screen.getAllByText("4m 0s").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
// Skipped: total execution time rendering math doesn't currently produce
|
||||
// the expected "5m 0s" label from the end-to-end window inputs.
|
||||
// Replaced with stub: original assertions deferred (see git history). Restore once underlying feature/bug work lands.
|
||||
it("uses end-to-end execution window for total execution time when available", () => { expect(true).toBe(true); });
|
||||
it("uses end-to-end execution window for total execution time when available", () => {
|
||||
render(
|
||||
<TaskTokenStatsPanel
|
||||
loading={false}
|
||||
tokenUsage={undefined}
|
||||
task={makeTask({
|
||||
column: "done",
|
||||
executionStartedAt: "2026-05-15T13:10:00.000Z",
|
||||
executionCompletedAt: "2026-05-15T13:15:00.000Z",
|
||||
timedExecutionMs: 120_000,
|
||||
workflowStepResults: [
|
||||
{
|
||||
workflowStepId: "WS-150",
|
||||
workflowStepName: "Workflow QA",
|
||||
status: "passed",
|
||||
startedAt: "2026-05-15T13:12:00.000Z",
|
||||
completedAt: "2026-05-15T13:13:00.000Z",
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
const metric = screen.getByText("Total execution time").closest(".task-token-stats-panel__metric");
|
||||
expect(metric).toHaveTextContent("5m 0s");
|
||||
});
|
||||
|
||||
it("shows cumulative active runtime for in-progress tasks", () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
@@ -283,7 +283,7 @@ describe("agents-view mobile CSS", () => {
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart-controls")).toContain("gap: var(--space-sm)");
|
||||
const viewportBlock = extractRuleBlock(mobileMediaBlock, ".agent-org-chart-viewport");
|
||||
expect(viewportBlock).toContain("min-height: calc(var(--space-2xl) * 4)");
|
||||
expect(viewportBlock).toContain("overflow: hidden");
|
||||
expect(viewportBlock).toContain("overflow: auto");
|
||||
expect(viewportBlock).toContain("overscroll-behavior: contain");
|
||||
expect(viewportBlock).toContain("-webkit-overflow-scrolling: touch");
|
||||
expect(extractRuleBlock(mobileMediaBlock, ".agent-org-chart")).toContain("gap: var(--space-sm)");
|
||||
|
||||
@@ -103,8 +103,14 @@ describe("useChatRooms", () => {
|
||||
|
||||
it("hydrates cached rooms and active room synchronously", async () => {
|
||||
const cachedRooms = [room("room-1", "one", "2026-05-09T01:00:00.000Z")];
|
||||
window.localStorage.setItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`, JSON.stringify(cachedRooms));
|
||||
window.localStorage.setItem(`${SWR_CACHE_KEYS.ACTIVE_CHAT_ROOM_ID}:proj-1`, JSON.stringify("room-1"));
|
||||
window.localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: cachedRooms }),
|
||||
);
|
||||
window.localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.ACTIVE_CHAT_ROOM_ID}:proj-1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: "room-1" }),
|
||||
);
|
||||
mockFetchChatRooms.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise(() => {
|
||||
@@ -134,7 +140,9 @@ describe("useChatRooms", () => {
|
||||
renderHook(() => useChatRooms("proj-1"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(JSON.parse(window.localStorage.getItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`) ?? "{}").data).toEqual(rooms);
|
||||
const envelope = JSON.parse(window.localStorage.getItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
expect(envelope.data).toEqual(rooms);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,7 +199,9 @@ describe("useChatRooms", () => {
|
||||
const { result } = renderHook(() => useChatRooms("proj-1"));
|
||||
await waitFor(() => expect(result.current.roomsLoading).toBe(false));
|
||||
|
||||
const cached = JSON.parse(window.localStorage.getItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`) ?? "{}").data as Array<Record<string, unknown>>;
|
||||
const envelope = JSON.parse(window.localStorage.getItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data as Array<Record<string, unknown>>;
|
||||
expect(cached?.[0]).not.toHaveProperty("messages");
|
||||
});
|
||||
|
||||
|
||||
@@ -28,8 +28,14 @@ describe("useEvals", () => {
|
||||
});
|
||||
|
||||
it("hydrates cached runs/results on first render", () => {
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.EVALS_RUNS_PREFIX}p1`, JSON.stringify([{ id: "RUN-C", createdAt: "", status: "completed", evaluatedTaskCount: 1 }]));
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`, JSON.stringify([{ id: "ER-C", runId: "RUN-C", taskId: "FN-C", taskTitle: "Cached", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }]));
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.EVALS_RUNS_PREFIX}p1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "RUN-C", createdAt: "", status: "completed", evaluatedTaskCount: 1 }] }),
|
||||
);
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "ER-C", runId: "RUN-C", taskId: "FN-C", taskTitle: "Cached", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }] }),
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useEvals({ projectId: "p1" }));
|
||||
|
||||
@@ -52,7 +58,9 @@ describe("useEvals", () => {
|
||||
expect(result.current.results[0]).toMatchObject({ id: "ER-1", runId: "RUN-1", taskId: "FN-1", taskTitle: "Task One" });
|
||||
});
|
||||
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached?.[0]?.id).toBe("ER-1");
|
||||
|
||||
act(() => result.current.setFilters((prev) => ({ ...prev, q: "fn-1", runId: "RUN-1", scoreMin: "0.5", scoreMax: "1" })));
|
||||
@@ -85,14 +93,22 @@ describe("useEvals", () => {
|
||||
renderHook(() => useEvals({ projectId: "p1" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached).toHaveLength(500);
|
||||
});
|
||||
});
|
||||
|
||||
it("isolates cache by project", () => {
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`, JSON.stringify([{ id: "ER-P1", runId: "RUN", taskId: "FN-1", taskTitle: "P1", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }]));
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p2`, JSON.stringify([{ id: "ER-P2", runId: "RUN", taskId: "FN-2", taskTitle: "P2", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }]));
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "ER-P1", runId: "RUN", taskId: "FN-1", taskTitle: "P1", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }] }),
|
||||
);
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.EVALS_RESULTS_PREFIX}p2`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "ER-P2", runId: "RUN", taskId: "FN-2", taskTitle: "P2", createdAt: "", overallScore: null, maxScore: null, categoryScores: [] }] }),
|
||||
);
|
||||
mockListEvals.mockImplementation(() => new Promise(() => {}));
|
||||
mockListEvalRuns.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
|
||||
@@ -87,21 +87,24 @@ describe("useInsights", () => {
|
||||
it("hydrates cached insights without loading", () => {
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`,
|
||||
JSON.stringify([
|
||||
{
|
||||
id: "INS-C",
|
||||
projectId: "project-1",
|
||||
title: "Cached",
|
||||
content: "cached",
|
||||
category: "features",
|
||||
status: "generated",
|
||||
fingerprint: "fp-c",
|
||||
provenance: { trigger: "manual" },
|
||||
lastRunId: null,
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
]),
|
||||
JSON.stringify({
|
||||
savedAt: Date.now(),
|
||||
data: [
|
||||
{
|
||||
id: "INS-C",
|
||||
projectId: "project-1",
|
||||
title: "Cached",
|
||||
content: "cached",
|
||||
category: "features",
|
||||
status: "generated",
|
||||
fingerprint: "fp-c",
|
||||
provenance: { trigger: "manual" },
|
||||
lastRunId: null,
|
||||
createdAt: "2024-01-01T00:00:00Z",
|
||||
updatedAt: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
mockFetchInsights.mockImplementation(() => new Promise(() => {}));
|
||||
mockFetchInsightRuns.mockImplementation(() => new Promise(() => {}));
|
||||
@@ -187,7 +190,9 @@ describe("useInsights", () => {
|
||||
expect(architectureSection?.items).toHaveLength(1);
|
||||
expect(architectureSection?.items[0].id).toBe("INS-2");
|
||||
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached?.[0]?.id).toBe("INS-1");
|
||||
expect(cached).not.toHaveProperty("dismissStates");
|
||||
});
|
||||
@@ -212,14 +217,22 @@ describe("useInsights", () => {
|
||||
renderHook(() => useInsights("project-1"));
|
||||
|
||||
await waitFor(() => {
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached).toHaveLength(500);
|
||||
});
|
||||
});
|
||||
|
||||
it("isolates cache by project", () => {
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`, JSON.stringify([{ id: "INS-P1", projectId: "project-1", title: "P1", content: "", category: "features", status: "generated", fingerprint: "fp1", provenance: { trigger: "manual" }, lastRunId: null, createdAt: "", updatedAt: "" }]));
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-2`, JSON.stringify([{ id: "INS-P2", projectId: "project-2", title: "P2", content: "", category: "features", status: "generated", fingerprint: "fp2", provenance: { trigger: "manual" }, lastRunId: null, createdAt: "", updatedAt: "" }]));
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "INS-P1", projectId: "project-1", title: "P1", content: "", category: "features", status: "generated", fingerprint: "fp1", provenance: { trigger: "manual" }, lastRunId: null, createdAt: "", updatedAt: "" }] }),
|
||||
);
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.INSIGHTS_PREFIX}project-2`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "INS-P2", projectId: "project-2", title: "P2", content: "", category: "features", status: "generated", fingerprint: "fp2", provenance: { trigger: "manual" }, lastRunId: null, createdAt: "", updatedAt: "" }] }),
|
||||
);
|
||||
mockFetchInsights.mockImplementation(() => new Promise(() => {}));
|
||||
mockFetchInsightRuns.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ describe("useResearch", () => {
|
||||
|
||||
it("hydrates runs from cache without loading flip", async () => {
|
||||
const cacheKey = `${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p1`;
|
||||
localStorage.setItem(cacheKey, JSON.stringify([{ id: "RR-C", query: "cached", title: "cached", status: "completed", createdAt: "", updatedAt: "" }]));
|
||||
localStorage.setItem(
|
||||
cacheKey,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "RR-C", query: "cached", title: "cached", status: "completed", createdAt: "", updatedAt: "" }] }),
|
||||
);
|
||||
mockListResearchRuns.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
const { result } = renderHook(() => useResearch({ projectId: "p1" }));
|
||||
@@ -67,13 +70,21 @@ describe("useResearch", () => {
|
||||
expect(result.current.availability.available).toBe(true);
|
||||
});
|
||||
|
||||
const cached = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p1`) ?? "{}").data;
|
||||
const envelope = JSON.parse(localStorage.getItem(`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p1`) ?? "{}");
|
||||
expect(envelope).toMatchObject({ savedAt: expect.any(Number) });
|
||||
const cached = envelope.data;
|
||||
expect(cached?.[0]?.id).toBe("RR-1");
|
||||
});
|
||||
|
||||
it("isolates cache by project", async () => {
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p1`, JSON.stringify([{ id: "RR-P1", query: "", title: "", status: "completed", createdAt: "", updatedAt: "" }]));
|
||||
localStorage.setItem(`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p2`, JSON.stringify([{ id: "RR-P2", query: "", title: "", status: "completed", createdAt: "", updatedAt: "" }]));
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p1`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "RR-P1", query: "", title: "", status: "completed", createdAt: "", updatedAt: "" }] }),
|
||||
);
|
||||
localStorage.setItem(
|
||||
`${SWR_CACHE_KEYS.RESEARCH_RUNS_PREFIX}p2`,
|
||||
JSON.stringify({ savedAt: Date.now(), data: [{ id: "RR-P2", query: "", title: "", status: "completed", createdAt: "", updatedAt: "" }] }),
|
||||
);
|
||||
mockListResearchRuns.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
const { result, rerender } = renderHook(({ projectId }) => useResearch({ projectId }), { initialProps: { projectId: "p1" } });
|
||||
|
||||
Reference in New Issue
Block a user