refactor: remote album repository test to use context (#26481)

* refactor: remote album repository test to use context

* refactor: medium repo context (#26482)

* refactor: medium repo context

* store userId in closure

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2026-02-24 18:55:07 +05:30
committed by GitHub
parent 55ee9f76da
commit 4b8f90aa55
3 changed files with 375 additions and 449 deletions
@@ -1,7 +1,6 @@
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
import 'package:immich_mobile/utils/option.dart'; import 'package:immich_mobile/utils/option.dart';
@@ -11,8 +10,8 @@ void main() {
late MediumRepositoryContext ctx; late MediumRepositoryContext ctx;
late DriftLocalAssetRepository sut; late DriftLocalAssetRepository sut;
setUp(() async { setUp(() {
ctx = await MediumRepositoryContext.create(); ctx = MediumRepositoryContext();
sut = DriftLocalAssetRepository(ctx.db); sut = DriftLocalAssetRepository(ctx.db);
}); });
@@ -24,103 +23,104 @@ void main() {
final cutoffDate = DateTime(2024, 1, 1); final cutoffDate = DateTime(2024, 1, 1);
final beforeCutoff = DateTime(2023, 12, 31); final beforeCutoff = DateTime(2023, 12, 31);
final afterCutoff = DateTime(2024, 1, 2); final afterCutoff = DateTime(2024, 1, 2);
late UserEntityData user; late String userId;
setUp(() { setUp(() async {
user = ctx.user; final user = await ctx.newUser();
userId = user.id;
}); });
test('returns only assets that match all criteria', () async { test('returns only assets that match all criteria', () async {
final otherUser = await ctx.insertUser(); final otherUser = await ctx.newUser();
// Asset 1: Should be included - backed up, before cutoff, correct owner, not deleted, not favorite // Asset 1: Should be included - backed up, before cutoff, correct owner, not deleted, not favorite
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final includedAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); final includedAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
// Asset 2: Should NOT be included - not backed up (no remote asset) // Asset 2: Should NOT be included - not backed up (no remote asset)
await ctx.insertLocalAsset(createdAt: beforeCutoff); await ctx.newLocalAsset(createdAt: beforeCutoff);
// Asset 3: Should NOT be included - after cutoff date // Asset 3: Should NOT be included - after cutoff date
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: afterCutoff); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: afterCutoff);
// Asset 4: Should NOT be included - different owner // Asset 4: Should NOT be included - different owner
final otherRemoteAsset = await ctx.insertRemoteAsset(ownerId: otherUser.id); final otherRemoteAsset = await ctx.newRemoteAsset(ownerId: otherUser.id);
await ctx.insertLocalAsset(checksum: otherRemoteAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: otherRemoteAsset.checksum, createdAt: beforeCutoff);
// Asset 5: Should NOT be included - remote asset is deleted // Asset 5: Should NOT be included - remote asset is deleted
final deletedAsset = await ctx.insertRemoteAsset(ownerId: user.id, deletedAt: DateTime(2024, 1, 1)); final deletedAsset = await ctx.newRemoteAsset(ownerId: userId, deletedAt: DateTime(2024, 1, 1));
await ctx.insertLocalAsset(checksum: deletedAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: deletedAsset.checksum, createdAt: beforeCutoff);
// Asset 6: Should NOT be included - is favorite (when keepFavorites=true) // Asset 6: Should NOT be included - is favorite (when keepFavorites=true)
final favoriteAsset = await ctx.insertRemoteAsset(ownerId: user.id, isFavorite: true); final favoriteAsset = await ctx.newRemoteAsset(ownerId: userId, isFavorite: true);
await ctx.insertLocalAsset(checksum: favoriteAsset.checksum, createdAt: beforeCutoff, isFavorite: true); await ctx.newLocalAsset(checksum: favoriteAsset.checksum, createdAt: beforeCutoff, isFavorite: true);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: true); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: true);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, includedAsset.id); expect(result.assets.first.id, includedAsset.id);
}); });
test('includes favorites when keepFavorites is false', () async { test('includes favorites when keepFavorites is false', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final favoriteAsset = await ctx.insertLocalAsset( final favoriteAsset = await ctx.newLocalAsset(
checksum: remoteAsset.checksum, checksum: remoteAsset.checksum,
createdAt: beforeCutoff, createdAt: beforeCutoff,
isFavorite: true, isFavorite: true,
); );
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: false); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: false);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, favoriteAsset.id); expect(result.assets.first.id, favoriteAsset.id);
expect(result.assets.first.isFavorite, true); expect(result.assets.first.isFavorite, true);
}); });
test('excludes asset when both local and remote are favorites', () async { test('excludes asset when both local and remote are favorites', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id, isFavorite: true); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId, isFavorite: true);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff, isFavorite: true); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff, isFavorite: true);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: true); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: true);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('excludes asset when only local is favorite', () async { test('excludes asset when only local is favorite', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff, isFavorite: true); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff, isFavorite: true);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: true); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: true);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('excludes asset when only remote is favorite', () async { test('excludes asset when only remote is favorite', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id, isFavorite: true); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId, isFavorite: true);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: true); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: true);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('includes asset when neither local nor remote is favorite', () async { test('includes asset when neither local nor remote is favorite', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final localAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); final localAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepFavorites: true); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepFavorites: true);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, localAsset.id); expect(result.assets.first.id, localAsset.id);
}); });
test('keepMediaType photosOnly returns only videos for deletion', () async { test('keepMediaType photosOnly returns only videos for deletion', () async {
final photoAsset = await ctx.insertRemoteAsset(ownerId: user.id); final photoAsset = await ctx.newRemoteAsset(ownerId: userId);
// Photo - should be kept // Photo - should be kept
await ctx.insertLocalAsset(checksum: photoAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: photoAsset.checksum, createdAt: beforeCutoff);
final videoRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final videoRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
// Video - should be deleted // Video - should be deleted
final videoLocalAsset = await ctx.insertLocalAsset( final videoLocalAsset = await ctx.newLocalAsset(
checksum: videoRemoteAsset.checksum, checksum: videoRemoteAsset.checksum,
createdAt: beforeCutoff, createdAt: beforeCutoff,
type: AssetType.video, type: AssetType.video,
); );
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepMediaType: AssetKeepType.photosOnly); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepMediaType: AssetKeepType.photosOnly);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, videoLocalAsset.id); expect(result.assets.first.id, videoLocalAsset.id);
expect(result.assets.first.type, AssetType.video); expect(result.assets.first.type, AssetType.video);
@@ -128,14 +128,14 @@ void main() {
test('keepMediaType videosOnly returns only photos for deletion', () async { test('keepMediaType videosOnly returns only photos for deletion', () async {
// Photo - should be deleted // Photo - should be deleted
final photoRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final photoRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final photoAsset = await ctx.insertLocalAsset(checksum: photoRemoteAsset.checksum, createdAt: beforeCutoff); final photoAsset = await ctx.newLocalAsset(checksum: photoRemoteAsset.checksum, createdAt: beforeCutoff);
// Video - should be kept // Video - should be kept
final videoRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final videoRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
await ctx.insertLocalAsset(checksum: videoRemoteAsset.checksum, createdAt: beforeCutoff, type: AssetType.video); await ctx.newLocalAsset(checksum: videoRemoteAsset.checksum, createdAt: beforeCutoff, type: AssetType.video);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepMediaType: AssetKeepType.videosOnly); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepMediaType: AssetKeepType.videosOnly);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, photoAsset.id); expect(result.assets.first.id, photoAsset.id);
expect(result.assets.first.type, AssetType.image); expect(result.assets.first.type, AssetType.image);
@@ -143,18 +143,18 @@ void main() {
test('returns both photos and videos with keepMediaType.all', () async { test('returns both photos and videos with keepMediaType.all', () async {
// Photo // Photo
final photoRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final photoRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final photoAsset = await ctx.insertLocalAsset(checksum: photoRemoteAsset.checksum, createdAt: beforeCutoff); final photoAsset = await ctx.newLocalAsset(checksum: photoRemoteAsset.checksum, createdAt: beforeCutoff);
// Video // Video
final videoRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final videoRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final videoAsset = await ctx.insertLocalAsset( final videoAsset = await ctx.newLocalAsset(
checksum: videoRemoteAsset.checksum, checksum: videoRemoteAsset.checksum,
createdAt: beforeCutoff, createdAt: beforeCutoff,
type: AssetType.video, type: AssetType.video,
); );
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepMediaType: AssetKeepType.none); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepMediaType: AssetKeepType.none);
expect(result.assets.length, 2); expect(result.assets.length, 2);
final ids = result.assets.map((a) => a.id).toSet(); final ids = result.assets.map((a) => a.id).toSet();
expect(ids, containsAll([photoAsset.id, videoAsset.id])); expect(ids, containsAll([photoAsset.id, videoAsset.id]));
@@ -162,106 +162,106 @@ void main() {
test('excludes assets in iOS shared albums', () async { test('excludes assets in iOS shared albums', () async {
// Regular album // Regular album
final regularAlbum = await ctx.insertLocalAlbum(); final regularAlbum = await ctx.newLocalAlbum();
// iOS shared album // iOS shared album
final sharedAlbum = await ctx.insertLocalAlbum(isIosSharedAlbum: true); final sharedAlbum = await ctx.newLocalAlbum(isIosSharedAlbum: true);
// Asset in regular album (should be included) // Asset in regular album (should be included)
final regularRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final regularRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final regularAsset = await ctx.insertLocalAsset(checksum: regularRemoteAsset.checksum, createdAt: beforeCutoff); final regularAsset = await ctx.newLocalAsset(checksum: regularRemoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: regularAlbum.id, assetId: regularAsset.id); await ctx.newLocalAlbumAsset(albumId: regularAlbum.id, assetId: regularAsset.id);
// Asset in iOS shared album (should be excluded) // Asset in iOS shared album (should be excluded)
final sharedRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final sharedRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final sharedAsset = await ctx.insertLocalAsset(checksum: sharedRemoteAsset.checksum, createdAt: beforeCutoff); final sharedAsset = await ctx.newLocalAsset(checksum: sharedRemoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: sharedAlbum.id, assetId: sharedAsset.id); await ctx.newLocalAlbumAsset(albumId: sharedAlbum.id, assetId: sharedAsset.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, regularAsset.id); expect(result.assets.first.id, regularAsset.id);
}); });
test('includes assets at exact cutoff date', () async { test('includes assets at exact cutoff date', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final localAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: cutoffDate); final localAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: cutoffDate);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, localAsset.id); expect(result.assets.first.id, localAsset.id);
}); });
test('returns empty list when no assets match criteria', () async { test('returns empty list when no assets match criteria', () async {
// Only assets after cutoff // Only assets after cutoff
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: afterCutoff); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: afterCutoff);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('handles multiple assets with same checksum', () async { test('handles multiple assets with same checksum', () async {
// Two local assets with same checksum (edge case, but should handle it) // Two local assets with same checksum (edge case, but should handle it)
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets.length, 2); expect(result.assets.length, 2);
expect(result.assets.map((a) => a.checksum).toSet(), equals({remoteAsset.checksum})); expect(result.assets.map((a) => a.checksum).toSet(), equals({remoteAsset.checksum}));
}); });
test('includes assets not in any album', () async { test('includes assets not in any album', () async {
// Asset not in any album should be included // Asset not in any album should be included
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final localAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); final localAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, localAsset.id); expect(result.assets.first.id, localAsset.id);
}); });
test('excludes asset that is in both regular and iOS shared album', () async { test('excludes asset that is in both regular and iOS shared album', () async {
// Regular album // Regular album
final regularAlbum = await ctx.insertLocalAlbum(); final regularAlbum = await ctx.newLocalAlbum();
// iOS shared album // iOS shared album
final sharedAlbum = await ctx.insertLocalAlbum(isIosSharedAlbum: true); final sharedAlbum = await ctx.newLocalAlbum(isIosSharedAlbum: true);
// Asset in BOTH albums - should be excluded because it's in an iOS shared album // Asset in BOTH albums - should be excluded because it's in an iOS shared album
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final localAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); final localAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: regularAlbum.id, assetId: localAsset.id); await ctx.newLocalAlbumAsset(albumId: regularAlbum.id, assetId: localAsset.id);
await ctx.insertLocalAlbumAsset(albumId: sharedAlbum.id, assetId: localAsset.id); await ctx.newLocalAlbumAsset(albumId: sharedAlbum.id, assetId: localAsset.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('excludes assets with null checksum (not backed up)', () async { test('excludes assets with null checksum (not backed up)', () async {
// Asset with null checksum cannot be matched to remote asset // Asset with null checksum cannot be matched to remote asset
await ctx.insertLocalAsset(checksumOption: const Option.none()); await ctx.newLocalAsset(checksumOption: const Option.none());
final result = await sut.getRemovalCandidates(user.id, cutoffDate); final result = await sut.getRemovalCandidates(userId, cutoffDate);
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('excludes assets in user-excluded albums', () async { test('excludes assets in user-excluded albums', () async {
// Create two regular albums // Create two regular albums
final includeAlbum = await ctx.insertLocalAlbum(); final includeAlbum = await ctx.newLocalAlbum();
final excludeAlbum = await ctx.insertLocalAlbum(); final excludeAlbum = await ctx.newLocalAlbum();
// Asset in included album - should be included // Asset in included album - should be included
final includedRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final includedRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final includedAsset = await ctx.insertLocalAsset(checksum: includedRemoteAsset.checksum, createdAt: beforeCutoff); final includedAsset = await ctx.newLocalAsset(checksum: includedRemoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: includeAlbum.id, assetId: includedAsset.id); await ctx.newLocalAlbumAsset(albumId: includeAlbum.id, assetId: includedAsset.id);
// Asset in excluded album - should NOT be included // Asset in excluded album - should NOT be included
final excludedRemoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final excludedRemoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final excludedAsset = await ctx.insertLocalAsset(checksum: excludedRemoteAsset.checksum, createdAt: beforeCutoff); final excludedAsset = await ctx.newLocalAsset(checksum: excludedRemoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: excludeAlbum.id, assetId: excludedAsset.id); await ctx.newLocalAlbumAsset(albumId: excludeAlbum.id, assetId: excludedAsset.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepAlbumIds: {excludeAlbum.id}); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {excludeAlbum.id});
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, includedAsset.id); expect(result.assets.first.id, includedAsset.id);
@@ -269,107 +269,104 @@ void main() {
test('excludes assets that are in any of multiple excluded albums', () async { test('excludes assets that are in any of multiple excluded albums', () async {
// Create multiple albums // Create multiple albums
final album1 = await ctx.insertLocalAlbum(); final album1 = await ctx.newLocalAlbum();
final album2 = await ctx.insertLocalAlbum(); final album2 = await ctx.newLocalAlbum();
final album3 = await ctx.insertLocalAlbum(); final album3 = await ctx.newLocalAlbum();
// Asset in album-1 (excluded) - should NOT be included // Asset in album-1 (excluded) - should NOT be included
final remote1 = await ctx.insertRemoteAsset(ownerId: user.id); final remote1 = await ctx.newRemoteAsset(ownerId: userId);
final local1 = await ctx.insertLocalAsset(checksum: remote1.checksum, createdAt: beforeCutoff); final local1 = await ctx.newLocalAsset(checksum: remote1.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: album1.id, assetId: local1.id); await ctx.newLocalAlbumAsset(albumId: album1.id, assetId: local1.id);
// Asset in album-2 (excluded) - should NOT be included // Asset in album-2 (excluded) - should NOT be included
final remote2 = await ctx.insertRemoteAsset(ownerId: user.id); final remote2 = await ctx.newRemoteAsset(ownerId: userId);
final local2 = await ctx.insertLocalAsset(checksum: remote2.checksum, createdAt: beforeCutoff); final local2 = await ctx.newLocalAsset(checksum: remote2.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: album2.id, assetId: local2.id); await ctx.newLocalAlbumAsset(albumId: album2.id, assetId: local2.id);
// Asset in album-3 (not excluded) - should be included // Asset in album-3 (not excluded) - should be included
final remote3 = await ctx.insertRemoteAsset(ownerId: user.id); final remote3 = await ctx.newRemoteAsset(ownerId: userId);
final local3 = await ctx.insertLocalAsset(checksum: remote3.checksum, createdAt: beforeCutoff); final local3 = await ctx.newLocalAsset(checksum: remote3.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: album3.id, assetId: local3.id); await ctx.newLocalAlbumAsset(albumId: album3.id, assetId: local3.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepAlbumIds: {album1.id, album2.id}); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {album1.id, album2.id});
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, local3.id); expect(result.assets.first.id, local3.id);
}); });
test('excludes asset that is in both excluded and non-excluded album', () async { test('excludes asset that is in both excluded and non-excluded album', () async {
final includedAlbum = await ctx.insertLocalAlbum(); final includedAlbum = await ctx.newLocalAlbum();
final excludedAlbum = await ctx.insertLocalAlbum(); final excludedAlbum = await ctx.newLocalAlbum();
// Asset in BOTH albums - should be excluded because it's in an excluded album // Asset in BOTH albums - should be excluded because it's in an excluded album
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final localAsset = await ctx.insertLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff); final localAsset = await ctx.newLocalAsset(checksum: remoteAsset.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: includedAlbum.id, assetId: localAsset.id); await ctx.newLocalAlbumAsset(albumId: includedAlbum.id, assetId: localAsset.id);
await ctx.insertLocalAlbumAsset(albumId: excludedAlbum.id, assetId: localAsset.id); await ctx.newLocalAlbumAsset(albumId: excludedAlbum.id, assetId: localAsset.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepAlbumIds: {excludedAlbum.id}); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {excludedAlbum.id});
expect(result.assets, isEmpty); expect(result.assets, isEmpty);
}); });
test('includes all assets when excludedAlbumIds is empty', () async { test('includes all assets when excludedAlbumIds is empty', () async {
final album1 = await ctx.insertLocalAlbum(); final album1 = await ctx.newLocalAlbum();
final remote1 = await ctx.insertRemoteAsset(ownerId: user.id); final remote1 = await ctx.newRemoteAsset(ownerId: userId);
final local1 = await ctx.insertLocalAsset(checksum: remote1.checksum, createdAt: beforeCutoff); final local1 = await ctx.newLocalAsset(checksum: remote1.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: album1.id, assetId: local1.id); await ctx.newLocalAlbumAsset(albumId: album1.id, assetId: local1.id);
final remote2 = await ctx.insertRemoteAsset(ownerId: user.id); final remote2 = await ctx.newRemoteAsset(ownerId: userId);
await ctx.insertLocalAsset(checksum: remote2.checksum, createdAt: beforeCutoff); await ctx.newLocalAsset(checksum: remote2.checksum, createdAt: beforeCutoff);
// Empty excludedAlbumIds should include all eligible assets // Empty excludedAlbumIds should include all eligible assets
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepAlbumIds: {}); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {});
expect(result.assets.length, 2); expect(result.assets.length, 2);
}); });
test('excludes asset not in any album when album is excluded', () async { test('excludes asset not in any album when album is excluded', () async {
final excludedAlbum = await ctx.insertLocalAlbum(); final excludedAlbum = await ctx.newLocalAlbum();
// Asset NOT in any album - should be included // Asset NOT in any album - should be included
final noAlbumRemote = await ctx.insertRemoteAsset(ownerId: user.id); final noAlbumRemote = await ctx.newRemoteAsset(ownerId: userId);
final noAlbumAsset = await ctx.insertLocalAsset(checksum: noAlbumRemote.checksum, createdAt: beforeCutoff); final noAlbumAsset = await ctx.newLocalAsset(checksum: noAlbumRemote.checksum, createdAt: beforeCutoff);
// Asset in excluded album - should NOT be included // Asset in excluded album - should NOT be included
final excludedRemote = await ctx.insertRemoteAsset(ownerId: user.id); final excludedRemote = await ctx.newRemoteAsset(ownerId: userId);
final excludedAsset = await ctx.insertLocalAsset(checksum: excludedRemote.checksum, createdAt: beforeCutoff); final excludedAsset = await ctx.newLocalAsset(checksum: excludedRemote.checksum, createdAt: beforeCutoff);
await ctx.insertLocalAlbumAsset(albumId: excludedAlbum.id, assetId: excludedAsset.id); await ctx.newLocalAlbumAsset(albumId: excludedAlbum.id, assetId: excludedAsset.id);
final result = await sut.getRemovalCandidates(user.id, cutoffDate, keepAlbumIds: {excludedAlbum.id}); final result = await sut.getRemovalCandidates(userId, cutoffDate, keepAlbumIds: {excludedAlbum.id});
expect(result.assets.length, 1); expect(result.assets.length, 1);
expect(result.assets.first.id, noAlbumAsset.id); expect(result.assets.first.id, noAlbumAsset.id);
}); });
test('combines excludedAlbumIds with keepMediaType correctly', () async { test('combines excludedAlbumIds with keepMediaType correctly', () async {
final excludedAlbum = await ctx.insertLocalAlbum(); final excludedAlbum = await ctx.newLocalAlbum();
final regularAlbum = await ctx.insertLocalAlbum(); final regularAlbum = await ctx.newLocalAlbum();
// Photo in excluded album - should NOT be included (album excluded) // Photo in excluded album - should NOT be included (album excluded)
final photoExcludedRemote = await ctx.insertRemoteAsset(ownerId: user.id); final photoExcludedRemote = await ctx.newRemoteAsset(ownerId: userId);
final photoExcludedAsset = await ctx.insertLocalAsset( final photoExcludedAsset = await ctx.newLocalAsset(
checksum: photoExcludedRemote.checksum, checksum: photoExcludedRemote.checksum,
createdAt: beforeCutoff, createdAt: beforeCutoff,
); );
await ctx.insertLocalAlbumAsset(albumId: excludedAlbum.id, assetId: photoExcludedAsset.id); await ctx.newLocalAlbumAsset(albumId: excludedAlbum.id, assetId: photoExcludedAsset.id);
// Video in regular album - should be included (keepMediaType photosOnly = delete videos) // Video in regular album - should be included (keepMediaType photosOnly = delete videos)
final videoRemote = await ctx.insertRemoteAsset(ownerId: user.id); final videoRemote = await ctx.newRemoteAsset(ownerId: userId);
final videoAsset = await ctx.insertLocalAsset( final videoAsset = await ctx.newLocalAsset(
checksum: videoRemote.checksum, checksum: videoRemote.checksum,
createdAt: beforeCutoff, createdAt: beforeCutoff,
type: AssetType.video, type: AssetType.video,
); );
await ctx.insertLocalAlbumAsset(albumId: regularAlbum.id, assetId: videoAsset.id); await ctx.newLocalAlbumAsset(albumId: regularAlbum.id, assetId: videoAsset.id);
// Photo in regular album - should NOT be included (keepMediaType photosOnly = keep photos) // Photo in regular album - should NOT be included (keepMediaType photosOnly = keep photos)
final photoRegularRemote = await ctx.insertRemoteAsset(ownerId: user.id); final photoRegularRemote = await ctx.newRemoteAsset(ownerId: userId);
final photoRegularAsset = await ctx.insertLocalAsset( final photoRegularAsset = await ctx.newLocalAsset(checksum: photoRegularRemote.checksum, createdAt: beforeCutoff);
checksum: photoRegularRemote.checksum, await ctx.newLocalAlbumAsset(albumId: regularAlbum.id, assetId: photoRegularAsset.id);
createdAt: beforeCutoff,
);
await ctx.insertLocalAlbumAsset(albumId: regularAlbum.id, assetId: photoRegularAsset.id);
final result = await sut.getRemovalCandidates( final result = await sut.getRemovalCandidates(
user.id, userId,
cutoffDate, cutoffDate,
keepMediaType: AssetKeepType.photosOnly, keepMediaType: AssetKeepType.photosOnly,
keepAlbumIds: {excludedAlbum.id}, keepAlbumIds: {excludedAlbum.id},
@@ -381,16 +378,17 @@ void main() {
}); });
group('reconcileHashesFromCloudId', () { group('reconcileHashesFromCloudId', () {
late UserEntityData user; late String userId;
setUp(() { setUp(() async {
user = ctx.user; final user = await ctx.newUser();
userId = user.id;
}); });
test('updates local asset checksum when all metadata matches', () async { test('updates local asset checksum when all metadata matches', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final remoteCloudAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id); final remoteCloudAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id);
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: remoteCloudAsset.cloudId, iCloudId: remoteCloudAsset.cloudId,
createdAt: remoteCloudAsset.createdAt, createdAt: remoteCloudAsset.createdAt,
@@ -405,10 +403,10 @@ void main() {
}); });
test('does not update when local asset already has checksum', () async { test('does not update when local asset already has checksum', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final remoteCloudAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id); final remoteCloudAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id);
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksum: 'existing', checksum: 'existing',
iCloudId: remoteCloudAsset.cloudId, iCloudId: remoteCloudAsset.cloudId,
createdAt: remoteCloudAsset.createdAt, createdAt: remoteCloudAsset.createdAt,
@@ -423,12 +421,9 @@ void main() {
}); });
test('does not update when adjustment_time does not match', () async { test('does not update when adjustment_time does not match', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId( final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id, adjustmentTime: DateTime(2024, 1, 12));
id: remoteAsset.id, final localAsset = await ctx.newLocalAsset(
adjustmentTime: DateTime(2024, 1, 12),
);
final localAsset = await ctx.insertLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -443,9 +438,9 @@ void main() {
}); });
test('does not update when latitude does not match', () async { test('does not update when latitude does not match', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id, latitude: const Option.none()); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id, latitude: const Option.none());
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -460,9 +455,9 @@ void main() {
}); });
test('does not update when longitude does not match', () async { test('does not update when longitude does not match', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id, longitude: (-74.006).toOption()); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id, longitude: (-74.006).toOption());
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -477,9 +472,9 @@ void main() {
}); });
test('does not update when createdAt does not match', () async { test('does not update when createdAt does not match', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id, createdAt: DateTime(2024, 1, 5)); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id, createdAt: DateTime(2024, 1, 5));
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: DateTime(2024, 6, 1), createdAt: DateTime(2024, 6, 1),
@@ -494,9 +489,9 @@ void main() {
}); });
test('does not update when iCloudId is null', () async { test('does not update when iCloudId is null', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id);
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: null, iCloudId: null,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -511,9 +506,9 @@ void main() {
}); });
test('does not update when cloudId does not match iCloudId', () async { test('does not update when cloudId does not match iCloudId', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id);
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: 'different-cloud-id', iCloudId: 'different-cloud-id',
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -528,12 +523,12 @@ void main() {
}); });
test('handles partial null metadata fields matching correctly', () async { test('handles partial null metadata fields matching correctly', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId( final cloudIdAsset = await ctx.newRemoteAssetCloudId(
id: remoteAsset.id, id: remoteAsset.id,
adjustmentTimeOption: const Option.none(), adjustmentTimeOption: const Option.none(),
); );
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -548,9 +543,9 @@ void main() {
}); });
test('does not update when one has null and other has value', () async { test('does not update when one has null and other has value', () async {
final remoteAsset = await ctx.insertRemoteAsset(ownerId: user.id); final remoteAsset = await ctx.newRemoteAsset(ownerId: userId);
final cloudIdAsset = await ctx.insertRemoteAssetCloudId(id: remoteAsset.id); final cloudIdAsset = await ctx.newRemoteAssetCloudId(id: remoteAsset.id);
final localAsset = await ctx.insertLocalAsset( final localAsset = await ctx.newLocalAsset(
checksumOption: const Option.none(), checksumOption: const Option.none(),
iCloudId: cloudIdAsset.cloudId, iCloudId: cloudIdAsset.cloudId,
createdAt: cloudIdAsset.createdAt, createdAt: cloudIdAsset.createdAt,
@@ -565,7 +560,7 @@ void main() {
}); });
test('handles no matching assets gracefully', () async { test('handles no matching assets gracefully', () async {
final localAsset = await ctx.insertLocalAsset(checksumOption: const Option.none(), iCloudId: 'cloud-no-match'); final localAsset = await ctx.newLocalAsset(checksumOption: const Option.none(), iCloudId: 'cloud-no-match');
await sut.reconcileHashesFromCloudId(); await sut.reconcileHashesFromCloudId();
final updated = await sut.getById(localAsset.id); final updated = await sut.getById(localAsset.id);
@@ -1,305 +1,210 @@
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/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/remote_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
void main() { import '../../medium/repository_context.dart';
late Drift db;
late DriftRemoteAlbumRepository repository;
setUp(() { void main() {
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); late MediumRepositoryContext ctx;
repository = DriftRemoteAlbumRepository(db); late DriftRemoteAlbumRepository sut;
setUp(() async {
ctx = MediumRepositoryContext();
sut = DriftRemoteAlbumRepository(ctx.db);
}); });
tearDown(() async { tearDown(() async {
await db.close(); await ctx.dispose();
}); });
group('getSortedAlbumIds', () { group('getSortedAlbumIds', () {
Future<void> createUser(String userId, String name) async { late String userId;
await db
.into(db.userEntity)
.insert(
UserEntityCompanion(
id: Value(userId),
name: Value(name),
email: Value('$userId@test.com'),
avatarColor: const Value(AvatarColor.primary),
),
);
}
Future<void> createAlbum(String albumId, String ownerId, String name) async { setUp(() async {
await db final user = await ctx.newUser();
.into(db.remoteAlbumEntity) userId = user.id;
.insert( });
RemoteAlbumEntityCompanion(
id: Value(albumId),
name: Value(name),
ownerId: Value(ownerId),
createdAt: Value(DateTime.now()),
updatedAt: Value(DateTime.now()),
description: const Value(''),
isActivityEnabled: const Value(false),
order: const Value(AlbumAssetOrder.asc),
),
);
}
Future<void> createAsset(String assetId, String ownerId, DateTime createdAt) async {
await db
.into(db.remoteAssetEntity)
.insert(
RemoteAssetEntityCompanion(
id: Value(assetId),
checksum: Value('checksum-$assetId'),
name: Value('asset-$assetId'),
ownerId: Value(ownerId),
type: const Value(AssetType.image),
createdAt: Value(createdAt),
updatedAt: Value(createdAt),
localDateTime: Value(createdAt),
durationInSeconds: const Value(0),
height: const Value(1080),
width: const Value(1920),
visibility: const Value(AssetVisibility.timeline),
),
);
}
Future<void> linkAssetToAlbum(String albumId, String assetId) async {
await db
.into(db.remoteAlbumAssetEntity)
.insert(RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)));
}
test('returns empty list when albumIds is empty', () async { test('returns empty list when albumIds is empty', () async {
final result = await repository.getSortedAlbumIds([], aggregation: AssetDateAggregation.start); final result = await sut.getSortedAlbumIds([], aggregation: AssetDateAggregation.start);
expect(result, isEmpty); expect(result, isEmpty);
}); });
test('returns single album when only one album exists', () async { test('returns single album when only one album exists', () async {
const userId = 'user1'; final album = await ctx.newRemoteAlbum(ownerId: userId);
const albumId = 'album1'; final asset = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 1));
await ctx.insertRemoteAlbumAsset(albumId: album.id, assetId: asset.id);
await createUser(userId, 'Test User'); final result = await sut.getSortedAlbumIds([album.id], aggregation: AssetDateAggregation.start);
await createAlbum(albumId, userId, 'Album 1'); expect(result, [album.id]);
await createAsset('asset1', userId, DateTime(2024, 1, 1));
await linkAssetToAlbum(albumId, 'asset1');
final result = await repository.getSortedAlbumIds([albumId], aggregation: AssetDateAggregation.start);
expect(result, [albumId]);
}); });
test('sorts albums by start date (MIN) ascending', () async { test('sorts albums by start date (MIN) ascending', () async {
const userId = 'user1';
await createUser(userId, 'Test User');
// Album 1: Assets from Jan 10 to Jan 20 (start: Jan 10) // Album 1: Assets from Jan 10 to Jan 20 (start: Jan 10)
await createAlbum('album1', userId, 'Album 1'); final album1 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset1', userId, DateTime(2024, 1, 10)); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10));
await createAsset('asset2', userId, DateTime(2024, 1, 20)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20));
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id);
await linkAssetToAlbum('album1', 'asset2'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id);
// Album 2: Assets from Jan 5 to Jan 15 (start: Jan 5) // Album 2: Assets from Jan 5 to Jan 15 (start: Jan 5)
await createAlbum('album2', userId, 'Album 2'); final album2 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset3', userId, DateTime(2024, 1, 5)); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5));
await createAsset('asset4', userId, DateTime(2024, 1, 15)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15));
await linkAssetToAlbum('album2', 'asset3'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id);
await linkAssetToAlbum('album2', 'asset4'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id);
// Album 3: Assets from Jan 25 to Jan 30 (start: Jan 25) // Album 3: Assets from Jan 25 to Jan 30 (start: Jan 25)
await createAlbum('album3', userId, 'Album 3'); final album3 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset5', userId, DateTime(2024, 1, 25)); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25));
await createAsset('asset6', userId, DateTime(2024, 1, 30)); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 30));
await linkAssetToAlbum('album3', 'asset5'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id);
await linkAssetToAlbum('album3', 'asset6'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id);
final result = await repository.getSortedAlbumIds([ final result = await sut.getSortedAlbumIds([
'album1', album1.id,
'album2', album2.id,
'album3', album3.id,
], aggregation: AssetDateAggregation.start); ], aggregation: AssetDateAggregation.start);
// Expected order: album2 (Jan 5), album1 (Jan 10), album3 (Jan 25) // Expected order: album2 (Jan 5), album1 (Jan 10), album3 (Jan 25)
expect(result, ['album2', 'album1', 'album3']); expect(result, [album2.id, album1.id, album3.id]);
}); });
test('sorts albums by end date (MAX) ascending', () async { test('sorts albums by end date (MAX) ascending', () async {
const userId = 'user1';
await createUser(userId, 'Test User');
// Album 1: Assets from Jan 10 to Jan 20 (end: Jan 20) // Album 1: Assets from Jan 10 to Jan 20 (end: Jan 20)
await createAlbum('album1', userId, 'Album 1'); final album1 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset1', userId, DateTime(2024, 1, 10)); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10));
await createAsset('asset2', userId, DateTime(2024, 1, 20)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20));
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id);
await linkAssetToAlbum('album1', 'asset2'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id);
// Album 2: Assets from Jan 5 to Jan 15 (end: Jan 15) // Album 2: Assets from Jan 5 to Jan 15 (end: Jan 15)
await createAlbum('album2', userId, 'Album 2'); final album2 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset3', userId, DateTime(2024, 1, 5)); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5));
await createAsset('asset4', userId, DateTime(2024, 1, 15)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15));
await linkAssetToAlbum('album2', 'asset3'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset3.id);
await linkAssetToAlbum('album2', 'asset4'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset4.id);
// Album 3: Assets from Jan 25 to Jan 30 (end: Jan 30) // Album 3: Assets from Jan 25 to Jan 30 (end: Jan 30)
await createAlbum('album3', userId, 'Album 3'); final album3 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset5', userId, DateTime(2024, 1, 25)); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25));
await createAsset('asset6', userId, DateTime(2024, 1, 30)); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 30));
await linkAssetToAlbum('album3', 'asset5'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset5.id);
await linkAssetToAlbum('album3', 'asset6'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset6.id);
final result = await repository.getSortedAlbumIds([ final result = await sut.getSortedAlbumIds([
'album1', album1.id,
'album2', album2.id,
'album3', album3.id,
], aggregation: AssetDateAggregation.end); ], aggregation: AssetDateAggregation.end);
// Expected order: album2 (Jan 15), album1 (Jan 20), album3 (Jan 30) // Expected order: album2 (Jan 15), album1 (Jan 20), album3 (Jan 30)
expect(result, ['album2', 'album1', 'album3']); expect(result, [album2.id, album1.id, album3.id]);
}); });
test('handles albums with single asset', () async { test('handles albums with single asset', () async {
const userId = 'user1'; 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 createUser(userId, 'Test User'); 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 createAlbum('album1', userId, 'Album 1'); final result = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start);
await createAsset('asset1', userId, DateTime(2024, 1, 15));
await linkAssetToAlbum('album1', 'asset1');
await createAlbum('album2', userId, 'Album 2'); expect(result, [album2.id, album1.id]);
await createAsset('asset2', userId, DateTime(2024, 1, 10));
await linkAssetToAlbum('album2', 'asset2');
final result = await repository.getSortedAlbumIds(['album1', 'album2'], aggregation: AssetDateAggregation.start);
expect(result, ['album2', 'album1']);
}); });
test('only returns requested album IDs in the result', () async { test('only returns requested album IDs in the result', () async {
const userId = 'user1';
await createUser(userId, 'Test User');
// Create 3 albums // Create 3 albums
await createAlbum('album1', userId, 'Album 1'); final album1 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset1', userId, DateTime(2024, 1, 10)); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10));
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id);
await createAlbum('album2', userId, 'Album 2'); final album2 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset2', userId, DateTime(2024, 1, 5)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5));
await linkAssetToAlbum('album2', 'asset2'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id);
await createAlbum('album3', userId, 'Album 3'); final album3 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset3', userId, DateTime(2024, 1, 15)); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15));
await linkAssetToAlbum('album3', 'asset3'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id);
// Only request album1 and album3 // Only request album1 and album3
final result = await repository.getSortedAlbumIds(['album1', 'album3'], aggregation: AssetDateAggregation.start); final result = await sut.getSortedAlbumIds([album1.id, album3.id], aggregation: AssetDateAggregation.start);
// Should only return album1 and album3, not album2 // Should only return album1 and album3, not album2
expect(result, ['album1', 'album3']); expect(result, [album1.id, album3.id]);
}); });
test('handles albums with same date correctly', () async { test('handles albums with same date correctly', () async {
const userId = 'user1';
await createUser(userId, 'Test User');
final sameDate = DateTime(2024, 1, 10); final sameDate = DateTime(2024, 1, 10);
await createAlbum('album1', userId, 'Album 1'); final album1 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset1', userId, sameDate); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: sameDate);
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id);
await createAlbum('album2', userId, 'Album 2'); final album2 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset2', userId, sameDate); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: sameDate);
await linkAssetToAlbum('album2', 'asset2'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset2.id);
final result = await repository.getSortedAlbumIds(['album1', 'album2'], aggregation: AssetDateAggregation.start); final result = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start);
// Both albums have the same date, so both should be returned // Both albums have the same date, so both should be returned
expect(result, hasLength(2)); expect(result, hasLength(2));
expect(result, containsAll(['album1', 'album2'])); expect(result, containsAll([album1.id, album2.id]));
}); });
test('handles albums across different years', () async { test('handles albums across different years', () async {
const userId = 'user1'; 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 createUser(userId, 'Test User'); 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 createAlbum('album1', userId, 'Album 1'); final album3 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset1', userId, DateTime(2023, 12, 25)); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2025, 1, 1));
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album3.id, assetId: asset3.id);
await createAlbum('album2', userId, 'Album 2'); final result = await sut.getSortedAlbumIds([
await createAsset('asset2', userId, DateTime(2024, 1, 5)); album1.id,
await linkAssetToAlbum('album2', 'asset2'); album2.id,
album3.id,
await createAlbum('album3', userId, 'Album 3');
await createAsset('asset3', userId, DateTime(2025, 1, 1));
await linkAssetToAlbum('album3', 'asset3');
final result = await repository.getSortedAlbumIds([
'album1',
'album2',
'album3',
], aggregation: AssetDateAggregation.start); ], aggregation: AssetDateAggregation.start);
expect(result, ['album1', 'album2', 'album3']); expect(result, [album1.id, album2.id, album3.id]);
}); });
test('handles album with multiple assets correctly', () async { test('handles album with multiple assets correctly', () async {
const userId = 'user1'; final album1 = await ctx.newRemoteAlbum(ownerId: userId);
await createUser(userId, 'Test User');
await createAlbum('album1', userId, 'Album 1');
// Album 1 has 5 assets from Jan 5 to Jan 25 // Album 1 has 5 assets from Jan 5 to Jan 25
await createAsset('asset1', userId, DateTime(2024, 1, 5)); final asset1 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 5));
await createAsset('asset2', userId, DateTime(2024, 1, 10)); final asset2 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 10));
await createAsset('asset3', userId, DateTime(2024, 1, 15)); final asset3 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 15));
await createAsset('asset4', userId, DateTime(2024, 1, 20)); final asset4 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 20));
await createAsset('asset5', userId, DateTime(2024, 1, 25)); final asset5 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 25));
await linkAssetToAlbum('album1', 'asset1'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset1.id);
await linkAssetToAlbum('album1', 'asset2'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset2.id);
await linkAssetToAlbum('album1', 'asset3'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset3.id);
await linkAssetToAlbum('album1', 'asset4'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset4.id);
await linkAssetToAlbum('album1', 'asset5'); await ctx.insertRemoteAlbumAsset(albumId: album1.id, assetId: asset5.id);
await createAlbum('album2', userId, 'Album 2'); final album2 = await ctx.newRemoteAlbum(ownerId: userId);
await createAsset('asset6', userId, DateTime(2024, 1, 1)); final asset6 = await ctx.newRemoteAsset(ownerId: userId, createdAt: DateTime(2024, 1, 1));
await linkAssetToAlbum('album2', 'asset6'); await ctx.insertRemoteAlbumAsset(albumId: album2.id, assetId: asset6.id);
final resultStart = await repository.getSortedAlbumIds([ final resultStart = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.start);
'album1',
'album2',
], aggregation: AssetDateAggregation.start);
// album2 (Jan 1) should come before album1 (Jan 5) // album2 (Jan 1) should come before album1 (Jan 5)
expect(resultStart, ['album2', 'album1']); expect(resultStart, [album2.id, album1.id]);
final resultEnd = await repository.getSortedAlbumIds(['album1', 'album2'], aggregation: AssetDateAggregation.end); final resultEnd = await sut.getSortedAlbumIds([album1.id, album2.id], aggregation: AssetDateAggregation.end);
// album2 (Jan 1) should come before album1 (Jan 25) // album2 (Jan 1) should come before album1 (Jan 25)
expect(resultEnd, ['album2', 'album1']); expect(resultEnd, [album2.id, album1.id]);
}); });
}); });
} }
+87 -61
View File
@@ -2,12 +2,15 @@ import 'dart:math';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/native.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/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.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/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.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_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.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_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
@@ -17,21 +20,9 @@ import 'package:uuid/uuid.dart';
class MediumRepositoryContext { class MediumRepositoryContext {
final Drift db; final Drift db;
late UserEntityData user;
final Random _random = Random(); final Random _random = Random();
MediumRepositoryContext._() MediumRepositoryContext() : db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
: db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
static Future<MediumRepositoryContext> create() async {
final ctx = MediumRepositoryContext._();
await ctx.setup();
return ctx;
}
Future<void> setup() async {
user = await insertUser();
}
Future<void> dispose() async { Future<void> dispose() async {
await db.close(); await db.close();
@@ -53,7 +44,7 @@ class MediumRepositoryContext {
return Value(fallback); return Value(fallback);
} }
Future<UserEntityData> insertUser({ Future<UserEntityData> newUser({
String? id, String? id,
String? email, String? email,
AvatarColor? avatarColor, AvatarColor? avatarColor,
@@ -75,50 +66,7 @@ class MediumRepositoryContext {
); );
} }
Future<LocalAssetEntityData> insertLocalAsset({ Future<RemoteAssetEntityData> newRemoteAsset({
String? id,
String? name,
String? checksum,
Option<String>? checksumOption,
DateTime? createdAt,
AssetType? type,
bool? isFavorite,
String? iCloudId,
DateTime? adjustmentTime,
Option<DateTime>? adjustmentTimeOption,
double? latitude,
double? longitude,
int? width,
int? height,
int? durationInSeconds,
int? orientation,
DateTime? updatedAt,
}) async {
id = id ?? const Uuid().v4();
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)),
durationInSeconds: Value(durationInSeconds ?? 0),
orientation: Value(orientation ?? 0),
updatedAt: Value(updatedAt ?? DateTime.now()),
checksum: _resolveUndefined(checksum, checksumOption, const Uuid().v4()),
createdAt: Value(createdAt ?? DateTime.now()),
type: Value(type ?? AssetType.image),
isFavorite: Value(isFavorite ?? false),
iCloudId: Value(iCloudId ?? const Uuid().v4()),
adjustmentTime: _resolveUndefined(adjustmentTime, adjustmentTimeOption, DateTime.now()),
latitude: Value(latitude ?? _random.nextDouble() * 180 - 90),
longitude: Value(longitude ?? _random.nextDouble() * 360 - 180),
),
);
}
Future<RemoteAssetEntityData> insertRemoteAsset({
String? id, String? id,
String? checksum, String? checksum,
String? ownerId, String? ownerId,
@@ -166,7 +114,7 @@ class MediumRepositoryContext {
); );
} }
Future<RemoteAssetCloudIdEntityData> insertRemoteAssetCloudId({ Future<RemoteAssetCloudIdEntityData> newRemoteAssetCloudId({
String? id, String? id,
String? cloudId, String? cloudId,
DateTime? createdAt, DateTime? createdAt,
@@ -189,7 +137,85 @@ class MediumRepositoryContext {
); );
} }
Future<LocalAlbumEntityData> insertLocalAlbum({ Future<RemoteAlbumEntityData> newRemoteAlbum({
String? id,
String? name,
String? ownerId,
DateTime? createdAt,
DateTime? updatedAt,
String? description,
bool? isActivityEnabled,
AlbumAssetOrder? order,
String? thumbnailAssetId,
}) async {
id = id ?? const Uuid().v4();
return db
.into(db.remoteAlbumEntity)
.insertReturning(
RemoteAlbumEntityCompanion(
id: Value(id),
name: Value(name ?? 'remote_album_$id'),
ownerId: Value(ownerId ?? const Uuid().v4()),
createdAt: Value(createdAt ?? DateTime.now()),
updatedAt: Value(updatedAt ?? DateTime.now()),
description: Value(description ?? 'Description for album $id'),
isActivityEnabled: Value(isActivityEnabled ?? false),
order: Value(order ?? AlbumAssetOrder.asc),
thumbnailAssetId: Value(thumbnailAssetId),
),
);
}
Future<void> insertRemoteAlbumAsset({required String albumId, required String assetId}) {
return db
.into(db.remoteAlbumAssetEntity)
.insert(RemoteAlbumAssetEntityCompanion.insert(albumId: albumId, assetId: assetId));
}
Future<LocalAssetEntityData> newLocalAsset({
String? id,
String? name,
String? checksum,
Option<String>? checksumOption,
DateTime? createdAt,
AssetType? type,
bool? isFavorite,
String? iCloudId,
DateTime? adjustmentTime,
Option<DateTime>? adjustmentTimeOption,
double? latitude,
double? longitude,
int? width,
int? height,
int? durationInSeconds,
int? orientation,
DateTime? updatedAt,
}) async {
id = id ?? const Uuid().v4();
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)),
durationInSeconds: Value(durationInSeconds ?? 0),
orientation: Value(orientation ?? 0),
updatedAt: Value(updatedAt ?? DateTime.now()),
checksum: _resolveUndefined(checksum, checksumOption, const Uuid().v4()),
createdAt: Value(createdAt ?? DateTime.now()),
type: Value(type ?? AssetType.image),
isFavorite: Value(isFavorite ?? false),
iCloudId: Value(iCloudId ?? const Uuid().v4()),
adjustmentTime: _resolveUndefined(adjustmentTime, adjustmentTimeOption, DateTime.now()),
latitude: Value(latitude ?? _random.nextDouble() * 180 - 90),
longitude: Value(longitude ?? _random.nextDouble() * 360 - 180),
),
);
}
Future<LocalAlbumEntityData> newLocalAlbum({
String? id, String? id,
String? name, String? name,
DateTime? updatedAt, DateTime? updatedAt,
@@ -212,7 +238,7 @@ class MediumRepositoryContext {
); );
} }
Future<void> insertLocalAlbumAsset({required String albumId, required String assetId}) { Future<void> newLocalAlbumAsset({required String albumId, required String assetId}) {
return db return db
.into(db.localAlbumAssetEntity) .into(db.localAlbumAssetEntity)
.insert(LocalAlbumAssetEntityCompanion.insert(albumId: albumId, assetId: assetId)); .insert(LocalAlbumAssetEntityCompanion.insert(albumId: albumId, assetId: assetId));