// Tests des Roh-Aufräumens (kern/ablage/rohaufraeumen.ts, 5.4.0): die // Regel, die Frist, die drei Sicherungen — und dass fremde Ordner nur // gezeigt, nie von selbst gelöscht werden. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { aufraeumen, faelligAm, fremdeOrdner, istUnterRohWurzel, loeschHindernis, ordnerGroesse, regelLesen, regelText, type AufraeumKandidat, type AufraeumZugriff, } from '../src/kern/ablage/rohaufraeumen' let wegwerf: string[] = [] afterEach(() => { for (const o of wegwerf.splice(0)) rmSync(o, { recursive: true, force: true }) }) describe('regelLesen', () => { it('kennt behalten, sofort und Tage — und die Vorgabe 7', () => { expect(regelLesen('behalten')).toEqual({ art: 'behalten' }) expect(regelLesen('sofort')).toEqual({ art: 'sofort' }) expect(regelLesen('30')).toEqual({ art: 'tage', tage: 30 }) expect(regelLesen(null)).toEqual({ art: 'tage', tage: 7 }) expect(regelLesen('')).toEqual({ art: 'tage', tage: 7 }) expect(regelLesen('quatsch')).toEqual({ art: 'tage', tage: 7 }) expect(regelLesen('-3')).toEqual({ art: 'tage', tage: 7 }) expect(regelText(regelLesen('7'))).toContain('7 Tage') }) }) describe('faelligAm', () => { it('Tage nach dem Ende; sofort = am Ende; behalten = nie', () => { expect(faelligAm('2026-09-01T20:00:00.000Z', { art: 'tage', tage: 7 })).toBe('2026-09-08T20:00:00.000Z') expect(faelligAm('2026-09-01T20:00:00.000Z', { art: 'sofort' })).toBe('2026-09-01T20:00:00.000Z') expect(faelligAm('2026-09-01T20:00:00.000Z', { art: 'behalten' })).toBe('') expect(faelligAm(null, { art: 'sofort' })).toBe('') expect(faelligAm('kaputt', { art: 'sofort' })).toBe('') }) }) const fertigeDateien = ['E:\\Rippy\\Filme\\Akira (1988)\\title_t00.mkv'] const kandidat = (teil: Partial = {}): AufraeumKandidat => ({ id: 'bluray-1', phase: 'fertig', komprimiert: true, rohOrdner: 'E:\\Rippy\\roh\\bluray-1', rohGeloeschtAm: null, beendet: '2026-09-01T20:00:00.000Z', fertigeDateien, ...teil, }) const zugriff = (überschreiben: Partial = {}): AufraeumZugriff & { geloescht: string[] } => { const geloescht: string[] = [] return { existiert: () => true, groesse: () => 1000, ordnerLoeschen: (p) => { geloescht.push(p) }, geloescht, ...überschreiben, } } const regel7 = { art: 'tage' as const, tage: 7 } const spaeter = new Date('2026-09-10T00:00:00Z') const frueher = new Date('2026-09-03T00:00:00Z') describe('loeschHindernis — die drei Sicherungen', () => { it('fertig, komprimiert, Ergebnis da, Frist um: löschbar', () => { expect(loeschHindernis(kandidat(), regel7, spaeter, zugriff())).toBe('') }) it('vor der Frist: nicht löschbar, mit Datum', () => { expect(loeschHindernis(kandidat(), regel7, frueher, zugriff())).toContain('Frist läuft bis 2026-09-08') }) it('nicht fertig / nicht komprimiert / schon gelöscht / Regel behalten', () => { expect(loeschHindernis(kandidat({ phase: 'fehler' }), regel7, spaeter, zugriff())).toContain('nicht fertig') expect(loeschHindernis(kandidat({ komprimiert: false }), regel7, spaeter, zugriff())).toContain('nicht komprimiert') expect(loeschHindernis(kandidat({ rohGeloeschtAm: 'x' }), regel7, spaeter, zugriff())).toBe('schon gelöscht') expect(loeschHindernis(kandidat(), { art: 'behalten' }, spaeter, zugriff())).toBe('Regel: behalten') }) it('die fertige Datei fehlt oder ist leer: NIE löschen', () => { expect(loeschHindernis(kandidat(), regel7, spaeter, zugriff({ existiert: (p) => p.endsWith('bluray-1') }))).toContain('fertige Datei fehlt') expect(loeschHindernis(kandidat(), regel7, spaeter, zugriff({ groesse: () => 0 }))).toContain('ist leer') expect(loeschHindernis(kandidat({ fertigeDateien: [] }), regel7, spaeter, zugriff())).toContain('keine fertige Datei') }) it('sofort: gleich nach dem Ende löschbar', () => { expect(loeschHindernis(kandidat(), { art: 'sofort' }, new Date('2026-09-01T20:00:01Z'), zugriff())).toBe('') }) }) describe('aufraeumen', () => { it('löscht nur die Fälligen und sagt, warum der Rest blieb', () => { const z = zugriff() const ergebnis = aufraeumen( [kandidat(), kandidat({ id: 'jung', beendet: '2026-09-09T00:00:00.000Z' }), kandidat({ id: 'roh', komprimiert: false })], regel7, spaeter, z, ) expect(ergebnis.geloescht).toEqual(['bluray-1']) expect(z.geloescht).toEqual(['E:\\Rippy\\roh\\bluray-1']) expect(ergebnis.uebersprungen.map((u) => u.id)).toEqual(['jung', 'roh']) expect(ergebnis.fehler).toEqual([]) }) it('ein Ordner, der sich nicht löschen lässt, ist ein lauter Fehler, kein stilles Nichts', () => { const ergebnis = aufraeumen([kandidat()], regel7, spaeter, zugriff({ ordnerLoeschen: () => { throw new Error('EBUSY') } })) expect(ergebnis.geloescht).toEqual([]) expect(ergebnis.fehler[0].grund).toContain('EBUSY') }) }) describe('Belegung und fremde Ordner — am echten Dateisystem', () => { it('ordnerGroesse zählt rekursiv; fremdeOrdner kennt die Vorgänge und lässt sie aus', () => { const wurzel = mkdtempSync(join(tmpdir(), 'rippy-roh-')) wegwerf.push(wurzel) const roh = join(wurzel, 'roh') mkdirSync(join(roh, 'bluray-1', 'tief'), { recursive: true }) mkdirSync(join(roh, 'alt-vor-540'), { recursive: true }) writeFileSync(join(roh, 'bluray-1', 'a.mkv'), 'x'.repeat(100)) writeFileSync(join(roh, 'bluray-1', 'tief', 'b.mkv'), 'x'.repeat(50)) writeFileSync(join(roh, 'alt-vor-540', 'c.mkv'), 'x'.repeat(7)) writeFileSync(join(roh, 'lose.txt'), 'x') expect(ordnerGroesse(join(roh, 'bluray-1'))).toBe(150) expect(ordnerGroesse(join(roh, 'gibt-es-nicht'))).toBe(0) const fremde = fremdeOrdner(roh, new Set([join(roh, 'bluray-1')])) expect(fremde).toEqual([{ ordner: join(roh, 'alt-vor-540'), bytes: 7 }]) expect(fremdeOrdner(join(wurzel, 'nix'), new Set())).toEqual([]) }) }) describe('istUnterRohWurzel — nur Ordner DIREKT unter roh\\', () => { it('erlaubt genau eine Ebene', () => { expect(istUnterRohWurzel('E:\\Rippy\\roh', 'E:\\Rippy\\roh\\bluray-1')).toBe(true) expect(istUnterRohWurzel('E:\\Rippy\\roh\\', 'e:\\rippy\\roh\\bluray-1\\')).toBe(true) expect(istUnterRohWurzel('E:\\Rippy\\roh', 'E:\\Rippy\\roh')).toBe(false) expect(istUnterRohWurzel('E:\\Rippy\\roh', 'E:\\Rippy\\roh\\a\\b')).toBe(false) expect(istUnterRohWurzel('E:\\Rippy\\roh', 'E:\\Rippy\\Filme\\Akira')).toBe(false) expect(istUnterRohWurzel('E:\\Rippy\\roh', 'E:\\Rippy\\rohling\\x')).toBe(false) }) })