8c8a34e320
Add Request.force (migration add_request_force): the pipeline's intake now skips the "already in library" dedupe for a forced job, so an owned album is re-downloaded. The import step still keeps the new copy only when it's higher quality, so a forced upgrade can never downgrade what's on disk. - Worker: _is_force_job → dedupe bypassed when Request.force. - POST /api/library/[id]/upgrade finds the matching MonitoredRelease and enqueues a force request (guards against stacking on an in-flight job). - Library route exposes monitoredReleaseId; the album modal shows a "Replace / upgrade" button when a release exists. Worker + web tests added (pipeline force re-acquire; upgrade route). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.3 KiB
TypeScript
55 lines
2.3 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { prisma } from "@/lib/db";
|
|
import { POST } from "./route";
|
|
|
|
const ctx = (id: string) => ({ params: Promise.resolve({ id }) });
|
|
|
|
async function owned(artist: string, album: string, rgMbid: string | null) {
|
|
return prisma.libraryItem.create({
|
|
data: { artist, album, path: "/m", source: "scan", format: "FLAC", qualityClass: 2, rgMbid },
|
|
});
|
|
}
|
|
|
|
describe("POST /api/library/[id]/upgrade", () => {
|
|
it("enqueues a forced re-acquire request against the matching release", async () => {
|
|
await prisma.monitoredRelease.create({
|
|
data: { artistMbid: "a1", artistName: "Adele", rgMbid: "rg-25", album: "25", secondaryTypes: [],
|
|
monitored: true, state: "fulfilled", currentQualityClass: 2 },
|
|
});
|
|
const item = await owned("Adele", "25", "rg-25");
|
|
|
|
const res = await POST(new Request("http://x", { method: "POST" }), ctx(item.id));
|
|
expect(res.status).toBe(202);
|
|
expect((await res.json()).enqueued).toBe(true);
|
|
|
|
const reqRow = await prisma.request.findFirst({ where: { album: "25" }, include: { job: true } });
|
|
expect(reqRow?.force).toBe(true);
|
|
expect(reqRow?.monitoredReleaseId).toBeTruthy();
|
|
expect(reqRow?.job).toBeTruthy();
|
|
});
|
|
|
|
it("400s when there is no matching release", async () => {
|
|
const item = await owned("Nobody", "Untracked", null);
|
|
const res = await POST(new Request("http://x", { method: "POST" }), ctx(item.id));
|
|
expect(res.status).toBe(400);
|
|
});
|
|
|
|
it("does not stack a second job while one is in flight", async () => {
|
|
const mr = await prisma.monitoredRelease.create({
|
|
data: { artistMbid: "a1", artistName: "Adele", rgMbid: "rg-21", album: "21", secondaryTypes: [], monitored: true, state: "wanted" },
|
|
});
|
|
const item = await owned("Adele", "21", "rg-21");
|
|
await prisma.request.create({
|
|
data: { artist: "Adele", album: "21", monitoredReleaseId: mr.id, job: { create: { state: "downloading" } } },
|
|
});
|
|
const res = await POST(new Request("http://x", { method: "POST" }), ctx(item.id));
|
|
expect(res.status).toBe(202);
|
|
expect((await res.json()).enqueued).toBe(false);
|
|
});
|
|
|
|
it("404s an unknown library item", async () => {
|
|
const res = await POST(new Request("http://x", { method: "POST" }), ctx("nope"));
|
|
expect(res.status).toBe(404);
|
|
});
|
|
});
|