f975321b67
P3 计划 Task 2(apps/mobile):
- core/config.dart:API_BASE/VIEWER_BASE 经 --dart-define 覆盖,缺省
10.0.2.2 模拟器宿主回环;viewer 相册链接构造 {VIEWER_BASE}/a/{token}。
- core/error_codes.dart:errors.ts 17 码 1:1 镜像(含注释),另设 4 个
客户端本地伪码(NETWORK_ERROR/NETWORK_TIMEOUT/CANCELLED/HTTP_ERROR)。
- core/api_client.dart:dio 封装——Bearer 注入(登录端点豁免)、401
驱逐(清会话 + onUnauthorized 回调去重,App 壳注册跳 /login)、
DioException/错误体 {code,message,requestId} 归一 AppException、
通用游标分页助手;auth/albums/members/categories/admin/uploads/photos
全端点方法;riverpod provider 出口。
- core/session_store.dart:SessionStore 抽象 + flutter_secure_storage
实现(token/expiresAt/account 三键,encode/decode 纯函数可测)。
- features/{auth,albums,uploads,photos}/models.dart:手写 fromJson/
toJson,逐字段对齐 openapi.yaml(类注释标注契约 schema/operationId);
locale 按字符串透传(zh-CN 标识符不兼容),业务枚举 Dart 化并在
未知值 fail-fast。
- 测试 66 项:17 码表驱动 + 网络/超时/取消 + 兜底映射、游标边界
(含 CursorPageMeta 不变式违约容错)、模型往返(fixture 转写自
openapi.yaml schema 与 api e2e 断言,来源在测试头注释)、拦截器
行为(手写 FakeAdapter:Bearer 注入/401 驱逐/登录豁免/并发去重/
尽力而为 logout/refresh 滑动续期)。flutter analyze 零告警。
Co-Authored-By: Claude Code <noreply@anthropic.com>
42 lines
1.7 KiB
Dart
42 lines
1.7 KiB
Dart
/// 认证域模型往返测试(P3 计划 Task 2)。
|
|
///
|
|
/// fixture 来源:openapi.yaml components.schemas.LoginResponse /
|
|
/// RefreshResponse(必返字段 sessionToken+expiresAt / expiresAt);
|
|
/// 时间形态取 auth.e2e-spec.ts 直种会话的「Date.now()+3_600_000」毫秒精度,
|
|
/// 此处用 Dart 规范形(整秒无小数)保证 toJson 往返逐字节一致,
|
|
/// 毫秒形(.000Z)解析另有冒烟断言。
|
|
library;
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:photo_live/features/auth/models.dart';
|
|
|
|
void main() {
|
|
test('LoginResponse fromJson/toJson 往返', () {
|
|
final fixture = <String, dynamic>{
|
|
'sessionToken': 'f2a9c8b17d3e4f5a6b7c8d9e0f1a2b3c',
|
|
'expiresAt': '2026-09-19T12:30:00.000Z',
|
|
};
|
|
final model = LoginResponse.fromJson(fixture);
|
|
expect(model.sessionToken, 'f2a9c8b17d3e4f5a6b7c8d9e0f1a2b3c');
|
|
expect(model.expiresAt, DateTime.utc(2026, 9, 19, 12, 30));
|
|
expect(model.toJson(), equals(fixture));
|
|
});
|
|
|
|
test('LoginResponse 解析毫秒精度时间(服务端 Date.toISOString 形态)', () {
|
|
final model = LoginResponse.fromJson({
|
|
'sessionToken': 't',
|
|
'expiresAt': '2026-09-19T12:30:00.500Z',
|
|
});
|
|
expect(model.expiresAt.millisecond, 500);
|
|
// toJson 规范化为 Dart ISO 输出(同值不同形,服务端 Date.parse 均可)。
|
|
expect(model.toJson()['expiresAt'], '2026-09-19T12:30:00.500Z');
|
|
});
|
|
|
|
test('RefreshResponse fromJson/toJson 往返', () {
|
|
final fixture = <String, dynamic>{'expiresAt': '2026-09-20T08:00:00.000Z'};
|
|
final model = RefreshResponse.fromJson(fixture);
|
|
expect(model.expiresAt, DateTime.utc(2026, 9, 20, 8));
|
|
expect(model.toJson(), equals(fixture));
|
|
});
|
|
}
|