// @vitest-environment jsdom import { act, renderHook, waitFor } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { type ApplicationResult, useApplicationQuery, } from "../../src/presentation/adapters/query/application-query.ts"; import { createFailure } from "../../src/contracts/errors.ts"; import { queryClient, wrapper } from "./application-query-fixture.tsx"; describe("application query inbound bridge", () => { it("latches a background failure over stale data and clears it on retry success", async () => { const client = queryClient(); const responses: ApplicationResult[] = [ { ok: true, value: ["first"] }, { ok: false, error: createFailure("SERVER_FAILURE", "LIST", 0), }, { ok: true, value: ["recovered"] }, ]; const execute = vi.fn(async () => responses.shift() ?? responses[0]); const hook = renderHook( () => useApplicationQuery({ queryKey: ["resource", "list"], execute, }), { wrapper: wrapper(client) }, ); await waitFor(() => expect(hook.result.current.data).toEqual(["first"])); await act(() => hook.result.current.retry()); await waitFor(() => expect(hook.result.current.state.indicator).toBe("stale-degraded"), ); expect(hook.result.current.state.base).toBe("success"); expect(hook.result.current.data).toEqual(["first"]); await act(() => hook.result.current.retry()); await waitFor(() => expect(hook.result.current.data).toEqual(["recovered"]), ); expect(hook.result.current.state.indicator).toBeNull(); expect(execute).toHaveBeenCalledTimes(3); }); it("projects an initial application failure into terminal state", async () => { const client = queryClient(); const failure = createFailure("FORBIDDEN", "LIST", 0); const hook = renderHook( () => useApplicationQuery({ queryKey: ["forbidden"], execute: async () => ({ ok: false, error: failure }), }), { wrapper: wrapper(client) }, ); await waitFor(() => expect(hook.result.current.state.base).toBe("terminal-error"), ); expect(hook.result.current.state.failure).toBe(failure); }); it("normalizes an unexpected execute rejection into a safe terminal failure", async () => { const client = queryClient(); const hook = renderHook( () => useApplicationQuery({ queryKey: ["unexpected-rejection"], execute: async () => { throw new Error("private upstream detail"); }, }), { wrapper: wrapper(client) }, ); await waitFor(() => expect(hook.result.current.state.base).toBe("terminal-error"), ); expect(hook.result.current.state.failure).toMatchObject({ kind: "UNKNOWN_FAILURE", operationId: "APPLICATION_QUERY", userMessageKey: "error.unknown_failure", action: "contact-support", }); expect(JSON.stringify(hook.result.current.state.failure)).not.toContain( "private upstream detail", ); }); it("passes cancellation to the application and does not retain an unmounted error", async () => { const client = queryClient(); let aborted = false; const execute = vi.fn( ({ signal }: { signal: AbortSignal }) => new Promise>((resolve) => { signal.addEventListener( "abort", () => { aborted = true; resolve({ ok: false, error: createFailure("REQUEST_ABORTED", "LIST", 0), }); }, { once: true }, ); }), ); const hook = renderHook( () => useApplicationQuery({ queryKey: ["cancelled"], execute, }), { wrapper: wrapper(client) }, ); await waitFor(() => expect(execute).toHaveBeenCalledOnce()); hook.unmount(); await waitFor(() => expect(aborted).toBe(true)); expect(client.getQueryState(["cancelled"])?.status).not.toBe("error"); }); });