feat: add request create/list API
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { POST, GET } from "./route";
|
||||||
|
|
||||||
|
function postReq(body: unknown) {
|
||||||
|
return new Request("http://localhost/api/requests", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("requests API", () => {
|
||||||
|
it("creates a request with an initial job", async () => {
|
||||||
|
const res = await POST(postReq({ artist: "Radiohead", album: "In Rainbows" }));
|
||||||
|
expect(res.status).toBe(201);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.artist).toBe("Radiohead");
|
||||||
|
expect(body.status).toBe("pending");
|
||||||
|
expect(body.job.state).toBe("requested");
|
||||||
|
expect(body.job.currentStage).toBe("intake");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects a request missing fields", async () => {
|
||||||
|
const res = await POST(postReq({ artist: "Radiohead" }));
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists requests newest first with job state", async () => {
|
||||||
|
await POST(postReq({ artist: "A", album: "1" }));
|
||||||
|
await POST(postReq({ artist: "B", album: "2" }));
|
||||||
|
const res = await GET();
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(body.requests).toHaveLength(2);
|
||||||
|
expect(body.requests[0].artist).toBe("B");
|
||||||
|
expect(body.requests[0].job.state).toBe("requested");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { prisma } from "@/lib/db";
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
let body: unknown;
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return Response.json({ error: "invalid JSON" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { artist, album } = (body ?? {}) as { artist?: unknown; album?: unknown };
|
||||||
|
if (typeof artist !== "string" || !artist.trim() || typeof album !== "string" || !album.trim()) {
|
||||||
|
return Response.json({ error: "artist and album are required" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const created = await prisma.request.create({
|
||||||
|
data: {
|
||||||
|
artist: artist.trim(),
|
||||||
|
album: album.trim(),
|
||||||
|
job: { create: {} },
|
||||||
|
},
|
||||||
|
include: { job: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
id: created.id,
|
||||||
|
artist: created.artist,
|
||||||
|
album: created.album,
|
||||||
|
status: created.status,
|
||||||
|
job: { id: created.job!.id, state: created.job!.state, currentStage: created.job!.currentStage },
|
||||||
|
},
|
||||||
|
{ status: 201 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const requests = await prisma.request.findMany({
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
include: { job: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
requests: requests.map((r) => ({
|
||||||
|
id: r.id,
|
||||||
|
artist: r.artist,
|
||||||
|
album: r.album,
|
||||||
|
status: r.status,
|
||||||
|
createdAt: r.createdAt,
|
||||||
|
job: r.job ? { state: r.job.state, currentStage: r.job.currentStage } : null,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { beforeEach } from "vitest";
|
||||||
|
import { prisma } from "@/lib/db";
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Job has a cascade FK to Request; delete Job first.
|
||||||
|
await prisma.job.deleteMany();
|
||||||
|
await prisma.request.deleteMany();
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
environment: "node",
|
||||||
|
setupFiles: ["./src/test/setup.ts"],
|
||||||
|
fileParallelism: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user