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>
This commit is contained in:
@@ -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<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');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user