Files
immich/mobile/test/services/action.service_test.dart
Peter Ombodi 8ed81ac3e1 feat(mobile): do not restore locally deleted assets during trash sync (Android) (#24218)
* feat(trash_sync): do not restore assets deleted locally only
small fixes

* feat(trash_sync): revert tag name

* feat(trash_sync): resolve merge conflicts

* refactor(trash_sync): consolidate local asset deletion logic

* feat(mobile): Add TrashOrigin enum
Replace isRestorable to sourse
change related logic in repo

* feat(mobile): fix format

* fix(mobile): fix restoration scope

* fix(mobile): Add coverage for ActionService deleteLocal paths
Update LocalSyncService tests
Set default value for source column

* fix(mobile): db - require trash origin and update drift schema

---------

Co-authored-by: Peter Ombodi <peter.ombodi@gmail.com>
2026-01-12 21:46:36 +05:30

119 lines
4.4 KiB
Dart

import 'package:drift/drift.dart' as drift;
import 'package:drift/native.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:immich_mobile/repositories/download.repository.dart';
import 'package:immich_mobile/services/action.service.dart';
import 'package:mocktail/mocktail.dart';
import '../infrastructure/repository.mock.dart';
import '../repository.mocks.dart';
class MockDownloadRepository extends Mock implements DownloadRepository {}
void main() {
late ActionService sut;
late MockAssetApiRepository assetApiRepository;
late MockRemoteAssetRepository remoteAssetRepository;
late MockDriftLocalAssetRepository localAssetRepository;
late MockDriftAlbumApiRepository albumApiRepository;
late MockRemoteAlbumRepository remoteAlbumRepository;
late MockTrashedLocalAssetRepository trashedLocalAssetRepository;
late MockAssetMediaRepository assetMediaRepository;
late MockDownloadRepository downloadRepository;
late Drift db;
setUpAll(() async {
TestWidgetsFlutterBinding.ensureInitialized();
debugDefaultTargetPlatformOverride = TargetPlatform.android;
db = Drift(drift.DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
await StoreService.init(storeRepository: DriftStoreRepository(db));
});
tearDownAll(() async {
debugDefaultTargetPlatformOverride = null;
await Store.clear();
await db.close();
});
setUp(() {
assetApiRepository = MockAssetApiRepository();
remoteAssetRepository = MockRemoteAssetRepository();
localAssetRepository = MockDriftLocalAssetRepository();
albumApiRepository = MockDriftAlbumApiRepository();
remoteAlbumRepository = MockRemoteAlbumRepository();
trashedLocalAssetRepository = MockTrashedLocalAssetRepository();
assetMediaRepository = MockAssetMediaRepository();
downloadRepository = MockDownloadRepository();
sut = ActionService(
assetApiRepository,
remoteAssetRepository,
localAssetRepository,
albumApiRepository,
remoteAlbumRepository,
trashedLocalAssetRepository,
assetMediaRepository,
downloadRepository,
);
});
tearDown(() async {
await Store.clear();
});
group('ActionService.deleteLocal', () {
test('routes deleted ids to trashed repository when Android trash handling is enabled', () async {
await Store.put(StoreKey.manageLocalMediaAndroid, true);
const ids = ['a', 'b'];
when(() => assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => ids);
when(() => trashedLocalAssetRepository.applyTrashedAssets(ids)).thenAnswer((_) async {});
final result = await sut.deleteLocal(ids);
expect(result, ids.length);
verify(() => assetMediaRepository.deleteAll(ids)).called(1);
verify(() => trashedLocalAssetRepository.applyTrashedAssets(ids)).called(1);
verifyNever(() => localAssetRepository.delete(any()));
});
test('deletes locally when Android trash handling is disabled', () async {
await Store.put(StoreKey.manageLocalMediaAndroid, false);
const ids = ['c'];
when(() => assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => ids);
when(() => localAssetRepository.delete(ids)).thenAnswer((_) async {});
final result = await sut.deleteLocal(ids);
expect(result, ids.length);
verify(() => assetMediaRepository.deleteAll(ids)).called(1);
verify(() => localAssetRepository.delete(ids)).called(1);
verifyNever(() => trashedLocalAssetRepository.applyTrashedAssets(any()));
});
test('short-circuits when nothing was deleted', () async {
await Store.put(StoreKey.manageLocalMediaAndroid, true);
const ids = ['x'];
when(() => assetMediaRepository.deleteAll(ids)).thenAnswer((_) async => <String>[]);
final result = await sut.deleteLocal(ids);
expect(result, 0);
verify(() => assetMediaRepository.deleteAll(ids)).called(1);
verifyNever(() => trashedLocalAssetRepository.applyTrashedAssets(any()));
verifyNever(() => localAssetRepository.delete(any()));
});
});
}