import { describe, expect, it, vi } from "vitest"; // `vi.mock` factories are hoisted above regular top-level `const`s, so // any cross-references between the spy/mock fns and the factories have // to live inside `vi.hoisted`. See `mollifierDrainerHandler.test.ts` // for the same pattern. const { warnSpy, applyMetadataMutationToBufferedRunMock } = vi.hoisted(() => ({ warnSpy: vi.fn(), applyMetadataMutationToBufferedRunMock: vi.fn(), })); // The route module's import graph (createActionApiRoute, the env, the // services singleton) is heavier than the helper actually needs. Stub // the leaf modules so only the helper under test executes; the route's // top-level `createActionApiRoute(...)` call runs against the stubbed // builder and never touches platform.v3.server / prisma. vi.mock("~/db.server", () => ({ prisma: {}, $replica: {} })); vi.mock("~/env.server", () => ({ env: { TASK_RUN_METADATA_MAXIMUM_SIZE: 256 * 1024 }, })); vi.mock("~/services/routeBuilders/apiBuilder.server", () => ({ createActionApiRoute: () => ({ action: vi.fn() }), })); vi.mock("~/services/apiAuth.server", () => ({ authenticateApiRequest: vi.fn(), })); vi.mock("~/v3/services/common.server", () => ({ ServiceValidationError: class extends Error { constructor( public override message: string, public status?: number ) { super(message); } }, })); vi.mock("~/services/metadata/updateMetadataInstance.server", () => ({ updateMetadataService: { call: vi.fn(async () => undefined) }, })); vi.mock("~/v3/mollifier/applyMetadataMutation.server", () => ({ applyMetadataMutationToBufferedRun: applyMetadataMutationToBufferedRunMock, })); vi.mock("~/v3/mollifier/readFallback.server", () => ({ findRunByIdWithMollifierFallback: vi.fn(), })); vi.mock("~/services/logger.server", () => ({ logger: { warn: warnSpy, info: vi.fn(), error: vi.fn(), debug: vi.fn(), }, })); import { routeOperationsToRun } from "~/routes/api.v1.runs.$runId.metadata"; import type { AuthenticatedEnvironment } from "~/services/apiAuth.server"; const env = { id: "env_a", organizationId: "org_1", } as unknown as AuthenticatedEnvironment; const opsFixture = [{ type: "set", key: "k", value: "v" }] as Parameters< typeof routeOperationsToRun >[1]; describe("routeOperationsToRun — non-throw buffer outcome logging", () => { // Each non-success outcome `applyMetadataMutationToBufferedRun` can // return (`not_found`, `busy`, `version_exhausted`, `metadata_too_large`) // must produce a warn log so ops can trace silent drops. Without this // branch the parent/root operation would disappear with no record — // `tryCatch` only catches throws, and the outcome object was // previously ignored. for (const kind of ["not_found", "busy", "version_exhausted", "metadata_too_large"] as const) { it(`warn-logs when buffer outcome is { kind: "${kind}" }`, async () => { warnSpy.mockClear(); applyMetadataMutationToBufferedRunMock.mockResolvedValueOnce({ kind }); await routeOperationsToRun("run_buffered_1", opsFixture, env); expect(warnSpy).toHaveBeenCalledWith( "metadata route: parent/root buffer op did not apply", expect.objectContaining({ targetRunId: "run_buffered_1", kind }) ); }); } it("does NOT warn on the happy path (kind: 'applied')", async () => { warnSpy.mockClear(); applyMetadataMutationToBufferedRunMock.mockResolvedValueOnce({ kind: "applied", newMetadata: { k: "v" }, parentTaskRunFriendlyId: undefined, rootTaskRunFriendlyId: undefined, }); await routeOperationsToRun("run_buffered_1", opsFixture, env); expect(warnSpy).not.toHaveBeenCalledWith( "metadata route: parent/root buffer op did not apply", expect.anything() ); }); it("warn-logs once when the helper throws (the pre-existing throw branch keeps working)", async () => { warnSpy.mockClear(); applyMetadataMutationToBufferedRunMock.mockRejectedValueOnce(new Error("ECONNRESET")); await routeOperationsToRun("run_buffered_1", opsFixture, env); // Pre-existing branch — the catch logs `buffer fallback for parent/root // op failed`. The new non-throw branch must NOT also fire (we return // early on bufferError). expect(warnSpy).toHaveBeenCalledWith( "metadata route: buffer fallback for parent/root op failed", expect.objectContaining({ targetRunId: "run_buffered_1" }) ); expect(warnSpy).not.toHaveBeenCalledWith( "metadata route: parent/root buffer op did not apply", expect.anything() ); }); it("skips both PG and buffer when targetRunId is missing or operations is empty", async () => { warnSpy.mockClear(); applyMetadataMutationToBufferedRunMock.mockClear(); await routeOperationsToRun(undefined, opsFixture, env); await routeOperationsToRun("run_x", undefined, env); await routeOperationsToRun("run_x", [], env); expect(applyMetadataMutationToBufferedRunMock).not.toHaveBeenCalled(); expect(warnSpy).not.toHaveBeenCalled(); }); });