test: add AssetCategoryAttributeDef API tests
28 tests covering lib-level validation/CRUD and route-level HTTP behavior: malformed JSON, non-object body, invalid dataType, categoryId FK violation, duplicate (categoryId, key) rejected with a clean 400 (not 500), and same key allowed across two different categories (the "not globally unique" case), plus auth gating.
This commit is contained in:
@@ -0,0 +1,660 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Mock } from "vitest";
|
||||
import type { Session } from "next-auth";
|
||||
import {
|
||||
createAssetCategoryAttributeDef,
|
||||
listAssetCategoryAttributeDefs,
|
||||
validateCreateAssetCategoryAttributeDefInput,
|
||||
} from "@/lib/asset-category-attribute-defs";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Note: this file has two layers of coverage, mirroring
|
||||
// tests/purchase-orders.test.ts:
|
||||
// - lib-level tests below exercise src/lib/asset-category-attribute-defs.ts
|
||||
// directly (validation, FK/unique error handling, list ordering).
|
||||
// - route-level tests (further down) exercise
|
||||
// src/app/api/asset-category-attribute-defs/route.ts over HTTP, including
|
||||
// the auth() gate.
|
||||
vi.mock("@/auth", () => ({ auth: vi.fn() }));
|
||||
|
||||
import { GET, POST } from "@/app/api/asset-category-attribute-defs/route";
|
||||
import { auth } from "@/auth";
|
||||
|
||||
const mockedAuth = auth as unknown as Mock<() => Promise<Session | null>>;
|
||||
|
||||
const testSession: Session = {
|
||||
user: { id: "test-user-id" },
|
||||
expires: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
||||
};
|
||||
|
||||
function postRequest(body: unknown): Request {
|
||||
return new Request("http://localhost/api/asset-category-attribute-defs", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
// AssetCategoryAttributeDef.categoryId is a real FK to AssetCategory (see
|
||||
// schema.prisma / src/lib/asset-category-attribute-defs.ts) — tests that
|
||||
// need valid ids create real rows first and use their ids. Mirrors
|
||||
// tests/purchase-orders.test.ts's createTestVendor()/createTestEmployee()
|
||||
// helpers.
|
||||
function createTestAssetCategory(overrides: { name?: string } = {}) {
|
||||
return prisma.assetCategory.create({
|
||||
data: {
|
||||
name: overrides.name ?? "Laptop",
|
||||
valueGroup: "high_value",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
// Each test starts from empty tables so listing/order/uniqueness
|
||||
// assertions are deterministic. asset_category_attribute_defs must be
|
||||
// cleared before asset_categories since it has an FK (ON DELETE RESTRICT)
|
||||
// to it.
|
||||
await prisma.assetCategoryAttributeDef.deleteMany();
|
||||
await prisma.assetCategory.deleteMany();
|
||||
// Default to a logged-in session — the lib-level tests below don't go
|
||||
// through the route at all (so this is a no-op for them), and the
|
||||
// route-level tests exercise business logic, not auth gating (that's
|
||||
// covered by the "auth gating" describe block further down).
|
||||
mockedAuth.mockResolvedValue(testSession);
|
||||
});
|
||||
|
||||
describe("validateCreateAssetCategoryAttributeDefInput", () => {
|
||||
it("accepts valid input", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.value).toMatchObject({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts each of the four fixed dataType values", () => {
|
||||
for (const dataType of ["string", "number", "boolean", "date"] as const) {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: `key_${dataType}`,
|
||||
label: "Label",
|
||||
dataType,
|
||||
required: false,
|
||||
sortOrder: 0,
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a missing categoryId", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/categoryId/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a categoryId that is not a valid UUID", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "not-a-uuid",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/categoryId/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an empty key", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/key/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an empty label", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/label/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an invalid dataType", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "float",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/dataType/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a missing dataType", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/dataType/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a non-boolean required", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: "yes",
|
||||
sortOrder: 1,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/required/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a non-integer sortOrder", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1.5,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/sortOrder/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a missing sortOrder", () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "11111111-1111-1111-1111-111111111111",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/sortOrder/i);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createAssetCategoryAttributeDef", () => {
|
||||
it("creates an attribute def for a real category", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
|
||||
const created = await createAssetCategoryAttributeDef(result.value);
|
||||
|
||||
expect(created.ok).toBe(true);
|
||||
if (!created.ok) return;
|
||||
expect(created.value).toMatchObject({
|
||||
category_id: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
data_type: "number",
|
||||
required: true,
|
||||
sort_order: 1,
|
||||
});
|
||||
expect(typeof created.value.id).toBe("string");
|
||||
expect(created.value.id.length).toBeGreaterThan(0);
|
||||
expect(Number.isNaN(Date.parse(created.value.created_at))).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a clean error when categoryId doesn't reference an existing category", async () => {
|
||||
const result = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: "00000000-0000-0000-0000-000000000000",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
|
||||
const created = await createAssetCategoryAttributeDef(result.value);
|
||||
expect(created.ok).toBe(false);
|
||||
if (created.ok) return;
|
||||
expect(created.error).toMatch(/categoryId/i);
|
||||
expect(created.error).toMatch(/does not reference an existing asset category/i);
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("rejects a duplicate key within the same category with a clean 400-style error", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
const first = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(first.ok).toBe(true);
|
||||
if (first.ok) {
|
||||
const firstCreated = await createAssetCategoryAttributeDef(first.value);
|
||||
expect(firstCreated.ok).toBe(true);
|
||||
}
|
||||
|
||||
const second = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB) duplicate",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 2,
|
||||
});
|
||||
expect(second.ok).toBe(true);
|
||||
if (!second.ok) return;
|
||||
|
||||
const created = await createAssetCategoryAttributeDef(second.value);
|
||||
expect(created.ok).toBe(false);
|
||||
if (created.ok) return;
|
||||
expect(created.error).toMatch(/key/i);
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("allows the same key across two different categories", async () => {
|
||||
const laptop = await createTestAssetCategory({ name: "Laptop" });
|
||||
const monitor = await createTestAssetCategory({ name: "Màn hình" });
|
||||
|
||||
const first = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: laptop.id,
|
||||
key: "weight_kg",
|
||||
label: "Khối lượng (kg)",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(first.ok).toBe(true);
|
||||
if (first.ok) {
|
||||
const firstCreated = await createAssetCategoryAttributeDef(first.value);
|
||||
expect(firstCreated.ok).toBe(true);
|
||||
}
|
||||
|
||||
const second = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: monitor.id,
|
||||
key: "weight_kg",
|
||||
label: "Khối lượng (kg)",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(second.ok).toBe(true);
|
||||
if (!second.ok) return;
|
||||
|
||||
const created = await createAssetCategoryAttributeDef(second.value);
|
||||
expect(created.ok).toBe(true);
|
||||
if (!created.ok) return;
|
||||
expect(created.value.category_id).toBe(monitor.id);
|
||||
expect(created.value.key).toBe("weight_kg");
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("listAssetCategoryAttributeDefs", () => {
|
||||
it("lists created attribute defs, newest first", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
|
||||
const first = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
});
|
||||
expect(first.ok).toBe(true);
|
||||
if (first.ok) await createAssetCategoryAttributeDef(first.value);
|
||||
|
||||
// Ensure a distinct created_at ordering even on fast filesystems/clocks.
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
|
||||
const second = validateCreateAssetCategoryAttributeDefInput({
|
||||
categoryId: category.id,
|
||||
key: "cpu",
|
||||
label: "CPU",
|
||||
dataType: "string",
|
||||
required: false,
|
||||
sortOrder: 2,
|
||||
});
|
||||
expect(second.ok).toBe(true);
|
||||
if (second.ok) await createAssetCategoryAttributeDef(second.value);
|
||||
|
||||
const records = await listAssetCategoryAttributeDefs();
|
||||
|
||||
expect(records).toHaveLength(2);
|
||||
// newest first
|
||||
expect(records[0]).toMatchObject({ key: "cpu" });
|
||||
expect(records[1]).toMatchObject({ key: "ram_gb" });
|
||||
});
|
||||
|
||||
it("returns an empty list when there are no attribute defs", async () => {
|
||||
const records = await listAssetCategoryAttributeDefs();
|
||||
expect(records).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/asset-category-attribute-defs", () => {
|
||||
it("creates an attribute def on valid input", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
const res = await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
const body = await res.json();
|
||||
|
||||
expect(body).toMatchObject({
|
||||
category_id: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
data_type: "number",
|
||||
required: true,
|
||||
sort_order: 1,
|
||||
});
|
||||
expect(typeof body.id).toBe("string");
|
||||
expect(body.id.length).toBeGreaterThan(0);
|
||||
expect(Number.isNaN(Date.parse(body.created_at))).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects an invalid dataType", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
const res = await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "float",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.error).toMatch(/dataType/i);
|
||||
});
|
||||
|
||||
it("rejects a categoryId that is a well-formed UUID but doesn't reference an existing category", async () => {
|
||||
const res = await POST(
|
||||
postRequest({
|
||||
categoryId: "00000000-0000-0000-0000-000000000000",
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.error).toMatch(/categoryId/i);
|
||||
expect(body.error).toMatch(/does not reference an existing asset category/i);
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("rejects a duplicate (categoryId, key) with a clean 400, not a raw 500", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
const first = await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
expect(first.status).toBe(201);
|
||||
|
||||
const second = await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB) duplicate",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 2,
|
||||
})
|
||||
);
|
||||
|
||||
expect(second.status).toBe(400);
|
||||
const body = await second.json();
|
||||
expect(body.error).toMatch(/key/i);
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("allows the same key across two different categories via the route", async () => {
|
||||
const laptop = await createTestAssetCategory({ name: "Laptop" });
|
||||
const monitor = await createTestAssetCategory({ name: "Màn hình" });
|
||||
|
||||
const first = await POST(
|
||||
postRequest({
|
||||
categoryId: laptop.id,
|
||||
key: "weight_kg",
|
||||
label: "Khối lượng (kg)",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
expect(first.status).toBe(201);
|
||||
|
||||
const second = await POST(
|
||||
postRequest({
|
||||
categoryId: monitor.id,
|
||||
key: "weight_kg",
|
||||
label: "Khối lượng (kg)",
|
||||
dataType: "number",
|
||||
required: false,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
expect(second.status).toBe(201);
|
||||
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("rejects an invalid JSON body", async () => {
|
||||
const req = new Request("http://localhost/api/asset-category-attribute-defs", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: "not json",
|
||||
});
|
||||
|
||||
const res = await POST(req);
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.error).toMatch(/JSON/i);
|
||||
});
|
||||
|
||||
it("rejects a non-object JSON body", async () => {
|
||||
// Note: arrays are `typeof "object"` in JS, so they pass the route's
|
||||
// object check (same behavior as /api/asset-categories) — use a JSON
|
||||
// string primitive here to actually exercise the "not an object" branch.
|
||||
const res = await POST(postRequest("not an object"));
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
const body = await res.json();
|
||||
expect(body.error).toMatch(/JSON object/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /api/asset-category-attribute-defs", () => {
|
||||
it("lists created attribute defs, newest first", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
// Ensure a distinct created_at ordering even on fast filesystems/clocks.
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "cpu",
|
||||
label: "CPU",
|
||||
dataType: "string",
|
||||
required: false,
|
||||
sortOrder: 2,
|
||||
})
|
||||
);
|
||||
|
||||
const res = await GET();
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const body = await res.json();
|
||||
expect(Array.isArray(body)).toBe(true);
|
||||
expect(body).toHaveLength(2);
|
||||
|
||||
// newest first
|
||||
expect(body[0]).toMatchObject({ key: "cpu" });
|
||||
expect(body[1]).toMatchObject({ key: "ram_gb" });
|
||||
});
|
||||
|
||||
it("returns an empty list when there are no attribute defs", async () => {
|
||||
const res = await GET();
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("auth gating on /api/asset-category-attribute-defs", () => {
|
||||
// Mirrors tests/asset-categories.test.ts's "auth gating" block — only
|
||||
// checks session presence, per the flat permission model (no
|
||||
// role/permission logic).
|
||||
it("POST /api/asset-category-attribute-defs returns 401 without a session", async () => {
|
||||
const category = await createTestAssetCategory();
|
||||
mockedAuth.mockResolvedValue(null);
|
||||
|
||||
const res = await POST(
|
||||
postRequest({
|
||||
categoryId: category.id,
|
||||
key: "ram_gb",
|
||||
label: "RAM (GB)",
|
||||
dataType: "number",
|
||||
required: true,
|
||||
sortOrder: 1,
|
||||
})
|
||||
);
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ error: "Unauthorized" });
|
||||
|
||||
// And it must not have created anything.
|
||||
const remaining = await prisma.assetCategoryAttributeDef.findMany();
|
||||
expect(remaining).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("GET /api/asset-category-attribute-defs returns 401 without a session", async () => {
|
||||
mockedAuth.mockResolvedValue(null);
|
||||
|
||||
const res = await GET();
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ error: "Unauthorized" });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user