Files
optima/tests/unit/apiResponse.test.ts
T

155 lines
5.2 KiB
TypeScript

import { describe, test, expect, beforeEach } from "bun:test";
import { apiResponse } from "../../src/modules/api-utils/apiResponse";
import { z } from "zod";
import GenericError from "../../src/Errors/GenericError";
describe("apiResponse", () => {
// -------------------------------------------------------------------
// successful
// -------------------------------------------------------------------
describe("successful()", () => {
test("returns status 200 and successful: true", () => {
const res = apiResponse.successful("OK");
expect(res.status).toBe(200);
expect(res.successful).toBe(true);
expect(res.message).toBe("OK");
});
test("includes data when provided", () => {
const data = { id: 1, name: "Test" };
const res = apiResponse.successful("OK", data);
expect(res.data).toEqual(data);
});
test("data is undefined when not provided", () => {
const res = apiResponse.successful("OK");
expect(res.data).toBeUndefined();
});
test("includes meta.timestamp", () => {
const before = Date.now();
const res = apiResponse.successful("OK");
const after = Date.now();
expect(res.meta.timestamp).toBeGreaterThanOrEqual(before);
expect(res.meta.timestamp).toBeLessThanOrEqual(after);
});
test("accepts optional meta parameter (overridden by timestamp)", () => {
const res = apiResponse.successful("OK", null, { custom: true } as any);
// The implementation replaces meta entirely with { timestamp }
expect(res.meta.timestamp).toBeDefined();
});
});
// -------------------------------------------------------------------
// created
// -------------------------------------------------------------------
describe("created()", () => {
test("returns status 201 and successful: true", () => {
const res = apiResponse.created("Created");
expect(res.status).toBe(201);
expect(res.successful).toBe(true);
expect(res.message).toBe("Created");
});
test("includes data when provided", () => {
const res = apiResponse.created("Created", { id: "abc" });
expect(res.data).toEqual({ id: "abc" });
});
test("data is undefined when not provided", () => {
const res = apiResponse.created("Created");
expect(res.data).toBeUndefined();
});
test("includes meta.timestamp", () => {
const res = apiResponse.created("Created");
expect(res.meta.timestamp).toBeDefined();
expect(typeof res.meta.timestamp).toBe("number");
});
});
// -------------------------------------------------------------------
// error
// -------------------------------------------------------------------
describe("error()", () => {
test("reads status from error object", () => {
const err = new GenericError({
name: "Oops",
message: "bad",
status: 422,
});
const res = apiResponse.error(err);
expect(res.status).toBe(422);
expect(res.message).toBe("bad");
expect(res.error).toBe("Oops");
expect(res.successful).toBe(false);
});
test("defaults status to 400 when error has no status", () => {
const err = new Error("plain error");
const res = apiResponse.error(err);
expect(res.status).toBe(400);
});
test("includes meta.timestamp", () => {
const err = new Error("x");
const res = apiResponse.error(err);
expect(res.meta.timestamp).toBeDefined();
});
});
// -------------------------------------------------------------------
// internalError
// -------------------------------------------------------------------
describe("internalError()", () => {
test("returns status 500", () => {
const res = apiResponse.internalError();
expect(res.status).toBe(500);
expect(res.successful).toBe(false);
expect(res.error).toBe("InternalServerError");
expect(res.message).toContain("Internal Server Error");
});
test("includes meta.timestamp", () => {
const res = apiResponse.internalError();
expect(res.meta.timestamp).toBeDefined();
});
});
// -------------------------------------------------------------------
// zodError
// -------------------------------------------------------------------
describe("zodError()", () => {
test("returns status 400 with parsed error data", () => {
const schema = z.object({ name: z.string() });
let zodErr: z.ZodError;
try {
schema.parse({ name: 123 });
throw new Error("should not reach");
} catch (e) {
zodErr = e as z.ZodError;
}
const res = apiResponse.zodError(zodErr!);
expect(res.status).toBe(400);
expect(res.successful).toBe(false);
expect(res.message).toBe("TypeError");
expect(Array.isArray(res.error)).toBe(true);
expect(res.error[0].code).toBe("invalid_type");
});
test("includes meta.timestamp", () => {
const schema = z.object({ x: z.string() });
let zodErr: z.ZodError;
try {
schema.parse({});
throw new Error("should not reach");
} catch (e) {
zodErr = e as z.ZodError;
}
const res = apiResponse.zodError(zodErr!);
expect(res.meta.timestamp).toBeDefined();
});
});
});