From 1fbcfab05dccad23c12ef75ce45f99d711a82fb7 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 28 Aug 2026 10:06:47 -0700 Subject: [PATCH] test: cover session search fallback on hub timeout and rejection (#13642) * feat: add searchable session history Rebased onto main and updated to supersede the sidebar search dialog from #13533: the sidebar search icon now opens the indexed command bar (Cmd/Ctrl+P) instead of a sidebar-local cmdk dialog that eagerly loaded the entire session history via loadAllSessions(). CommandDialog gains a shouldFilter passthrough so server-ranked FTS hits are displayed as-is. * fix: harden session history search * fix: evict failed restoration sessions from search * fix: preserve deletion when search eviction fails * fix: address session search review feedback * fix: preserve search suppression during reconciliation * test: cover sidecar search fallback on hub timeout and rejection The existing search_sessions tests only exercised the index-hit and empty-index-fallback paths with an immediately-resolved hub reply. Add coverage for the two other realistic Hub-connection failure modes the fallback is meant to tolerate: the hub call rejecting, and the hub call hanging past the 750ms withSearchDeadline race. --------- Co-authored-by: abeatrix Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com> --- .../desktop-app/sidecar/context.test.ts | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/apps/examples/desktop-app/sidecar/context.test.ts b/apps/examples/desktop-app/sidecar/context.test.ts index 06542fce4e..67cb3aebdb 100644 --- a/apps/examples/desktop-app/sidecar/context.test.ts +++ b/apps/examples/desktop-app/sidecar/context.test.ts @@ -288,6 +288,79 @@ describe("Code sidecar runtime capabilities", () => { expect(list).toHaveBeenCalledOnce(); }); + it("falls back to session metadata when the hub search call rejects", async () => { + const { handleCommand } = await import("./commands"); + const { createSidecarContext } = await import("./context"); + const ctx = createSidecarContext("/workspace/project"); + const command = vi.fn(async () => { + throw new Error("hub connection lost"); + }); + const list = vi.fn(async () => [ + { + sessionId: "session-1", + startedAt: "2026-08-27T12:00:00.000Z", + workspaceRoot: "/workspace/project", + prompt: "generate an image of a puppy", + metadata: { title: "generate an image of a puppy" }, + }, + ]); + ctx.hubClient = { command } as never; + ctx.sessionManager = { list } as never; + + const results = (await handleCommand(ctx, "search_sessions", { + query: "generate", + })) as Array<{ sessionId: string; documentId: string }>; + + expect(results).toEqual([ + expect.objectContaining({ + sessionId: "session-1", + documentId: "session-1:metadata", + }), + ]); + expect(command).toHaveBeenCalledOnce(); + expect(list).toHaveBeenCalledOnce(); + }); + + it("falls back to session metadata when the hub search call exceeds the deadline", async () => { + vi.useFakeTimers(); + try { + const { handleCommand } = await import("./commands"); + const { createSidecarContext } = await import("./context"); + const ctx = createSidecarContext("/workspace/project"); + // Never resolves: exercises the withSearchDeadline race timing out + // rather than the hub call rejecting. + const command = vi.fn(() => new Promise(() => {})); + const list = vi.fn(async () => [ + { + sessionId: "session-1", + startedAt: "2026-08-27T12:00:00.000Z", + workspaceRoot: "/workspace/project", + prompt: "generate an image of a puppy", + metadata: { title: "generate an image of a puppy" }, + }, + ]); + ctx.hubClient = { command } as never; + ctx.sessionManager = { list } as never; + + const pending = handleCommand(ctx, "search_sessions", { + query: "generate", + }) as Promise>; + await vi.advanceTimersByTimeAsync(750); + const results = await pending; + + expect(results).toEqual([ + expect.objectContaining({ + sessionId: "session-1", + documentId: "session-1:metadata", + }), + ]); + expect(command).toHaveBeenCalledOnce(); + expect(list).toHaveBeenCalledOnce(); + } finally { + vi.useRealTimers(); + } + }); + it("forwards raw hub tool updates to attached desktop sessions", async () => { const { createSidecarContext, handleHubLiveEvent } = await import( "./context"