chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { postgresTest } from "@internal/testcontainers";
|
||||
import { Prisma, PrismaClient } from "@trigger.dev/database";
|
||||
import {
|
||||
captureInfrastructureErrors,
|
||||
clientSafeErrorMessage,
|
||||
infraErrorAlreadyLogged,
|
||||
logTransactionInfrastructureError,
|
||||
} from "~/utils/prismaErrors";
|
||||
|
||||
vi.setConfig({ testTimeout: 60_000 });
|
||||
|
||||
function capturingLogger() {
|
||||
const captured: Array<{ message: string; fields?: Record<string, unknown> }> = [];
|
||||
return {
|
||||
captured,
|
||||
error: (message: string, fields?: Record<string, unknown>) => {
|
||||
captured.push({ message, fields });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("captureInfrastructureErrors", () => {
|
||||
postgresTest(
|
||||
"P2025 (not found) passes through with code intact and unlogged",
|
||||
async ({ prisma }) => {
|
||||
const log = capturingLogger();
|
||||
const client = captureInfrastructureErrors(prisma, log);
|
||||
|
||||
const error = await client.secretStore
|
||||
.update({ where: { key: "does-not-exist" }, data: { version: "2" } })
|
||||
.then(() => undefined)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(Prisma.PrismaClientKnownRequestError);
|
||||
expect((error as Prisma.PrismaClientKnownRequestError).code).toBe("P2025");
|
||||
expect(log.captured).toHaveLength(0);
|
||||
}
|
||||
);
|
||||
|
||||
postgresTest(
|
||||
"P2002 (unique violation) passes through with code intact and unlogged",
|
||||
async ({ prisma }) => {
|
||||
const log = capturingLogger();
|
||||
const client = captureInfrastructureErrors(prisma, log);
|
||||
|
||||
await client.secretStore.create({ data: { key: "dup-key", value: { a: 1 } } });
|
||||
|
||||
const error = await client.secretStore
|
||||
.create({ data: { key: "dup-key", value: { a: 2 } } })
|
||||
.then(() => undefined)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(Prisma.PrismaClientKnownRequestError);
|
||||
expect((error as Prisma.PrismaClientKnownRequestError).code).toBe("P2002");
|
||||
expect(log.captured).toHaveLength(0);
|
||||
}
|
||||
);
|
||||
|
||||
postgresTest(
|
||||
"errors raised inside an interactive $transaction keep their code",
|
||||
async ({ prisma }) => {
|
||||
const log = capturingLogger();
|
||||
const client = captureInfrastructureErrors(prisma, log);
|
||||
|
||||
// Proves $allOperations fires per-statement inside a transaction — the
|
||||
// basis for transaction retry logic (which branches on error.code) staying
|
||||
// intact.
|
||||
const error = await client
|
||||
.$transaction(async (tx) => {
|
||||
await tx.secretStore.update({ where: { key: "missing-in-tx" }, data: { version: "2" } });
|
||||
})
|
||||
.then(() => undefined)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(Prisma.PrismaClientKnownRequestError);
|
||||
expect((error as Prisma.PrismaClientKnownRequestError).code).toBe("P2025");
|
||||
expect(log.captured).toHaveLength(0);
|
||||
}
|
||||
);
|
||||
|
||||
postgresTest("raw queries (model undefined) are wrapped without crashing", async ({ prisma }) => {
|
||||
const log = capturingLogger();
|
||||
const client = captureInfrastructureErrors(prisma, log);
|
||||
|
||||
const rows = await client.$queryRaw<Array<{ one: number }>>(Prisma.sql`SELECT 1 as one`);
|
||||
expect(rows[0].one).toBe(1);
|
||||
|
||||
// A failing raw query (non-infra) must still rethrow rather than throw on
|
||||
// the undefined `model`.
|
||||
const error = await client
|
||||
.$queryRaw(Prisma.sql`SELECT 1 / 0`)
|
||||
.then(() => undefined)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(log.captured).toHaveLength(0);
|
||||
});
|
||||
|
||||
postgresTest("a genuine connectivity failure is logged with model/operation/code", async () => {
|
||||
const log = capturingLogger();
|
||||
// Point at a closed port to provoke a real P1001 / initialization error —
|
||||
// no mocking.
|
||||
const unreachable = new PrismaClient({
|
||||
datasources: {
|
||||
db: { url: "postgresql://postgres:postgres@127.0.0.1:1/postgres?connect_timeout=2" },
|
||||
},
|
||||
});
|
||||
const client = captureInfrastructureErrors(unreachable, log);
|
||||
|
||||
try {
|
||||
const error = await client.secretStore
|
||||
.findFirst({ where: { key: "anything" } })
|
||||
.then(() => undefined)
|
||||
.catch((e) => e);
|
||||
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(log.captured).toHaveLength(1);
|
||||
expect(log.captured[0].message).toBe("prisma infrastructure error");
|
||||
expect(log.captured[0].fields?.operation).toBe("findFirst");
|
||||
expect(log.captured[0].fields?.model).toBe("SecretStore");
|
||||
|
||||
// Dedupe: the extension tagged it, so a $transaction-boundary logger
|
||||
// seeing the same error must NOT log it a second time.
|
||||
expect(infraErrorAlreadyLogged(error)).toBe(true);
|
||||
const boundaryLog = capturingLogger();
|
||||
expect(logTransactionInfrastructureError(error, boundaryLog)).toBe(false);
|
||||
expect(boundaryLog.captured).toHaveLength(0);
|
||||
} finally {
|
||||
await unreachable.$disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("logTransactionInfrastructureError", () => {
|
||||
// Covers the transaction boundary, which $allOperations cannot reach.
|
||||
it("logs an uncoded infra error (PrismaClientInitializationError)", () => {
|
||||
const log = capturingLogger();
|
||||
const error = new Prisma.PrismaClientInitializationError(
|
||||
"Can't reach database server",
|
||||
"6.14.0",
|
||||
"P1001"
|
||||
);
|
||||
|
||||
expect(logTransactionInfrastructureError(error, log)).toBe(true);
|
||||
expect(log.captured).toHaveLength(1);
|
||||
expect(log.captured[0].message).toBe("prisma.$transaction infrastructure error");
|
||||
expect(log.captured[0].fields?.name).toBe("PrismaClientInitializationError");
|
||||
});
|
||||
|
||||
it("skips a coded infra error (transac's callback already logs those)", () => {
|
||||
const log = capturingLogger();
|
||||
const error = new Prisma.PrismaClientKnownRequestError("Can't reach database server", {
|
||||
code: "P1001",
|
||||
clientVersion: "6.14.0",
|
||||
});
|
||||
|
||||
expect(logTransactionInfrastructureError(error, log)).toBe(false);
|
||||
expect(log.captured).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("skips a non-infra coded error (P2002)", () => {
|
||||
const log = capturingLogger();
|
||||
const error = new Prisma.PrismaClientKnownRequestError("Unique constraint failed", {
|
||||
code: "P2002",
|
||||
clientVersion: "6.14.0",
|
||||
});
|
||||
|
||||
expect(logTransactionInfrastructureError(error, log)).toBe(false);
|
||||
expect(log.captured).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("skips a plain non-Prisma error", () => {
|
||||
const log = capturingLogger();
|
||||
|
||||
expect(logTransactionInfrastructureError(new Error("boom"), log)).toBe(false);
|
||||
expect(log.captured).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clientSafeErrorMessage", () => {
|
||||
// Guards the API-route leak: an infra error's message carries the DB hostname.
|
||||
it("obfuscates a coded infra error (P1001) message", () => {
|
||||
const error = new Prisma.PrismaClientKnownRequestError(
|
||||
"Can't reach database server at `db-internal.example.com:5432`",
|
||||
{ code: "P1001", clientVersion: "6.14.0" }
|
||||
);
|
||||
|
||||
expect(clientSafeErrorMessage(error)).toBe("Internal Server Error");
|
||||
});
|
||||
|
||||
it("obfuscates an uncoded infra error (PrismaClientInitializationError) message", () => {
|
||||
const error = new Prisma.PrismaClientInitializationError(
|
||||
"Can't reach database server at `db-internal.example.com:5432`",
|
||||
"6.14.0",
|
||||
"P1001"
|
||||
);
|
||||
|
||||
expect(clientSafeErrorMessage(error)).toBe("Internal Server Error");
|
||||
});
|
||||
|
||||
it("leaves non-infra Prisma error (P2002) messages unchanged", () => {
|
||||
const error = new Prisma.PrismaClientKnownRequestError("Unique constraint failed on email", {
|
||||
code: "P2002",
|
||||
clientVersion: "6.14.0",
|
||||
});
|
||||
|
||||
expect(clientSafeErrorMessage(error)).toBe("Unique constraint failed on email");
|
||||
});
|
||||
|
||||
it("leaves plain domain/validation error messages unchanged", () => {
|
||||
expect(clientSafeErrorMessage(new Error("Invalid delay value"))).toBe("Invalid delay value");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user