feat(buyer-tasks): GET /api/v1/buyer-tasks route

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-07-29 20:02:04 +02:00
parent 05242f09f4
commit 275a47e28f
2 changed files with 92 additions and 0 deletions

View file

@ -0,0 +1,63 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
vi.mock("@/lib/server/buyer-tasks-service", () => ({
listBuyerTasks: vi.fn(),
}));
import { GET } from "./route";
import { listBuyerTasks } from "@/lib/server/buyer-tasks-service";
function makeRequest(tab: string | null) {
const url = tab ? `http://test/api/v1/buyer-tasks?tab=${tab}` : "http://test/api/v1/buyer-tasks";
return new Request(url);
}
describe("GET /api/v1/buyer-tasks", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns groups for valid tab", async () => {
vi.mocked(listBuyerTasks).mockResolvedValue([
{
groupId: "order-1::sup-1",
orderId: "order-1",
orderNumber: "ORD-001",
supplierId: "sup-1",
supplierName: "Steel Co",
customerName: "J. Smith",
companyName: "Acme",
items: [],
itemCount: 1,
totalCost: 50,
purchaseOrderId: null,
poStatus: null,
step: "needs_po",
},
]);
const res = await GET(makeRequest("needs_po") as never);
expect(res.status).toBe(200);
const body = await res.json();
expect(body.groups).toHaveLength(1);
expect(body.groups[0].supplierName).toBe("Steel Co");
});
it("returns 400 on invalid tab", async () => {
const res = await GET(makeRequest("invalid") as never);
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toMatch(/tab/i);
});
it("returns 400 when tab is missing", async () => {
const res = await GET(makeRequest(null) as never);
expect(res.status).toBe(400);
});
it("returns 500 on service error", async () => {
vi.mocked(listBuyerTasks).mockRejectedValue(new Error("PB down"));
const res = await GET(makeRequest("needs_po") as never);
expect(res.status).toBe(500);
});
});

View file

@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from "next/server";
import { listBuyerTasks, type BuyerTaskTab } from "@/lib/server/buyer-tasks-service";
const VALID_TABS: BuyerTaskTab[] = ["needs_po", "po_draft", "po_sent"];
export async function GET(req: NextRequest | Request) {
// NextRequest exposes a parsed `nextUrl`; a plain Request (used in
// unit tests with `new Request(url)`) doesn't. Fall back to parsing
// `req.url` so both shapes work.
const tab = (req as NextRequest).nextUrl?.searchParams?.get("tab")
?? new URL(req.url).searchParams.get("tab");
if (!tab || !VALID_TABS.includes(tab as BuyerTaskTab)) {
return NextResponse.json(
{ error: "Invalid or missing 'tab' parameter" },
{ status: 400 }
);
}
try {
const groups = await listBuyerTasks(tab as BuyerTaskTab);
return NextResponse.json({ groups });
} catch (err) {
return NextResponse.json(
{ error: err instanceof Error ? err.message : "Failed to load buyer tasks" },
{ status: 500 }
);
}
}