forked from eatvio/eatvio_app
test(session): cover avatar refresh and session teardown
Add regression tests for avatar reset and token-change refresh, stale 401 handling, idempotent session teardown and account-scoped preference cleanup. Update existing cubit tests for the new session dependency. Refs: #1113
This commit is contained in:
223
test/core/session/session_lifecycle_service_test.dart
Normal file
223
test/core/session/session_lifecycle_service_test.dart
Normal file
@@ -0,0 +1,223 @@
|
||||
import 'package:eatvio_app/core/error_reporting/error_reporter.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/prefs.dart';
|
||||
import 'package:eatvio_app/core/prefs_keys.dart';
|
||||
import 'package:eatvio_app/core/session/session_lifecycle_service.dart';
|
||||
import 'package:eatvio_app/core/token_storage.dart';
|
||||
import 'package:eatvio_app/core/websockets/ws_manager.dart';
|
||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
|
||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
|
||||
import 'package:eatvio_app/features/video_calls/presentation/bloc/video_call_session/video_call_session_cubit.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class _FakeWsManager implements WsManager {
|
||||
int clearCalls = 0;
|
||||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
clearCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('WsManager.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeErrorReporter implements ErrorReporter {
|
||||
int reportCalls = 0;
|
||||
|
||||
@override
|
||||
Future<void> report(
|
||||
Object error,
|
||||
StackTrace? stackTrace, {
|
||||
String? origin,
|
||||
Map<String, Object?>? extras,
|
||||
}) async {
|
||||
reportCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeProfileAvatarCubit implements ProfileAvatarCubit {
|
||||
int resetCalls = 0;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
resetCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('ProfileAvatarCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeChatsCubit implements ChatsCubit {
|
||||
int resetCalls = 0;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
resetCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('ChatsCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeNotificationsCubit implements NotificationsCubit {
|
||||
int resetCalls = 0;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
resetCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('NotificationsCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeNewMessagesCubit implements NewMessagesCubit {
|
||||
int resetCalls = 0;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
resetCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('NewMessagesCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeFeedCubit implements FeedCubit {
|
||||
int resetCalls = 0;
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
resetCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('FeedCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeVideoCallSessionCubit implements VideoCallSessionCubit {
|
||||
int endCallCalls = 0;
|
||||
|
||||
@override
|
||||
Future<void> endCall() async {
|
||||
endCallCalls++;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
throw UnimplementedError('VideoCallSessionCubit.${invocation.memberName}');
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
late _FakeWsManager wsManager;
|
||||
late _FakeProfileAvatarCubit profileAvatarCubit;
|
||||
late _FakeChatsCubit chatsCubit;
|
||||
late _FakeNotificationsCubit notificationsCubit;
|
||||
late _FakeNewMessagesCubit newMessagesCubit;
|
||||
late _FakeFeedCubit feedCubit;
|
||||
late _FakeVideoCallSessionCubit videoCallSessionCubit;
|
||||
late SessionLifecycleService service;
|
||||
|
||||
setUp(() async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'token': 'token-a',
|
||||
'socketId': 'socket-a',
|
||||
PrefsKeys.customBaseUrl.name: 'https://old.example',
|
||||
PrefsKeys.isEmailVerificationSkipped.name: true,
|
||||
PrefsKeys.registrationDate.name: '2024-01-01',
|
||||
PrefsKeys.welcomeScreenWasShowed.name: true,
|
||||
});
|
||||
TokenStorage.clearCache();
|
||||
await sl.reset();
|
||||
|
||||
wsManager = _FakeWsManager();
|
||||
profileAvatarCubit = _FakeProfileAvatarCubit();
|
||||
chatsCubit = _FakeChatsCubit();
|
||||
notificationsCubit = _FakeNotificationsCubit();
|
||||
newMessagesCubit = _FakeNewMessagesCubit();
|
||||
feedCubit = _FakeFeedCubit();
|
||||
videoCallSessionCubit = _FakeVideoCallSessionCubit();
|
||||
|
||||
sl.registerSingleton<ProfileAvatarCubit>(profileAvatarCubit);
|
||||
sl.registerSingleton<ChatsCubit>(chatsCubit);
|
||||
sl.registerSingleton<NotificationsCubit>(notificationsCubit);
|
||||
sl.registerSingleton<NewMessagesCubit>(newMessagesCubit);
|
||||
sl.registerSingleton<FeedCubit>(feedCubit);
|
||||
sl.registerSingleton<VideoCallSessionCubit>(videoCallSessionCubit);
|
||||
sl.registerSingleton<Prefs>(Prefs(await SharedPreferences.getInstance()));
|
||||
|
||||
service = SessionLifecycleService(
|
||||
wsManager: wsManager,
|
||||
errorReporter: _FakeErrorReporter(),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await sl.reset();
|
||||
TokenStorage.clearCache();
|
||||
});
|
||||
|
||||
group('SessionLifecycleService.endSession', () {
|
||||
test(
|
||||
'resets user-scoped state and clears only session-scoped keys',
|
||||
() async {
|
||||
await service.endSession(reason: SessionEndReason.logout);
|
||||
|
||||
expect(wsManager.clearCalls, 1);
|
||||
expect(profileAvatarCubit.resetCalls, 1);
|
||||
expect(chatsCubit.resetCalls, 1);
|
||||
expect(notificationsCubit.resetCalls, 1);
|
||||
expect(newMessagesCubit.resetCalls, 1);
|
||||
expect(feedCubit.resetCalls, 1);
|
||||
expect(videoCallSessionCubit.endCallCalls, 1);
|
||||
|
||||
expect(await TokenStorage.getTokenFromStorage(), isNull);
|
||||
expect(await SocketIdStorage.getSocketIdFromStorage(), isNull);
|
||||
|
||||
final prefs = sl<Prefs>();
|
||||
expect(prefs.getString(PrefsKeys.customBaseUrl), 'https://old.example');
|
||||
expect(prefs.getBool(PrefsKeys.isEmailVerificationSkipped), isNull);
|
||||
expect(prefs.getString(PrefsKeys.registrationDate), isNull);
|
||||
expect(prefs.getBool(PrefsKeys.welcomeScreenWasShowed), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
test('is idempotent for concurrent calls', () async {
|
||||
final first = service.endSession(reason: SessionEndReason.logout);
|
||||
final second = service.endSession(reason: SessionEndReason.unauthorized);
|
||||
|
||||
await Future.wait([first, second]);
|
||||
|
||||
expect(wsManager.clearCalls, 1);
|
||||
expect(profileAvatarCubit.resetCalls, 1);
|
||||
expect(chatsCubit.resetCalls, 1);
|
||||
expect(notificationsCubit.resetCalls, 1);
|
||||
expect(newMessagesCubit.resetCalls, 1);
|
||||
expect(feedCubit.resetCalls, 1);
|
||||
expect(videoCallSessionCubit.endCallCalls, 1);
|
||||
|
||||
expect(await TokenStorage.getTokenFromStorage(), isNull);
|
||||
expect(await SocketIdStorage.getSocketIdFromStorage(), isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user