import { describe, expect, it } from "vitest"; import { createActor, waitFor } from "xstate"; import { createTestChatMachine, TEST_CHAT_MACHINE_INPUT, } from "./chat-machine.test-utils"; describe("chat session flow", () => { it("initializes the conversation from the supplied character", () => { const actor = createActor(createTestChatMachine(), { input: { characterId: "character_aria" }, }).start(); expect(actor.getSnapshot().context.characterId).toBe("character_aria"); actor.stop(); }); it("keeps guest logout disabled and supports user to guest transition", async () => { const actor = createActor(createTestChatMachine(), { input: TEST_CHAT_MACHINE_INPUT, }).start(); actor.send({ type: "ChatGuestLogin" }); await waitFor(actor, (snapshot) => snapshot.matches({ guestSession: "ready" }), ); actor.send({ type: "ChatLogout" }); expect(actor.getSnapshot().matches({ guestSession: "ready" })).toBe(true); actor.send({ type: "ChatUserLogin", token: "token" }); await waitFor(actor, (snapshot) => snapshot.matches({ userSession: "ready" }), ); actor.send({ type: "ChatGuestLogin" }); await waitFor(actor, (snapshot) => snapshot.matches({ guestSession: "ready" }), ); actor.stop(); }); it("allows explicit logout from user session", async () => { const actor = createActor(createTestChatMachine(), { input: TEST_CHAT_MACHINE_INPUT, }).start(); actor.send({ type: "ChatUserLogin", token: "token" }); await waitFor(actor, (snapshot) => snapshot.matches({ userSession: "ready" }), ); actor.send({ type: "ChatLogout" }); expect(actor.getSnapshot().matches("idle")).toBe(true); actor.stop(); }); it("keeps a promotion separate from normal history", async () => { const actor = createActor( createTestChatMachine({ historyMessages: [ { id: "history-1", content: "Existing history", isFromAI: true, date: "2026-07-13", }, ], }), { input: TEST_CHAT_MACHINE_INPUT }, ).start(); actor.send({ type: "ChatUserLogin", token: "token" }); await waitFor(actor, (snapshot) => snapshot.matches({ userSession: "ready" }), ); actor.send({ type: "ChatPromotionInjected", promotion: { characterId: "character_elio", promotionType: "voice", lockType: "voice_message", clientLockId: "promotion-1", createdAt: 1, }, }); const context = actor.getSnapshot().context; expect(context.messages).toHaveLength(1); expect(context.promotion?.message).toMatchObject({ id: "promotion:promotion-1", locked: true, lockReason: "voice_message", }); actor.stop(); }); it("switches to guest session when guest login arrives during user session", async () => { const actor = createActor(createTestChatMachine(), { input: TEST_CHAT_MACHINE_INPUT, }).start(); actor.send({ type: "ChatUserLogin", token: "token" }); await waitFor(actor, (snapshot) => snapshot.matches({ userSession: "ready" }), ); actor.send({ type: "ChatGuestLogin" }); await waitFor(actor, (snapshot) => snapshot.matches({ guestSession: "ready" }), ); actor.stop(); }); it("ignores repeated user login while an authenticated user session is active", async () => { const actor = createActor(createTestChatMachine(), { input: TEST_CHAT_MACHINE_INPUT, }).start(); actor.send({ type: "ChatUserLogin", token: "token" }); await waitFor(actor, (snapshot) => snapshot.matches({ userSession: "ready" }), ); actor.send({ type: "ChatSendMessage", content: "keep this message" }); await waitFor( actor, (snapshot) => snapshot.context.messages.length === 1, ); actor.send({ type: "ChatUserLogin", token: "another-token" }); expect(actor.getSnapshot().matches({ userSession: "ready" })).toBe(true); expect(actor.getSnapshot().context.messages).toMatchObject([ { content: "keep this message", isFromAI: false }, ]); actor.stop(); }); });