Files
rippy/rippy-windows/test/waechter.test.ts
T
HitonabiandClaude Fable 5 5f386e5406
Ampel / ampel (push) Failing after 1m20s
feat(v5): Etappe W-4 — Metadaten-Automatik mit Sicherheitsgrad
metadaten/: discmerkmale.ts (BDMT-Studio-Titel, ISO-9660- und
UDF-Volume-Label direkt vom Medium — OHNE koffi, ueber fs auf \.\G: —,
Label-Normalisierung, Fingerabdruck, progressive Titel-Kandidaten),
tmdb.ts (v3-Key UND v4-Token, de-DE, /find fuer deutsche Texte zu
OMDb-Treffern), omdb.ts, zuordnung.ts (die Confidence-Kaskade
0,95/0,90/0,80/0,60/0,30 aus prescan.py). ablage/poster.ts. Kern meldet
'disc-info' beim Einlegen; die Pipeline benennt den fertigen Ordner
'<Titel> (Jahr)', schreibt movie.nfo/tvshow.nfo mit echten Metadaten
und laedt das Poster. Fenster zeigt 'sehr wahrscheinlich: … · 95 %'.

ZWEI Kaskaden-Schwaechen LIVE GEFUNDEN und gehaertet (gegen die echten
APIs gemessen, Keys aus den Docker-Settings der VM, nicht persistiert):
1. 'Spartacus Gods Of The Arena' wurde zum FILM 'Spartacus' (1960) —
   der stark gekuerzte Kandidat traf woertlich, bevor die volle Anfrage
   als spezifische SERIE galt. Kaskade jetzt JE KANDIDAT
   woertlich→spezifisch (spezifisch nur fuer die volle Anfrage, auch
   fuer Serien). Nachgemessen: jetzt Serie, 90 %.
2. OMDbs t=-Suche machte aus dem Label 'Bd Evg D2' selbstbewusst
   'BD Scream 5' — jetzt Wort-Jaccard-Gate >= 0,5. Nachgemessen: der
   Fall ist jetzt ein ehrlicher 60-%-Vorschlag, den das UI nachfragt.

Messwerte: Akira→95% · Evangelion 2.22→80% (der dokumentierte Fund) ·
Spartacus GotA→Serie 90% · Bd Evg D2→Vorschlag 60%.

Dazu ein neuer Quelltext-Hygiene-Waechter (keine rohen Steuerbytes —
zweimal beim Schreiben passiert, einmal haette ein Test dadurch anders
gemessen als gelesen). 165 Tests gruen, Smoke gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 18:39:09 +02:00

111 lines
4.5 KiB
TypeScript

