feat: add AssetItem.purchaseOrderId (nullable FK)

This commit is contained in:
toanhoang
2026-08-02 20:09:06 -04:00
parent 4d12885500
commit a6d7c7b302
5 changed files with 155 additions and 20 deletions
+97 -2
View File
@@ -24,10 +24,42 @@ function postRequest(body: unknown): Request {
});
}
// AssetItem.purchaseOrderId is a real FK to PurchaseOrder (see
// schema.prisma / src/lib/assets.ts), and PurchaseOrder.vendorId is a real
// FK to Vendor — tests that need a valid purchaseOrderId create real rows
// first and use their ids. Mirrors tests/purchase-orders.test.ts's
// createTestVendor() helper.
function createTestVendor(overrides: { name?: string } = {}) {
return prisma.vendor.create({
data: {
name: overrides.name ?? "Test Vendor",
contactEmail: "[email protected]",
contactPhone: "0281234567",
address: "123 Test Street",
},
});
}
function createTestPurchaseOrder(overrides: { vendorId?: string } = {}) {
return prisma.purchaseOrder.create({
data: {
vendorId: overrides.vendorId as string,
orderDate: new Date("2026-08-01"),
expectedDate: new Date("2026-08-10"),
},
});
}
beforeEach(async () => {
// Each test starts from an empty table so listing/order assertions are
// deterministic.
// Each test starts from empty tables so listing/order assertions are
// deterministic. asset_items must be cleared before purchase_orders/
// vendors since asset_items has FKs (ON DELETE SET NULL to
// purchase_orders) — deleteMany order avoids relying on that cascade
// behavior in tests. purchase_orders must be cleared before vendors
// (ON DELETE RESTRICT to vendors, mirrors tests/purchase-orders.test.ts).
await prisma.assetItem.deleteMany();
await prisma.purchaseOrder.deleteMany();
await prisma.vendor.deleteMany();
// Default to a logged-in session — the tests below exercise business
// logic, not auth gating (that's covered by the "auth gating" describe
// block further down).
@@ -111,6 +143,69 @@ describe("POST /api/assets", () => {
expect(body.error).toMatch(/warrantyEndDate/i);
});
it("creates an asset item with a purchaseOrderId that references a real purchase order", async () => {
const vendor = await createTestVendor();
const purchaseOrder = await createTestPurchaseOrder({ vendorId: vendor.id });
const res = await POST(
postRequest({
serialNumber: "SN-007",
model: "Dell Latitude 5440",
purchaseDate: "2026-01-15",
purchaseOrderId: purchaseOrder.id,
})
);
expect(res.status).toBe(201);
const body = await res.json();
expect(body.purchase_order_id).toBe(purchaseOrder.id);
});
it("creates an asset item with no purchaseOrderId (stays null)", async () => {
const res = await POST(
postRequest({ serialNumber: "SN-008", model: "Dell Latitude 5440", purchaseDate: "2026-01-15" })
);
expect(res.status).toBe(201);
const body = await res.json();
expect(body.purchase_order_id).toBeNull();
});
it("rejects a purchaseOrderId that is a well-formed UUID but doesn't reference an existing purchase order", async () => {
const res = await POST(
postRequest({
serialNumber: "SN-009",
model: "Dell Latitude 5440",
purchaseDate: "2026-01-15",
purchaseOrderId: "00000000-0000-0000-0000-000000000000",
})
);
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/purchaseOrderId/i);
expect(body.error).toMatch(/does not reference an existing purchase order/i);
// And it must not have created anything.
const remaining = await prisma.assetItem.findMany();
expect(remaining).toHaveLength(0);
});
it("rejects a purchaseOrderId that is not a valid UUID", async () => {
const res = await POST(
postRequest({
serialNumber: "SN-012",
model: "Dell Latitude 5440",
purchaseDate: "2026-01-15",
purchaseOrderId: "not-a-uuid",
})
);
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/purchaseOrderId/i);
});
it("rejects a duplicate serialNumber", async () => {
await POST(postRequest({ serialNumber: "SN-006", model: "Dell", purchaseDate: "2026-01-15" }));
const res = await POST(postRequest({ serialNumber: "SN-006", model: "HP", purchaseDate: "2026-02-01" }));