84e669922b
Build and Push Docker Image / build (push) Successful in 3m54s
- execute/clear now also resets sorted=0 so cleared items land back in the inbox where the distributor can re-classify them; previously they got stranded in Review with auto_class='auto', unreachable by both "Approve all ready" and "Auto Review" - pipelinecard: action row moves to the top so Skip/Approve/Back-to-review sit in the same place regardless of card body height; title row follows; file info (copy/transcode reasons) gets a dedicated row with the ready/needs-decision badge pushed to the right - tests: clearQueue preserves running/completed jobs, only pending plans flip back to sorted=0 pending Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { Database } from "bun:sqlite";
|
|
import { describe, expect, test } from "bun:test";
|
|
import { SCHEMA } from "../../db/schema";
|
|
import { clearQueue } from "../execute";
|
|
|
|
function makeDb(): Database {
|
|
const db = new Database(":memory:");
|
|
for (const stmt of SCHEMA.split(";")) {
|
|
const trimmed = stmt.trim();
|
|
if (trimmed) db.run(trimmed);
|
|
}
|
|
return db;
|
|
}
|
|
|
|
function seedQueuedItem(db: Database, id: number, autoClass: "auto" | "auto_heuristic" | "manual") {
|
|
db
|
|
.prepare(
|
|
"INSERT INTO media_items (id, jellyfin_id, type, name, file_path, container) VALUES (?, ?, 'Movie', ?, ?, 'mkv')",
|
|
)
|
|
.run(id, `jf-${id}`, `Item ${id}`, `/x/${id}.mkv`);
|
|
db
|
|
.prepare(
|
|
"INSERT INTO review_plans (item_id, status, is_noop, auto_class, sorted, apple_compat, job_type) VALUES (?, 'approved', 0, ?, 1, 'direct_play', 'copy')",
|
|
)
|
|
.run(id, autoClass);
|
|
db.prepare("INSERT INTO jobs (item_id, command, job_type, status) VALUES (?, 'ffmpeg …', 'audio', 'pending')").run(id);
|
|
}
|
|
|
|
describe("clearQueue", () => {
|
|
test("deletes pending jobs and returns plans to the Inbox (sorted=0, pending)", () => {
|
|
const db = makeDb();
|
|
seedQueuedItem(db, 1, "auto");
|
|
seedQueuedItem(db, 2, "auto");
|
|
|
|
const cleared = clearQueue(db);
|
|
expect(cleared).toBe(2);
|
|
|
|
const plans = db.prepare("SELECT item_id, status, sorted FROM review_plans ORDER BY item_id").all() as {
|
|
item_id: number;
|
|
status: string;
|
|
sorted: number;
|
|
}[];
|
|
expect(plans).toEqual([
|
|
{ item_id: 1, status: "pending", sorted: 0 },
|
|
{ item_id: 2, status: "pending", sorted: 0 },
|
|
]);
|
|
|
|
const jobCount = (db.prepare("SELECT COUNT(*) as n FROM jobs WHERE status = 'pending'").get() as { n: number }).n;
|
|
expect(jobCount).toBe(0);
|
|
});
|
|
|
|
test("leaves running + completed jobs alone", () => {
|
|
const db = makeDb();
|
|
seedQueuedItem(db, 1, "auto");
|
|
db.prepare("UPDATE jobs SET status = 'running' WHERE item_id = 1").run();
|
|
seedQueuedItem(db, 2, "auto");
|
|
db.prepare("UPDATE jobs SET status = 'done' WHERE item_id = 2").run();
|
|
seedQueuedItem(db, 3, "auto"); // stays pending
|
|
|
|
const cleared = clearQueue(db);
|
|
expect(cleared).toBe(1);
|
|
|
|
const surviving = db.prepare("SELECT item_id, status FROM jobs ORDER BY item_id").all() as {
|
|
item_id: number;
|
|
status: string;
|
|
}[];
|
|
expect(surviving).toEqual([
|
|
{ item_id: 1, status: "running" },
|
|
{ item_id: 2, status: "done" },
|
|
]);
|
|
|
|
// Only the plan whose job was pending should be reset.
|
|
const plan3 = db.prepare("SELECT status, sorted FROM review_plans WHERE item_id = 3").get() as {
|
|
status: string;
|
|
sorted: number;
|
|
};
|
|
expect(plan3).toEqual({ status: "pending", sorted: 0 });
|
|
const plan1 = db.prepare("SELECT status, sorted FROM review_plans WHERE item_id = 1").get() as {
|
|
status: string;
|
|
sorted: number;
|
|
};
|
|
expect(plan1).toEqual({ status: "approved", sorted: 1 });
|
|
});
|
|
});
|