/// 照片域模型往返测试(P3 计划 Task 2)。 /// /// fixture 来源(字段逐一转写): /// - openapi.yaml components.schemas.PhotoRecord / AdminPhoto / /// AdminPhotoUploader / PhotoVisibility / PhotoProcessingState; /// - services/api/test/photos-admin.e2e-spec.ts 的 ADMIN_PHOTO_KEYS /// (PhotoRecord 必返字段 + uploader 内嵌)与种子形态 /// (visibility=published / processingState=ready、categoryId 可空、 /// displayName 取资料字段而非账号)。 library; import 'package:flutter_test/flutter_test.dart'; import 'package:photo_live/features/photos/models.dart'; final Map photoRecordFixture = { 'photoId': 'pa-photo-p1', 'albumId': 'cktragt000001', 'categoryId': 'ckucat0000001', 'uploaderId': 'usr-col-02', 'visibility': 'published', 'processingState': 'ready', 'bytes': 4096000, 'width': 4032, 'height': 3024, 'shotAt': '2026-09-12T07:55:00.000Z', 'uploadedAt': '2026-09-12T08:05:00.000Z', 'publishedAt': '2026-09-12T08:06:00.000Z', 'likeCount': 3, }; void main() { test('PhotoRecord fromJson/toJson 往返(管理端全字段)', () { final model = PhotoRecord.fromJson(photoRecordFixture); expect(model.visibility, PhotoVisibility.published); expect(model.processingState, PhotoProcessingState.ready); expect(model.likeCount, 3); expect(model.toJson(), equals(photoRecordFixture)); }); test('PhotoRecord 可空字段:处理前宽高为 null、未发布为 null', () { final fixture = { ...photoRecordFixture, 'categoryId': null, 'width': null, 'height': null, 'shotAt': null, 'publishedAt': null, 'visibility': 'unpublished', 'processingState': 'uploaded', }; final model = PhotoRecord.fromJson(fixture); expect(model.categoryId, isNull); expect(model.width, isNull); expect(model.height, isNull); expect(model.shotAt, isNull); expect(model.publishedAt, isNull); expect(model.visibility, PhotoVisibility.unpublished); expect(model.processingState, PhotoProcessingState.uploaded); expect(model.toJson(), equals(fixture)); }); test('PhotoRecord 解析毫秒精度时间(服务端 Date.toISOString 形态)', () { final model = PhotoRecord.fromJson({ ...photoRecordFixture, 'uploadedAt': '2026-09-12T08:05:00.000Z', }); expect(model.uploadedAt, DateTime.utc(2026, 9, 12, 8, 5)); }); test('AdminPhoto fromJson/toJson 往返(PhotoRecord + uploader 内嵌)', () { final fixture = { ...photoRecordFixture, 'uploader': { 'userId': 'usr-col-02', 'account': 'collab01', 'displayName': '李四', }, }; final model = AdminPhoto.fromJson(fixture); // e2e:uploader 映射取的是资料 displayName,而非账号。 expect(model.uploader.displayName, '李四'); expect(model.uploader.userId, model.uploaderId); expect(model.toJson(), equals(fixture)); }); test('PhotoVisibility / PhotoProcessingState 全枚举解析', () { for (final value in const [ 'unpublished', 'published', 'hidden', 'deleted', ]) { expect(PhotoVisibility.fromJson(value).wire, value); } for (final value in const [ 'pending_upload', 'uploaded', 'processing', 'ready', 'processing_failed', ]) { expect(PhotoProcessingState.fromJson(value).wire, value); } expect(() => PhotoVisibility.fromJson('archived'), throwsFormatException); expect( () => PhotoProcessingState.fromJson('failed'), throwsFormatException, ); }); }