d05f1a7398
Replaces Remix compiler with the Vite plugin. The Express server (cluster, socket.io, ws) and the Docker image contract are unchanged.
136 lines
5.0 KiB
TypeScript
136 lines
5.0 KiB
TypeScript
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 "~/v3/mollifier/routeOperationsToRun.server";
|
|
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();
|
|
});
|
|
});
|