Files
pyramid/test/auth_log_test.dart
T
Bernd Steckmeister e0ac5cb9fd test: AuthLog-Regressionstests (wirft nie, 500-Zeilen-Kappung)
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 <noreply@anthropic.com>
2026-07-03 22:51:35 +02:00

83 lines
3.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<String?> getApplicationSupportPath() async =>
throw StateError('Plattform nicht verfügbar');
}
class _TempPathProvider extends PathProviderPlatform {
_TempPathProvider(this.path);
final String path;
@override
Future<String?> 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');
});
}