1import { CommandId, EnvironmentId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts";
2import { AtomRegistry } from "effect/reactivity";
3import { expect, it, vi } from "vite-plus/test";
4
5import type { QueuedThreadMessage } from "./thread-outbox-model";
6
7vi.mock("./thread-outbox", async () => {
8 const { Atom } = await import("effect/reactivity");
9 return {
10 threadOutboxManager: {
11 queuedMessagesByThreadKeyAtom: Atom.make<
12 Readonly<Record<string, ReadonlyArray<QueuedThreadMessage>>>
13 >({}).pipe(Atom.keepAlive),
14 },
15 };
16});
17vi.mock("./atom-registry", () => ({ appAtomRegistry: undefined }));
18vi.mock("./shell", () => ({ environmentShell: {} }));
19
20import { threadOutboxManager } from "./thread-outbox";
21import { queuedThreadKeysAtom } from "./use-thread-outbox";
22
23it("retains queue membership while chat lists are hidden and follows queued messages", () => {
24 const scheduled = new Set<() => void>();
25 const registry = AtomRegistry.make({
26 scheduleTask: (task) => {
27 scheduled.add(task);
28 return () => {
29 scheduled.delete(task);
30 };
31 },
32 });
33 using _registry = { [Symbol.dispose]: () => registry.dispose() };
34 const flush = () => {
35 for (const task of scheduled) {
36 scheduled.delete(task);
37 task();
38 }
39 };
40 const release = registry.mount(queuedThreadKeysAtom);
41 const initial = registry.get(queuedThreadKeysAtom);
42 expect([...initial]).toEqual([]);
43 release();
44 flush();
45 expect(registry.get(queuedThreadKeysAtom)).toBe(initial);
46
47 const message: QueuedThreadMessage = {
48 environmentId: EnvironmentId.make("environment-1"),
49 threadId: ThreadId.make("thread-1"),
50 messageId: MessageId.make("message-1"),
51 commandId: CommandId.make("command-1"),
52 text: "Follow up",
53 attachments: [],
54 createdAt: "2026-10-29T12:00:00.000Z",
55 };
56 registry.set(threadOutboxManager.queuedMessagesByThreadKeyAtom, {
57 "environment-1:thread-1": [message],
58 "environment-1:new-thread": [
59 {
60 ...message,
61 threadId: ThreadId.make("new-thread"),
62 creation: {
63 projectId: ProjectId.make("project-1"),
64 workspaceMode: "local",
65 branch: null,
66 worktreePath: null,
67 },
68 },
69 ],
70 });
71 const queued = registry.get(queuedThreadKeysAtom);
72 expect([...queued]).toEqual(["environment-1:thread-1"]);
73 const releaseAgain = registry.mount(queuedThreadKeysAtom);
74 releaseAgain();
75 flush();
76 expect(registry.get(queuedThreadKeysAtom)).toBe(queued);
77
78 registry.set(threadOutboxManager.queuedMessagesByThreadKeyAtom, {});
79 expect([...registry.get(queuedThreadKeysAtom)]).toEqual([]);
80});