Ampel / ampel (push) Successful in 1m23s
haupt/: tray.ts (Symbol mit Zustand, Kontextmenue, Fenster zu = weiter- laufen, Beenden nur noch gewollt), Windows-Toasts bei Disc erkannt / fertig / Fehler (Klick holt das Fenster), Taskleisten-Fortschritt (setProgressBar, Fehlermodus), echter Windows-Ordnerdialog per IPC. Eigenes Icon (bau/icon.png + tray.png, Disc-Optik). Kern: Einstellungen lesen/schreiben NUR ueber die eine Quelle (§ 6.7, Whitelist der Schluessel), Werkzeug-Auskunft (Katalog + gemessene HandBrake-Presets/Backends), Bibliothek in db.ts (Schema v2 — fingerprint, Titel, Groesse, Ort, Dauer), Duplikat-Erkennung beim Einlegen (§ 6.6: 'schon einmal gerippt am …' als disc-info + Toast), Pipeline traegt fertige Vorgaenge ein. Fenster: drei Bereiche (Uebersicht / Einstellungen / Bibliothek). Einstellungen: Ablage mit Durchsuchen-Dialog, Kompression je Disc-Typ (Preset-Liste VOM eigenen HandBrake gemessen, 'NICHT komprimieren' waehlbar), Sprachlisten, TMDb/OMDb-Keys, Medienserver, Werkzeug-Stand mit Klartext (gefunden/fehlt + Folgen). Bibliothek als Tabelle. Der Smoke-Screenshot zeigt nebenbei den Ernstfall in schoen: Das Laufwerk ist nach dem rc11-Vorfall inzwischen auch am Storage-Stack in 'unknown' gekippt — und die Kachel zeigt den ZUGRIFFS_GRUENDE-Klartext samt Abhilfe statt eines stummen Zustands. 165 Tests gruen, Smoke gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
74 lines
3.0 KiB
TypeScript
74 lines
3.0 KiB
TypeScript
// Tests für kern/speicher/db.ts — laufen mit dem System-Node (node:sqlite
|
|
// gibt es dort wie in Electrons Node, KONZEPT-WINDOWS.md § 3.4).
|
|
import { mkdtempSync, rmSync, existsSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
import { Datenbank, SCHEMA_VERSION } from '../src/kern/speicher/db'
|
|
|
|
let aufraeumen: Array<() => void> = []
|
|
|
|
afterEach(() => {
|
|
// LIFO — zuletzt Registriertes zuerst: die Datenbank muss ZU sein, bevor
|
|
// ihr Ordner gelöscht wird (Windows verweigert das Löschen offener Dateien).
|
|
for (const weg of aufraeumen.splice(0).reverse()) weg()
|
|
})
|
|
|
|
function frischerPfad(): string {
|
|
const ordner = mkdtempSync(join(tmpdir(), 'rippy-db-test-'))
|
|
aufraeumen.push(() => rmSync(ordner, { recursive: true, force: true }))
|
|
return join(ordner, 'unterordner', 'rippy.db')
|
|
}
|
|
|
|
describe('Datenbank', () => {
|
|
it('legt Datei samt fehlender Ordner an und meldet Schema-Version', () => {
|
|
const pfad = frischerPfad()
|
|
const db = new Datenbank(pfad)
|
|
aufraeumen.push(() => db.schliessen())
|
|
expect(existsSync(pfad)).toBe(true)
|
|
expect(db.schemaVersion()).toBe(SCHEMA_VERSION)
|
|
})
|
|
|
|
it('schreibt und liest eine Einstellung', () => {
|
|
const db = new Datenbank(':memory:')
|
|
aufraeumen.push(() => db.schliessen())
|
|
db.einstellungSchreiben('ablage', 'E:\\Rippy')
|
|
expect(db.einstellungLesen('ablage')).toBe('E:\\Rippy')
|
|
})
|
|
|
|
it('überschreibt eine vorhandene Einstellung', () => {
|
|
const db = new Datenbank(':memory:')
|
|
aufraeumen.push(() => db.schliessen())
|
|
db.einstellungSchreiben('ablage', 'C:\\alt')
|
|
db.einstellungSchreiben('ablage', 'E:\\neu')
|
|
expect(db.einstellungLesen('ablage')).toBe('E:\\neu')
|
|
})
|
|
|
|
it('unbekannter Schlüssel ist null, kein Fehler und kein Leerwert', () => {
|
|
// R2: „nicht gefunden" ist eine Aussage (null) — kein erfundener
|
|
// Leerstring, der als Wert gelesen würde.
|
|
const db = new Datenbank(':memory:')
|
|
aufraeumen.push(() => db.schliessen())
|
|
expect(db.einstellungLesen('gibt-es-nicht')).toBeNull()
|
|
})
|
|
|
|
it('bleibt über Schließen und Neuöffnen erhalten', () => {
|
|
const pfad = frischerPfad()
|
|
const erste = new Datenbank(pfad)
|
|
erste.einstellungSchreiben('letzter_start', '2026-08-30T12:00:00Z')
|
|
erste.schliessen()
|
|
const zweite = new Datenbank(pfad)
|
|
aufraeumen.push(() => zweite.schliessen())
|
|
expect(zweite.einstellungLesen('letzter_start')).toBe('2026-08-30T12:00:00Z')
|
|
})
|
|
|
|
it('wirft bei nicht anlegbarem Pfad, statt still weiterzumachen', () => {
|
|
// R4: Der Aufrufer (kern/index.ts) fängt das und MELDET es — aber die
|
|
// Datenbank selbst darf so einen Zustand nie verschlucken.
|
|
// NUL ist unter Windows als Ordnername unzulässig; unter Linux (CI)
|
|
// scheitert das Anlegen unterhalb einer Datei genauso zuverlässig.
|
|
const kaputt = join(process.platform === 'win32' ? 'C:\\NUL<>:' : '/dev/null/x', 'rippy.db')
|
|
expect(() => new Datenbank(kaputt)).toThrow()
|
|
})
|
|
})
|