From faab9e620d1004152b821619dbea43282e0f250f Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Sat, 9 May 2026 20:19:31 +0700 Subject: [PATCH] refactor: medium repository context helpers (#28311) * refactor: medium repository context helpers * test: add regress test for 26723 --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../remote_album_repository_test.dart | 58 ++--- .../timeline_repository_test.dart | 38 +++- mobile/test/medium/repository_context.dart | 198 +++++++++++------- mobile/test/utils.dart | 12 ++ 4 files changed, 194 insertions(+), 112 deletions(-) diff --git a/mobile/test/medium/repositories/remote_album_repository_test.dart b/mobile/test/medium/repositories/remote_album_repository_test.dart index 1ae994f68b..5e923ea09b 100644 --- a/mobile/test/medium/repositories/remote_album_repository_test.dart +++ b/mobile/test/medium/repositories/remote_album_repository_test.dart @@ -33,7 +33,7 @@ void main() { test('returns single album when only one album exists', () async { final album = await ctx.newRemoteAlbum(ownerId: userId); final asset = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 1)); - await ctx.insertRemoteAlbumAsset(albumId: album.id, assetId: asset.id); + await ctx.newRemoteAlbumAsset(albumId: album.id, assetId: asset.id); final result = await sut.getSortedAlbumIds([album.id], aggregation: AssetDateAggregation.start); expect(result, [album.id]); @@ -44,22 +44,22 @@ void main() { final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); // Album 2: Assets from Jan 5 to Jan 15 (start: Jan 5) final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id); // Album 3: Assets from Jan 25 to Jan 30 (start: Jan 25) final album3 = await ctx.newRemoteAlbum(ownerId: userId); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25)); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 30)); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id); final result = await sut.getSortedAlbumIds([ album1.id, @@ -76,22 +76,22 @@ void main() { final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); // Album 2: Assets from Jan 5 to Jan 15 (end: Jan 15) final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id); // Album 3: Assets from Jan 25 to Jan 30 (end: Jan 30) final album3 = await ctx.newRemoteAlbum(ownerId: userId); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25)); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 30)); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id); final result = await sut.getSortedAlbumIds([ album1.id, @@ -106,11 +106,11 @@ void main() { test('handles albums with single asset', () async { final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); final result = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start); @@ -121,15 +121,15 @@ void main() { // Create 3 albums final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); final album3 = await ctx.newRemoteAlbum(ownerId: userId); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15)); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id); // Only request album1 and album3 final result = await sut.getSortedAlbumIds([album1.id, album3.id], aggregation: AssetDateAggregation.start); @@ -143,11 +143,11 @@ void main() { final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: sameDate); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: sameDate); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); final result = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start); @@ -159,15 +159,15 @@ void main() { test('handles albums across different years', () async { final album1 = await ctx.newRemoteAlbum(ownerId: userId); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2023, 12, 25)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id); final album3 = await ctx.newRemoteAlbum(ownerId: userId); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2025, 1, 1)); - await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id); + await ctx.newRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id); final result = await sut.getSortedAlbumIds([ album1.id, @@ -186,15 +186,15 @@ void main() { final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20)); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25)); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset3.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset4.id); - await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset5.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset3.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset4.id); + await ctx.newRemoteAlbumAsset(albumId: album1.id, assetId: asset5.id); final album2 = await ctx.newRemoteAlbum(ownerId: userId); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 1)); - await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset6.id); + await ctx.newRemoteAlbumAsset(albumId: album2.id, assetId: asset6.id); final resultStart = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start); diff --git a/mobile/test/medium/repositories/timeline_repository_test.dart b/mobile/test/medium/repositories/timeline_repository_test.dart index 70e9a3080a..f604f53c3e 100644 --- a/mobile/test/medium/repositories/timeline_repository_test.dart +++ b/mobile/test/medium/repositories/timeline_repository_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; +import 'package:intl/date_symbol_data_local.dart'; import '../repository_context.dart'; @@ -9,6 +9,10 @@ void main() { late MediumRepositoryContext ctx; late DriftTimelineRepository sut; + setUpAll(() async { + await initializeDateFormatting(); + }); + setUp(() { ctx = MediumRepositoryContext(); sut = DriftTimelineRepository(ctx.db); @@ -26,16 +30,44 @@ void main() { final checksum = 'yolo'; final album = await ctx.newRemoteAlbum(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: user.id, checksum: checksum); - await ctx.insertRemoteAlbumAsset(albumId: album.id, assetId: remoteAsset.id); + await ctx.newRemoteAlbumAsset(albumId: album.id, assetId: remoteAsset.id); final localAsset1 = await ctx.newLocalAsset(checksum: checksum); final localAsset2 = await ctx.newLocalAsset(checksum: checksum); - final assets = await sut.remoteAlbum(album.id, GroupAssetsBy.day).assetSource(0, 10); + final query = sut.remoteAlbum(album.id, .day); + final buckets = await query.bucketSource().first; + expect(buckets, hasLength(1)); + expect(buckets.single.assetCount, 1); + + final assets = await query.assetSource(0, 10); expect(assets, hasLength(1)); expect((assets.first as RemoteAsset).id, remoteAsset.id); expect([localAsset1.id, localAsset2.id], contains((assets.first as RemoteAsset).localId)); }); }); + + group('person assets', () { + test('does not duplicate an asset that has multiple face records for the same person', () async { + // Regression check for #26723: an INNER JOIN between remote_asset_entity and asset_face_entity + // fanned out one asset into N rows when N face records pointed at the same (asset, person) pair + final user = await ctx.newUser(); + final asset = await ctx.newRemoteAsset(ownerId: user.id); + + final person = await ctx.newPerson(ownerId: user.id); + await ctx.newFace(assetId: asset.id, personId: person.id); + await ctx.newFace(assetId: asset.id, personId: person.id); + + final query = sut.person(user.id, person.id, .day); + + final buckets = await query.bucketSource().first; + expect(buckets, hasLength(1)); + expect(buckets.single.assetCount, 1); + + final assets = await query.assetSource(0, 10); + expect(assets, hasLength(1)); + expect((assets.first as RemoteAsset).id, asset.id); + }); + }); } diff --git a/mobile/test/medium/repository_context.dart b/mobile/test/medium/repository_context.dart index 6e00f268fa..13f9a0234e 100644 --- a/mobile/test/medium/repository_context.dart +++ b/mobile/test/medium/repository_context.dart @@ -1,14 +1,14 @@ -import 'dart:math'; - import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'; @@ -23,7 +23,6 @@ import '../utils.dart'; class MediumRepositoryContext { final Drift db; - final Random _random = Random(); MediumRepositoryContext() : db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); @@ -33,7 +32,7 @@ class MediumRepositoryContext { static Value _resolveUndefined(T? plain, Option? option, T fallback) { if (plain != null) { - return Value(plain); + return .new(plain); } return _resolveOption(option, fallback); @@ -44,7 +43,7 @@ class MediumRepositoryContext { return option.fold(Value.new, Value.absent); } - return Value(fallback); + return .new(fallback); } Future newUser({ @@ -54,17 +53,17 @@ class MediumRepositoryContext { DateTime? profileChangedAt, bool? hasProfileImage, }) async { - id = TestUtils.uuid(id); + id ??= TestUtils.uuid(); return await db .into(db.userEntity) .insertReturning( UserEntityCompanion( - id: Value(id), - email: Value(email ?? '$id@test.com'), - name: Value(email ?? 'user_$id'), - avatarColor: Value(avatarColor ?? AvatarColor.values[_random.nextInt(AvatarColor.values.length)]), - profileChangedAt: Value(TestUtils.date(profileChangedAt)), - hasProfileImage: Value(hasProfileImage ?? false), + id: .new(id), + email: .new(email ?? '$id@test.com'), + name: .new(email ?? 'user_$id'), + avatarColor: .new(avatarColor ?? TestUtils.randElement(AvatarColor.values)), + profileChangedAt: .new(TestUtils.date(profileChangedAt)), + hasProfileImage: .new(hasProfileImage ?? false), ), ); } @@ -88,31 +87,31 @@ class MediumRepositoryContext { String? thumbHash, String? libraryId, }) async { - id = TestUtils.uuid(id); - createdAt = TestUtils.date(createdAt); + id ??= TestUtils.uuid(); + createdAt ??= TestUtils.date(); return db .into(db.remoteAssetEntity) .insertReturning( RemoteAssetEntityCompanion( - id: Value(id), - name: Value('remote_$id.jpg'), - checksum: Value(TestUtils.uuid(checksum)), - type: Value(type ?? AssetType.image), - createdAt: Value(createdAt), - updatedAt: Value(TestUtils.date(updatedAt)), - ownerId: Value(TestUtils.uuid(ownerId)), - visibility: Value(visibility ?? AssetVisibility.timeline), - deletedAt: Value(deletedAt), - durationMs: Value(durationMs ?? 0), - width: Value(width ?? _random.nextInt(1000)), - height: Value(height ?? _random.nextInt(1000)), - isFavorite: Value(isFavorite ?? false), - isEdited: Value(isEdited ?? false), - livePhotoVideoId: Value(livePhotoVideoId), - stackId: Value(stackId), - localDateTime: Value(createdAt.toLocal()), - thumbHash: Value(TestUtils.uuid(thumbHash)), - libraryId: Value(TestUtils.uuid(libraryId)), + id: .new(id), + name: .new('remote_$id.jpg'), + checksum: .new(TestUtils.uuid(checksum)), + type: .new(type ?? .image), + createdAt: .new(createdAt), + updatedAt: .new(TestUtils.date(updatedAt)), + ownerId: .new(TestUtils.uuid(ownerId)), + visibility: .new(visibility ?? .timeline), + deletedAt: .new(deletedAt), + durationMs: .new(durationMs ?? 0), + width: .new(width ?? TestUtils.randInt(1000)), + height: .new(height ?? TestUtils.randInt(1000)), + isFavorite: .new(isFavorite ?? false), + isEdited: .new(isEdited ?? false), + livePhotoVideoId: .new(livePhotoVideoId), + stackId: .new(stackId), + localDateTime: .new(createdAt.toLocal()), + thumbHash: .new(TestUtils.uuid(thumbHash)), + libraryId: .new(TestUtils.uuid(libraryId)), ), ); } @@ -130,12 +129,12 @@ class MediumRepositoryContext { .into(db.remoteAssetCloudIdEntity) .insertReturning( RemoteAssetCloudIdEntityCompanion( - assetId: Value(TestUtils.uuid(id)), - cloudId: Value(TestUtils.uuid(cloudId)), - createdAt: Value(TestUtils.date(createdAt)), + assetId: .new(TestUtils.uuid(id)), + cloudId: .new(TestUtils.uuid(cloudId)), + createdAt: .new(TestUtils.date(createdAt)), adjustmentTime: _resolveUndefined(adjustmentTime, adjustmentTimeOption, DateTime.now()), - latitude: _resolveOption(latitude, _random.nextDouble() * 180 - 90), - longitude: _resolveOption(longitude, _random.nextDouble() * 360 - 180), + latitude: _resolveOption(latitude, TestUtils.randDouble(-90, 90)), + longitude: _resolveOption(longitude, TestUtils.randDouble(-180, 180)), ), ); } @@ -151,40 +150,81 @@ class MediumRepositoryContext { AlbumAssetOrder? order, String? thumbnailAssetId, }) async { - id = TestUtils.uuid(id); - + id ??= TestUtils.uuid(); final album = await db .into(db.remoteAlbumEntity) .insertReturning( RemoteAlbumEntityCompanion( - id: Value(id), - name: Value(name ?? 'remote_album_$id'), - createdAt: Value(TestUtils.date(createdAt)), - updatedAt: Value(TestUtils.date(updatedAt)), - description: Value(description ?? 'Description for album $id'), - isActivityEnabled: Value(isActivityEnabled ?? false), - order: Value(order ?? AlbumAssetOrder.asc), - thumbnailAssetId: Value(thumbnailAssetId), + id: .new(id), + name: .new(name ?? 'remote_album_$id'), + createdAt: .new(TestUtils.date(createdAt)), + updatedAt: .new(TestUtils.date(updatedAt)), + description: .new(description ?? 'Description for album $id'), + isActivityEnabled: .new(isActivityEnabled ?? false), + order: .new(order ?? .asc), + thumbnailAssetId: .new(thumbnailAssetId), ), ); await db .into(db.remoteAlbumUserEntity) .insert( - RemoteAlbumUserEntityCompanion.insert( - albumId: id, - userId: ownerId ?? const Uuid().v4(), - role: AlbumUserRole.owner, + RemoteAlbumUserEntityCompanion( + albumId: .new(id), + userId: .new(TestUtils.uuid(ownerId)), + role: const .new(.owner), ), ); return album; } - Future insertRemoteAlbumAsset({required String albumId, required String assetId}) { + Future newRemoteAlbumAsset({required String albumId, required String assetId}) { return db .into(db.remoteAlbumAssetEntity) - .insert(RemoteAlbumAssetEntityCompanion.insert(albumId: albumId, assetId: assetId)); + .insert(RemoteAlbumAssetEntityCompanion(albumId: .new(albumId), assetId: .new(assetId))); + } + + Future newPerson({String? id, String? ownerId, String? name, bool? isFavorite, bool? isHidden}) { + id ??= TestUtils.uuid(); + return db + .into(db.personEntity) + .insertReturning( + PersonEntityCompanion( + id: .new(id), + ownerId: .new(TestUtils.uuid(ownerId)), + name: .new(name ?? 'person_$id'), + isFavorite: .new(isFavorite ?? false), + isHidden: .new(isHidden ?? false), + ), + ); + } + + Future newFace({String? assetId, String? personId, int? imageWidth, int? imageHeight}) { + imageWidth ??= TestUtils.randInt(999) + 1; + imageHeight ??= TestUtils.randInt(999) + 1; + + final x1 = TestUtils.randInt(imageWidth - 1); + final y1 = TestUtils.randInt(imageHeight - 1); + final x2 = x1 + 1 + TestUtils.randInt(imageWidth - x1 - 1); + final y2 = y1 + 1 + TestUtils.randInt(imageHeight - y1 - 1); + + return db + .into(db.assetFaceEntity) + .insertReturning( + AssetFaceEntityCompanion( + id: .new(TestUtils.uuid()), + assetId: .new(TestUtils.uuid(assetId)), + personId: .new(TestUtils.uuid(personId)), + imageWidth: .new(imageWidth), + imageHeight: .new(imageHeight), + boundingBoxX1: .new(x1), + boundingBoxY1: .new(y1), + boundingBoxX2: .new(x2), + boundingBoxY2: .new(y2), + sourceType: const .new('machine-learning'), + ), + ); } Future newLocalAsset({ @@ -206,26 +246,26 @@ class MediumRepositoryContext { int? orientation, DateTime? updatedAt, }) async { - id = TestUtils.uuid(id); + id ??= TestUtils.uuid(); return db .into(db.localAssetEntity) .insertReturning( LocalAssetEntityCompanion( - id: Value(id), - name: Value(name ?? 'local_$id.jpg'), - height: Value(height ?? _random.nextInt(1000)), - width: Value(width ?? _random.nextInt(1000)), - durationMs: Value(durationMs ?? 0), - orientation: Value(orientation ?? 0), - updatedAt: Value(TestUtils.date(updatedAt)), + id: .new(id), + name: .new(name ?? 'local_$id.jpg'), + height: .new(height ?? TestUtils.randInt(1000)), + width: .new(width ?? TestUtils.randInt(1000)), + durationMs: .new(durationMs ?? 0), + orientation: .new(orientation ?? 0), + updatedAt: .new(TestUtils.date(updatedAt)), checksum: _resolveUndefined(checksum, checksumOption, const Uuid().v4()), - createdAt: Value(TestUtils.date(createdAt)), - type: Value(type ?? AssetType.image), - isFavorite: Value(isFavorite ?? false), - iCloudId: Value(TestUtils.uuid(iCloudId)), + createdAt: .new(TestUtils.date(createdAt)), + type: .new(type ?? .image), + isFavorite: .new(isFavorite ?? false), + iCloudId: .new(TestUtils.uuid(iCloudId)), adjustmentTime: _resolveUndefined(adjustmentTime, adjustmentTimeOption, DateTime.now()), - latitude: Value(latitude ?? _random.nextDouble() * 180 - 90), - longitude: Value(longitude ?? _random.nextDouble() * 360 - 180), + latitude: .new(latitude ?? TestUtils.randDouble(-90, 90)), + longitude: .new(longitude ?? TestUtils.randDouble(-180, 180)), ), ); } @@ -238,24 +278,22 @@ class MediumRepositoryContext { bool? isIosSharedAlbum, String? linkedRemoteAlbumId, }) { - id = TestUtils.uuid(id); + id ??= TestUtils.uuid(); return db .into(db.localAlbumEntity) .insertReturning( LocalAlbumEntityCompanion( - id: Value(id), - name: Value(name ?? 'local_album_$id'), - updatedAt: Value(TestUtils.date(updatedAt)), - backupSelection: Value(backupSelection ?? BackupSelection.none), - isIosSharedAlbum: Value(isIosSharedAlbum ?? false), - linkedRemoteAlbumId: Value(linkedRemoteAlbumId), + id: .new(id), + name: .new(name ?? 'local_album_$id'), + updatedAt: .new(TestUtils.date(updatedAt)), + backupSelection: .new(backupSelection ?? .none), + isIosSharedAlbum: .new(isIosSharedAlbum ?? false), + linkedRemoteAlbumId: .new(linkedRemoteAlbumId), ), ); } - Future newLocalAlbumAsset({required String albumId, required String assetId}) { - return db - .into(db.localAlbumAssetEntity) - .insert(LocalAlbumAssetEntityCompanion.insert(albumId: albumId, assetId: assetId)); - } + Future newLocalAlbumAsset({required String albumId, required String assetId}) => db + .into(db.localAlbumAssetEntity) + .insert(LocalAlbumAssetEntityCompanion(albumId: .new(albumId), assetId: .new(assetId))); } diff --git a/mobile/test/utils.dart b/mobile/test/utils.dart index 7967083efc..aa1f82dc2b 100644 --- a/mobile/test/utils.dart +++ b/mobile/test/utils.dart @@ -1,10 +1,22 @@ +import 'dart:math'; + import 'package:uuid/uuid.dart'; class TestUtils { + static final _random = Random(); + static String uuid([String? id]) => id ?? const Uuid().v4(); static DateTime date([DateTime? date]) => date ?? DateTime.now(); static DateTime now() => DateTime.now(); static DateTime yesterday() => DateTime.now().subtract(const Duration(days: 1)); static DateTime tomorrow() => DateTime.now().add(const Duration(days: 1)); + + static T randElement(List list) => list[_random.nextInt(list.length)]; + static int randInt([int? max]) => max != null ? _random.nextInt(max) : _random.nextInt(1 << 32); + static double randDouble([int? max, int? min]) { + final minValue = min ?? 0; + final maxValue = max ?? 1; + return minValue + _random.nextDouble() * (maxValue - minValue); + } }