feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain - Enums: visibility, transport, tier, role - audit_log is metadata-only (E2E encryption enforced at broker/client) - Cascade on mesh delete, soft-delete via archivedAt/revokedAt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
115
packages/api/src/utils/test/on-error.test.ts
Normal file
115
packages/api/src/utils/test/on-error.test.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { onError } from "../on-error";
|
||||
|
||||
import type { Context } from "hono";
|
||||
|
||||
vi.mock("@turbostarter/i18n/server", () => ({
|
||||
getTranslation: vi.fn().mockResolvedValue({
|
||||
t: (key: string) => key,
|
||||
i18n: {},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@turbostarter/i18n", () => ({
|
||||
isKey: vi.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
vi.mock("@turbostarter/shared/utils", () => ({
|
||||
getStatusCode: vi.fn().mockReturnValue(500),
|
||||
}));
|
||||
|
||||
vi.mock("@turbostarter/monitoring-web/server", () => ({
|
||||
captureException: vi.fn(),
|
||||
}));
|
||||
|
||||
interface ErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
status: number;
|
||||
path: string;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
describe("onError", () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(console, "log").mockImplementation(() => {
|
||||
/* empty */
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should return a formatted error response for valid error object", async () => {
|
||||
const error = {
|
||||
code: "ERROR_CODE",
|
||||
message: "Something went wrong",
|
||||
};
|
||||
|
||||
const context = {
|
||||
req: { raw: { url: "http://localhost/api/test" } },
|
||||
var: { locale: "en" },
|
||||
} as Context<{
|
||||
Bindings: { NODE_ENV: string };
|
||||
Variables: { locale: string };
|
||||
}>;
|
||||
|
||||
const response = await onError(error, context);
|
||||
|
||||
const data = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(data).toMatchObject({
|
||||
code: "ERROR_CODE",
|
||||
message: "Something went wrong",
|
||||
status: 500,
|
||||
path: "/api/test",
|
||||
});
|
||||
expect(data.timestamp).toBeDefined();
|
||||
});
|
||||
|
||||
it("should translate error code if message is missing", async () => {
|
||||
const error = {
|
||||
code: "auth.error.invalid_credentials",
|
||||
message: "",
|
||||
};
|
||||
|
||||
const context = {
|
||||
req: { raw: { url: "http://localhost/api/test" } },
|
||||
var: { locale: "en" },
|
||||
} as Context<{
|
||||
Bindings: { NODE_ENV: string };
|
||||
Variables: { locale: string };
|
||||
}>;
|
||||
|
||||
const response = await onError(error, context);
|
||||
|
||||
const data = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(data.message).toBe("auth.error.invalid_credentials");
|
||||
});
|
||||
|
||||
it("should fallback to general error if input is not a recognized error object", async () => {
|
||||
const error = "Just a string error";
|
||||
|
||||
const context = {
|
||||
req: { raw: { url: "http://localhost/api/test" } },
|
||||
var: { locale: "en" },
|
||||
} as Context<{
|
||||
Bindings: { NODE_ENV: string };
|
||||
Variables: { locale: string };
|
||||
}>;
|
||||
|
||||
const response = await onError(error, context);
|
||||
|
||||
const data = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(data).toMatchObject({
|
||||
code: "common:error.general",
|
||||
message: "common:error.general",
|
||||
status: 500,
|
||||
path: "/api/test",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user