Files
rippy/rippy-windows/test/warteschlange.test.ts
T
HitonabiandClaude Fable 5.1 3dc6798c79 feat(v5): 5.5.0 — Kern und Oberfläche aufgeteilt, Übersicht und Einstellungen neu, Erst-Einrichtung mit Poster-Kulisse
Commander-Wünsche vom 02.09.2026 vor der Freigabe von 5.4.0 (die deshalb nie
veröffentlicht wurde):

Kern: kern/index.ts ist nur noch Bootstrap und Router; Dienste in
kern/dienste/ (Einstellungen, Werkzeuge, Laufwerke, Erkennung, Vorgänge,
Speicher, Benachrichtigung, Automatik), Kontext in kern/kontext.ts,
Protokoll-Datei in gemeinsam/protokoll.ts, Platz-Rechnung in gemeinsam/platz.ts.

Oberfläche: fenster/App.tsx ist nur noch der Rahmen; bausteine.tsx, dialoge/,
uebersicht/ (LaufwerkKachel, KompressionsKarte, SpeicherKarte, VerlaufKarte),
einstellungen/ (Karten je Funktion, SprachAuswahl), einrichtung/
(ErstEinrichtung, PosterHintergrund).

Neu: Bibliothek weg, dafür Verlauf; Speicher-und-Bilanz-Karte (statfs);
LED-Fußleiste; MakeMKV-Kopf in die Einstellungen; Sprach-Chips mit Dropdown
statt Allgemeinem Preset; Karten Automatik/Benachrichtigung/Allgemein/Update;
Durchsuchen bei Werkzeugen; Arbeitsordner lokal (Encode dort, dann in die
Ablage); Platz-Prüfung vor dem Rip (Dialog und Kern); Automatik mit Countdown;
Serien-Merker über Discs; Rettungs-Abbild mit Nullen nur nach gesehenen
Lesefehlern (kern/rip/rettung.ts, Phase rettet, iso:-Quelle); Warteschlange
pausieren; Discord/Telegram-Benachrichtigung; Protokoll-Datei je Tag;
Fensterlage 80 % mittig, gemerkt; Erst-Einrichtung in acht Schritten vor dem
Poster-Grid.

Beweise: 413 Tests (neu: speicher, benachrichtigung, automatik, rettung,
fensterlage, protokoll, kleinkram-550), drei Typprüfungen, Klick-Beweis mit
29 Schritten inklusive Erst-Einrichtung (zweiter Start mit
RIPPY_START_BEREICH=einrichtung), Bilder beweise/klick/01…09.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 17:51:15 +02:00

133 lines
4.9 KiB
TypeScript

// 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<AblageErgebnis>((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<void> => 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'])
})
})