import { ProjectId, ThreadId, type DesktopAppActivationRequest } from "@t3tools/contracts"; import { describe, expect, it, vi } from "vite-plus/test"; import { DesktopAppActivationBroker } from "./DesktopAppActivationBroker.ts"; const request: DesktopAppActivationRequest = { version: 1, requestId: "request-1", type: "open-workspace", workspaceRoot: "/workspace/project", platform: "linux", }; describe("DesktopAppActivationBroker", () => { it("focuses immediately and waits for renderer readiness", async () => { const activate = vi.fn(); const send = vi.fn(); const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate }); const response = broker.request(request); expect(activate).toHaveBeenCalledOnce(); expect(send).not.toHaveBeenCalled(); broker.registerRenderer(send); expect(send).toHaveBeenCalledWith(request); broker.complete({ version: 1, requestId: request.requestId, ok: true, projectId: ProjectId.make("project-1"), threadId: ThreadId.make("thread-1"), }); await expect(response).resolves.toMatchObject({ ok: true, projectId: "project-1" }); broker.close(); }); it("fails an in-flight request when the renderer goes away", async () => { const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate: vi.fn() }); broker.registerRenderer(vi.fn()); const response = broker.request(request); broker.clearRenderer(); await expect(response).resolves.toMatchObject({ ok: false, code: "renderer-unavailable", }); broker.close(); }); it("queues requests after unsubscribe until a new renderer registers", async () => { const previousSend = vi.fn(); const nextSend = vi.fn(); const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate: vi.fn() }); broker.registerRenderer(previousSend); broker.clearRenderer(); const response = broker.request(request); expect(previousSend).not.toHaveBeenCalled(); expect(nextSend).not.toHaveBeenCalled(); broker.registerRenderer(nextSend); expect(nextSend).toHaveBeenCalledWith(request); broker.complete({ version: 1, requestId: request.requestId, ok: true, projectId: ProjectId.make("project-1"), threadId: ThreadId.make("thread-1"), }); await expect(response).resolves.toMatchObject({ ok: true }); broker.close(); }); it("removes a queued request when its CLI connection closes", async () => { const send = vi.fn(); const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate: vi.fn() }); const response = broker.request(request); broker.cancel(request.requestId); broker.registerRenderer(send); await expect(response).resolves.toMatchObject({ ok: false, code: "renderer-unavailable" }); expect(send).not.toHaveBeenCalled(); broker.close(); }); it("never sends a canceled request that was queued behind another request", async () => { const send = vi.fn(); const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate: vi.fn() }); broker.registerRenderer(send); const secondRequest = { ...request, requestId: "request-2" }; const firstResponse = broker.request(request); const secondResponse = broker.request(secondRequest); expect(send).toHaveBeenCalledTimes(1); expect(send).toHaveBeenLastCalledWith(request); broker.cancel(secondRequest.requestId); broker.complete({ version: 1, requestId: request.requestId, ok: true, projectId: ProjectId.make("project-1"), threadId: ThreadId.make("thread-1"), }); await expect(firstResponse).resolves.toMatchObject({ ok: true }); await expect(secondResponse).resolves.toMatchObject({ ok: false }); expect(send).toHaveBeenCalledTimes(1); broker.close(); }); it("times out a request without polling", async () => { vi.useFakeTimers(); try { const broker = new DesktopAppActivationBroker({ requestTimeoutMs: 1_000, activate: vi.fn() }); const response = broker.request(request); await vi.advanceTimersByTimeAsync(1_000); await expect(response).resolves.toMatchObject({ ok: false, code: "request-timeout" }); broker.close(); } finally { vi.useRealTimers(); } }); });