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