diff --git a/mobile/lib/platform/messages.g.dart b/mobile/lib/platform/messages.g.dart index 50871ea9b8..530737615a 100644 --- a/mobile/lib/platform/messages.g.dart +++ b/mobile/lib/platform/messages.g.dart @@ -14,21 +14,22 @@ PlatformException _createConnectionError(String channelName) { message: 'Unable to establish connection on channel: "$channelName".', ); } + bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + return a.length == b.length && + a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } - class PlatformAsset { PlatformAsset({ required this.id, @@ -63,7 +64,8 @@ class PlatformAsset { } Object encode() { - return _toList(); } + return _toList(); + } static PlatformAsset decode(Object result) { result as List; @@ -91,8 +93,7 @@ class PlatformAsset { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SyncDelta { @@ -121,7 +122,8 @@ class SyncDelta { } Object encode() { - return _toList(); } + return _toList(); + } static SyncDelta decode(Object result) { result as List; @@ -129,7 +131,8 @@ class SyncDelta { hasChanges: result[0]! as bool, updates: (result[1] as List?)!.cast(), deletes: (result[2] as List?)!.cast(), - albumAssets: (result[3] as Map?)!.cast>(), + albumAssets: + (result[3] as Map?)!.cast>(), ); } @@ -147,11 +150,9 @@ class SyncDelta { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -159,10 +160,10 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformAsset) { + } else if (value is PlatformAsset) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is SyncDelta) { + } else if (value is SyncDelta) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -173,9 +174,9 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return PlatformAsset.decode(readValue(buffer)!); - case 130: + case 130: return SyncDelta.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -187,9 +188,11 @@ class ImHostApi { /// Constructor for [ImHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + ImHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -197,8 +200,10 @@ class ImHostApi { final String pigeonVar_messageChannelSuffix; Future shouldFullSync() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostApi.shouldFullSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostApi.shouldFullSync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -225,8 +230,10 @@ class ImHostApi { } Future getMediaChanges() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostApi.getMediaChanges$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostApi.getMediaChanges$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -253,8 +260,10 @@ class ImHostApi { } Future checkpointSync() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostApi.checkpointSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostApi.checkpointSync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -276,8 +285,10 @@ class ImHostApi { } Future clearSyncCheckpoint() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostApi.clearSyncCheckpoint$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostApi.clearSyncCheckpoint$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -299,13 +310,16 @@ class ImHostApi { } Future> getAssetIdsForAlbum(String albumId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostApi.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostApi.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([albumId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index cd0e2db918..1cba2ee8c2 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -14,7 +14,7 @@ part of 'router.dart'; /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { const ActivitiesRoute({List? children}) - : super(ActivitiesRoute.name, initialChildren: children); + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -35,13 +35,13 @@ class AlbumAdditionalSharedUserSelectionRoute required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -83,14 +83,14 @@ class AlbumAssetSelectionRoute bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -130,7 +130,7 @@ class AlbumAssetSelectionRouteArgs { /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { const AlbumOptionsRoute({List? children}) - : super(AlbumOptionsRoute.name, initialChildren: children); + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -150,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -188,10 +188,10 @@ class AlbumSharedUserSelectionRoute required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -225,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -258,7 +258,7 @@ class AlbumViewerRouteArgs { /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { const AlbumsRoute({List? children}) - : super(AlbumsRoute.name, initialChildren: children); + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -274,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -290,7 +290,7 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { const AllPeopleRoute({List? children}) - : super(AllPeopleRoute.name, initialChildren: children); + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -306,7 +306,7 @@ class AllPeopleRoute extends PageRouteInfo { /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { const AllPlacesRoute({List? children}) - : super(AllPlacesRoute.name, initialChildren: children); + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -322,7 +322,7 @@ class AllPlacesRoute extends PageRouteInfo { /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { const AllVideosRoute({List? children}) - : super(AllVideosRoute.name, initialChildren: children); + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -342,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -375,7 +375,7 @@ class AppLogDetailRouteArgs { /// [AppLogPage] class AppLogRoute extends PageRouteInfo { const AppLogRoute({List? children}) - : super(AppLogRoute.name, initialChildren: children); + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -391,7 +391,7 @@ class AppLogRoute extends PageRouteInfo { /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { const ArchiveRoute({List? children}) - : super(ArchiveRoute.name, initialChildren: children); + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -407,7 +407,7 @@ class ArchiveRoute extends PageRouteInfo { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -423,7 +423,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -439,7 +439,7 @@ class BackupControllerRoute extends PageRouteInfo { /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { const BackupOptionsRoute({List? children}) - : super(BackupOptionsRoute.name, initialChildren: children); + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -455,7 +455,7 @@ class BackupOptionsRoute extends PageRouteInfo { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -475,10 +475,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -515,10 +515,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -560,15 +560,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -612,7 +612,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -628,7 +628,7 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { const FavoritesRoute({List? children}) - : super(FavoritesRoute.name, initialChildren: children); + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -644,7 +644,7 @@ class FavoritesRoute extends PageRouteInfo { /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { const FeatInDevRoute({List? children}) - : super(FeatInDevRoute.name, initialChildren: children); + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -665,10 +665,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -712,10 +712,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -754,16 +754,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -811,7 +811,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -827,7 +827,7 @@ class HeaderSettingsRoute extends PageRouteInfo { /// [LibraryPage] class LibraryRoute extends PageRouteInfo { const LibraryRoute({List? children}) - : super(LibraryRoute.name, initialChildren: children); + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -843,7 +843,7 @@ class LibraryRoute extends PageRouteInfo { /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { const LocalAlbumsRoute({List? children}) - : super(LocalAlbumsRoute.name, initialChildren: children); + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -859,7 +859,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -875,7 +875,7 @@ class LocalMediaSummaryRoute extends PageRouteInfo { /// [LoginPage] class LoginRoute extends PageRouteInfo { const LoginRoute({List? children}) - : super(LoginRoute.name, initialChildren: children); + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -895,13 +895,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -939,11 +939,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -980,14 +980,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1034,16 +1034,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1095,10 +1095,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1128,7 +1128,7 @@ class PartnerDetailRouteArgs { /// [PartnerPage] class PartnerRoute extends PageRouteInfo { const PartnerRoute({List? children}) - : super(PartnerRoute.name, initialChildren: children); + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1144,7 +1144,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1160,7 +1160,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1181,14 +1181,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1228,7 +1228,7 @@ class PersonResultRouteArgs { /// [PhotosPage] class PhotosRoute extends PageRouteInfo { const PhotosRoute({List? children}) - : super(PhotosRoute.name, initialChildren: children); + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1248,13 +1248,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -1289,7 +1289,7 @@ class PlacesCollectionRouteArgs { /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { const RecentlyTakenRoute({List? children}) - : super(RecentlyTakenRoute.name, initialChildren: children); + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -1309,10 +1309,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -1344,7 +1344,7 @@ class SearchRouteArgs { /// [SettingsPage] class SettingsRoute extends PageRouteInfo { const SettingsRoute({List? children}) - : super(SettingsRoute.name, initialChildren: children); + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -1364,10 +1364,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -1401,10 +1401,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -1440,15 +1440,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -1494,7 +1494,7 @@ class SharedLinkEditRouteArgs { /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { const SharedLinkRoute({List? children}) - : super(SharedLinkRoute.name, initialChildren: children); + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -1510,7 +1510,7 @@ class SharedLinkRoute extends PageRouteInfo { /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { const SplashScreenRoute({List? children}) - : super(SplashScreenRoute.name, initialChildren: children); + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -1526,7 +1526,7 @@ class SplashScreenRoute extends PageRouteInfo { /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { const TabControllerRoute({List? children}) - : super(TabControllerRoute.name, initialChildren: children); + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -1542,7 +1542,7 @@ class TabControllerRoute extends PageRouteInfo { /// [TrashPage] class TrashRoute extends PageRouteInfo { const TrashRoute({List? children}) - : super(TrashRoute.name, initialChildren: children); + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/test/domain/services/device_sync_service_test.dart b/mobile/test/domain/services/device_sync_service_test.dart index 417840095e..6bff0c292e 100644 --- a/mobile/test/domain/services/device_sync_service_test.dart +++ b/mobile/test/domain/services/device_sync_service_test.dart @@ -13,25 +13,23 @@ import '../../infrastructure/repository.mock.dart'; import '../service.mock.dart'; void main() { - late IAlbumMediaRepository mockAlbumMediaRepo; - late ILocalAlbumRepository mockLocalAlbumRepo; - late ImHostApi mockHostApi; - late MockPlatform mockPlatform; + late IAlbumMediaRepository albumMediaRepo; + late ILocalAlbumRepository localAlbumRepo; + late ImHostApi hostApi; + late MockPlatform platform; late DeviceSyncService sut; - Future mockTransaction(Future Function() action) => action(); - setUp(() { - mockAlbumMediaRepo = MockAlbumMediaRepository(); - mockLocalAlbumRepo = MockLocalAlbumRepository(); - mockHostApi = MockHostApi(); - mockPlatform = MockPlatform(); + albumMediaRepo = MockAlbumMediaRepository(); + localAlbumRepo = MockLocalAlbumRepository(); + hostApi = MockHostApi(); + platform = MockPlatform(); sut = DeviceSyncService( - albumMediaRepository: mockAlbumMediaRepo, - localAlbumRepository: mockLocalAlbumRepo, - hostApi: mockHostApi, - platform: mockPlatform, + albumMediaRepository: albumMediaRepo, + localAlbumRepository: localAlbumRepo, + hostApi: hostApi, + platform: platform, ); registerFallbackValue(LocalAlbumStub.album1); @@ -39,49 +37,42 @@ void main() { registerFallbackValue( SyncDelta(hasChanges: true, updates: [], deletes: [], albumAssets: {}), ); + registerFallbackValue(SortLocalAlbumsBy.id); - when(() => mockAlbumMediaRepo.getAll()).thenAnswer((_) async => []); - when(() => mockAlbumMediaRepo.refresh(any())).thenAnswer( - (inv) async => LocalAlbumStub.album1.copyWith( - id: inv.positionalArguments.first as String, - assetCount: 0, - ), - ); when( - () => mockAlbumMediaRepo.getAssetsForAlbum( + () => albumMediaRepo.getAll( + withModifiedTime: any(named: 'withModifiedTime'), + ), + ).thenAnswer((_) async => []); + when(() => albumMediaRepo.getAll()).thenAnswer((_) async => []); + when( + () => albumMediaRepo.getAssetsForAlbum( any(), updateTimeCond: any(named: 'updateTimeCond'), ), ).thenAnswer((_) async => []); - when(() => mockAlbumMediaRepo.getAssetsForAlbum(any())) + when(() => albumMediaRepo.getAssetsForAlbum(any())) .thenAnswer((_) async => []); - - when(() => mockLocalAlbumRepo.getAll(sortBy: any(named: 'sortBy'))) + when(() => localAlbumRepo.getAll(sortBy: any(named: 'sortBy'))) .thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.getAll()).thenAnswer((_) async => []); + when(() => localAlbumRepo.getAll()).thenAnswer((_) async => []); when( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any(), toUpsert: any(named: 'toUpsert'), toDelete: any(named: 'toDelete'), ), ).thenAnswer((_) async => {}); - when(() => mockLocalAlbumRepo.delete(any())).thenAnswer((_) async => true); - when(() => mockLocalAlbumRepo.updateAll(any())).thenAnswer((_) async => {}); - when(() => mockLocalAlbumRepo.processDelta(any())) + when(() => localAlbumRepo.upsert(any())).thenAnswer((_) async => {}); + when(() => localAlbumRepo.delete(any())).thenAnswer((_) async => true); + when(() => localAlbumRepo.updateAll(any())).thenAnswer((_) async => {}); + when(() => localAlbumRepo.processDelta(any())).thenAnswer((_) async => {}); + when(() => localAlbumRepo.syncAlbumDeletes(any(), any())) .thenAnswer((_) async => {}); - when(() => mockLocalAlbumRepo.syncAlbumDeletes(any(), any())) - .thenAnswer((_) async => {}); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(any())) + when(() => localAlbumRepo.getAssetsForAlbum(any())) .thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.transaction(any())).thenAnswer( - (inv) => mockTransaction( - inv.positionalArguments.first as Future Function(), - ), - ); - - when(() => mockHostService.shouldFullSync()).thenAnswer((_) async => true); - when(() => mockHostService.getMediaChanges()).thenAnswer( + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => true); + when(() => hostApi.getMediaChanges()).thenAnswer( (_) async => SyncDelta( hasChanges: false, updates: [], @@ -89,41 +80,37 @@ void main() { albumAssets: {}, ), ); - when(() => mockHostService.getAssetIdsForAlbum(any())) - .thenAnswer((_) async => []); - when(() => mockHostService.checkpointSync()).thenAnswer((_) async => {}); - - when(() => mockPlatform.isAndroid).thenReturn(false); + when(() => hostApi.getAssetIdsForAlbum(any())).thenAnswer((_) async => []); + when(() => hostApi.checkpointSync()).thenAnswer((_) async => {}); + when(() => platform.isAndroid).thenReturn(false); }); group('sync', () { test( 'performs full sync and checkpoints when shouldFullSync is true', () async { - when(() => mockHostService.shouldFullSync()) - .thenAnswer((_) async => true); - when(() => mockAlbumMediaRepo.getAll()) + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => true); + when(() => albumMediaRepo.getAll()) .thenAnswer((_) async => [LocalAlbumStub.album1]); - when(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + when(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .thenAnswer((_) async => []); await sut.sync(); - verify(() => mockHostService.shouldFullSync()).called(1); - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verify(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + verify(() => hostApi.shouldFullSync()).called(1); + verify(() => albumMediaRepo.getAll()).called(1); + verify(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .called(1); - verify(() => mockHostService.checkpointSync()).called(1); - verifyNever(() => mockHostService.getMediaChanges()); + verify(() => hostApi.checkpointSync()).called(1); + verifyNever(() => hostApi.getMediaChanges()); }, ); test( 'skips sync and does not checkpoint when shouldFullSync is false and no media changes', () async { - when(() => mockHostService.shouldFullSync()) - .thenAnswer((_) async => false); - when(() => mockHostService.getMediaChanges()).thenAnswer( + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => false); + when(() => hostApi.getMediaChanges()).thenAnswer( (_) async => SyncDelta( hasChanges: false, updates: [], @@ -134,12 +121,16 @@ void main() { await sut.sync(); - verify(() => mockHostService.shouldFullSync()).called(1); - verify(() => mockHostService.getMediaChanges()).called(1); - verifyNever(() => mockAlbumMediaRepo.getAll()); - verifyNever(() => mockLocalAlbumRepo.updateAll(any())); - verifyNever(() => mockLocalAlbumRepo.processDelta(any())); - verifyNever(() => mockHostService.checkpointSync()); + verify(() => hostApi.shouldFullSync()).called(1); + verify(() => hostApi.getMediaChanges()).called(1); + verifyNever( + () => albumMediaRepo.getAll( + withModifiedTime: any(named: 'withModifiedTime'), + ), + ); + verifyNever(() => localAlbumRepo.updateAll(any())); + verifyNever(() => localAlbumRepo.processDelta(any())); + verifyNever(() => hostApi.checkpointSync()); }, ); @@ -154,27 +145,25 @@ void main() { "albumId": ["asset1", "asset2"], }, ); - final deviceAlbums = [LocalAlbumStub.album1]; + final devAlbums = [LocalAlbumStub.album1]; - when(() => mockHostService.shouldFullSync()) - .thenAnswer((_) async => false); - when(() => mockHostService.getMediaChanges()) - .thenAnswer((_) async => delta); - when(() => mockAlbumMediaRepo.getAll()) - .thenAnswer((_) async => deviceAlbums); - when(() => mockPlatform.isAndroid).thenReturn(false); + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => false); + when(() => hostApi.getMediaChanges()).thenAnswer((_) async => delta); + when(() => albumMediaRepo.getAll(withModifiedTime: true)) + .thenAnswer((_) async => devAlbums); + when(() => platform.isAndroid).thenReturn(false); await sut.sync(); verifyInOrder([ - () => mockHostService.shouldFullSync(), - () => mockHostService.getMediaChanges(), - () => mockAlbumMediaRepo.getAll(), - () => mockLocalAlbumRepo.updateAll(deviceAlbums), - () => mockLocalAlbumRepo.processDelta(delta), - () => mockHostService.checkpointSync(), + () => hostApi.shouldFullSync(), + () => hostApi.getMediaChanges(), + () => albumMediaRepo.getAll(withModifiedTime: true), + () => localAlbumRepo.updateAll(devAlbums), + () => localAlbumRepo.processDelta(delta), + () => hostApi.checkpointSync(), ]); - verifyNever(() => mockHostService.getAssetIdsForAlbum(any())); + verifyNever(() => hostApi.getAssetIdsForAlbum(any())); }, ); @@ -189,67 +178,55 @@ void main() { "dbAlbumId": ["asset1", "asset2"], }, ); - final deviceAlbums = [LocalAlbumStub.album1]; + final devAlbums = [LocalAlbumStub.album1]; final dbAlbums = [LocalAlbumStub.album2.copyWith(id: "dbAlbumId")]; - final assetIdsForDbAlbum = ["asset1", "asset2"]; + final assetIds = ["asset1", "asset2"]; - when(() => mockHostService.shouldFullSync()) - .thenAnswer((_) async => false); - when(() => mockHostService.getMediaChanges()) - .thenAnswer((_) async => delta); - when(() => mockAlbumMediaRepo.getAll()) - .thenAnswer((_) async => deviceAlbums); - when(() => mockLocalAlbumRepo.getAll()) - .thenAnswer((_) async => dbAlbums); - when(() => mockPlatform.isAndroid).thenReturn(true); - when(() => mockHostService.getAssetIdsForAlbum(dbAlbums.first.id)) - .thenAnswer((_) async => assetIdsForDbAlbum); + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => false); + when(() => hostApi.getMediaChanges()).thenAnswer((_) async => delta); + when(() => albumMediaRepo.getAll(withModifiedTime: true)) + .thenAnswer((_) async => devAlbums); + when(() => localAlbumRepo.getAll()).thenAnswer((_) async => dbAlbums); + when(() => platform.isAndroid).thenReturn(true); + when(() => hostApi.getAssetIdsForAlbum(dbAlbums.first.id)) + .thenAnswer((_) async => assetIds); await sut.sync(); verifyInOrder([ - () => mockHostService.shouldFullSync(), - () => mockHostService.getMediaChanges(), - () => mockAlbumMediaRepo.getAll(), - () => mockLocalAlbumRepo.updateAll(deviceAlbums), - () => mockLocalAlbumRepo.processDelta(delta), - () => mockPlatform.isAndroid, - () => mockLocalAlbumRepo.getAll(), - () => mockHostService.getAssetIdsForAlbum(dbAlbums.first.id), - () => mockLocalAlbumRepo.syncAlbumDeletes( - dbAlbums.first.id, - assetIdsForDbAlbum, - ), - () => mockHostService.checkpointSync(), + () => hostApi.shouldFullSync(), + () => hostApi.getMediaChanges(), + () => albumMediaRepo.getAll(withModifiedTime: true), + () => localAlbumRepo.updateAll(devAlbums), + () => localAlbumRepo.processDelta(delta), + () => platform.isAndroid, + () => localAlbumRepo.getAll(), + () => hostApi.getAssetIdsForAlbum(dbAlbums.first.id), + () => localAlbumRepo.syncAlbumDeletes(dbAlbums.first.id, assetIds), + () => hostApi.checkpointSync(), ]); }, ); test('handles error from shouldFullSync and does not checkpoint', () async { - when(() => mockHostService.shouldFullSync()) - .thenThrow(Exception("Host error")); - + when(() => hostApi.shouldFullSync()).thenThrow(Exception("Host error")); await sut.sync(); - - verify(() => mockHostService.shouldFullSync()).called(1); - verifyNever(() => mockHostService.getMediaChanges()); - verifyNever(() => mockHostService.checkpointSync()); + verify(() => hostApi.shouldFullSync()).called(1); + verifyNever(() => hostApi.getMediaChanges()); + verifyNever(() => hostApi.checkpointSync()); }); test( 'handles error from getMediaChanges and does not checkpoint', () async { - when(() => mockHostService.shouldFullSync()) - .thenAnswer((_) async => false); - when(() => mockHostService.getMediaChanges()) + when(() => hostApi.shouldFullSync()).thenAnswer((_) async => false); + when(() => hostApi.getMediaChanges()) .thenThrow(Exception("Host error")); - await sut.sync(); - - verify(() => mockHostService.shouldFullSync()).called(1); - verify(() => mockHostService.getMediaChanges()).called(1); - verifyNever(() => mockLocalAlbumRepo.updateAll(any())); - verifyNever(() => mockHostService.checkpointSync()); + verify(() => hostApi.shouldFullSync()).called(1); + verify(() => hostApi.getMediaChanges()).called(1); + verifyNever(() => localAlbumRepo.updateAll(any())); + verifyNever(() => hostApi.checkpointSync()); }, ); }); @@ -258,193 +235,170 @@ void main() { test( 'completes and checkpoints when no albums exist on device or DB', () async { - when(() => mockAlbumMediaRepo.getAll()).thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + when(() => albumMediaRepo.getAll()).thenAnswer((_) async => []); + when(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .thenAnswer((_) async => []); - await sut.fullSync(); - - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verify(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + verify(() => albumMediaRepo.getAll()).called(1); + verify(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .called(1); verifyNever( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any(), toUpsert: any(named: 'toUpsert'), + toDelete: any(named: 'toDelete'), ), ); - verifyNever(() => mockLocalAlbumRepo.delete(any())); - verify(() => mockHostService.checkpointSync()).called(1); + verifyNever(() => localAlbumRepo.delete(any())); + verify(() => hostApi.checkpointSync()).called(1); }, ); test('calls addAlbum for new device albums and checkpoints', () async { - final deviceAlbums = [LocalAlbumStub.album1, LocalAlbumStub.album2]; - when(() => mockAlbumMediaRepo.getAll()) - .thenAnswer((_) async => deviceAlbums); - when(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) - .thenAnswer((_) async => []); + final devAlbums = [LocalAlbumStub.album1, LocalAlbumStub.album2]; + final album1Assets = [LocalAssetStub.image1]; - final refreshedAlbum1 = deviceAlbums.first.copyWith(assetCount: 1); - final refreshedAlbum2 = deviceAlbums[1].copyWith(assetCount: 0); - when(() => mockAlbumMediaRepo.refresh(deviceAlbums.first.id)) - .thenAnswer((_) async => refreshedAlbum1); - when(() => mockAlbumMediaRepo.refresh(deviceAlbums[1].id)) - .thenAnswer((_) async => refreshedAlbum2); - when(() => mockAlbumMediaRepo.getAssetsForAlbum(deviceAlbums.first.id)) - .thenAnswer((_) async => [LocalAssetStub.image1]); + when(() => albumMediaRepo.getAll()).thenAnswer((_) async => devAlbums); + when(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + .thenAnswer((_) async => []); + when(() => albumMediaRepo.getAssetsForAlbum(devAlbums.first.id)) + .thenAnswer((_) async => album1Assets); await sut.fullSync(); - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verify(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + verify(() => albumMediaRepo.getAll()).called(1); + verify(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .called(1); - verify(() => mockAlbumMediaRepo.refresh(deviceAlbums.first.id)).called(1); - verify(() => mockAlbumMediaRepo.refresh(deviceAlbums[1].id)).called(1); - verify(() => mockAlbumMediaRepo.getAssetsForAlbum(deviceAlbums.first.id)) + verify(() => albumMediaRepo.getAssetsForAlbum(devAlbums.first.id)) .called(1); verify( - () => mockLocalAlbumRepo.upsert( - refreshedAlbum1, - toUpsert: [LocalAssetStub.image1], + () => localAlbumRepo.upsert( + devAlbums.first, + toUpsert: album1Assets, toDelete: [], ), ).called(1); + verify(() => albumMediaRepo.getAssetsForAlbum(devAlbums[1].id)).called(1); verify( - () => mockLocalAlbumRepo.upsert( - refreshedAlbum2, - toUpsert: [], - toDelete: [], - ), + () => localAlbumRepo.upsert(devAlbums[1], toUpsert: [], toDelete: []), ).called(1); - verify(() => mockHostService.checkpointSync()).called(1); + verify(() => hostApi.checkpointSync()).called(1); }); test( 'calls removeAlbum for DB albums not on device and checkpoints', () async { final dbAlbums = [LocalAlbumStub.album1, LocalAlbumStub.album2]; - when(() => mockAlbumMediaRepo.getAll()).thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + when(() => albumMediaRepo.getAll()).thenAnswer((_) async => []); + when(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .thenAnswer((_) async => dbAlbums); - await sut.fullSync(); - - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verify(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + verify(() => albumMediaRepo.getAll()).called(1); + verify(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) .called(1); - verify(() => mockLocalAlbumRepo.delete(dbAlbums.first.id)).called(1); - verify(() => mockLocalAlbumRepo.delete(dbAlbums[1].id)).called(1); - verify(() => mockHostService.checkpointSync()).called(1); + verify(() => localAlbumRepo.delete(dbAlbums.first.id)).called(1); + verify(() => localAlbumRepo.delete(dbAlbums[1].id)).called(1); + verify(() => hostApi.checkpointSync()).called(1); }, ); - test('calls updateAlbum for common albums and checkpoints', () async { - final commonAlbum = LocalAlbumStub.album1; - final deviceAlbums = [commonAlbum]; - final dbAlbums = [ - commonAlbum.copyWith( - updatedAt: commonAlbum.updatedAt.subtract(const Duration(days: 10)), - ), - ]; - when(() => mockAlbumMediaRepo.getAll()) - .thenAnswer((_) async => deviceAlbums); - when(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) - .thenAnswer((_) async => dbAlbums); + test( + 'calls updateAlbum for common albums which then calls fullDiff and checkpoints', + () async { + final dbAlbum = LocalAlbumStub.album1 + .copyWith(updatedAt: DateTime(2023, 12, 31), assetCount: 1); + final devAlbum = LocalAlbumStub.album1 + .copyWith(updatedAt: DateTime(2024, 1, 1), assetCount: 3); - final refreshedAlbum = - commonAlbum.copyWith(updatedAt: DateTime(2024, 1, 1), assetCount: 2); - when(() => mockAlbumMediaRepo.refresh(commonAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - when( - () => mockAlbumMediaRepo.getAssetsForAlbum( - commonAlbum.id, - updateTimeCond: any(named: 'updateTimeCond'), - ), - ).thenAnswer((_) async => [LocalAssetStub.image2]); - - await sut.fullSync(); - - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verify(() => mockLocalAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) - .called(1); - verify(() => mockAlbumMediaRepo.refresh(commonAlbum.id)).called(1); - verify( - () => mockAlbumMediaRepo.getAssetsForAlbum( - commonAlbum.id, - updateTimeCond: any(named: 'updateTimeCond'), - ), - ).called(1); - verify( - () => mockLocalAlbumRepo.upsert( - any( - that: predicate( - (a) => a.id == commonAlbum.id && a.assetCount == 2, - ), + when(() => albumMediaRepo.getAll()).thenAnswer((_) async => [devAlbum]); + when(() => localAlbumRepo.getAll(sortBy: SortLocalAlbumsBy.id)) + .thenAnswer((_) async => [dbAlbum]); + when( + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, + updateTimeCond: any(named: 'updateTimeCond'), ), - toUpsert: [LocalAssetStub.image2], - ), - ).called(1); - verify(() => mockHostService.checkpointSync()).called(1); - }); + ).thenAnswer((_) async => [LocalAssetStub.image2]); + + final devAssets = [LocalAssetStub.image1, LocalAssetStub.image2]; + final dbAssets = [LocalAssetStub.image1, LocalAssetStub.video1]; + + when(() => albumMediaRepo.getAssetsForAlbum(devAlbum.id)) + .thenAnswer((_) async => devAssets); + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + .thenAnswer((_) async => dbAssets); + + await sut.fullSync(); + + verify( + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, + updateTimeCond: any(named: 'updateTimeCond'), + ), + ).called(2); + verify(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); + verify( + () => localAlbumRepo.upsert( + any( + that: predicate( + (a) => + a.id == LocalAlbumStub.album1.id && + a.assetCount == devAlbum.assetCount && + a.updatedAt == devAlbum.updatedAt && + a.backupSelection == dbAlbum.backupSelection, + ), + ), + toUpsert: [LocalAssetStub.image2], + toDelete: [LocalAssetStub.video1.id], + ), + ).called(1); + verify(() => hostApi.checkpointSync()).called(1); + }, + ); test( 'handles repository errors gracefully and does not checkpoint', () async { - when(() => mockAlbumMediaRepo.getAll()) - .thenThrow(Exception("Repo error")); - + when(() => albumMediaRepo.getAll()).thenThrow(Exception("Repo error")); await sut.fullSync(); - - verify(() => mockAlbumMediaRepo.getAll()).called(1); - verifyNever( - () => mockLocalAlbumRepo.getAll(sortBy: any(named: 'sortBy')), - ); - verifyNever(() => mockHostService.checkpointSync()); + verify(() => albumMediaRepo.getAll()).called(1); + verifyNever(() => localAlbumRepo.getAll(sortBy: any(named: 'sortBy'))); + verifyNever(() => hostApi.checkpointSync()); }, ); }); group('addAlbum', () { - test('refreshes, gets assets, and updates for non-empty album', () async { - final newAlbum = LocalAlbumStub.album1.copyWith(assetCount: 0); - final refreshedAlbum = newAlbum.copyWith(assetCount: 1); + test('gets assets and upserts for album with assetCount > 0', () async { + final newAlbum = LocalAlbumStub.album1.copyWith(assetCount: 1); final assets = [LocalAssetStub.image1]; - - when(() => mockAlbumMediaRepo.refresh(newAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - when(() => mockAlbumMediaRepo.getAssetsForAlbum(newAlbum.id)) + when(() => albumMediaRepo.getAssetsForAlbum(newAlbum.id)) .thenAnswer((_) async => assets); - await sut.addAlbum(newAlbum); - - verify(() => mockAlbumMediaRepo.refresh(newAlbum.id)).called(1); - verify(() => mockAlbumMediaRepo.getAssetsForAlbum(newAlbum.id)).called(1); - verify(() => mockLocalAlbumRepo.upsert(refreshedAlbum, toUpsert: assets)) - .called(1); + verify(() => albumMediaRepo.getAssetsForAlbum(newAlbum.id)).called(1); + verify( + () => localAlbumRepo.upsert(newAlbum, toUpsert: assets, toDelete: []), + ).called(1); }); - test('refreshes, skips assets, and updates for empty album', () async { - final newAlbum = LocalAlbumStub.album1.copyWith(assetCount: 0); - final refreshedAlbum = newAlbum.copyWith(assetCount: 0); - - when(() => mockAlbumMediaRepo.refresh(newAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - - await sut.addAlbum(newAlbum); - - verify(() => mockAlbumMediaRepo.refresh(newAlbum.id)).called(1); - verifyNever(() => mockAlbumMediaRepo.getAssetsForAlbum(newAlbum.id)); - verify(() => mockLocalAlbumRepo.upsert(refreshedAlbum, toUpsert: [])) - .called(1); - }); + test( + 'skips getting assets and upserts with empty list for album with assetCount = 0', + () async { + final newAlbum = LocalAlbumStub.album1.copyWith(assetCount: 0); + await sut.addAlbum(newAlbum); + verifyNever(() => albumMediaRepo.getAssetsForAlbum(newAlbum.id)); + verify( + () => localAlbumRepo.upsert(newAlbum, toUpsert: [], toDelete: []), + ).called(1); + }, + ); }); group('removeAlbum', () { test('calls localAlbumRepository.delete', () async { - final albumToDelete = LocalAlbumStub.album1; - await sut.removeAlbum(albumToDelete); - verify(() => mockLocalAlbumRepo.delete(albumToDelete.id)).called(1); + final album = LocalAlbumStub.album1; + await sut.removeAlbum(album); + verify(() => localAlbumRepo.delete(album.id)).called(1); }); }); @@ -452,17 +406,11 @@ void main() { final dbAlbum = LocalAlbumStub.album1 .copyWith(updatedAt: DateTime(2024, 1, 1), assetCount: 1); - test('returns early if refresh shows no changes', () async { - final refreshedAlbum = dbAlbum; - when(() => mockAlbumMediaRepo.refresh(dbAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - - final result = await sut.updateAlbum(dbAlbum, LocalAlbumStub.album2); - + test('returns early (false) if albums are considered equal', () async { + final result = await sut.updateAlbum(dbAlbum, dbAlbum); expect(result, false); - verify(() => mockAlbumMediaRepo.refresh(dbAlbum.id)).called(1); verifyNever( - () => mockAlbumMediaRepo.getAssetsForAlbum( + () => albumMediaRepo.getAssetsForAlbum( any(), updateTimeCond: any(named: 'updateTimeCond'), ), @@ -470,76 +418,82 @@ void main() { }); test('calls checkAddition and returns true if it succeeds', () async { - final refreshedAlbum = + final updatedAlbum = dbAlbum.copyWith(updatedAt: DateTime(2024, 1, 2), assetCount: 2); - when(() => mockAlbumMediaRepo.refresh(dbAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - - final newAsset = LocalAssetStub.image2.copyWith(id: "new_asset"); + final newAsset = LocalAssetStub.image2; when( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + updatedAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).thenAnswer((_) async => [newAsset]); - final result = await sut.updateAlbum(dbAlbum, LocalAlbumStub.album2); + final result = await sut.updateAlbum(dbAlbum, updatedAlbum); expect(result, isTrue); - verify(() => mockAlbumMediaRepo.refresh(dbAlbum.id)).called(1); verify( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + updatedAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( - (a) => a.id == dbAlbum.id && a.assetCount == 2, + (a) => + a.id == dbAlbum.id && + a.assetCount == updatedAlbum.assetCount && + a.updatedAt == updatedAlbum.updatedAt && + a.backupSelection == dbAlbum.backupSelection, ), ), toUpsert: [newAsset], + toDelete: any(named: 'toDelete', that: isEmpty), ), ).called(1); }); test( - 'calls fullDiff and returns true if checkAddition returns false', + 'calls fullDiff and returns true if _albumsEqual is false and checkAddition returns false', () async { - final refreshedAlbum = - dbAlbum.copyWith(updatedAt: DateTime(2024, 1, 2), assetCount: 0); - when(() => mockAlbumMediaRepo.refresh(dbAlbum.id)) - .thenAnswer((_) async => refreshedAlbum); - - when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) + final updatedAlbum = dbAlbum.copyWith( + name: "Changed Name", + updatedAt: DateTime(2024, 1, 2), + assetCount: 0, + ); + final dbAssets = [ + LocalAssetStub.image1.copyWith(id: "${dbAlbum.id}_asset1"), + ]; + when(() => albumMediaRepo.getAssetsForAlbum(updatedAlbum.id)) .thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) - .thenAnswer((_) async => [LocalAssetStub.image1]); + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + .thenAnswer((_) async => dbAssets); - final result = await sut.updateAlbum(dbAlbum, LocalAlbumStub.album2); + final result = await sut.updateAlbum(dbAlbum, updatedAlbum); expect(result, isTrue); - verify(() => mockAlbumMediaRepo.refresh(dbAlbum.id)).called(1); verifyNever( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + updatedAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ); - - verifyNever(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)); - verify(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) - .called(1); + verify(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( - (a) => a.id == dbAlbum.id && a.assetCount == 0, + (a) => + a.id == dbAlbum.id && + a.assetCount == updatedAlbum.assetCount && + a.updatedAt == updatedAlbum.updatedAt && + a.name == updatedAlbum.name && + a.backupSelection == dbAlbum.backupSelection, ), ), - toDelete: [LocalAssetStub.image1.id], + toUpsert: [], + toDelete: [dbAssets.first.id], ), ).called(1); }, @@ -549,92 +503,112 @@ void main() { group('checkAddition', () { final dbAlbum = LocalAlbumStub.album1 .copyWith(updatedAt: DateTime(2024, 1, 1, 10, 0, 0), assetCount: 1); - final refreshedAlbum = dbAlbum.copyWith( - updatedAt: DateTime(2024, 1, 1, 11, 0, 0), - assetCount: 2, - ); + final devAlbumBase = + dbAlbum.copyWith(updatedAt: DateTime(2024, 1, 1, 11, 0, 0)); test('returns true and updates assets/metadata on success', () async { - final newAsset = LocalAssetStub.image2 - .copyWith(id: "asset2", createdAt: DateTime(2024, 1, 1, 10, 30, 0)); + final devAlbum = devAlbumBase.copyWith(assetCount: 2); + final newAsset = LocalAssetStub.image2; when( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).thenAnswer((_) async => [newAsset]); - final result = await sut.checkAddition(dbAlbum, refreshedAlbum); + final result = await sut.checkAddition(dbAlbum, devAlbum); expect(result, isTrue); verify( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( (a) => a.id == dbAlbum.id && - a.assetCount == 2 && - a.updatedAt == refreshedAlbum.updatedAt, + a.assetCount == devAlbum.assetCount && + a.updatedAt == devAlbum.updatedAt && + a.backupSelection == dbAlbum.backupSelection, ), ), toUpsert: [newAsset], - ), - ).called(1); - }); - - test('returns false if assetCount decreased', () async { - final decreasedCountAlbum = refreshedAlbum.copyWith(assetCount: 0); - final result = await sut.checkAddition(dbAlbum, decreasedCountAlbum); - expect(result, isFalse); - verifyNever( - () => mockAlbumMediaRepo.getAssetsForAlbum( - any(), - updateTimeCond: any(named: 'updateTimeCond'), - ), - ); - }); - - test('returns false if no new assets found by query', () async { - when( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, - updateTimeCond: any(named: 'updateTimeCond'), - ), - ).thenAnswer((_) async => []); - final result = await sut.checkAddition(dbAlbum, refreshedAlbum); - expect(result, isFalse); - verify( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, - updateTimeCond: any(named: 'updateTimeCond'), + toDelete: any(named: 'toDelete', that: isEmpty), ), ).called(1); }); test( - 'returns false if asset count mismatch after finding new assets (implies deletion)', + 'returns false if deviceAlbum.assetCount <= dbAlbum.assetCount', () async { - final newAsset = LocalAssetStub.image2.copyWith(id: "asset2"); + final result = await sut.checkAddition( + dbAlbum, + devAlbumBase.copyWith(assetCount: 1), + ); + expect(result, isFalse); + verifyNever( + () => albumMediaRepo.getAssetsForAlbum( + any(), + updateTimeCond: any(named: 'updateTimeCond'), + ), + ); + + final result2 = await sut.checkAddition( + dbAlbum, + devAlbumBase.copyWith(assetCount: 0), + ); + expect(result2, isFalse); + verifyNever( + () => albumMediaRepo.getAssetsForAlbum( + any(), + updateTimeCond: any(named: 'updateTimeCond'), + ), + ); + }, + ); + + test( + 'returns false if no new assets found by query (when assetCount increased)', + () async { + final devAlbum = devAlbumBase.copyWith(assetCount: 2); when( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, + updateTimeCond: any(named: 'updateTimeCond'), + ), + ).thenAnswer((_) async => []); + final result = await sut.checkAddition(dbAlbum, devAlbum); + expect(result, isFalse); + verify( + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, + updateTimeCond: any(named: 'updateTimeCond'), + ), + ).called(1); + }, + ); + + test( + 'returns false if asset count mismatch after finding new assets', + () async { + final devAlbum = devAlbumBase.copyWith(assetCount: 3); + final newAsset = + LocalAssetStub.image2.copyWith(id: "asset2_for_mismatch"); + when( + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).thenAnswer((_) async => [newAsset]); - // dbAlbum.assetCount = 1, newAssets.length = 1. Expected refreshedAlbum.assetCount = 2 - // But we set it to 3, indicating a mismatch. - final mismatchedCountAlbum = refreshedAlbum.copyWith(assetCount: 3); - final result = await sut.checkAddition(dbAlbum, mismatchedCountAlbum); + final result = await sut.checkAddition(dbAlbum, devAlbum); expect(result, isFalse); verify( - () => mockAlbumMediaRepo.getAssetsForAlbum( - dbAlbum.id, + () => albumMediaRepo.getAssetsForAlbum( + devAlbum.id, updateTimeCond: any(named: 'updateTimeCond'), ), ).called(1); @@ -643,105 +617,108 @@ void main() { }); group('fullDiff', () { - final dbAlbum = LocalAlbumStub.album1 + final dbAlbumBase = LocalAlbumStub.album1 .copyWith(updatedAt: DateTime(2024, 1, 1), assetCount: 2); - final refreshedAlbum = dbAlbum.copyWith( - updatedAt: DateTime(2024, 1, 2), - assetCount: 2, - ); - - final dbAsset1 = LocalAssetStub.image1.copyWith(id: "asset1"); - final dbAsset2 = LocalAssetStub.image2.copyWith(id: "asset2"); - final deviceAsset1Updated = - LocalAssetStub.image1.copyWith(id: "asset1", updatedAt: DateTime(2025)); - final deviceAsset3New = LocalAssetStub.video1.copyWith(id: "asset3"); + final devAlbumBase = dbAlbumBase.copyWith(updatedAt: DateTime(2024, 1, 2)); + final dbAsset1 = LocalAssetStub.image1; + final dbAsset2 = LocalAssetStub.image2; + final devAsset1Updated = dbAsset1.copyWith(updatedAt: DateTime(2025)); + final devAsset3New = LocalAssetStub.video1; test('handles empty device album -> deletes all DB assets', () async { - final emptyRefreshedAlbum = refreshedAlbum.copyWith(assetCount: 0); - when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) + final dbAlbum = dbAlbumBase.copyWith(); + final devAlbumEmpty = devAlbumBase.copyWith(assetCount: 0); + when(() => albumMediaRepo.getAssetsForAlbum(devAlbumEmpty.id)) .thenAnswer((_) async => []); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)) .thenAnswer((_) async => [dbAsset1, dbAsset2]); - final result = await sut.fullDiff(dbAlbum, emptyRefreshedAlbum); + final result = await sut.fullDiff(dbAlbum, devAlbumEmpty); expect(result, isTrue); - verify(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); + verifyNever(() => albumMediaRepo.getAssetsForAlbum(devAlbumEmpty.id)); + verify(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( - (a) => a.id == dbAlbum.id && a.assetCount == 0, + (a) => + a.id == dbAlbum.id && + a.assetCount == devAlbumEmpty.assetCount && + a.updatedAt == devAlbumEmpty.updatedAt && + a.backupSelection == dbAlbum.backupSelection, ), ), - toDelete: ["asset1", "asset2"], + toUpsert: [], + toDelete: [dbAsset1.id, dbAsset2.id], ), ).called(1); }); test('handles empty DB album -> adds all device assets', () async { - final emptyDbAlbum = dbAlbum.copyWith(assetCount: 0); - final deviceAssets = [deviceAsset1Updated, deviceAsset3New]; - final refreshedWithAssets = - refreshedAlbum.copyWith(assetCount: deviceAssets.length); - - when(() => mockAlbumMediaRepo.getAssetsForAlbum(emptyDbAlbum.id)) - .thenAnswer((_) async => deviceAssets); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(emptyDbAlbum.id)) + final dbAlbumEmpty = dbAlbumBase.copyWith(assetCount: 0); + final devAssets = [devAsset1Updated, devAsset3New]; + final devAlbumWithAssets = + devAlbumBase.copyWith(assetCount: devAssets.length); + when(() => albumMediaRepo.getAssetsForAlbum(devAlbumWithAssets.id)) + .thenAnswer((_) async => devAssets); + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbumEmpty.id)) .thenAnswer((_) async => []); - final result = await sut.fullDiff(emptyDbAlbum, refreshedWithAssets); + final result = await sut.fullDiff(dbAlbumEmpty, devAlbumWithAssets); expect(result, isTrue); - verify(() => mockAlbumMediaRepo.getAssetsForAlbum(emptyDbAlbum.id)) + verify(() => albumMediaRepo.getAssetsForAlbum(devAlbumWithAssets.id)) .called(1); + verifyNever(() => localAlbumRepo.getAssetsForAlbum(dbAlbumEmpty.id)); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( (a) => - a.id == emptyDbAlbum.id && - a.assetCount == deviceAssets.length, + a.id == dbAlbumEmpty.id && + a.assetCount == devAlbumWithAssets.assetCount && + a.updatedAt == devAlbumWithAssets.updatedAt && + a.backupSelection == dbAlbumEmpty.backupSelection, ), ), - toUpsert: - any(named: 'toUpsert', that: containsAllInOrder(deviceAssets)), + toUpsert: any(named: 'toUpsert', that: containsAllInOrder(devAssets)), + toDelete: [], ), ).called(1); }); test('handles mix of additions, updates, and deletions', () async { - final currentDeviceAssets = [deviceAsset1Updated, deviceAsset3New]; - final currentDbAssets = [dbAsset1, dbAsset2]; + final dbAlbum = dbAlbumBase.copyWith(assetCount: 2); + final devAssets = [devAsset1Updated, devAsset3New]; + final devAlbum = devAlbumBase.copyWith(assetCount: devAssets.length); + final dbAssetsInRepo = [dbAsset1, dbAsset2]; + when(() => albumMediaRepo.getAssetsForAlbum(devAlbum.id)) + .thenAnswer((_) async => devAssets); + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + .thenAnswer((_) async => dbAssetsInRepo); - final currentRefreshedAlbum = - refreshedAlbum.copyWith(assetCount: currentDeviceAssets.length); - - when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) - .thenAnswer((_) async => currentDeviceAssets); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) - .thenAnswer((_) async => currentDbAssets); - - final result = await sut.fullDiff(dbAlbum, currentRefreshedAlbum); + final result = await sut.fullDiff(dbAlbum, devAlbum); expect(result, isTrue); - verify(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)).called(1); - verify(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); - + verify(() => albumMediaRepo.getAssetsForAlbum(devAlbum.id)).called(1); + verify(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( (a) => a.id == dbAlbum.id && - a.assetCount == currentDeviceAssets.length, + a.assetCount == devAlbum.assetCount && + a.updatedAt == devAlbum.updatedAt && + a.backupSelection == dbAlbum.backupSelection, ), ), toUpsert: any( named: 'toUpsert', - that: containsAllInOrder([deviceAsset1Updated, deviceAsset3New]), + that: containsAllInOrder([devAsset1Updated, devAsset3New]), ), - toDelete: ["asset2"], + toDelete: [dbAsset2.id], ), ).called(1); }); @@ -749,33 +726,33 @@ void main() { test( 'handles identical assets (only metadata update if album changed)', () async { - final dbAssets = [dbAsset1, dbAsset2]; - final deviceAssets = [dbAsset1, dbAsset2]; + final dbAlbum = dbAlbumBase.copyWith(assetCount: 2); + final devAlbumMetaChg = + devAlbumBase.copyWith(updatedAt: DateTime(2025), assetCount: 2); + final commonAssets = [ + dbAsset1, + dbAsset2.copyWith(updatedAt: dbAsset1.updatedAt), + ]; + when(() => albumMediaRepo.getAssetsForAlbum(devAlbumMetaChg.id)) + .thenAnswer((_) async => commonAssets); + when(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + .thenAnswer((_) async => commonAssets); - final changedRefreshedAlbum = refreshedAlbum.copyWith( - updatedAt: DateTime(2025), - assetCount: deviceAssets.length, - ); - - when(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) - .thenAnswer((_) async => deviceAssets); - when(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) - .thenAnswer((_) async => dbAssets); - - final result = await sut.fullDiff(dbAlbum, changedRefreshedAlbum); + final result = await sut.fullDiff(dbAlbum, devAlbumMetaChg); expect(result, isTrue); - verify(() => mockAlbumMediaRepo.getAssetsForAlbum(dbAlbum.id)) - .called(1); - verify(() => mockLocalAlbumRepo.getAssetsForAlbum(dbAlbum.id)) + verify(() => albumMediaRepo.getAssetsForAlbum(devAlbumMetaChg.id)) .called(1); + verify(() => localAlbumRepo.getAssetsForAlbum(dbAlbum.id)).called(1); verify( - () => mockLocalAlbumRepo.upsert( + () => localAlbumRepo.upsert( any( that: predicate( (a) => a.id == dbAlbum.id && - a.updatedAt == changedRefreshedAlbum.updatedAt, + a.assetCount == devAlbumMetaChg.assetCount && + a.updatedAt == devAlbumMetaChg.updatedAt && + a.backupSelection == dbAlbum.backupSelection, ), ), toUpsert: [],