import { beforeEach, describe, expect, it } from "bun:test"; import { Hono } from "hono"; import { globalItems, itemGlobalLinks, items } from "../../src/db/schema.ts"; import { globalItemRoutes } from "../../src/server/routes/global-items.ts"; import { itemRoutes } from "../../src/server/routes/items.ts"; import { createTestDb } from "../helpers/db.ts"; type TestDb = ReturnType; function createTestApp() { const db = createTestDb(); const app = new Hono(); app.use("*", async (c, next) => { c.set("db", db); await next(); }); app.route("/api/global-items", globalItemRoutes); app.route("/api/items", itemRoutes); return { app, db }; } function insertGlobalItem(db: TestDb, brand: string, model: string) { return db .insert(globalItems) .values({ brand, model, category: "bags" }) .returning() .get(); } function insertItem(db: TestDb, name: string) { return db.insert(items).values({ name, categoryId: 1 }).returning().get(); } describe("Global Item Routes", () => { let app: Hono; let db: TestDb; beforeEach(() => { const testApp = createTestApp(); app = testApp.app; db = testApp.db; }); describe("GET /api/global-items", () => { it("returns 200 with all global items", async () => { insertGlobalItem(db, "Revelate Designs", "Terrapin System"); insertGlobalItem(db, "Apidura", "Handlebar Pack"); const res = await app.request("/api/global-items"); expect(res.status).toBe(200); const body = await res.json(); expect(body).toHaveLength(2); }); it("filters results by query parameter", async () => { insertGlobalItem(db, "Revelate Designs", "Terrapin System"); insertGlobalItem(db, "Apidura", "Handlebar Pack"); const res = await app.request("/api/global-items?q=tent"); expect(res.status).toBe(200); const body = await res.json(); // "tent" doesn't match "Terrapin" or "Handlebar" — expect 0 // Actually let's search for something that matches const res2 = await app.request("/api/global-items?q=revelate"); const body2 = await res2.json(); expect(body2).toHaveLength(1); expect(body2[0].brand).toBe("Revelate Designs"); }); }); describe("GET /api/global-items/:id", () => { it("returns item with ownerCount", async () => { const gi = insertGlobalItem(db, "MSR", "PocketRocket 2"); const res = await app.request(`/api/global-items/${gi.id}`); expect(res.status).toBe(200); const body = await res.json(); expect(body.brand).toBe("MSR"); expect(body.ownerCount).toBe(0); }); it("returns 404 for non-existent id", async () => { const res = await app.request("/api/global-items/999"); expect(res.status).toBe(404); }); it("returns 400 for invalid id", async () => { const res = await app.request("/api/global-items/abc"); expect(res.status).toBe(400); }); }); describe("POST /api/items/:id/link", () => { it("returns 201 when linking item to global item", async () => { const gi = insertGlobalItem(db, "MSR", "PocketRocket 2"); const item = insertItem(db, "My Stove"); const res = await app.request(`/api/items/${item.id}/link`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ globalItemId: gi.id }), }); expect(res.status).toBe(201); const body = await res.json(); expect(body.itemId).toBe(item.id); expect(body.globalItemId).toBe(gi.id); }); it("returns 409 when item already linked", async () => { const gi = insertGlobalItem(db, "MSR", "PocketRocket 2"); const item = insertItem(db, "My Stove"); // Link once await app.request(`/api/items/${item.id}/link`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ globalItemId: gi.id }), }); // Link again — should conflict const res = await app.request(`/api/items/${item.id}/link`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ globalItemId: gi.id }), }); expect(res.status).toBe(409); }); it("returns 404 when item does not exist", async () => { const gi = insertGlobalItem(db, "MSR", "PocketRocket 2"); const res = await app.request("/api/items/999/link", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ globalItemId: gi.id }), }); expect(res.status).toBe(404); }); }); describe("DELETE /api/items/:id/link", () => { it("returns 200 when unlinking", async () => { const gi = insertGlobalItem(db, "MSR", "PocketRocket 2"); const item = insertItem(db, "My Stove"); // Link first await app.request(`/api/items/${item.id}/link`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ globalItemId: gi.id }), }); // Unlink const res = await app.request(`/api/items/${item.id}/link`, { method: "DELETE", }); expect(res.status).toBe(200); }); it("returns 404 when item does not exist", async () => { const res = await app.request("/api/items/999/link", { method: "DELETE", }); expect(res.status).toBe(404); }); }); });