From e0ac5cb9fda564cbc99684a1280c07c9addd5852 Mon Sep 17 00:00:00 2001 From: Bernd Steckmeister Date: Fri, 3 Jul 2026 22:51:35 +0200 Subject: [PATCH] test: AuthLog-Regressionstests (wirft nie, 500-Zeilen-Kappung) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AuthLog.write ist der einzige Aufruf im catch-Pfad des Soft-Logout-Guards; ein Throw dort würde im SDK zu logout()+clear() eskalieren. 4 neue Tests sichern genau das ab (kaputte Plattform, leeres Log, Zeitstempel, Kappung). path_provider_platform_interface als dev_dependency (war schon transitiv). Alle 14 Tests grün auf dem Pi, flutter analyze unverändert. Co-Authored-By: Claude Fable 5 --- CHANGES.md | 4 ++ PROGRESS.md | 40 ++++++++++++++++++++ pubspec.lock | 14 +++---- pubspec.yaml | 4 ++ test/auth_log_test.dart | 82 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 test/auth_log_test.dart diff --git a/CHANGES.md b/CHANGES.md index 644cd84..8075042 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1205,3 +1205,7 @@ 2026-07-03 22:22 [Edit] /home/steggi/pyramid/PROGRESS.md 2026-07-03 22:23 [Write] /home/steggi/.claude-pyramid/projects/-home-steggi-pyramid/memory/pyramid-arbeitsstand.md 2026-07-03 22:23 [Edit] /home/steggi/pyramid/docs/PC_TESTPLAN.md +2026-07-03 22:48 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 22:50 [Write] /home/steggi/pyramid/test/auth_log_test.dart +2026-07-03 22:50 [Edit] /home/steggi/pyramid/pubspec.yaml +2026-07-03 22:51 [Edit] /home/steggi/pyramid/PROGRESS.md diff --git a/PROGRESS.md b/PROGRESS.md index 1bf7bf0..4e6edbe 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -13,6 +13,46 @@ Schritt (und beim Abbruch mitten im Schritt den Zwischenstand). Format: --- +## 2026-07-03 – Review-Nachzügler abgeschlossen + AuthLog-Regressionstests + +**Erledigt:** +- **Fable-5-Review vervollständigt (eebf8ae):** Die drei seit Review-Abschluss + dazugekommenen erledigten Arbeiten kritisch geprüft und als Punkte (e)–(g) im + Review-Abschnitt abgehakt: Unit-Tests 99cde9a (korrekt, Fake-Clients schlagen + bei jedem Fremd-Aufruf laut fehl), Doku-Commits 16561bb/566938f (alle + Behauptungen gegen Code/pubspec/matrix-6.2.0 verifiziert – Factory-Stellen, + Tabellenname `box_inbound_group_session`), Autopilot-Konto-Wechsel bfe5876 + (kein App-Code, Logik in Ordnung; harmloser False-Positive der + Limit-Erkennung dokumentiert). **Kein Befund, keine Code-Änderung nötig.** + Damit sind ALLE als erledigt markierten Punkte reviewt. +- **`test/auth_log_test.dart` (4 Tests):** `AuthLog` ist die einzige weitere + öffentliche Schnittstelle auf dem heiligen Pfad, die headless testbar ist + (der Soft-Logout-Guard ruft `AuthLog.write` im catch-Pfad auf – würde es + werfen, eskalierte das SDK zu `logout()`+`clear()`). Getestet: write/read + werfen NIE, auch wenn die Plattform fehlt (kaputter PathProvider); + Platzhalter statt Fehler bei leerem Log; ISO-Zeitstempel pro Zeile; + 500-Zeilen-Kappung (älteste Einträge fliegen raus, neueste überleben). + Dafür `path_provider_platform_interface ^2.1.2` als dev_dependency ergänzt – + offizieller Mock-Weg, war schon transitiv im Lockfile, kein neues externes + Paket. `flutter test`: **alle 14 Tests grün auf dem Pi**, `flutter analyze` + unverändert (1 bekannter Hinweis `chat_provider.dart:42`). + +**Offen/Nächster Schritt:** Unverändert blockiert: Alle offenen ROADMAP-Punkte +(M0 SQLCipher, M1 Härtetest, M2 Call-Schicht/State-Management) brauchen PC/GUI +oder Bernds Antworten (Call-Pilot, SQLCipher-Priorität) – siehe +`docs/PC_TESTPLAN.md` und die Fragen an Bernd weiter unten. Weitere sichere +Pi-Testkandidaten sind erschöpft (Rest ist bibliotheksprivat oder braucht +Widget-Tests mit gemocktem Matrix-Client – lohnt erst nach dem M2-Modulschnitt +mit echten öffentlichen Schnittstellen). + +**Stolperfallen:** `AuthLog` cached sein File-Handle statisch – der Test für +„Plattform kaputt" muss deshalb VOR den Tests mit funktionierender Plattform +laufen (im Testfile kommentiert). Bei künftigen Tests an Singletons mit +statischem Zustand auf Testreihenfolge/Isolate-Grenzen achten (flutter test +isoliert pro Datei, nicht pro Test). + +--- + ## 2026-07-03 – Erste Unit-Tests: Regressionsnetz für die zwei heiligsten Codepfade **Erledigt:** Eine Vorsession hatte (unprotokolliert, Abbruch vor dem Testlauf – diff --git a/pubspec.lock b/pubspec.lock index 18176dc..e37919d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -801,10 +801,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: "direct main" description: @@ -918,7 +918,7 @@ packages: source: hosted version: "2.2.1" path_provider_platform_interface: - dependency: transitive + dependency: "direct dev" description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" @@ -1294,18 +1294,18 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" test_core: dependency: transitive description: name: test_core - sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" + sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" url: "https://pub.dev" source: hosted - version: "0.6.16" + version: "0.6.17" timezone: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ad788d2..4e8e3a0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,10 @@ dev_dependencies: sdk: flutter flutter_lints: ^6.0.0 flutter_launcher_icons: ^0.13.1 + # Nur für Tests: offizieller Weg, path_provider ohne echte Plattform zu + # mocken (PathProviderPlatform.instance überschreiben). War schon transitiv + # im Lockfile, kein neues externes Paket. + path_provider_platform_interface: ^2.1.2 dependency_overrides: webcrypto: diff --git a/test/auth_log_test.dart b/test/auth_log_test.dart new file mode 100644 index 0000000..6b677c3 --- /dev/null +++ b/test/auth_log_test.dart @@ -0,0 +1,82 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:pyramid/core/auth_log.dart'; + +/// Plattform kaputt/nicht verfügbar (z. B. sehr früher App-Start, exotischer +/// Fehler): genau der Fall, in dem AuthLog.write NIEMALS werfen darf – der +/// Soft-Logout-Guard ruft es in seinem catch-Pfad auf, und ein Throw dort +/// würde im SDK zu logout()+clear() eskalieren (der Uta-Bug). +class _BrokenPathProvider extends PathProviderPlatform { + @override + Future getApplicationSupportPath() async => + throw StateError('Plattform nicht verfügbar'); +} + +class _TempPathProvider extends PathProviderPlatform { + _TempPathProvider(this.path); + final String path; + + @override + Future getApplicationSupportPath() async => path; +} + +void main() { + // WICHTIG: Reihenfolge der Tests ist Absicht. AuthLog cached sein File-Handle + // statisch; der Kaputt-Plattform-Test muss laufen, BEVOR ein Test mit + // funktionierender Plattform das Handle in den Cache legt. + late Directory tempDir; + + setUpAll(() { + tempDir = Directory.systemTemp.createTempSync('pyramid_auth_log_test'); + }); + + tearDownAll(() { + tempDir.deleteSync(recursive: true); + }); + + test( + 'write/read werfen NIE, auch wenn die Plattform fehlt ' + '(CLAUDE.md: Fehlerpfade dürfen nie in einem stillen Logout enden)', + () async { + PathProviderPlatform.instance = _BrokenPathProvider(); + // Darf normal zurückkehren, obwohl getApplicationSupportPath wirft. + await AuthLog.write('darf nicht werfen'); + expect(await AuthLog.read(), startsWith('Log konnte nicht gelesen werden')); + }); + + test('read ohne bisherige Einträge liefert Platzhalter statt Fehler', + () async { + PathProviderPlatform.instance = _TempPathProvider(tempDir.path); + expect(await AuthLog.read(), '(noch keine Einträge)'); + }); + + test('write hängt Zeile mit ISO-Zeitstempel an, read liefert sie zurück', + () async { + PathProviderPlatform.instance = _TempPathProvider(tempDir.path); + await AuthLog.write('Hallo Log'); + final content = await AuthLog.read(); + expect(content, contains('Hallo Log')); + expect( + RegExp(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}').hasMatch(content), + isTrue, + reason: 'jede Zeile beginnt mit einem ISO-8601-Zeitstempel', + ); + }); + + test( + 'Log wird auf 500 Zeilen gekappt (älteste fliegen raus), ' + 'damit es nie unbegrenzt wächst', () async { + PathProviderPlatform.instance = _TempPathProvider(tempDir.path); + for (var i = 0; i < 520; i++) { + await AuthLog.write('Zeile $i'); + } + final lines = (await AuthLog.read()).trimRight().split('\n'); + expect(lines.length, 500); + expect(lines.last, contains('Zeile 519'), + reason: 'neueste Einträge müssen überleben'); + expect((await AuthLog.read()).contains('Hallo Log'), isFalse, + reason: 'ältester Eintrag (aus dem Test davor) muss weggekappt sein'); + }); +}