155 lines
5.2 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|