99cde9a1e2
Erste Testsuite des Projekts, gezielt für die zwei heiligsten Codepfade: guardedSoftLogoutRefresh (wirft nie -> kein SDK-logout()+clear(), Uta-Bug) und updatePowerLevelsSafely (Selbst-Aussperr-Schutz, Server-Stand bleibt erhalten). 10 Tests, alle gruen auf dem Pi (flutter test laeuft headless). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.3 KiB
Dart
68 lines
2.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:matrix/matrix.dart';
|
||
import 'package:pyramid/core/soft_logout_guard.dart';
|
||
|
||
/// Fake-Client: nur `refreshAccessToken` ist implementiert. Jeder andere
|
||
/// SDK-Aufruf (insbesondere `logout()`/`clear()`!) schlägt laut mit
|
||
/// NoSuchMethodError fehl – so würde der Test es sofort merken, wenn der
|
||
/// Guard je etwas anderes am Client aufriefe als den Refresh.
|
||
class _FakeClient implements Client {
|
||
_FakeClient(this._onRefresh);
|
||
|
||
final Future<void> Function(int attempt) _onRefresh;
|
||
int attempts = 0;
|
||
|
||
@override
|
||
Future<void> refreshAccessToken({Duration? customRefreshTokenLifetime}) {
|
||
attempts++;
|
||
return _onRefresh(attempts);
|
||
}
|
||
|
||
@override
|
||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||
}
|
||
|
||
void main() {
|
||
test('erfolgreicher Refresh beim ersten Versuch: genau 1 Aufruf', () async {
|
||
final client = _FakeClient((_) async {});
|
||
await guardedSoftLogoutRefresh(client);
|
||
expect(client.attempts, 1);
|
||
});
|
||
|
||
test(
|
||
'transienter Fehler (z. B. Netz weg / Token-Rotations-Race mit dem '
|
||
'Push-Isolate): Retry heilt, kein Fehler nach außen',
|
||
() async {
|
||
final client = _FakeClient((attempt) async {
|
||
if (attempt == 1) throw Exception('Netzwerkfehler');
|
||
});
|
||
await guardedSoftLogoutRefresh(client);
|
||
expect(client.attempts, 2);
|
||
},
|
||
);
|
||
|
||
test(
|
||
'dauerhafter Fehler: genau 3 Versuche, und der Guard wirft NIE '
|
||
'(CLAUDE.md: Fehlerpfade dürfen nie in einem stillen Logout enden – '
|
||
'ein Throw hier würde im SDK zu logout()+clear() eskalieren)',
|
||
() async {
|
||
final client = _FakeClient((_) async {
|
||
throw Exception('Server dauerhaft nicht erreichbar');
|
||
});
|
||
// Darf trotz 3 Fehlversuchen normal (ohne Exception) zurückkehren.
|
||
await guardedSoftLogoutRefresh(client);
|
||
expect(client.attempts, 3);
|
||
},
|
||
// Der Guard wartet real 2s+4s zwischen den Versuchen.
|
||
timeout: const Timeout(Duration(seconds: 30)),
|
||
);
|
||
|
||
test('auch ein Error (nicht nur Exception) im Refresh entkommt nie', () async {
|
||
final client = _FakeClient((_) async {
|
||
throw StateError('unerwarteter Zustand');
|
||
});
|
||
await guardedSoftLogoutRefresh(client);
|
||
expect(client.attempts, 3);
|
||
}, timeout: const Timeout(Duration(seconds: 30)));
|
||
}
|