chore: bump line length to 120 (#20191)

This commit is contained in:
shenlong
2025-07-25 08:07:22 +05:30
committed by GitHub
parent 977c9b96ba
commit ad65e9011a
517 changed files with 4520 additions and 9514 deletions
@@ -50,8 +50,7 @@ void main() {
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer(
(_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)],
);
when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id))
.thenAnswer((_) async => []);
when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)).thenAnswer((_) async => []);
await sut.hashAssets();
@@ -64,12 +63,9 @@ void main() {
test('skips assets without files', () async {
final album = LocalAlbumStub.recent;
final asset = LocalAssetStub.image1;
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id))
.thenAnswer((_) async => null);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => null);
await sut.hashAssets();
@@ -85,12 +81,9 @@ void main() {
when(() => mockFile.length()).thenAnswer((_) async => 1000);
when(() => mockFile.path).thenReturn('image-path');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id))
.thenAnswer((_) async => mockFile);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile);
when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer(
(_) async => [hash],
);
@@ -98,9 +91,7 @@ void main() {
await sut.hashAssets();
verify(() => mockNativeApi.hashPaths(['image-path'])).called(1);
final captured = verify(() => mockAssetRepo.updateHashes(captureAny()))
.captured
.first as List<LocalAsset>;
final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List<LocalAsset>;
expect(captured.length, 1);
expect(captured[0].checksum, base64.encode(hash));
});
@@ -112,21 +103,15 @@ void main() {
when(() => mockFile.length()).thenAnswer((_) async => 1000);
when(() => mockFile.path).thenReturn('image-path');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id))
.thenAnswer((_) async => mockFile);
when(() => mockNativeApi.hashPaths(['image-path']))
.thenAnswer((_) async => [null]);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile);
when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [null]);
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
await sut.hashAssets();
final captured = verify(() => mockAssetRepo.updateHashes(captureAny()))
.captured
.first as List<LocalAsset>;
final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List<LocalAsset>;
expect(captured.length, 0);
});
@@ -137,23 +122,17 @@ void main() {
when(() => mockFile.length()).thenAnswer((_) async => 1000);
when(() => mockFile.path).thenReturn('image-path');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id))
.thenAnswer((_) async => mockFile);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]);
when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile);
final invalidHash = Uint8List.fromList([1, 2, 3]);
when(() => mockNativeApi.hashPaths(['image-path']))
.thenAnswer((_) async => [invalidHash]);
when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [invalidHash]);
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
await sut.hashAssets();
final captured = verify(() => mockAssetRepo.updateHashes(captureAny()))
.captured
.first as List<LocalAsset>;
final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List<LocalAsset>;
expect(captured.length, 0);
});
@@ -176,18 +155,13 @@ void main() {
when(() => mockFile2.length()).thenAnswer((_) async => 100);
when(() => mockFile2.path).thenReturn('path-2');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id))
.thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id))
.thenAnswer((_) async => mockFile2);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2);
final hash = Uint8List.fromList(List.generate(20, (i) => i));
when(() => mockNativeApi.hashPaths(any()))
.thenAnswer((_) async => [hash]);
when(() => mockNativeApi.hashPaths(any())).thenAnswer((_) async => [hash]);
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
await sut.hashAssets();
@@ -216,18 +190,13 @@ void main() {
when(() => mockFile2.length()).thenAnswer((_) async => 100);
when(() => mockFile2.path).thenReturn('path-2');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id))
.thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id))
.thenAnswer((_) async => mockFile2);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2);
final hash = Uint8List.fromList(List.generate(20, (i) => i));
when(() => mockNativeApi.hashPaths(any()))
.thenAnswer((_) async => [hash]);
when(() => mockNativeApi.hashPaths(any())).thenAnswer((_) async => [hash]);
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
await sut.hashAssets();
@@ -248,25 +217,18 @@ void main() {
when(() => mockFile2.length()).thenAnswer((_) async => 100);
when(() => mockFile2.path).thenReturn('path-2');
when(() => mockAlbumRepo.getAll(sortBy: sortBy))
.thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id))
.thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id))
.thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id))
.thenAnswer((_) async => mockFile2);
when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]);
when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]);
when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1);
when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2);
final validHash = Uint8List.fromList(List.generate(20, (i) => i));
when(() => mockNativeApi.hashPaths(['path-1', 'path-2']))
.thenAnswer((_) async => [validHash, null]);
when(() => mockNativeApi.hashPaths(['path-1', 'path-2'])).thenAnswer((_) async => [validHash, null]);
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
await sut.hashAssets();
final captured = verify(() => mockAssetRepo.updateHashes(captureAny()))
.captured
.first as List<LocalAsset>;
final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List<LocalAsset>;
expect(captured.length, 1);
expect(captured.first.id, asset1.id);
});
@@ -37,10 +37,8 @@ void main() {
registerFallbackValue(_kInfoLog);
when(() => mockLogRepo.truncate(limit: any(named: 'limit')))
.thenAnswer((_) async => {});
when(() => mockStoreRepo.tryGet<int>(StoreKey.logLevel))
.thenAnswer((_) async => LogLevel.fine.index);
when(() => mockLogRepo.truncate(limit: any(named: 'limit'))).thenAnswer((_) async => {});
when(() => mockStoreRepo.tryGet<int>(StoreKey.logLevel)).thenAnswer((_) async => LogLevel.fine.index);
when(() => mockLogRepo.getAll()).thenAnswer((_) async => []);
when(() => mockLogRepo.insert(any())).thenAnswer((_) async => true);
when(() => mockLogRepo.insertAll(any())).thenAnswer((_) async => true);
@@ -57,10 +55,7 @@ void main() {
group("Log Service Init:", () {
test('Truncates the existing logs on init', () {
final limit =
verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit')))
.captured
.firstOrNull as int?;
final limit = verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit'))).captured.firstOrNull as int?;
expect(limit, kLogTruncateLimit);
});
@@ -72,8 +67,7 @@ void main() {
group("Log Service Set Level:", () {
setUp(() async {
when(() => mockStoreRepo.insert<int>(StoreKey.logLevel, any()))
.thenAnswer((_) async => true);
when(() => mockStoreRepo.insert<int>(StoreKey.logLevel, any())).thenAnswer((_) async => true);
await sut.setLogLevel(LogLevel.shout);
});
@@ -86,8 +86,7 @@ void main() {
group('Store Service put:', () {
setUp(() {
when(() => mockStoreRepo.insert<String>(any<StoreKey<String>>(), any()))
.thenAnswer((_) async => true);
when(() => mockStoreRepo.insert<String>(any<StoreKey<String>>(), any())).thenAnswer((_) async => true);
});
test('Skip insert when value is not modified', () async {
@@ -101,8 +100,7 @@ void main() {
final newAccessToken = _kAccessToken.toUpperCase();
await sut.put(StoreKey.accessToken, newAccessToken);
verify(
() =>
mockStoreRepo.insert<String>(StoreKey.accessToken, newAccessToken),
() => mockStoreRepo.insert<String>(StoreKey.accessToken, newAccessToken),
).called(1);
expect(sut.tryGet(StoreKey.accessToken), newAccessToken);
});
@@ -113,8 +111,7 @@ void main() {
setUp(() {
valueController = StreamController<String?>.broadcast();
when(() => mockStoreRepo.watch<String>(any<StoreKey<String>>()))
.thenAnswer((_) => valueController.stream);
when(() => mockStoreRepo.watch<String>(any<StoreKey<String>>())).thenAnswer((_) => valueController.stream);
});
tearDown(() async {
@@ -143,14 +140,12 @@ void main() {
group('Store Service delete:', () {
setUp(() {
when(() => mockStoreRepo.delete<String>(any<StoreKey<String>>()))
.thenAnswer((_) async => true);
when(() => mockStoreRepo.delete<String>(any<StoreKey<String>>())).thenAnswer((_) async => true);
});
test('Removes the value from the DB', () async {
await sut.delete(StoreKey.accessToken);
verify(() => mockStoreRepo.delete<String>(StoreKey.accessToken))
.called(1);
verify(() => mockStoreRepo.delete<String>(StoreKey.accessToken)).called(1);
});
test('Removes the value from the cache', () async {
@@ -42,53 +42,41 @@ void main() {
when(() => mockAbortCallbackWrapper()).thenReturn(false);
when(() => mockSyncApiRepo.streamChanges(any()))
.thenAnswer((invocation) async {
when(() => mockSyncApiRepo.streamChanges(any())).thenAnswer((invocation) async {
handleEventsCallback = invocation.positionalArguments.first;
});
when(() => mockSyncApiRepo.ack(any())).thenAnswer((_) async => {});
when(() => mockSyncStreamRepo.updateUsersV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteUsersV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updatePartnerV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deletePartnerV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetsV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateUsersV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updatePartnerV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deletePartnerV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetsV1(any())).thenAnswer(successHandler);
when(
() => mockSyncStreamRepo.updateAssetsV1(
any(),
debugLabel: any(named: 'debugLabel'),
),
).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteAssetsV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteAssetsV1(any())).thenAnswer(successHandler);
when(
() => mockSyncStreamRepo.deleteAssetsV1(
any(),
debugLabel: any(named: 'debugLabel'),
),
).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetsExifV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetsExifV1(any())).thenAnswer(successHandler);
when(
() => mockSyncStreamRepo.updateAssetsExifV1(
any(),
debugLabel: any(named: 'debugLabel'),
),
).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateMemoriesV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteMemoriesV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteMemoriesV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())).thenAnswer(successHandler);
when(
() => mockSyncStreamRepo.updateStacksV1(
any(),
@@ -101,18 +89,12 @@ void main() {
debugLabel: any(named: 'debugLabel'),
),
).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateUserMetadatasV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updatePeopleV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deletePeopleV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetFacesV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteAssetFacesV1(any()))
.thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updatePeopleV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deletePeopleV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.updateAssetFacesV1(any())).thenAnswer(successHandler);
when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())).thenAnswer(successHandler);
sut = SyncStreamService(
syncApiRepository: mockSyncApiRepo,
@@ -221,8 +203,7 @@ void main() {
final processingCompleter = Completer<void>();
bool handler1Started = false;
when(() => mockSyncStreamRepo.deleteUsersV1(any()))
.thenAnswer((_) async {
when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async {
handler1Started = true;
return processingCompleter.future;
});
@@ -241,8 +222,7 @@ void main() {
SyncStreamStub.partnerDeleteV1,
];
final processingFuture =
handleEventsCallback(events, mockAbortCallbackWrapper.call);
final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call);
await pumpEventQueue();
expect(handler1Started, isTrue);
@@ -307,8 +287,7 @@ void main() {
});
test("handles memory sync failure gracefully", () async {
when(() => mockSyncStreamRepo.updateMemoriesV1(any()))
.thenThrow(Exception("Memory sync failed"));
when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenThrow(Exception("Memory sync failed"));
final events = [
SyncStreamStub.memoryV1,
@@ -339,8 +318,7 @@ void main() {
verify(() => mockSyncApiRepo.ack(["6"])).called(1);
});
test("processes memory create/update events with correct data types",
() async {
test("processes memory create/update events with correct data types", () async {
final events = [SyncStreamStub.memoryV1];
await simulateEvents(events);
@@ -29,10 +29,8 @@ void main() {
);
registerFallbackValue(UserStub.admin);
when(() => mockStoreService.get(StoreKey.currentUser))
.thenReturn(UserStub.admin);
when(() => mockStoreService.tryGet(StoreKey.currentUser))
.thenReturn(UserStub.admin);
when(() => mockStoreService.get(StoreKey.currentUser)).thenReturn(UserStub.admin);
when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(UserStub.admin);
});
group('getMyUser', () {
@@ -42,8 +40,7 @@ void main() {
});
test('should handle user not found scenario', () {
when(() => mockStoreService.get(StoreKey.currentUser))
.thenThrow(Exception('User not found'));
when(() => mockStoreService.get(StoreKey.currentUser)).thenThrow(Exception('User not found'));
expect(() => sut.getMyUser(), throwsA(isA<Exception>()));
});
@@ -56,8 +53,7 @@ void main() {
});
test('should return null if user not found', () {
when(() => mockStoreService.tryGet(StoreKey.currentUser))
.thenReturn(null);
when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(null);
final result = sut.tryGetMyUser();
expect(result, isNull);
});
@@ -65,15 +61,13 @@ void main() {
group('watchMyUser', () {
test('should return user stream from store', () {
when(() => mockStoreService.watch(StoreKey.currentUser))
.thenAnswer((_) => Stream.value(UserStub.admin));
when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => Stream.value(UserStub.admin));
final result = sut.watchMyUser();
expect(result, emits(UserStub.admin));
});
test('should return an empty stream if user not found', () {
when(() => mockStoreService.watch(StoreKey.currentUser))
.thenAnswer((_) => const Stream.empty());
when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => const Stream.empty());
final result = sut.watchMyUser();
expect(result, emitsInOrder([]));
});
@@ -81,16 +75,12 @@ void main() {
group('refreshMyUser', () {
test('should return user from api and store it', () async {
when(() => mockUserApiRepo.getMyUser())
.thenAnswer((_) async => UserStub.admin);
when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin))
.thenAnswer((_) async => true);
when(() => mockUserRepo.update(UserStub.admin))
.thenAnswer((_) async => UserStub.admin);
when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => UserStub.admin);
when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).thenAnswer((_) async => true);
when(() => mockUserRepo.update(UserStub.admin)).thenAnswer((_) async => UserStub.admin);
final result = await sut.refreshMyUser();
verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin))
.called(1);
verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).called(1);
verify(() => mockUserRepo.update(UserStub.admin)).called(1);
expect(result, UserStub.admin);
});
@@ -110,8 +100,7 @@ void main() {
group('createProfileImage', () {
test('should return profile image path', () async {
const profileImagePath = 'profile.jpg';
final updatedUser =
UserStub.admin.copyWith(profileImagePath: profileImagePath);
final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath);
when(
() => mockUserApiRepo.createProfileImage(
@@ -119,24 +108,19 @@ void main() {
data: Uint8List(0),
),
).thenAnswer((_) async => profileImagePath);
when(() => mockStoreService.put(StoreKey.currentUser, updatedUser))
.thenAnswer((_) async => true);
when(() => mockUserRepo.update(updatedUser))
.thenAnswer((_) async => UserStub.admin);
when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true);
when(() => mockUserRepo.update(updatedUser)).thenAnswer((_) async => UserStub.admin);
final result =
await sut.createProfileImage(profileImagePath, Uint8List(0));
final result = await sut.createProfileImage(profileImagePath, Uint8List(0));
verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser))
.called(1);
verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).called(1);
verify(() => mockUserRepo.update(updatedUser)).called(1);
expect(result, profileImagePath);
});
test('should return null if profile image creation fails', () async {
const profileImagePath = 'profile.jpg';
final updatedUser =
UserStub.admin.copyWith(profileImagePath: profileImagePath);
final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath);
when(
() => mockUserApiRepo.createProfileImage(
@@ -145,8 +129,7 @@ void main() {
),
).thenThrow(Exception('Failed to create profile image'));
final result =
await sut.createProfileImage(profileImagePath, Uint8List(0));
final result = await sut.createProfileImage(profileImagePath, Uint8List(0));
verifyNever(
() => mockStoreService.put(StoreKey.currentUser, updatedUser),
);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -23,8 +23,7 @@ void main() {
group('getAll', () {
test('sorts albums by backupSelection & isIosSharedAlbum', () async {
final localAlbumRepo =
mediumFactory.getRepository<DriftLocalAlbumRepository>();
final localAlbumRepo = mediumFactory.getRepository<DriftLocalAlbumRepository>();
await localAlbumRepo.upsert(
mediumFactory.localAlbum(
id: '1',
@@ -66,8 +66,7 @@ void main() {
});
test('converts datetime', () async {
DateTime? backupFailedSince =
await sut.tryGet(StoreKey.backupFailedSince);
DateTime? backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince);
expect(backupFailedSince, isNull);
await sut.insert(StoreKey.backupFailedSince, _kTestBackupFailed);
backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince);
@@ -39,23 +39,19 @@ void main() {
mockSyncApi = MockSyncApi();
mockHttpClient = MockHttpClient();
mockStreamedResponse = MockStreamedResponse();
responseStreamController =
StreamController<List<int>>.broadcast(sync: true);
responseStreamController = StreamController<List<int>>.broadcast(sync: true);
registerFallbackValue(FakeBaseRequest());
when(() => mockApiService.apiClient).thenReturn(mockApiClient);
when(() => mockApiService.syncApi).thenReturn(mockSyncApi);
when(() => mockApiClient.basePath).thenReturn('http://demo.immich.app/api');
when(() => mockApiService.applyToParams(any(), any()))
.thenAnswer((_) async => {});
when(() => mockApiService.applyToParams(any(), any())).thenAnswer((_) async => {});
// Mock HTTP client behavior
when(() => mockHttpClient.send(any()))
.thenAnswer((_) async => mockStreamedResponse);
when(() => mockHttpClient.send(any())).thenAnswer((_) async => mockStreamedResponse);
when(() => mockStreamedResponse.statusCode).thenReturn(200);
when(() => mockStreamedResponse.stream)
.thenAnswer((_) => http.ByteStream(responseStreamController.stream));
when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(responseStreamController.stream));
when(() => mockHttpClient.close()).thenAnswer((_) => {});
sut = SyncApiRepository(mockApiService);
@@ -270,8 +266,7 @@ void main() {
test('streamChanges throws ApiException on non-200 status code', () async {
when(() => mockStreamedResponse.statusCode).thenReturn(401);
final errorBodyController = StreamController<List<int>>(sync: true);
when(() => mockStreamedResponse.stream)
.thenAnswer((_) => http.ByteStream(errorBodyController.stream));
when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(errorBodyController.stream));
int onDataCallCount = 0;
@@ -16,16 +16,13 @@ class MockLogRepository extends Mock implements IsarLogRepository {}
class MockIsarUserRepository extends Mock implements IsarUserRepository {}
class MockDeviceAssetRepository extends Mock
implements IsarDeviceAssetRepository {}
class MockDeviceAssetRepository extends Mock implements IsarDeviceAssetRepository {}
class MockSyncStreamRepository extends Mock implements SyncStreamRepository {}
class MockLocalAlbumRepository extends Mock
implements DriftLocalAlbumRepository {}
class MockLocalAlbumRepository extends Mock implements DriftLocalAlbumRepository {}
class MockLocalAssetRepository extends Mock
implements DriftLocalAssetRepository {}
class MockLocalAssetRepository extends Mock implements DriftLocalAssetRepository {}
class MockStorageRepository extends Mock implements StorageRepository {}
+6 -12
View File
@@ -18,15 +18,12 @@ class MockHttpOverrides extends HttpOverrides {
// Request mocks
when(() => request.headers).thenAnswer((_) => headers);
when(() => request.close())
.thenAnswer((_) => Future<HttpClientResponse>.value(response));
when(() => request.close()).thenAnswer((_) => Future<HttpClientResponse>.value(response));
// Response mocks
when(() => response.statusCode).thenReturn(HttpStatus.ok);
when(() => response.compressionState)
.thenReturn(HttpClientResponseCompressionState.decompressed);
when(() => response.contentLength)
.thenAnswer((_) => kTransparentImage.length);
when(() => response.compressionState).thenReturn(HttpClientResponseCompressionState.decompressed);
when(() => response.contentLength).thenAnswer((_) => kTransparentImage.length);
when(
() => response.listen(
captureAny(),
@@ -35,18 +32,15 @@ class MockHttpOverrides extends HttpOverrides {
onError: captureAny(named: 'onError'),
),
).thenAnswer((invocation) {
final onData =
invocation.positionalArguments[0] as void Function(List<int>);
final onData = invocation.positionalArguments[0] as void Function(List<int>);
final onDone = invocation.namedArguments[#onDone] as void Function();
final onError = invocation.namedArguments[#onError] as void
Function(Object, [StackTrace]);
final onError = invocation.namedArguments[#onError] as void Function(Object, [StackTrace]);
final cancelOnError = invocation.namedArguments[#cancelOnError] as bool;
return Stream<List<int>>.fromIterable([kTransparentImage.toList()])
.listen(
return Stream<List<int>>.fromIterable([kTransparentImage.toList()]).listen(
onData,
onDone: onDone,
onError: onError,
@@ -198,8 +198,7 @@ void main() {
);
await tester.pumpAndSettle();
when(() => activityMock.addComment(any()))
.thenAnswer((_) => Future.value());
when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value());
final textField = find.byType(TextField);
await tester.enterText(textField, 'Test comment');
@@ -6,9 +6,7 @@ import 'package:mocktail/mocktail.dart';
class ActivityServiceMock extends Mock implements ActivityService {}
class MockAlbumActivity extends AlbumActivityInternal
with Mock
implements AlbumActivity {
class MockAlbumActivity extends AlbumActivityInternal with Mock implements AlbumActivity {
List<Activity>? initActivities;
MockAlbumActivity([this.initActivities]);
@@ -18,6 +16,4 @@ class MockAlbumActivity extends AlbumActivityInternal
}
}
class ActivityStatisticsMock extends ActivityStatisticsInternal
with Mock
implements ActivityStatistics {}
class ActivityStatisticsMock extends ActivityStatisticsInternal with Mock implements ActivityStatistics {}
@@ -58,8 +58,7 @@ void main() {
container = TestUtils.createContainer(
overrides: [
activityServiceProvider.overrideWith((ref) => activityMock),
activityStatisticsProvider('test-album', 'test-asset')
.overrideWith(() => activityStatisticsMock),
activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock),
],
);
@@ -90,8 +89,7 @@ void main() {
[
isA<AsyncData<List<Activity>>>(),
predicate(
(AsyncData<List<Activity>> ad) =>
ad.requireValue.every((e) => _activities.contains(e)),
(AsyncData<List<Activity>> ad) => ad.requireValue.every((e) => _activities.contains(e)),
),
],
),
@@ -172,8 +170,7 @@ void main() {
group('removeActivity()', () {
test('Like successfully removed', () async {
when(() => activityMock.removeActivity('3'))
.thenAnswer((_) async => true);
when(() => activityMock.removeActivity('3')).thenAnswer((_) async => true);
await container.read(provider.notifier).removeActivity('3');
@@ -192,8 +189,7 @@ void main() {
});
test('Remove Like failed', () async {
when(() => activityMock.removeActivity('3'))
.thenAnswer((_) async => false);
when(() => activityMock.removeActivity('3')).thenAnswer((_) async => false);
await container.read(provider.notifier).removeActivity('3');
@@ -206,8 +202,7 @@ void main() {
});
test('Comment successfully removed', () async {
when(() => activityMock.removeActivity('1'))
.thenAnswer((_) async => true);
when(() => activityMock.removeActivity('1')).thenAnswer((_) async => true);
await container.read(provider.notifier).removeActivity('1');
@@ -229,10 +224,8 @@ void main() {
container = TestUtils.createContainer(
overrides: [
activityServiceProvider.overrideWith((ref) => activityMock),
activityStatisticsProvider('test-album', 'test-asset')
.overrideWith(() => activityStatisticsMock),
activityStatisticsProvider('test-album')
.overrideWith(() => albumActivityStatisticsMock),
activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock),
activityStatisticsProvider('test-album').overrideWith(() => albumActivityStatisticsMock),
],
);
});
@@ -255,8 +248,7 @@ void main() {
comment: 'Test-Comment',
),
).thenAnswer((_) async => AsyncData(comment));
when(() => activityStatisticsMock.build('test-album', 'test-asset'))
.thenReturn(4);
when(() => activityStatisticsMock.build('test-album', 'test-asset')).thenReturn(4);
when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2);
await container.read(provider.notifier).addComment('Test-Comment');
@@ -296,8 +288,7 @@ void main() {
),
).thenAnswer((_) async => AsyncData(comment));
when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2);
when(() => activityMock.getAllActivities('test-album'))
.thenAnswer((_) async => [..._activities]);
when(() => activityMock.getAllActivities('test-album')).thenAnswer((_) async => [..._activities]);
final albumProvider = albumActivityProvider('test-album');
await container.read(albumProvider.notifier).addComment('Test-Comment');
@@ -44,8 +44,7 @@ void main() {
activityMock = MockAlbumActivity();
overrides = [
currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider),
albumActivityProvider(AlbumStub.twoAsset.remoteId!)
.overrideWith(() => activityMock),
albumActivityProvider(AlbumStub.twoAsset.remoteId!).overrideWith(() => activityMock),
];
});
@@ -152,8 +151,7 @@ void main() {
overrides: overrides,
);
when(() => activityMock.removeActivity(any()))
.thenAnswer((_) => Future.value());
when(() => activityMock.removeActivity(any())).thenAnswer((_) => Future.value());
final suffixIcon = find.byType(IconButton);
await tester.tap(suffixIcon);
@@ -57,8 +57,7 @@ void main() {
expect(find.byType(ListTile), findsOneWidget);
});
testWidgets('No trailing widget when activity assetId == null',
(tester) async {
testWidgets('No trailing widget when activity assetId == null', (tester) async {
await tester.pumpConsumerWidget(
ActivityTile(
Activity(
@@ -75,9 +74,7 @@ void main() {
expect(listTile.trailing, isNull);
});
testWidgets(
'Asset Thumbanil as trailing widget when activity assetId != null',
(tester) async {
testWidgets('Asset Thumbanil as trailing widget when activity assetId != null', (tester) async {
await tester.pumpConsumerWidget(
ActivityTile(
Activity(
@@ -176,8 +173,7 @@ void main() {
user: UserStub.admin,
);
testWidgets('Comment contains User Circle Avatar as leading',
(tester) async {
testWidgets('Comment contains User Circle Avatar as leading', (tester) async {
await tester.pumpConsumerWidget(
ActivityTile(activity),
overrides: overrides,
@@ -55,9 +55,7 @@ void main() {
expect(find.byType(ConfirmDialog), findsOneWidget);
});
testWidgets(
'Ok action in ConfirmDialog should call onDismiss with activityId',
(tester) async {
testWidgets('Ok action in ConfirmDialog should call onDismiss with activityId', (tester) async {
String? receivedActivityId;
await tester.pumpConsumerWidget(
DismissibleActivity(
+1 -3
View File
@@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:mocktail/mocktail.dart';
class MockCurrentAlbumProvider extends CurrentAlbum
with Mock
implements CurrentAlbumInternal {
class MockCurrentAlbumProvider extends CurrentAlbum with Mock implements CurrentAlbumInternal {
Album? initAlbum;
MockCurrentAlbumProvider([this.initAlbum]);
@@ -261,9 +261,7 @@ void main() {
});
test('Properly saves the correct store index of sort mode', () {
container
.read(albumSortByOptionsProvider.notifier)
.changeSortMode(AlbumSortMode.mostOldest);
container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest);
verify(
() => settingsMock.setSetting(
@@ -286,14 +284,10 @@ void main() {
);
// Created -> Most Oldest
container
.read(albumSortByOptionsProvider.notifier)
.changeSortMode(AlbumSortMode.mostOldest);
container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest);
// Most Oldest -> Title
container
.read(albumSortByOptionsProvider.notifier)
.changeSortMode(AlbumSortMode.title);
container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.title);
verifyInOrder([
() => listener.call(null, AlbumSortMode.created),
@@ -368,9 +362,7 @@ void main() {
container.read(albumSortOrderProvider.notifier).changeSortDirection(true);
// true -> false
container
.read(albumSortOrderProvider.notifier)
.changeSortDirection(false);
container.read(albumSortOrderProvider.notifier).changeSortDirection(false);
verifyInOrder([
() => listener.call(null, false),
@@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:mocktail/mocktail.dart';
class MockCurrentAssetProvider extends CurrentAssetInternal
with Mock
implements CurrentAsset {
class MockCurrentAssetProvider extends CurrentAssetInternal with Mock implements CurrentAsset {
Asset? initAsset;
MockCurrentAssetProvider([this.initAsset]);
@@ -76,8 +76,7 @@ void main() {
expect(dateTimeInUTC.timeZoneOffset, tz);
});
test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone',
() {
test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', () {
final createdAt = DateTime.parse("2023-01-27T14:00:00-0500");
final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530");
final e = makeExif(
@@ -98,13 +97,11 @@ void main() {
final createdAt = DateTime.parse("2023-01-27T14:00:00-0500");
final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530");
const location = "Asia/Hong_Kong";
final e =
makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location);
final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location);
final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e);
final (dt, tz) = a.getTZAdjustedTimeAndOffset();
final adjustedTime =
TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location));
final adjustedTime = TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location));
expect(adjustedTime, dt);
expect(adjustedTime.timeZoneOffset, tz);
});
@@ -118,8 +115,7 @@ void main() {
final (dt, tz) = a.getTZAdjustedTimeAndOffset();
final location = getLocation("Asia/Hong_Kong");
final offsetFromLocation =
Duration(milliseconds: location.currentTimeZone.offset);
final offsetFromLocation = Duration(milliseconds: location.currentTimeZone.offset);
final adjustedTime = dateTimeOriginal.toUtc().add(offsetFromLocation);
// Adds the offset to the actual time and returns the offset separately
@@ -25,24 +25,21 @@ void main() {
test('returns date range format for this year', () {
final startDate = DateTime(currentYear, 3, 23); // Mar 23
final endDate = DateTime(currentYear, 5, 31); // May 31
final result =
DateRangeFormatting.formatDateRange(startDate, endDate, null);
final result = DateRangeFormatting.formatDateRange(startDate, endDate, null);
expect(result, 'Mar 23 - May 31');
});
test('returns date range format for other year (same year)', () {
final startDate = DateTime(2023, 8, 28); // Aug 28
final endDate = DateTime(2023, 9, 30); // Sep 30
final result =
DateRangeFormatting.formatDateRange(startDate, endDate, null);
final result = DateRangeFormatting.formatDateRange(startDate, endDate, null);
expect(result, 'Aug 28 - Sep 30, 2023');
});
test('returns date range format over multiple years', () {
final startDate = DateTime(2021, 4, 17); // Apr 17, 2021
final endDate = DateTime(2022, 4, 9); // Apr 9, 2022
final result =
DateRangeFormatting.formatDateRange(startDate, endDate, null);
final result = DateRangeFormatting.formatDateRange(startDate, endDate, null);
expect(result, 'Apr 17, 2021 - Apr 9, 2022');
});
});
+1 -3
View File
@@ -3,9 +3,7 @@ import 'package:immich_mobile/models/map/map_state.model.dart';
import 'package:immich_mobile/providers/map/map_state.provider.dart';
import 'package:mocktail/mocktail.dart';
class MockMapStateNotifier extends Notifier<MapState>
with Mock
implements MapStateNotifier {
class MockMapStateNotifier extends Notifier<MapState> with Mock implements MapStateNotifier {
final MapState initState;
MockMapStateNotifier(this.initState);
@@ -38,8 +38,7 @@ void main() {
];
});
testWidgets("Return dark theme style when theme mode is dark",
(tester) async {
testWidgets("Return dark theme style when theme mode is dark", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOverride(
@@ -51,8 +50,7 @@ void main() {
overrides: overrides,
);
mapStateNotifier.state =
mapState.copyWith(darkStyleFetched: const AsyncData("dark"));
mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncData("dark"));
await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "dark");
});
@@ -76,8 +74,7 @@ void main() {
expect(mapStyle?.hasError, isTrue);
});
testWidgets("Return light theme style when theme mode is light",
(tester) async {
testWidgets("Return light theme style when theme mode is light", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOverride(
@@ -110,8 +107,7 @@ void main() {
overrides: overrides,
);
tester.binding.platformDispatcher.platformBrightnessTestValue =
Brightness.dark;
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
mapStateNotifier.state = mapState.copyWith(
themeMode: ThemeMode.system,
darkStyleFetched: const AsyncData("dark"),
@@ -121,8 +117,7 @@ void main() {
expect(mapStyle?.valueOrNull, "dark");
});
testWidgets("Return light theme style when system is light",
(tester) async {
testWidgets("Return light theme style when system is light", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOverride(
@@ -134,8 +129,7 @@ void main() {
overrides: overrides,
);
tester.binding.platformDispatcher.platformBrightnessTestValue =
Brightness.light;
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light;
mapStateNotifier.state = mapState.copyWith(
themeMode: ThemeMode.system,
lightStyleFetched: const AsyncData("light"),
@@ -145,8 +139,7 @@ void main() {
expect(mapStyle?.valueOrNull, "light");
});
testWidgets("Switches style when system brightness changes",
(tester) async {
testWidgets("Switches style when system brightness changes", (tester) async {
AsyncValue<String>? mapStyle;
await tester.pumpConsumerWidget(
MapThemeOverride(
@@ -158,8 +151,7 @@ void main() {
overrides: overrides,
);
tester.binding.platformDispatcher.platformBrightnessTestValue =
Brightness.light;
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light;
mapStateNotifier.state = mapState.copyWith(
themeMode: ThemeMode.system,
lightStyleFetched: const AsyncData("light"),
@@ -168,8 +160,7 @@ void main() {
await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "light");
tester.binding.platformDispatcher.platformBrightnessTestValue =
Brightness.dark;
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "dark");
});
+1 -3
View File
@@ -3,9 +3,7 @@ import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:mocktail/mocktail.dart';
class MockCurrentUserProvider extends StateNotifier<UserDto?>
with Mock
implements CurrentUserProvider {
class MockCurrentUserProvider extends StateNotifier<UserDto?> with Mock implements CurrentUserProvider {
MockCurrentUserProvider() : super(null);
@override
@@ -57,14 +57,11 @@ void main() {
final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository();
final MockIsarUserRepository userRepository = MockIsarUserRepository();
final MockETagRepository eTagRepository = MockETagRepository();
final MockAlbumMediaRepository albumMediaRepository =
MockAlbumMediaRepository();
final MockAlbumMediaRepository albumMediaRepository = MockAlbumMediaRepository();
final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository();
final MockAppSettingService appSettingService = MockAppSettingService();
final MockLocalFilesManagerRepository localFilesManagerRepository =
MockLocalFilesManagerRepository();
final MockPartnerApiRepository partnerApiRepository =
MockPartnerApiRepository();
final MockLocalFilesManagerRepository localFilesManagerRepository = MockLocalFilesManagerRepository();
final MockPartnerApiRepository partnerApiRepository = MockPartnerApiRepository();
final MockUserApiRepository userApiRepository = MockUserApiRepository();
final MockPartnerRepository partnerRepository = MockPartnerRepository();
final MockUserService userService = MockUserService();
@@ -115,13 +112,11 @@ void main() {
userApiRepository,
);
when(() => userService.getMyUser()).thenReturn(owner);
when(() => eTagRepository.get(owner.id))
.thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now()));
when(() => eTagRepository.get(owner.id)).thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now()));
when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {});
when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {});
when(() => partnerRepository.getSharedWith()).thenAnswer((_) async => []);
when(() => userRepository.getAll(sortBy: SortUserBy.id))
.thenAnswer((_) async => [owner]);
when(() => userRepository.getAll(sortBy: SortUserBy.id)).thenAnswer((_) async => [owner]);
when(() => userRepository.getAll()).thenAnswer((_) async => [owner]);
when(
() => assetRepository.getAll(
@@ -133,8 +128,7 @@ void main() {
.thenAnswer((_) async => [initialAssets[3], null, null]);
when(() => assetRepository.updateAll(any())).thenAnswer((_) async => []);
when(() => assetRepository.deleteByIds(any())).thenAnswer((_) async {});
when(() => exifInfoRepository.updateAll(any()))
.thenAnswer((_) async => []);
when(() => exifInfoRepository.updateAll(any())).thenAnswer((_) async => []);
when(() => assetRepository.transaction<void>(any())).thenAnswer(
(call) => (call.positionalArguments.first as Function).call(),
);
@@ -143,8 +137,7 @@ void main() {
);
when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]);
registerFallbackValue(Direction.sharedByMe);
when(() => partnerApiRepository.getAll(any()))
.thenAnswer((_) async => []);
when(() => partnerApiRepository.getAll(any())).thenAnswer((_) async => []);
});
test('test inserting existing assets', () async {
final List<Asset> remoteAssets = [
@@ -178,8 +171,7 @@ void main() {
expect(c1, isTrue);
final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]);
verify(
() => assetRepository
.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]),
() => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]),
);
});
@@ -244,8 +236,7 @@ void main() {
return;
});
when(
() => assetRepository
.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged),
() => assetRepository.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged),
).thenAnswer((_) async => [initialAssets[2]]);
when(() => assetRepository.getAllByOwnerIdChecksum(any(), any()))
.thenAnswer((_) async => [initialAssets[0], null, null]); //afg
@@ -14,8 +14,7 @@ class _Counter {
}
void main() {
test('Executes the method immediately if no calls received previously',
() async {
test('Executes the method immediately if no calls received previously', () async {
var counter = _Counter();
final throttler = Throttler(interval: const Duration(milliseconds: 300));
throttler.run(() => counter.increment());
@@ -28,9 +28,7 @@ void main() {
expect(punycodeEncodeUrl(url), equals(expected));
});
test(
'should encode multi-segment Unicode host with multiple non-ASCII segments',
() {
test('should encode multi-segment Unicode host with multiple non-ASCII segments', () {
const url = 'https://bücher.münchen';
const expected = 'https://xn--bcher-kva.xn--mnchen-3ya';
expect(punycodeEncodeUrl(url), equals(expected));
@@ -42,8 +42,7 @@ void main() {
];
});
final emptyTextSearch = isA<MetadataSearchDto>()
.having((s) => s.originalFileName, 'originalFileName', null);
final emptyTextSearch = isA<MetadataSearchDto>().having((s) => s.originalFileName, 'originalFileName', null);
testWidgets('contextual search with/without text', (tester) async {
await tester.pumpConsumerWidget(
@@ -111,8 +110,7 @@ void main() {
expect(
captured.first,
isA<MetadataSearchDto>()
.having((s) => s.originalFileName, 'originalFileName', 'test'),
isA<MetadataSearchDto>().having((s) => s.originalFileName, 'originalFileName', 'test'),
);
await tester.enterText(searchField, '');
+1 -2
View File
@@ -45,5 +45,4 @@ class MockPartnerRepository extends Mock implements PartnerRepository {}
class MockPartnerApiRepository extends Mock implements PartnerApiRepository {}
class MockLocalFilesManagerRepository extends Mock
implements LocalFilesManagerRepository {}
class MockLocalFilesManagerRepository extends Mock implements LocalFilesManagerRepository {}
+11 -24
View File
@@ -54,28 +54,22 @@ void main() {
group('refreshDeviceAlbums', () {
test('empty selection with one album in db', () async {
when(() => backupRepository.getIdsBySelection(BackupSelection.exclude))
.thenAnswer((_) async => []);
when(() => backupRepository.getIdsBySelection(BackupSelection.select))
.thenAnswer((_) async => []);
when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []);
when(() => backupRepository.getIdsBySelection(BackupSelection.select)).thenAnswer((_) async => []);
when(() => albumMediaRepository.getAll()).thenAnswer((_) async => []);
when(() => albumRepository.count(local: true)).thenAnswer((_) async => 1);
when(() => syncService.removeAllLocalAlbumsAndAssets())
.thenAnswer((_) async => true);
when(() => syncService.removeAllLocalAlbumsAndAssets()).thenAnswer((_) async => true);
final result = await sut.refreshDeviceAlbums();
expect(result, false);
verify(() => syncService.removeAllLocalAlbumsAndAssets());
});
test('one selected albums, two on device', () async {
when(() => backupRepository.getIdsBySelection(BackupSelection.exclude))
.thenAnswer((_) async => []);
when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []);
when(() => backupRepository.getIdsBySelection(BackupSelection.select))
.thenAnswer((_) async => [AlbumStub.oneAsset.localId!]);
when(() => albumMediaRepository.getAll())
.thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]);
when(() => syncService.syncLocalAlbumAssetsToDb(any(), any()))
.thenAnswer((_) async => true);
when(() => albumMediaRepository.getAll()).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]);
when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())).thenAnswer((_) async => true);
final result = await sut.refreshDeviceAlbums();
expect(result, true);
verify(
@@ -88,10 +82,8 @@ void main() {
group('refreshRemoteAlbums', () {
test('is working', () async {
when(() => syncService.getUsersFromServer()).thenAnswer((_) async => []);
when(() => syncService.syncUsersFromServer(any()))
.thenAnswer((_) async => true);
when(() => albumApiRepository.getAll(shared: true))
.thenAnswer((_) async => [AlbumStub.sharedWithUser]);
when(() => syncService.syncUsersFromServer(any())).thenAnswer((_) async => true);
when(() => albumApiRepository.getAll(shared: true)).thenAnswer((_) async => [AlbumStub.sharedWithUser]);
when(() => albumApiRepository.getAll(shared: null))
.thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]);
@@ -142,8 +134,7 @@ void main() {
() => albumRepository.create(AlbumStub.oneAsset),
).thenAnswer((_) async => AlbumStub.twoAsset);
final result =
await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]);
final result = await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]);
expect(result, AlbumStub.twoAsset);
verify(
() => albumApiRepository.create(
@@ -163,10 +154,7 @@ void main() {
when(
() => albumApiRepository.addAssets(AlbumStub.oneAsset.remoteId!, any()),
).thenAnswer(
(_) async => (
added: [AssetStub.image2.remoteId!],
duplicates: [AssetStub.image1.remoteId!]
),
(_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!]),
);
when(
() => albumRepository.get(AlbumStub.oneAsset.id),
@@ -198,8 +186,7 @@ void main() {
group('addAdditionalUserToAlbum', () {
test('one added', () async {
when(
() =>
albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()),
() => albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()),
).thenAnswer(
(_) async => AlbumStub.sharedWithUser,
);
+6 -12
View File
@@ -69,8 +69,7 @@ void main() {
setUp(() {
assetsApi = MockAssetsApi();
when(() => apiService.assetsApi).thenReturn(assetsApi);
when(() => assetsApi.updateAssets(any()))
.thenAnswer((_) async => Future.value());
when(() => assetsApi.updateAssets(any())).thenAnswer((_) async => Future.value());
});
test("asset is updated with DateTime", () async {
@@ -79,11 +78,9 @@ void main() {
await sut.changeDateTime(assets, dateTime.toIso8601String());
verify(() => assetsApi.updateAssets(any())).called(1);
final upsertExifCallback =
verify(() => syncService.upsertAssetsWithExif(captureAny()));
final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny()));
upsertExifCallback.called(1);
final receivedAssets =
upsertExifCallback.captured.firstOrNull as List<Object>? ?? [];
final receivedAssets = upsertExifCallback.captured.firstOrNull as List<Object>? ?? [];
final receivedDatetime = receivedAssets.cast<Asset>().map(
(a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0),
);
@@ -96,14 +93,11 @@ void main() {
await sut.changeLocation(assets, latLng);
verify(() => assetsApi.updateAssets(any())).called(1);
final upsertExifCallback =
verify(() => syncService.upsertAssetsWithExif(captureAny()));
final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny()));
upsertExifCallback.called(1);
final receivedAssets =
upsertExifCallback.captured.firstOrNull as List<Object>? ?? [];
final receivedAssets = upsertExifCallback.captured.firstOrNull as List<Object>? ?? [];
final receivedCoords = receivedAssets.cast<Asset>().map(
(a) =>
LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0),
(a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0),
);
expect(receivedCoords.every((l) => l == latLng), isTrue);
});
+19 -38
View File
@@ -58,8 +58,7 @@ void main() {
const testUrl = 'http://ip:2283';
const resolvedUrl = 'http://ip:2283/api';
when(() => apiService.resolveAndSetEndpoint(testUrl))
.thenAnswer((_) async => resolvedUrl);
when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl);
when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {});
final result = await sut.validateServerUrl(testUrl);
@@ -74,8 +73,7 @@ void main() {
const testUrl = 'https://immich.domain.com';
const resolvedUrl = 'https://immich.domain.com/api';
when(() => apiService.resolveAndSetEndpoint(testUrl))
.thenAnswer((_) async => resolvedUrl);
when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl);
when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {});
final result = await sut.validateServerUrl(testUrl);
@@ -89,8 +87,7 @@ void main() {
test('Should throw error on invalid URL', () async {
const testUrl = 'invalid-url';
when(() => apiService.resolveAndSetEndpoint(testUrl))
.thenThrow(Exception('Invalid URL'));
when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Invalid URL'));
expect(
() async => await sut.validateServerUrl(testUrl),
@@ -104,8 +101,7 @@ void main() {
test('Should throw error on unreachable server', () async {
const testUrl = 'https://unreachable.server';
when(() => apiService.resolveAndSetEndpoint(testUrl))
.thenThrow(Exception('Server is not reachable'));
when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Server is not reachable'));
expect(
() async => await sut.validateServerUrl(testUrl),
@@ -121,8 +117,7 @@ void main() {
test('Should logout user', () async {
when(() => authApiRepository.logout()).thenAnswer((_) async => {});
when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {});
when(() => authRepository.clearLocalData())
.thenAnswer((_) => Future.value(null));
when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null));
await sut.logout();
@@ -132,11 +127,9 @@ void main() {
});
test('Should clear local data even on server error', () async {
when(() => authApiRepository.logout())
.thenThrow(Exception('Server error'));
when(() => authApiRepository.logout()).thenThrow(Exception('Server error'));
when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {});
when(() => authRepository.clearLocalData())
.thenAnswer((_) => Future.value(null));
when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null));
await sut.logout();
@@ -148,13 +141,11 @@ void main() {
group('setOpenApiServiceEndpoint', () {
setUp(() {
when(() => networkService.getWifiName())
.thenAnswer((_) async => 'TestWifi');
when(() => networkService.getWifiName()).thenAnswer((_) async => 'TestWifi');
});
test('Should return null if auto endpoint switching is disabled', () async {
when(() => authRepository.getEndpointSwitchingFeature())
.thenReturn((false));
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn((false));
final result = await sut.setOpenApiServiceEndpoint();
@@ -166,8 +157,7 @@ void main() {
test('Should set local connection if wifi name matches', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi');
when(() => authRepository.getLocalEndpoint())
.thenReturn('http://local.endpoint');
when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint');
when(() => apiService.resolveAndSetEndpoint('http://local.endpoint'))
.thenAnswer((_) async => 'http://local.endpoint');
@@ -178,14 +168,12 @@ void main() {
verify(() => networkService.getWifiName()).called(1);
verify(() => authRepository.getPreferredWifiName()).called(1);
verify(() => authRepository.getLocalEndpoint()).called(1);
verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint'))
.called(1);
verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1);
});
test('Should set external endpoint if wifi name not matching', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName())
.thenReturn('DifferentWifi');
when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi');
when(() => authRepository.getExternalEndpointList()).thenReturn([
const AuxilaryEndpoint(
url: 'https://external.endpoint',
@@ -208,11 +196,9 @@ void main() {
).called(1);
});
test('Should set second external endpoint if the first throw any error',
() async {
test('Should set second external endpoint if the first throw any error', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName())
.thenReturn('DifferentWifi');
when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi');
when(() => authRepository.getExternalEndpointList()).thenReturn([
const AuxilaryEndpoint(
url: 'https://external.endpoint',
@@ -243,11 +229,9 @@ void main() {
).called(1);
});
test('Should set second external endpoint if the first throw ApiException',
() async {
test('Should set second external endpoint if the first throw ApiException', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName())
.thenReturn('DifferentWifi');
when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi');
when(() => authRepository.getExternalEndpointList()).thenReturn([
const AuxilaryEndpoint(
url: 'https://external.endpoint',
@@ -281,8 +265,7 @@ void main() {
test('Should handle error when setting local connection', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi');
when(() => authRepository.getLocalEndpoint())
.thenReturn('http://local.endpoint');
when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint');
when(() => apiService.resolveAndSetEndpoint('http://local.endpoint'))
.thenThrow(Exception('Local endpoint error'));
@@ -293,14 +276,12 @@ void main() {
verify(() => networkService.getWifiName()).called(1);
verify(() => authRepository.getPreferredWifiName()).called(1);
verify(() => authRepository.getLocalEndpoint()).called(1);
verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint'))
.called(1);
verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1);
});
test('Should handle error when setting external connection', () async {
when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true);
when(() => authRepository.getPreferredWifiName())
.thenReturn('DifferentWifi');
when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi');
when(() => authRepository.getExternalEndpointList()).thenReturn([
const AuxilaryEndpoint(
url: 'https://external.endpoint',
+6 -12
View File
@@ -21,8 +21,7 @@ void main() {
});
group('fillAlbumWithDatabaseEntities', () {
test('remote album with owner, thumbnail, sharedUsers and assets',
() async {
test('remote album with owner, thumbnail, sharedUsers and assets', () async {
final Album album = Album(
name: "album-with-two-assets-and-two-users",
localId: "album-with-two-assets-and-two-users-local",
@@ -41,19 +40,14 @@ void main() {
[User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)],
);
when(() => userRepository.getByUserId(any()))
.thenAnswer((_) async => UserStub.admin);
when(() => userRepository.getByUserId(any()))
.thenAnswer((_) async => UserStub.admin);
when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin);
when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin);
when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!))
.thenAnswer((_) async => AssetStub.image1);
when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)).thenAnswer((_) async => AssetStub.image1);
when(() => userRepository.getByUserIds(any()))
.thenAnswer((_) async => [UserStub.user1, UserStub.user2]);
when(() => userRepository.getByUserIds(any())).thenAnswer((_) async => [UserStub.user1, UserStub.user2]);
when(() => assetRepository.getAllByRemoteId(any()))
.thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]);
when(() => assetRepository.getAllByRemoteId(any())).thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]);
await sut.fillAlbumWithDatabaseEntities(album);
expect(album.owner.value?.toDto(), UserStub.admin);
+32 -64
View File
@@ -36,27 +36,22 @@ void main() {
backgroundService: mockBackgroundService,
);
when(() => mockDeviceAssetRepository.transaction<Null>(any()))
.thenAnswer((_) async {
when(() => mockDeviceAssetRepository.transaction<Null>(any())).thenAnswer((_) async {
final capturedCallback = verify(
() => mockDeviceAssetRepository.transaction<Null>(captureAny()),
).captured;
// Invoke the transaction callback
await (capturedCallback.firstOrNull as Future<Null> Function()?)?.call();
});
when(() => mockDeviceAssetRepository.updateAll(any()))
.thenAnswer((_) async => true);
when(() => mockDeviceAssetRepository.deleteIds(any()))
.thenAnswer((_) async => true);
when(() => mockDeviceAssetRepository.updateAll(any())).thenAnswer((_) async => true);
when(() => mockDeviceAssetRepository.deleteIds(any())).thenAnswer((_) async => true);
});
group("HashService: No DeviceAsset entry", () {
test("hash successfully", () async {
final (mockAsset, file, deviceAsset, hash) =
await _createAssetMock(AssetStub.image1);
final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1);
when(() => mockBackgroundService.digestFiles([file.path]))
.thenAnswer((_) async => [hash]);
when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]);
// No DB entries for this asset
when(
() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]),
@@ -65,14 +60,12 @@ void main() {
final result = await sut.hashAssets([mockAsset]);
// Verify we stored the new hash in DB
when(() => mockDeviceAssetRepository.transaction<Null>(any()))
.thenAnswer((_) async {
when(() => mockDeviceAssetRepository.transaction<Null>(any())).thenAnswer((_) async {
final capturedCallback = verify(
() => mockDeviceAssetRepository.transaction<Null>(captureAny()),
).captured;
// Invoke the transaction callback
await (capturedCallback.firstOrNull as Future<Null> Function()?)
?.call();
await (capturedCallback.firstOrNull as Future<Null> Function()?)?.call();
verify(
() => mockDeviceAssetRepository.updateAll([
deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt),
@@ -115,25 +108,21 @@ void main() {
});
test("hashed successful when asset is modified", () async {
final (mockAsset, file, deviceAsset, hash) =
await _createAssetMock(AssetStub.image1);
final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1);
when(() => mockBackgroundService.digestFiles([file.path]))
.thenAnswer((_) async => [hash]);
when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]);
when(
() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]),
).thenAnswer((_) async => [deviceAsset]);
final result = await sut.hashAssets([mockAsset]);
when(() => mockDeviceAssetRepository.transaction<Null>(any()))
.thenAnswer((_) async {
when(() => mockDeviceAssetRepository.transaction<Null>(any())).thenAnswer((_) async {
final capturedCallback = verify(
() => mockDeviceAssetRepository.transaction<Null>(captureAny()),
).captured;
// Invoke the transaction callback
await (capturedCallback.firstOrNull as Future<Null> Function()?)
?.call();
await (capturedCallback.firstOrNull as Future<Null> Function()?)?.call();
verify(
() => mockDeviceAssetRepository.updateAll([
deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt),
@@ -157,11 +146,9 @@ void main() {
late File file;
setUp(() async {
(mockAsset, file, deviceAsset, hash) =
await _createAssetMock(AssetStub.image1);
(mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1);
when(() => mockBackgroundService.digestFiles([file.path]))
.thenAnswer((_) async => [hash]);
when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]);
when(
() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]),
).thenAnswer((_) async => [deviceAsset]);
@@ -182,14 +169,12 @@ void main() {
});
test("cleanups DeviceAsset when hashing failed", () async {
when(() => mockDeviceAssetRepository.transaction<Null>(any()))
.thenAnswer((_) async {
when(() => mockDeviceAssetRepository.transaction<Null>(any())).thenAnswer((_) async {
final capturedCallback = verify(
() => mockDeviceAssetRepository.transaction<Null>(captureAny()),
).captured;
// Invoke the transaction callback
await (capturedCallback.firstOrNull as Future<Null> Function()?)
?.call();
await (capturedCallback.firstOrNull as Future<Null> Function()?)?.call();
// Verify the callback inside the transaction because, doing it outside results
// in a small delay before the callback is invoked, resulting in other LOCs getting executed
@@ -210,8 +195,7 @@ void main() {
// and verify the results inside the transaction stub
verify(() => mockDeviceAssetRepository.updateAll([])).called(1);
verify(
() =>
mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]),
() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]),
).called(1);
});
@@ -240,14 +224,11 @@ void main() {
final (asset2, file2, deviceAsset2, hash2) = mock2;
final (asset3, file3, deviceAsset3, hash3) = mock3;
when(() => mockDeviceAssetRepository.getByIds(any()))
.thenAnswer((_) async => []);
when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []);
// Setup for multiple batch processing calls
when(() => mockBackgroundService.digestFiles([file1.path, file2.path]))
.thenAnswer((_) async => [hash1, hash2]);
when(() => mockBackgroundService.digestFiles([file3.path]))
.thenAnswer((_) async => [hash3]);
when(() => mockBackgroundService.digestFiles([file1.path, file2.path])).thenAnswer((_) async => [hash1, hash2]);
when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]);
final size = await file1.length() + await file2.length();
@@ -259,8 +240,7 @@ void main() {
final result = await sut.hashAssets([asset1, asset2, asset3]);
// Verify multiple batch process calls
verify(() => mockBackgroundService.digestFiles([file1.path, file2.path]))
.called(1);
verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])).called(1);
verify(() => mockBackgroundService.digestFiles([file3.path])).called(1);
expect(
@@ -285,15 +265,11 @@ void main() {
final (asset2, file2, deviceAsset2, hash2) = mock2;
final (asset3, file3, deviceAsset3, hash3) = mock3;
when(() => mockDeviceAssetRepository.getByIds(any()))
.thenAnswer((_) async => []);
when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []);
when(() => mockBackgroundService.digestFiles([file1.path]))
.thenAnswer((_) async => [hash1]);
when(() => mockBackgroundService.digestFiles([file2.path]))
.thenAnswer((_) async => [hash2]);
when(() => mockBackgroundService.digestFiles([file3.path]))
.thenAnswer((_) async => [hash3]);
when(() => mockBackgroundService.digestFiles([file1.path])).thenAnswer((_) async => [hash1]);
when(() => mockBackgroundService.digestFiles([file2.path])).thenAnswer((_) async => [hash2]);
when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]);
sut = HashService(
deviceAssetRepository: mockDeviceAssetRepository,
@@ -318,17 +294,12 @@ void main() {
});
test("HashService: Sort & Process different states", () async {
final (asset1, file1, deviceAsset1, hash1) =
await _createAssetMock(AssetStub.image1); // Will need rehashing
final (asset2, file2, deviceAsset2, hash2) =
await _createAssetMock(AssetStub.image2); // Will have matching hash
final (asset3, file3, deviceAsset3, hash3) =
await _createAssetMock(AssetStub.image3); // No DB entry
final asset4 =
AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed
final (asset1, file1, deviceAsset1, hash1) = await _createAssetMock(AssetStub.image1); // Will need rehashing
final (asset2, file2, deviceAsset2, hash2) = await _createAssetMock(AssetStub.image2); // Will have matching hash
final (asset3, file3, deviceAsset3, hash3) = await _createAssetMock(AssetStub.image3); // No DB entry
final asset4 = AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed
when(() => mockBackgroundService.digestFiles([file1.path, file3.path]))
.thenAnswer((_) async => [hash1, hash3]);
when(() => mockBackgroundService.digestFiles([file1.path, file3.path])).thenAnswer((_) async => [hash1, hash3]);
// DB entries are not sorted and a dummy entry added
when(
() => mockDeviceAssetRepository.getByIds([
@@ -349,8 +320,7 @@ void main() {
final result = await sut.hashAssets([asset1, asset2, asset3, asset4]);
// Verify correct processing of all assets
verify(() => mockBackgroundService.digestFiles([file1.path, file3.path]))
.called(1);
verify(() => mockBackgroundService.digestFiles([file1.path, file3.path])).called(1);
expect(result.length, 3);
expect(result, [
AssetStub.image2.copyWith(checksum: base64.encode(hash2)),
@@ -361,8 +331,7 @@ void main() {
group("HashService: Edge cases", () {
test("handles empty list of assets", () async {
when(() => mockDeviceAssetRepository.getByIds(any()))
.thenAnswer((_) async => []);
when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []);
final result = await sut.hashAssets([]);
@@ -398,8 +367,7 @@ Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock(
Asset asset,
) async {
final random = Random();
final hash =
Uint8List.fromList(List.generate(20, (i) => random.nextInt(255)));
final hash = Uint8List.fromList(List.generate(20, (i) => random.nextInt(255)));
final mockAsset = MockAsset();
final mockAssetEntity = MockAssetEntity();
final fs = MemoryFileSystem();
+3 -6
View File
@@ -25,10 +25,8 @@ class MediumFactory {
name: name ?? 'Asset ${random.nextInt(1000000)}',
checksum: checksum ?? '${random.nextInt(1000000)}',
type: type ?? AssetType.image,
createdAt: createdAt ??
DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
updatedAt: updatedAt ??
DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
createdAt: createdAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
);
}
@@ -45,8 +43,7 @@ class MediumFactory {
return LocalAlbum(
id: id ?? '${random.nextInt(1000000)}',
name: name ?? 'Album ${random.nextInt(1000000)}',
updatedAt: updatedAt ??
DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)),
assetCount: assetCount ?? random.nextInt(100),
backupSelection: backupSelection ?? BackupSelection.none,
isIosSharedAlbum: isIosSharedAlbum ?? false,