// Tests der Kompressions-Warteschlange (kern/ablauf/warteschlange.ts, // 5.4.0): eine zur Zeit, der Reihe nach; abbrechen wartend und laufend; // eine Ausnahme im Encode beendet nicht die Schlange; kein Doppel. import { describe, expect, it } from 'vitest' import type { KompressionsAuftrag } from '../src/kern/ablauf/auftrag' import type { AblageErgebnis } from '../src/kern/ablauf/ablegen' import { KompressionsWarteschlange, type WarteschlangenStand } from '../src/kern/ablauf/warteschlange' function auftrag(id: string): KompressionsAuftrag { return { vorgangId: id, laufwerk: 'G', discTyp: 'bluray', rohOrdner: `R:\\roh\\${id}`, dateien: [], zuordnung: null, staffel: null, ersteFolge: null, discNr: null, fingerprint: null, lesefehler: false, hinweise: [], begonnen: '2026-08-30T12:00:00Z', preset: '', zielOrdner: '', } } function fertig(text = 'ok'): AblageErgebnis { return { status: 'fertig', zielOrdner: 'Z', dateien: [], text, komprimiert: true, preset: 'p', fertigBytes: 1, hinweise: [], folgenVergeben: [] } } /** Ein Encode, den der Test von außen beendet. */ function steuerbar() { const freigaben: Array<() => void> = [] const gestartet: string[] = [] const ausfuehren = (a: KompressionsAuftrag, melden: (s: { phase: 'komprimiert'; prozent: number; text: string }) => void, abbruch: AbortSignal) => new Promise((loesen) => { gestartet.push(a.vorgangId) melden({ phase: 'komprimiert', prozent: 10, text: 'läuft' }) abbruch.addEventListener('abort', () => loesen({ ...fertig('abgebrochen'), status: 'abgebrochen' }), { once: true }) freigaben.push(() => loesen(fertig())) }) return { ausfuehren, freigaben, gestartet } } const tick = (): Promise => new Promise((r) => setTimeout(r, 0)) describe('KompressionsWarteschlange', () => { it('arbeitet der Reihe nach, meldet Stand und Ergebnis, vergibt Plätze', async () => { const { ausfuehren, freigaben, gestartet } = steuerbar() const staende: WarteschlangenStand[] = [] const ergebnisse: string[] = [] const q = new KompressionsWarteschlange(ausfuehren, (s) => staende.push(s), (a, e) => { ergebnisse.push(`${a.vorgangId}:${e.status}`) }) expect(q.einreihen(auftrag('a'), 'A')).toBe(1) expect(q.einreihen(auftrag('b'), 'B')).toBe(2) expect(q.einreihen(auftrag('c'), 'C')).toBe(3) await tick() expect(gestartet).toEqual(['a']) expect(q.stand().laeuft?.vorgangId).toBe('a') expect(q.stand().laeuft?.prozent).toBe(10) expect(q.stand().wartend.map((w) => w.vorgangId)).toEqual(['b', 'c']) expect(q.laeuftEtwas()).toBe(true) expect(q.kennt('b')).toBe(true) freigaben[0]() await tick() expect(gestartet).toEqual(['a', 'b']) expect(ergebnisse).toEqual(['a:fertig']) freigaben[1]() await tick() freigaben[2]() await tick() expect(ergebnisse).toEqual(['a:fertig', 'b:fertig', 'c:fertig']) expect(q.laeuftEtwas()).toBe(false) expect(q.stand().laeuft).toBeNull() expect(staende.length).toBeGreaterThan(3) }) it('kein Doppel: derselbe Vorgang bekommt seinen alten Platz zurück', async () => { const { ausfuehren } = steuerbar() const q = new KompressionsWarteschlange(ausfuehren, () => {}, () => {}) q.einreihen(auftrag('a'), 'A') q.einreihen(auftrag('b'), 'B') await tick() expect(q.einreihen(auftrag('a'), 'A')).toBe(1) expect(q.einreihen(auftrag('b'), 'B')).toBe(2) expect(q.stand().wartend).toHaveLength(1) }) it('abbrechen: wartend wird entfernt (mit Ergebnis), laufend bekommt das Signal', async () => { const { ausfuehren, freigaben } = steuerbar() const ergebnisse: string[] = [] const q = new KompressionsWarteschlange(ausfuehren, () => {}, (a, e) => { ergebnisse.push(`${a.vorgangId}:${e.status}`) }) q.einreihen(auftrag('a'), 'A') q.einreihen(auftrag('b'), 'B') await tick() expect(q.abbrechen('b')).toBe(true) expect(ergebnisse).toEqual(['b:abgebrochen']) expect(q.stand().wartend).toHaveLength(0) expect(q.abbrechen('a')).toBe(true) await tick() expect(ergebnisse).toEqual(['b:abgebrochen', 'a:abgebrochen']) expect(q.abbrechen('gibt-es-nicht')).toBe(false) expect(freigaben).toHaveLength(1) }) it('eine Ausnahme im Encode ist ein lautes Fehler-Ergebnis — die Schlange läuft weiter', async () => { let n = 0 const ergebnisse: string[] = [] const q = new KompressionsWarteschlange( async () => { n += 1 if (n === 1) throw new Error('HandBrake explodiert') return fertig() }, () => {}, (a, e) => { ergebnisse.push(`${a.vorgangId}:${e.status}:${e.text.includes('explodiert')}`) }, ) q.einreihen(auftrag('a'), 'A') q.einreihen(auftrag('b'), 'B') await tick() await tick() expect(ergebnisse).toEqual(['a:fehler:true', 'b:fertig:false']) }) })