9d65dd12be
Build and Push Docker Image / build (push) Successful in 2m10s
- clickable error count in header shows file names + error messages - inbox sort dropdown (scan time / name, asc / desc) - inbox movies no longer minimal (show available badges) - stop buttons use solid danger style, descriptive labels (Stop Scan, Stop Job, Stop Sorting) - double checkmarks overlap like WhatsApp read receipts - processInbox logs start/completion to stdout for Docker visibility - fix byTitle in language-resolver test, bump to 2026.04.21.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
import { Database } from "bun:sqlite";
|
|
import { describe, expect, test } from "bun:test";
|
|
import { SCHEMA } from "../../db/schema";
|
|
import { reopenAllDone, unsortAll } from "../review";
|
|
|
|
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 seedPlan(db: Database, id: number, opts: { sorted?: 0 | 1; status?: string; isNoop?: 0 | 1 } = {}) {
|
|
const { sorted = 1, status = "pending", isNoop = 0 } = opts;
|
|
db
|
|
.prepare("INSERT INTO media_items (id, type, name, file_path, container) VALUES (?, 'Movie', ?, ?, 'mkv')")
|
|
.run(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 (?, ?, ?, 'auto_heuristic', ?, 'direct_play', 'copy')",
|
|
)
|
|
.run(id, status, isNoop, sorted);
|
|
}
|
|
|
|
describe("unsortAll", () => {
|
|
test("flips sorted=1 pending plans back to sorted=0, skips is_noop and non-pending", () => {
|
|
const db = makeDb();
|
|
seedPlan(db, 1, { sorted: 1, status: "pending" });
|
|
seedPlan(db, 2, { sorted: 1, status: "pending" });
|
|
seedPlan(db, 3, { sorted: 0, status: "pending" }); // already in inbox
|
|
seedPlan(db, 4, { sorted: 1, status: "approved" }); // queued
|
|
seedPlan(db, 5, { sorted: 1, status: "pending", isNoop: 1 }); // noop
|
|
|
|
const count = unsortAll(db);
|
|
expect(count).toBe(2);
|
|
|
|
const rows = db.prepare("SELECT item_id, sorted, status FROM review_plans ORDER BY item_id").all() as {
|
|
item_id: number;
|
|
sorted: number;
|
|
status: string;
|
|
}[];
|
|
expect(rows).toEqual([
|
|
{ item_id: 1, sorted: 0, status: "pending" },
|
|
{ item_id: 2, sorted: 0, status: "pending" },
|
|
{ item_id: 3, sorted: 0, status: "pending" },
|
|
{ item_id: 4, sorted: 1, status: "approved" },
|
|
{ item_id: 5, sorted: 1, status: "pending" },
|
|
]);
|
|
});
|
|
|
|
test("noop when review column is empty", () => {
|
|
const db = makeDb();
|
|
expect(unsortAll(db)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("reopenAllDone", () => {
|
|
test("flips done/error plans back to pending + inbox (sorted=0) and drops their jobs", () => {
|
|
const db = makeDb();
|
|
seedPlan(db, 1, { status: "done" });
|
|
seedPlan(db, 2, { status: "error" });
|
|
seedPlan(db, 3, { status: "approved" }); // untouched
|
|
db.prepare("INSERT INTO jobs (item_id, command, job_type, status) VALUES (1, 'ffmpeg', 'copy', 'done')").run();
|
|
db.prepare("INSERT INTO jobs (item_id, command, job_type, status) VALUES (2, 'ffmpeg', 'copy', 'error')").run();
|
|
db.prepare("INSERT INTO jobs (item_id, command, job_type, status) VALUES (3, 'ffmpeg', 'copy', 'pending')").run();
|
|
|
|
const count = reopenAllDone(db);
|
|
expect(count).toBe(2);
|
|
|
|
const statuses = db
|
|
.prepare("SELECT item_id, status, sorted, reviewed_at FROM review_plans ORDER BY item_id")
|
|
.all() as {
|
|
item_id: number;
|
|
status: string;
|
|
sorted: number;
|
|
reviewed_at: string | null;
|
|
}[];
|
|
expect(statuses[0]).toMatchObject({ status: "pending", sorted: 0, reviewed_at: null });
|
|
expect(statuses[1]).toMatchObject({ status: "pending", sorted: 0, reviewed_at: null });
|
|
// Untouched plan keeps its pre-existing sorted=1 (default for the seed).
|
|
expect(statuses[2]).toMatchObject({ status: "approved", sorted: 1 });
|
|
|
|
const jobs = db.prepare("SELECT item_id, status FROM jobs ORDER BY item_id").all();
|
|
expect(jobs).toEqual([{ item_id: 3, status: "pending" }]);
|
|
});
|
|
|
|
test("noop when nothing is done or errored", () => {
|
|
const db = makeDb();
|
|
seedPlan(db, 1, { status: "pending" });
|
|
expect(reopenAllDone(db)).toBe(0);
|
|
});
|
|
});
|