test(FN-4854): cover chat-room cache hydration and invalidation
Fusion-Task-Id: FN-4854 Fusion-Task-Lineage: 969ec04a-efe3-4dee-b183-2d4f2de6b388
This commit is contained in:
committed by
gsxdsm
parent
4307d71c3a
commit
4caee9be30
@@ -47,6 +47,12 @@ export function DashboardLoader({ stage }: DashboardLoaderProps) {
|
|||||||
clearCache(SWR_CACHE_KEYS.TASKS_PREFIX);
|
clearCache(SWR_CACHE_KEYS.TASKS_PREFIX);
|
||||||
clearCache(SWR_CACHE_KEYS.PROJECTS);
|
clearCache(SWR_CACHE_KEYS.PROJECTS);
|
||||||
clearCache(SWR_CACHE_KEYS.CURRENT_PROJECT_ID);
|
clearCache(SWR_CACHE_KEYS.CURRENT_PROJECT_ID);
|
||||||
|
clearCache(SWR_CACHE_KEYS.AGENTS);
|
||||||
|
clearCache(SWR_CACHE_KEYS.AGENT_STATS);
|
||||||
|
clearCache(SWR_CACHE_KEYS.DOCUMENTS_PREFIX);
|
||||||
|
clearCache(SWR_CACHE_KEYS.TODO_LISTS_PREFIX);
|
||||||
|
clearCache(SWR_CACHE_KEYS.CHAT_ROOMS);
|
||||||
|
clearCache(SWR_CACHE_KEYS.ACTIVE_CHAT_ROOM_ID);
|
||||||
}
|
}
|
||||||
return versionUpdated;
|
return versionUpdated;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ describe("DashboardLoader", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockConsumeVersionUpdateFlag.mockReset();
|
mockConsumeVersionUpdateFlag.mockReset();
|
||||||
mockConsumeVersionUpdateFlag.mockReturnValue(false);
|
mockConsumeVersionUpdateFlag.mockReturnValue(false);
|
||||||
|
mockClearCache.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders projects stage with active first step and pending remaining steps", () => {
|
it("renders projects stage with active first step and pending remaining steps", () => {
|
||||||
@@ -75,6 +76,15 @@ describe("DashboardLoader", () => {
|
|||||||
|
|
||||||
expect(screen.getByText("Updating to a new frontend version...")).toBeInTheDocument();
|
expect(screen.getByText("Updating to a new frontend version...")).toBeInTheDocument();
|
||||||
expect(screen.getByRole("status", { name: "Updating Fusion dashboard" })).toBeInTheDocument();
|
expect(screen.getByRole("status", { name: "Updating Fusion dashboard" })).toBeInTheDocument();
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.TASKS_PREFIX);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.PROJECTS);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.CURRENT_PROJECT_ID);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.AGENTS);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.AGENT_STATS);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.DOCUMENTS_PREFIX);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.TODO_LISTS_PREFIX);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.CHAT_ROOMS);
|
||||||
|
expect(mockClearCache).toHaveBeenCalledWith(SWR_CACHE_KEYS.ACTIVE_CHAT_ROOM_ID);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps stage visuals stable across all stages", () => {
|
it("keeps stage visuals stable across all stages", () => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { ChatRoom, ChatRoomMember, ChatRoomMessage } from "@fusion/core";
|
|||||||
import { useChatRooms } from "../useChatRooms";
|
import { useChatRooms } from "../useChatRooms";
|
||||||
import * as apiModule from "../../api";
|
import * as apiModule from "../../api";
|
||||||
import * as sseBusModule from "../../sse-bus";
|
import * as sseBusModule from "../../sse-bus";
|
||||||
|
import { SWR_CACHE_KEYS } from "../../utils/swrCache";
|
||||||
|
|
||||||
vi.mock("../../api", () => ({
|
vi.mock("../../api", () => ({
|
||||||
fetchChatRooms: vi.fn(),
|
fetchChatRooms: vi.fn(),
|
||||||
@@ -78,6 +79,7 @@ describe("useChatRooms", () => {
|
|||||||
capturedEvents = sub.events ?? {};
|
capturedEvents = sub.events ?? {};
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
});
|
});
|
||||||
|
window.localStorage.clear();
|
||||||
mockFetchChatRooms.mockResolvedValue({ rooms: [] });
|
mockFetchChatRooms.mockResolvedValue({ rooms: [] });
|
||||||
mockFetchChatRoomMembers.mockResolvedValue({ members: [] });
|
mockFetchChatRoomMembers.mockResolvedValue({ members: [] });
|
||||||
mockFetchChatRoomMessages.mockResolvedValue({ messages: [] });
|
mockFetchChatRoomMessages.mockResolvedValue({ messages: [] });
|
||||||
@@ -87,8 +89,25 @@ describe("useChatRooms", () => {
|
|||||||
mockClearChatRoomMessages.mockResolvedValue({ success: true, deletedCount: 1 });
|
mockClearChatRoomMessages.mockResolvedValue({ success: true, deletedCount: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("loads rooms on mount", async () => {
|
it("hydrates cached rooms and active room synchronously", async () => {
|
||||||
mockFetchChatRooms.mockResolvedValueOnce({ rooms: [room("room-1", "one", "2026-05-09T01:00:00.000Z")] });
|
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"));
|
||||||
|
mockFetchChatRooms.mockImplementationOnce(
|
||||||
|
() =>
|
||||||
|
new Promise(() => {
|
||||||
|
// keep pending; assert fast-path hydration
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useChatRooms("proj-1"));
|
||||||
|
|
||||||
|
expect(result.current.rooms).toEqual(cachedRooms);
|
||||||
|
expect(result.current.activeRoom?.id).toBe("room-1");
|
||||||
|
expect(result.current.roomsLoading).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads rooms on mount", async () => { mockFetchChatRooms.mockResolvedValueOnce({ rooms: [room("room-1", "one", "2026-05-09T01:00:00.000Z")] });
|
||||||
const { result } = renderHook(() => useChatRooms("proj-1"));
|
const { result } = renderHook(() => useChatRooms("proj-1"));
|
||||||
|
|
||||||
await waitFor(() => expect(result.current.roomsLoading).toBe(false));
|
await waitFor(() => expect(result.current.roomsLoading).toBe(false));
|
||||||
@@ -96,8 +115,18 @@ describe("useChatRooms", () => {
|
|||||||
expect(mockFetchChatRooms).toHaveBeenCalledWith({}, "proj-1");
|
expect(mockFetchChatRooms).toHaveBeenCalledWith({}, "proj-1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("createRoom persists and loads active room members/messages", async () => {
|
it("writes rooms list to cache after successful refresh", async () => {
|
||||||
const { result } = renderHook(() => useChatRooms("proj-1"));
|
const rooms = [room("room-1", "one", "2026-05-09T01:00:00.000Z")];
|
||||||
|
mockFetchChatRooms.mockResolvedValueOnce({ rooms });
|
||||||
|
|
||||||
|
renderHook(() => useChatRooms("proj-1"));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(JSON.parse(window.localStorage.getItem(`${SWR_CACHE_KEYS.CHAT_ROOMS}:proj-1`) ?? "[]")).toEqual(rooms);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createRoom persists and loads active room members/messages", async () => { const { result } = renderHook(() => useChatRooms("proj-1"));
|
||||||
mockFetchChatRoomMembers.mockResolvedValueOnce({ members: [roomMember("room-new", "agent-1")] });
|
mockFetchChatRoomMembers.mockResolvedValueOnce({ members: [roomMember("room-new", "agent-1")] });
|
||||||
mockFetchChatRoomMessages.mockResolvedValueOnce({ messages: [roomMessage("msg-1", "room-new", "hello")] });
|
mockFetchChatRoomMessages.mockResolvedValueOnce({ messages: [roomMessage("msg-1", "room-new", "hello")] });
|
||||||
|
|
||||||
@@ -144,8 +173,17 @@ describe("useChatRooms", () => {
|
|||||||
await waitFor(() => expect(result.current.messages[0]?.id).toBe("msg-2"));
|
await waitFor(() => expect(result.current.messages[0]?.id).toBe("msg-2"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles room message SSE for active and inactive rooms", async () => {
|
it("keeps messages out of cache payload", async () => {
|
||||||
const older = room("room-1", "one", "2026-05-09T01:00:00.000Z");
|
const rooms = [room("room-1", "one", "2026-05-09T01:00:00.000Z")];
|
||||||
|
mockFetchChatRooms.mockResolvedValueOnce({ rooms });
|
||||||
|
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`) ?? "[]") as Array<Record<string, unknown>>;
|
||||||
|
expect(cached[0]).not.toHaveProperty("messages");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles room message SSE for active and inactive rooms", async () => { const older = room("room-1", "one", "2026-05-09T01:00:00.000Z");
|
||||||
const newer = room("room-2", "two", "2026-05-09T02:00:00.000Z");
|
const newer = room("room-2", "two", "2026-05-09T02:00:00.000Z");
|
||||||
mockFetchChatRooms.mockResolvedValueOnce({ rooms: [older, newer] });
|
mockFetchChatRooms.mockResolvedValueOnce({ rooms: [older, newer] });
|
||||||
const { result } = renderHook(() => useChatRooms("proj-1"));
|
const { result } = renderHook(() => useChatRooms("proj-1"));
|
||||||
|
|||||||
Reference in New Issue
Block a user