feat(FN-3981): document room message resync contract and reproduce reply vi

Fixes a room reply visibility gap in the chat rooms hook by correcting the message-resync condition and adding test coverage to reproduce the issue, with updated documentation for the resync contract.

Fusion-Task-Id: FN-3981
This commit is contained in:
Fusion
2026-05-11 00:41:56 -07:00
committed by gsxdsm
parent 00c46a056b
commit 544383ab3f
4 changed files with 25 additions and 11 deletions

View File

@@ -182,7 +182,7 @@ describe("useChatRooms", () => {
expect(result.current.activeRoom).toBeNull();
});
it("sendRoomMessage posts without optimistic insert", async () => {
it("sendRoomMessage resyncs room messages from server after post", async () => {
const active = room("room-1", "one", "2026-05-09T01:00:00.000Z");
mockFetchChatRooms.mockResolvedValueOnce({ rooms: [active] });
const { result } = renderHook(() => useChatRooms("proj-1"));
@@ -193,17 +193,20 @@ describe("useChatRooms", () => {
act(() => result.current.selectRoom("room-1"));
await waitFor(() => expect(result.current.activeRoom?.id).toBe("room-1"));
mockFetchChatRoomMessages.mockResolvedValueOnce({
messages: [
roomMessage("msg-user", "room-1", "hello"),
{ ...roomMessage("msg-assistant", "room-1", "Room reply"), role: "assistant", senderAgentId: "agent-1" },
],
});
await act(async () => {
await result.current.sendRoomMessage("hello");
});
expect(mockPostChatRoomMessage).toHaveBeenCalledWith("room-1", { content: "hello" }, "proj-1");
expect(result.current.messages).toHaveLength(0);
act(() => {
capturedEvents["chat:room:message:added"]?.({ data: JSON.stringify(roomMessage("msg-1", "room-1", "hello")) } as MessageEvent);
});
expect(result.current.messages).toHaveLength(1);
expect(mockFetchChatRoomMessages).toHaveBeenLastCalledWith("room-1", { limit: 100 }, "proj-1");
expect(result.current.messages.map((message) => message.id)).toEqual(["msg-user", "msg-assistant"]);
});
it("tears down sse subscription on unmount", async () => {

View File

@@ -168,15 +168,26 @@ export function useChatRooms(
}, [projectId]);
const sendRoomMessage = useCallback(async (content: string, opts?: { attachments?: ChatAttachment[] }) => {
const roomId = activeRoomRef.current?.id;
const activeRoomSnapshot = activeRoomRef.current;
const roomId = activeRoomSnapshot?.id;
if (!roomId) {
throw new Error("Select a room before sending a message");
}
await postChatRoomMessage(roomId, {
const postResult = await postChatRoomMessage(roomId, {
content,
...(opts?.attachments ? { attachments: opts.attachments } : {}),
}, projectId);
if (postResult.message?.createdAt && activeRoomSnapshot) {
setRooms((previous) => upsertRoom(previous, { ...activeRoomSnapshot, updatedAt: postResult.message.createdAt }));
}
const latestMessages = await fetchChatRoomMessages(roomId, { limit: 100 }, projectId);
if (activeRoomRef.current?.id !== roomId) {
return;
}
setMessages(latestMessages.messages);
}, [projectId]);
useEffect(() => {