Files
netfelix-audio-fix/server/api/__tests__/review-unsort-reopen.test.ts
T
felixfoertsch 686434f5c3 remove jellyfin, mqtt, webhook services, fix tests, add schema migrations
- delete server/services/jellyfin.ts, webhook.ts, mqtt.ts and their tests
- strip jellyfin/mqtt imports and startup calls from index.tsx and settings.ts
- remove /jellyfin, /mqtt, /mqtt/status, /mqtt/test, /jellyfin/webhook-plugin endpoints from settings router
- clean ENV_MAP and isEnvConfigured of jellyfin/mqtt keys
- add db/index.ts migrations for series_key, duration_seconds, scan_status, scan_error, last_scanned_at (new columns absent on older dev DBs)
- move idx_media_items_series_key out of SCHEMA into migrate() so it runs after the column is added
- fix all test fixtures: drop jellyfin_id/series_jellyfin_id column refs, update MediaItem/MediaStream object literals to match current types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 06:31:54 +02:00

97 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);
});
});