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 Function(int attempt) _onRefresh; int attempts = 0; @override Future 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))); }