// Die Wächter — mechanische Prüfung der Regeln aus KONZEPT-WINDOWS.md § 4.3.
// Sie LESEN Quelltext, sie führen nichts aus. Jede Regel ist aus einem
// bezahlten Fehler abgeleitet; die Fundstelle steht jeweils dabei.
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join, relative, sep } from 'node:path'
import { describe, expect, it } from 'vitest'
const WURZEL = join(__dirname, '..')
const SRC = join(WURZEL, 'src')
function quelldateien(ordner: string): string[] {
const ergebnis: string[] = []
for (const name of readdirSync(ordner)) {
const pfad = join(ordner, name)
if (statSync(pfad).isDirectory()) {
ergebnis.push(...quelldateien(pfad))
} else if (/\.(ts|tsx)$/.test(name)) {
ergebnis.push(pfad)
}
}
return ergebnis
}
function relativPosix(pfad: string): string {
return relative(WURZEL, pfad).split(sep).join('/')
}
const dateien = quelldateien(SRC).map((pfad) => ({
pfad: relativPosix(pfad),
inhalt: readFileSync(pfad, 'utf8'),
}))
describe('R1 — Win32 lebt nur an den zwei benannten Orten', () => {
// § 4.3 R1: kein koffi-Import irgendwo sonst. Der zweite Ort ist die
// Prozess-Leine im Haupt — Begründung im Kopf von src/haupt/leine.ts:
// Das Job-Handle muss bei dem Prozess liegen, dessen Tod alles mitreißt.
const erlaubt = new Set(['src/haupt/leine.ts', 'src/kern/laufwerk/win32.ts'])
it('koffi wird nirgendwo sonst angefasst', () => {
const verstoesse = dateien
.filter((d) => !erlaubt.has(d.pfad))
.filter((d) => /from\s+['"]koffi['"]|require\(\s*['"]koffi['"]\s*\)/.test(d.inhalt))
.map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
})
describe('R2/R4 — kein Fehlschlag verschwindet still', () => {
it('kein leerer catch-Block', () => {
// R4: „Kein stilles catch {}" — ein except-pass hat einmal eine Stunde
// gekostet (AGENTS.md). Wer einen Fehler bewusst trägt, schreibt hin warum.
const verstoesse = dateien
.filter((d) => /catch\s*(\([^)]*\))?\s*\{\s*\}/.test(d.inhalt))
.map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
it('kein catch, das leere Listen/Objekte als Antwort erfindet', () => {
// R2: Fünfmal catch(() => []) im alten UI — jeder verpasste Abruf hieß
// „es gibt keine Jobs", die Liste leerte sich im Sekundentakt.
const muster = /catch\s*(\([^)]*\))?\s*\{\s*return\s+(\[\s*\]|\{\s*\})/
const pfeilMuster = /catch\s*\(\s*\(\s*\)\s*=>\s*(\[\s*\]|\{\s*\})\s*\)/
const verstoesse = dateien
.filter((d) => muster.test(d.inhalt) || pfeilMuster.test(d.inhalt))
.map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
})
describe('§ 4.1 — kein HTTP-Server, kein Port, kein localhost', () => {
it('niemand öffnet einen Server oder lauscht auf einem Port', () => {
// Ein lokaler Webserver in einer Einzelplatz-Anwendung ist eine offene
// Tür ohne Gegenwert (§ 4.1/§ 14). ELECTRON_RENDERER_URL (dev-Server von
// electron-vite) ist Werkzeug, kein Programmteil — der Treffer auf den
// reinen Variablennamen ist erlaubt.
const muster = /createServer\s*\(|\.listen\s*\(\s*\d|from\s+['"]express['"]|localhost:\d/
const verstoesse = dateien.filter((d) => muster.test(d.inhalt)).map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
})
describe('Quelltext-Hygiene — keine rohen Steuerbytes', () => {
it('keine Datei enthält Steuerzeichen außer Tab/LF/CR', () => {
// Zweimal passiert (30.08.2026): Escape-Sequenzen wie \x00 landeten
// als ECHTE Bytes in der Datei — grep meldet „Binary file", Diffs
// leiden, und ein Test sah dadurch anders aus, als sein Quelltext las.
const testDateien = readdirSync(join(WURZEL, 'test'))
.filter((name) => /\.(ts|tsx)$/.test(name))
.map((name) => ({
pfad: `test/${name}`,
inhalt: readFileSync(join(WURZEL, 'test', name), 'utf8'),
}))
const verstoesse = [...dateien, ...testDateien]
.filter((d) => /[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(d.inhalt))
.map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
})
describe('§ 6.7 — EINE Quelle für Einstellungen', () => {
it('node:sqlite wird nur in kern/speicher/db.ts angefasst', () => {
// Der rc11-Fund: Oberfläche schrieb in die Datenbank, der Betrieb las
// aus einer Datei, die niemand schrieb. Hier ist die Datenbank EIN Modul.
const verstoesse = dateien
.filter((d) => d.pfad !== 'src/kern/speicher/db.ts')
.filter((d) => /['"]node:sqlite['"]/.test(d.inhalt))
.map((d) => d.pfad)
expect(verstoesse).toEqual([])
})
})