Files
social-app/apps/test/features/auth/data/auth_repository_test.dart
T
qzl b34697660d feat: 实现 Auth 全局状态机与 401 统一处理机制
- 新增 AuthSessionInvalidated 事件处理 token 失效场景
- ApiInterceptor 新增 authFailureCallback 单飞机制
- AuthBloc 区分 manual logout 与 auto expiry 语义
- 新增 startup recovery fallback 防止启动卡死

feat: 重构 Calendar DayWeek 视图事件布局引擎

- 新增 DayEventLayoutEngine 解耦事件计算与渲染
- 新增 DayTimelineMetrics 统一时间轴常量
- 新增 DayViewScale 支持捏合缩放

feat: 新增 Settings 页面共享 UI 组件

- 新增 BackTitlePageHeader 统一页面 header
- 新增 DetailHeaderActionMenu 统一操作菜单
- 新增 DestructiveActionSheet 统一删除确认
- 新增 AppToggleSwitch 统一开关组件

feat: Chat UI Schema 支持导航操作

- 支持 navigation 类型 action 触发内部路由跳转
- 新增路径验证与参数处理

chore: 更新相关测试覆盖 auth 失效路径
2026-03-18 13:35:25 +08:00

142 lines
4.6 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:social_app/features/auth/data/auth_api.dart';
import 'package:social_app/features/auth/data/auth_repository_impl.dart';
import 'package:social_app/features/auth/data/models/signup_request.dart';
import 'package:social_app/features/auth/data/models/login_request.dart';
import 'package:social_app/features/auth/data/models/auth_response.dart';
import 'package:social_app/core/storage/token_storage.dart';
import 'package:social_app/core/api/api_client.dart';
class MockAuthApi extends Mock implements AuthApi {}
class MockTokenStorage extends Mock implements TokenStorage {}
class MockApiClient extends Mock implements ApiClient {}
void main() {
late AuthRepositoryImpl repository;
late MockAuthApi mockApi;
late MockTokenStorage mockStorage;
setUp(() {
mockApi = MockAuthApi();
mockStorage = MockTokenStorage();
repository = AuthRepositoryImpl(api: mockApi, tokenStorage: mockStorage);
registerFallbackValue(
const SignupStartRequest(username: '', email: '', password: ''),
);
registerFallbackValue(const LoginRequest(email: '', password: ''));
registerFallbackValue(const SignupVerifyRequest(email: '', token: ''));
registerFallbackValue(const SignupResendRequest(email: ''));
registerFallbackValue(const LogoutRequest(refreshToken: ''));
registerFallbackValue(const RefreshRequest(refreshToken: ''));
});
group('AuthRepositoryImpl', () {
test('createVerification calls api and returns response', () async {
when(() => mockApi.createVerification(any())).thenAnswer(
(_) async =>
const VerificationCreateResponse(email: 'test@example.com'),
);
final result = await repository.createVerification(
const SignupStartRequest(
username: 'testuser',
email: 'test@example.com',
password: 'password123',
),
);
expect(result.email, 'test@example.com');
verify(() => mockApi.createVerification(any())).called(1);
});
test('createSession calls api and saves tokens', () async {
when(() => mockApi.createSession(any())).thenAnswer(
(_) async => AuthResponse(
accessToken: 'access_token',
refreshToken: 'refresh_token',
expiresIn: 3600,
tokenType: 'bearer',
user: const AuthUser(id: '123', email: 'test@example.com'),
),
);
when(
() => mockStorage.saveTokens(
access: any(named: 'access'),
refresh: any(named: 'refresh'),
),
).thenAnswer((_) async {});
final result = await repository.createSession(
const LoginRequest(email: 'test@example.com', password: 'password123'),
);
expect(result.accessToken, 'access_token');
verify(
() => mockStorage.saveTokens(
access: 'access_token',
refresh: 'refresh_token',
),
).called(1);
});
test(
'deleteSession calls api with refresh token and clears storage',
() async {
when(
() => mockStorage.getRefreshToken(),
).thenAnswer((_) async => 'refresh_token');
when(() => mockApi.deleteSession(any())).thenAnswer((_) async {});
when(() => mockStorage.clear()).thenAnswer((_) async {});
await repository.deleteSession();
verify(() => mockApi.deleteSession(any())).called(1);
verify(() => mockStorage.clear()).called(1);
},
);
test(
'clearSessionLocalOnly clears local tokens without api revoke',
() async {
when(() => mockStorage.clear()).thenAnswer((_) async {});
await repository.clearSessionLocalOnly();
verify(() => mockStorage.clear()).called(1);
verifyNever(() => mockApi.deleteSession(any()));
},
);
test('refreshSession saves new tokens', () async {
when(() => mockApi.refreshSession(any())).thenAnswer(
(_) async => AuthResponse(
accessToken: 'new_access',
refreshToken: 'new_refresh',
expiresIn: 3600,
tokenType: 'bearer',
user: const AuthUser(id: '123', email: 'test@example.com'),
),
);
when(
() => mockStorage.saveTokens(
access: any(named: 'access'),
refresh: any(named: 'refresh'),
),
).thenAnswer((_) async {});
final result = await repository.refreshSession('old_refresh');
expect(result.accessToken, 'new_access');
verify(
() => mockStorage.saveTokens(
access: 'new_access',
refresh: 'new_refresh',
),
).called(1);
});
});
}