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>
68 lines
3.0 KiB
TypeScript
68 lines
3.0 KiB
TypeScript
// Tests des freien Platzes (kern/dienste/speicher.ts, 5.5.0): der nächste
|
||
// vorhandene Elternpfad, die Messung mit nachgebautem statfs, das Urteil
|
||
// vor dem Rip. Der echte statfs-Aufruf ist am 01.09.2026 gemessen (siehe
|
||
// Kopf der Datei) — hier zählt die Rechnung. Die Pfade stehen mit
|
||
// Schrägstrichen: dirname versteht sie auf Windows UND auf dem Linux-Runner
|
||
// der CI gleich.
|
||
import { describe, expect, it } from 'vitest'
|
||
import { platzMessen, platzUrteil, vorhandenerPfad } from '../src/kern/dienste/speicher'
|
||
|
||
describe('vorhandenerPfad', () => {
|
||
it('nimmt den Ordner selbst, wenn es ihn gibt', () => {
|
||
expect(vorhandenerPfad('X:/rippy/ablage', (p) => p === 'X:/rippy/ablage')).toBe('X:/rippy/ablage')
|
||
})
|
||
it('geht hoch bis zum ersten vorhandenen Elternpfad — die Ablage darf noch fehlen', () => {
|
||
const da = new Set(['X:/', 'X:/rippy'])
|
||
expect(vorhandenerPfad('X:/rippy/ablage/Filme', (p) => da.has(p))).toBe('X:/rippy')
|
||
})
|
||
it('bleibt an der Wurzel stehen, wenn nichts existiert (Windows: Q:/ — auf dem Linux-Runner endet dirname bei „.")', () => {
|
||
expect(vorhandenerPfad('Q:/gibt/es/nicht', () => false)).toBe(process.platform === 'win32' ? 'Q:/' : '.')
|
||
})
|
||
})
|
||
|
||
describe('platzMessen', () => {
|
||
it('rechnet bavail × bsize und blocks × bsize', () => {
|
||
const platz = platzMessen('X:/rippy', () => ({ bavail: 1000, bsize: 4096, blocks: 5000 }), () => true)
|
||
expect(platz).toEqual({ pfad: 'X:/rippy', ok: true, freiBytes: 4_096_000, gesamtBytes: 20_480_000, fehler: '' })
|
||
})
|
||
it('ein leerer Pfad ist nicht messbar — und sagt es (R2)', () => {
|
||
const platz = platzMessen(' ', () => ({ bavail: 1, bsize: 1, blocks: 1 }))
|
||
expect(platz.ok).toBe(false)
|
||
expect(platz.fehler).toBe('kein Pfad')
|
||
})
|
||
it('ein Fehler der Messung wird zum Grund, nie zu einer erfundenen Null', () => {
|
||
const platz = platzMessen(
|
||
'Z:/weg',
|
||
() => {
|
||
throw new Error('ENOENT: no such file or directory')
|
||
},
|
||
() => true,
|
||
)
|
||
expect(platz.ok).toBe(false)
|
||
expect(platz.freiBytes).toBe(0)
|
||
expect(platz.fehler).toContain('ENOENT')
|
||
})
|
||
})
|
||
|
||
describe('platzUrteil', () => {
|
||
const GB = 1e9
|
||
it('reicht: Roh plus 25 % plus 2 GB Luft passen', () => {
|
||
expect(platzUrteil(60 * GB, 40 * GB, true)).toEqual({ text: '', hart: false })
|
||
})
|
||
it('knapp: der Rip passt, die Kompression nicht mehr sicher', () => {
|
||
const urteil = platzUrteil(45 * GB, 40 * GB, true)
|
||
expect(urteil.hart).toBe(false)
|
||
expect(urteil.text.startsWith('Knapp')).toBe(true)
|
||
expect(urteil.text).toContain('45,0 GB frei')
|
||
})
|
||
it('zu wenig: nicht einmal der verlustfreie Rip passt', () => {
|
||
const urteil = platzUrteil(30 * GB, 40 * GB, false)
|
||
expect(urteil.hart).toBe(true)
|
||
expect(urteil.text.startsWith('Zu wenig Platz')).toBe(true)
|
||
})
|
||
it('ohne Kompression zählt nur Roh plus Luft', () => {
|
||
expect(platzUrteil(43 * GB, 40 * GB, false).text).toBe('')
|
||
expect(platzUrteil(43 * GB, 40 * GB, true).text.startsWith('Knapp')).toBe(true)
|
||
})
|
||
})
|