// 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 } 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(1) }) 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() }) })