e0ac5cb9fd
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>
83 lines
3.0 KiB
Dart
83 lines
3.0 KiB
Dart
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');
|
||
});
|
||
}
|