mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	test(mobile): store (#16243)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									5acf6868b7
								
							
						
					
					
						commit
						94c0e8253a
					
				@ -1,6 +1,7 @@
 | 
				
			|||||||
 | 
					import 'package:immich_mobile/domain/interfaces/db.interface.dart';
 | 
				
			||||||
import 'package:immich_mobile/domain/models/store.model.dart';
 | 
					import 'package:immich_mobile/domain/models/store.model.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
abstract interface class IStoreRepository {
 | 
					abstract interface class IStoreRepository implements IDatabaseRepository {
 | 
				
			||||||
  Future<bool> insert<T>(StoreKey<T> key, T value);
 | 
					  Future<bool> insert<T>(StoreKey<T> key, T value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<T?> tryGet<T>(StoreKey<T> key);
 | 
					  Future<T?> tryGet<T>(StoreKey<T> key);
 | 
				
			||||||
 | 
				
			|||||||
@ -77,4 +77,23 @@ class StoreUpdateEvent<T> {
 | 
				
			|||||||
  final T? value;
 | 
					  final T? value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const StoreUpdateEvent(this.key, this.value);
 | 
					  const StoreUpdateEvent(this.key, this.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String toString() {
 | 
				
			||||||
 | 
					    return '''
 | 
				
			||||||
 | 
					StoreUpdateEvent: {
 | 
				
			||||||
 | 
					  key: $key,
 | 
				
			||||||
 | 
					  value: ${value ?? '<NA>'},
 | 
				
			||||||
 | 
					}''';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  bool operator ==(covariant StoreUpdateEvent<T> other) {
 | 
				
			||||||
 | 
					    if (identical(this, other)) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return other.key == key && other.value == value;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  int get hashCode => key.hashCode ^ value.hashCode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ class IsarStoreRepository extends IsarDatabaseRepository
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Stream<StoreUpdateEvent> watchAll() {
 | 
					  Stream<StoreUpdateEvent> watchAll() {
 | 
				
			||||||
    return _db.storeValues.where().watch().asyncExpand(
 | 
					    return _db.storeValues.where().watch(fireImmediately: true).asyncExpand(
 | 
				
			||||||
          (entities) =>
 | 
					          (entities) =>
 | 
				
			||||||
              Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e))),
 | 
					              Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e))),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -86,17 +86,11 @@ class IsarStoreRepository extends IsarDatabaseRepository
 | 
				
			|||||||
    final (int? intValue, String? strValue) = switch (key.type) {
 | 
					    final (int? intValue, String? strValue) = switch (key.type) {
 | 
				
			||||||
      const (int) => (value as int, null),
 | 
					      const (int) => (value as int, null),
 | 
				
			||||||
      const (String) => (null, value as String),
 | 
					      const (String) => (null, value as String),
 | 
				
			||||||
      const (bool) => (
 | 
					      const (bool) => ((value as bool) ? 1 : 0, null),
 | 
				
			||||||
          (value as bool) ? 1 : 0,
 | 
					      const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
 | 
				
			||||||
          null,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      const (DateTime) => (
 | 
					 | 
				
			||||||
          (value as DateTime).millisecondsSinceEpoch,
 | 
					 | 
				
			||||||
          null,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      const (User) => (
 | 
					      const (User) => (
 | 
				
			||||||
          (await UserRepository(_db).update(value as User)).isarId,
 | 
					          (await UserRepository(_db).update(value as User)).isarId,
 | 
				
			||||||
          null
 | 
					          null,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      _ => throw UnsupportedError(
 | 
					      _ => throw UnsupportedError(
 | 
				
			||||||
          "Unsupported primitive type: ${key.type} for key: ${key.name}",
 | 
					          "Unsupported primitive type: ${key.type} for key: ${key.name}",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										184
									
								
								mobile/test/domain/services/store_service_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								mobile/test/domain/services/store_service_test.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,184 @@
 | 
				
			|||||||
 | 
					// ignore_for_file: avoid-dynamic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'dart:async';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:flutter_test/flutter_test.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/domain/interfaces/store.interface.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/domain/models/store.model.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/domain/services/store.service.dart';
 | 
				
			||||||
 | 
					import 'package:mocktail/mocktail.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import '../../infrastructure/repository.mock.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const _kAccessToken = '#ThisIsAToken';
 | 
				
			||||||
 | 
					const _kBackgroundBackup = false;
 | 
				
			||||||
 | 
					const _kGroupAssetsBy = 2;
 | 
				
			||||||
 | 
					final _kBackupFailedSince = DateTime.utc(2023);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main() {
 | 
				
			||||||
 | 
					  late StoreService sut;
 | 
				
			||||||
 | 
					  late IStoreRepository mockStoreRepo;
 | 
				
			||||||
 | 
					  late StreamController<StoreUpdateEvent> controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setUp(() async {
 | 
				
			||||||
 | 
					    controller = StreamController<StoreUpdateEvent>.broadcast();
 | 
				
			||||||
 | 
					    mockStoreRepo = MockStoreRepository();
 | 
				
			||||||
 | 
					    // For generics, we need to provide fallback to each concrete type to avoid runtime errors
 | 
				
			||||||
 | 
					    registerFallbackValue(StoreKey.accessToken);
 | 
				
			||||||
 | 
					    registerFallbackValue(StoreKey.backupTriggerDelay);
 | 
				
			||||||
 | 
					    registerFallbackValue(StoreKey.backgroundBackup);
 | 
				
			||||||
 | 
					    registerFallbackValue(StoreKey.backupFailedSince);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    when(() => mockStoreRepo.tryGet(any<StoreKey<dynamic>>()))
 | 
				
			||||||
 | 
					        .thenAnswer((invocation) async {
 | 
				
			||||||
 | 
					      final key = invocation.positionalArguments.firstOrNull as StoreKey;
 | 
				
			||||||
 | 
					      return switch (key) {
 | 
				
			||||||
 | 
					        StoreKey.accessToken => _kAccessToken,
 | 
				
			||||||
 | 
					        StoreKey.backgroundBackup => _kBackgroundBackup,
 | 
				
			||||||
 | 
					        StoreKey.groupAssetsBy => _kGroupAssetsBy,
 | 
				
			||||||
 | 
					        StoreKey.backupFailedSince => _kBackupFailedSince,
 | 
				
			||||||
 | 
					        // ignore: avoid-wildcard-cases-with-enums
 | 
				
			||||||
 | 
					        _ => null,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    when(() => mockStoreRepo.watchAll()).thenAnswer((_) => controller.stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sut = await StoreService.create(storeRepository: mockStoreRepo);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tearDown(() async {
 | 
				
			||||||
 | 
					    sut.dispose();
 | 
				
			||||||
 | 
					    await controller.close();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group("Store Service Init:", () {
 | 
				
			||||||
 | 
					    test('Populates the internal cache on init', () {
 | 
				
			||||||
 | 
					      verify(() => mockStoreRepo.tryGet(any<StoreKey<dynamic>>()))
 | 
				
			||||||
 | 
					          .called(equals(StoreKey.values.length));
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.accessToken), _kAccessToken);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.backgroundBackup), _kBackgroundBackup);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.groupAssetsBy), _kGroupAssetsBy);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.backupFailedSince), _kBackupFailedSince);
 | 
				
			||||||
 | 
					      // Other keys should be null
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.currentUser), isNull);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Listens to stream of store updates', () async {
 | 
				
			||||||
 | 
					      final event =
 | 
				
			||||||
 | 
					          StoreUpdateEvent(StoreKey.accessToken, _kAccessToken.toUpperCase());
 | 
				
			||||||
 | 
					      controller.add(event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await pumpEventQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      verify(() => mockStoreRepo.watchAll()).called(1);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.accessToken), _kAccessToken.toUpperCase());
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Service get:', () {
 | 
				
			||||||
 | 
					    test('Returns the stored value for the given key', () {
 | 
				
			||||||
 | 
					      expect(sut.get(StoreKey.accessToken), _kAccessToken);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Throws StoreKeyNotFoundException for nonexistent keys', () {
 | 
				
			||||||
 | 
					      expect(
 | 
				
			||||||
 | 
					        () => sut.get(StoreKey.currentUser),
 | 
				
			||||||
 | 
					        throwsA(isA<StoreKeyNotFoundException>()),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Returns the stored value for the given key or the defaultValue', () {
 | 
				
			||||||
 | 
					      expect(sut.get(StoreKey.currentUser, 5), 5);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Service put:', () {
 | 
				
			||||||
 | 
					    setUp(() {
 | 
				
			||||||
 | 
					      when(() => mockStoreRepo.insert<String>(any<StoreKey<String>>(), any()))
 | 
				
			||||||
 | 
					          .thenAnswer((_) async => true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Skip insert when value is not modified', () async {
 | 
				
			||||||
 | 
					      await sut.put(StoreKey.accessToken, _kAccessToken);
 | 
				
			||||||
 | 
					      verifyNever(
 | 
				
			||||||
 | 
					        () => mockStoreRepo.insert<String>(StoreKey.accessToken, any()),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Insert value when modified', () async {
 | 
				
			||||||
 | 
					      final newAccessToken = _kAccessToken.toUpperCase();
 | 
				
			||||||
 | 
					      await sut.put(StoreKey.accessToken, newAccessToken);
 | 
				
			||||||
 | 
					      verify(
 | 
				
			||||||
 | 
					        () =>
 | 
				
			||||||
 | 
					            mockStoreRepo.insert<String>(StoreKey.accessToken, newAccessToken),
 | 
				
			||||||
 | 
					      ).called(1);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.accessToken), newAccessToken);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Service watch:', () {
 | 
				
			||||||
 | 
					    late StreamController<String?> valueController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setUp(() {
 | 
				
			||||||
 | 
					      valueController = StreamController<String?>.broadcast();
 | 
				
			||||||
 | 
					      when(() => mockStoreRepo.watch<String>(any<StoreKey<String>>()))
 | 
				
			||||||
 | 
					          .thenAnswer((_) => valueController.stream);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tearDown(() async {
 | 
				
			||||||
 | 
					      await valueController.close();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Watches a specific key for changes', () async {
 | 
				
			||||||
 | 
					      final stream = sut.watch(StoreKey.accessToken);
 | 
				
			||||||
 | 
					      final events = <String?>[
 | 
				
			||||||
 | 
					        _kAccessToken,
 | 
				
			||||||
 | 
					        _kAccessToken.toUpperCase(),
 | 
				
			||||||
 | 
					        null,
 | 
				
			||||||
 | 
					        _kAccessToken.toLowerCase(),
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expectLater(stream, emitsInOrder(events));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (final event in events) {
 | 
				
			||||||
 | 
					        valueController.add(event);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await pumpEventQueue();
 | 
				
			||||||
 | 
					      verify(() => mockStoreRepo.watch<String>(StoreKey.accessToken)).called(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Service delete:', () {
 | 
				
			||||||
 | 
					    setUp(() {
 | 
				
			||||||
 | 
					      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);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Removes the value from the cache', () async {
 | 
				
			||||||
 | 
					      await sut.delete(StoreKey.accessToken);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.accessToken), isNull);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Service clear:', () {
 | 
				
			||||||
 | 
					    setUp(() {
 | 
				
			||||||
 | 
					      when(() => mockStoreRepo.deleteAll()).thenAnswer((_) async => true);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('Clears all values from the store', () async {
 | 
				
			||||||
 | 
					      await sut.clear();
 | 
				
			||||||
 | 
					      verify(() => mockStoreRepo.deleteAll()).called(1);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.accessToken), isNull);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.backgroundBackup), isNull);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.groupAssetsBy), isNull);
 | 
				
			||||||
 | 
					      expect(sut.tryGet(StoreKey.backupFailedSince), isNull);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					// ignore_for_file: avoid-dynamic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:flutter_test/flutter_test.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/domain/interfaces/store.interface.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/domain/models/store.model.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/entities/user.entity.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 | 
				
			||||||
 | 
					import 'package:isar/isar.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import '../../fixtures/user.stub.dart';
 | 
				
			||||||
 | 
					import '../../test_utils.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const _kTestAccessToken = "#TestToken";
 | 
				
			||||||
 | 
					final _kTestBackupFailed = DateTime(2025, 2, 20, 11, 45);
 | 
				
			||||||
 | 
					const _kTestVersion = 10;
 | 
				
			||||||
 | 
					const _kTestColorfulInterface = false;
 | 
				
			||||||
 | 
					final _kTestUser = UserStub.admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<void> _addIntStoreValue(Isar db, StoreKey key, int? value) async {
 | 
				
			||||||
 | 
					  await db.storeValues.put(StoreValue(key.id, intValue: value, strValue: null));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<void> _addStrStoreValue(Isar db, StoreKey key, String? value) async {
 | 
				
			||||||
 | 
					  await db.storeValues.put(StoreValue(key.id, intValue: null, strValue: value));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<void> _populateStore(Isar db) async {
 | 
				
			||||||
 | 
					  await db.writeTxn(() async {
 | 
				
			||||||
 | 
					    await _addIntStoreValue(
 | 
				
			||||||
 | 
					      db,
 | 
				
			||||||
 | 
					      StoreKey.colorfulInterface,
 | 
				
			||||||
 | 
					      _kTestColorfulInterface ? 1 : 0,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await _addIntStoreValue(
 | 
				
			||||||
 | 
					      db,
 | 
				
			||||||
 | 
					      StoreKey.backupFailedSince,
 | 
				
			||||||
 | 
					      _kTestBackupFailed.millisecondsSinceEpoch,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await _addStrStoreValue(db, StoreKey.accessToken, _kTestAccessToken);
 | 
				
			||||||
 | 
					    await _addIntStoreValue(db, StoreKey.version, _kTestVersion);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main() {
 | 
				
			||||||
 | 
					  late Isar db;
 | 
				
			||||||
 | 
					  late IStoreRepository sut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setUp(() async {
 | 
				
			||||||
 | 
					    db = await TestUtils.initIsar();
 | 
				
			||||||
 | 
					    sut = IsarStoreRepository(db);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Repository converters:', () {
 | 
				
			||||||
 | 
					    test('converts int', () async {
 | 
				
			||||||
 | 
					      int? version = await sut.tryGet(StoreKey.version);
 | 
				
			||||||
 | 
					      expect(version, isNull);
 | 
				
			||||||
 | 
					      await sut.insert(StoreKey.version, _kTestVersion);
 | 
				
			||||||
 | 
					      version = await sut.tryGet(StoreKey.version);
 | 
				
			||||||
 | 
					      expect(version, _kTestVersion);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('converts string', () async {
 | 
				
			||||||
 | 
					      String? accessToken = await sut.tryGet(StoreKey.accessToken);
 | 
				
			||||||
 | 
					      expect(accessToken, isNull);
 | 
				
			||||||
 | 
					      await sut.insert(StoreKey.accessToken, _kTestAccessToken);
 | 
				
			||||||
 | 
					      accessToken = await sut.tryGet(StoreKey.accessToken);
 | 
				
			||||||
 | 
					      expect(accessToken, _kTestAccessToken);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('converts datetime', () async {
 | 
				
			||||||
 | 
					      DateTime? backupFailedSince =
 | 
				
			||||||
 | 
					          await sut.tryGet(StoreKey.backupFailedSince);
 | 
				
			||||||
 | 
					      expect(backupFailedSince, isNull);
 | 
				
			||||||
 | 
					      await sut.insert(StoreKey.backupFailedSince, _kTestBackupFailed);
 | 
				
			||||||
 | 
					      backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince);
 | 
				
			||||||
 | 
					      expect(backupFailedSince, _kTestBackupFailed);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('converts bool', () async {
 | 
				
			||||||
 | 
					      bool? colorfulInterface = await sut.tryGet(StoreKey.colorfulInterface);
 | 
				
			||||||
 | 
					      expect(colorfulInterface, isNull);
 | 
				
			||||||
 | 
					      await sut.insert(StoreKey.colorfulInterface, _kTestColorfulInterface);
 | 
				
			||||||
 | 
					      colorfulInterface = await sut.tryGet(StoreKey.colorfulInterface);
 | 
				
			||||||
 | 
					      expect(colorfulInterface, _kTestColorfulInterface);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('converts user', () async {
 | 
				
			||||||
 | 
					      User? user = await sut.tryGet(StoreKey.currentUser);
 | 
				
			||||||
 | 
					      expect(user, isNull);
 | 
				
			||||||
 | 
					      await sut.insert(StoreKey.currentUser, _kTestUser);
 | 
				
			||||||
 | 
					      user = await sut.tryGet(StoreKey.currentUser);
 | 
				
			||||||
 | 
					      expect(user, _kTestUser);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Repository Deletes:', () {
 | 
				
			||||||
 | 
					    setUp(() async {
 | 
				
			||||||
 | 
					      await _populateStore(db);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('delete()', () async {
 | 
				
			||||||
 | 
					      bool? isColorful = await sut.tryGet(StoreKey.colorfulInterface);
 | 
				
			||||||
 | 
					      expect(isColorful, isFalse);
 | 
				
			||||||
 | 
					      await sut.delete(StoreKey.colorfulInterface);
 | 
				
			||||||
 | 
					      isColorful = await sut.tryGet(StoreKey.colorfulInterface);
 | 
				
			||||||
 | 
					      expect(isColorful, isNull);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('deleteAll()', () async {
 | 
				
			||||||
 | 
					      final count = await db.storeValues.count();
 | 
				
			||||||
 | 
					      expect(count, isNot(isZero));
 | 
				
			||||||
 | 
					      await sut.deleteAll();
 | 
				
			||||||
 | 
					      expectLater(await db.storeValues.count(), isZero);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Repository Updates:', () {
 | 
				
			||||||
 | 
					    setUp(() async {
 | 
				
			||||||
 | 
					      await _populateStore(db);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('update()', () async {
 | 
				
			||||||
 | 
					      int? version = await sut.tryGet(StoreKey.version);
 | 
				
			||||||
 | 
					      expect(version, _kTestVersion);
 | 
				
			||||||
 | 
					      await sut.update(StoreKey.version, _kTestVersion + 10);
 | 
				
			||||||
 | 
					      version = await sut.tryGet(StoreKey.version);
 | 
				
			||||||
 | 
					      expect(version, _kTestVersion + 10);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  group('Store Repository Watchers:', () {
 | 
				
			||||||
 | 
					    setUp(() async {
 | 
				
			||||||
 | 
					      await _populateStore(db);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('watch()', () async {
 | 
				
			||||||
 | 
					      final stream = sut.watch(StoreKey.version);
 | 
				
			||||||
 | 
					      expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10]));
 | 
				
			||||||
 | 
					      await pumpEventQueue();
 | 
				
			||||||
 | 
					      await sut.update(StoreKey.version, _kTestVersion + 10);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('watchAll()', () async {
 | 
				
			||||||
 | 
					      final stream = sut.watchAll();
 | 
				
			||||||
 | 
					      expectLater(
 | 
				
			||||||
 | 
					        stream,
 | 
				
			||||||
 | 
					        emitsInAnyOrder([
 | 
				
			||||||
 | 
					          emits(
 | 
				
			||||||
 | 
					            const StoreUpdateEvent<dynamic>(StoreKey.version, _kTestVersion),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          emits(
 | 
				
			||||||
 | 
					            StoreUpdateEvent<dynamic>(
 | 
				
			||||||
 | 
					              StoreKey.backupFailedSince,
 | 
				
			||||||
 | 
					              _kTestBackupFailed,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          emits(
 | 
				
			||||||
 | 
					            const StoreUpdateEvent<dynamic>(
 | 
				
			||||||
 | 
					              StoreKey.accessToken,
 | 
				
			||||||
 | 
					              _kTestAccessToken,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          emits(
 | 
				
			||||||
 | 
					            const StoreUpdateEvent<dynamic>(
 | 
				
			||||||
 | 
					              StoreKey.colorfulInterface,
 | 
				
			||||||
 | 
					              _kTestColorfulInterface,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          emits(
 | 
				
			||||||
 | 
					            const StoreUpdateEvent<dynamic>(
 | 
				
			||||||
 | 
					              StoreKey.version,
 | 
				
			||||||
 | 
					              _kTestVersion + 10,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ]),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      await sut.update(StoreKey.version, _kTestVersion + 10);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										4
									
								
								mobile/test/infrastructure/repository.mock.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mobile/test/infrastructure/repository.mock.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					import 'package:immich_mobile/domain/interfaces/store.interface.dart';
 | 
				
			||||||
 | 
					import 'package:mocktail/mocktail.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MockStoreRepository extends Mock implements IStoreRepository {}
 | 
				
			||||||
@ -21,10 +21,11 @@ import 'mock_http_override.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Listener Mock to test when a provider notifies its listeners
 | 
					// Listener Mock to test when a provider notifies its listeners
 | 
				
			||||||
class ListenerMock<T> extends Mock {
 | 
					class ListenerMock<T> extends Mock {
 | 
				
			||||||
 | 
					  // ignore: avoid-declaring-call-method
 | 
				
			||||||
  void call(T? previous, T next);
 | 
					  void call(T? previous, T next);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final class TestUtils {
 | 
					abstract final class TestUtils {
 | 
				
			||||||
  const TestUtils._();
 | 
					  const TestUtils._();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Downloads Isar binaries (if required) and initializes a new Isar db
 | 
					  /// Downloads Isar binaries (if required) and initializes a new Isar db
 | 
				
			||||||
@ -50,13 +51,14 @@ final class TestUtils {
 | 
				
			|||||||
        AndroidDeviceAssetSchema,
 | 
					        AndroidDeviceAssetSchema,
 | 
				
			||||||
        IOSDeviceAssetSchema,
 | 
					        IOSDeviceAssetSchema,
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      maxSizeMiB: 1024,
 | 
					 | 
				
			||||||
      directory: "test/",
 | 
					      directory: "test/",
 | 
				
			||||||
 | 
					      maxSizeMiB: 1024,
 | 
				
			||||||
 | 
					      inspector: false,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Clear and close db on test end
 | 
					    // Clear and close db on test end
 | 
				
			||||||
    addTearDown(() async {
 | 
					    addTearDown(() async {
 | 
				
			||||||
      await db.writeTxn(() => db.clear());
 | 
					      await db.writeTxn(() async => await db.clear());
 | 
				
			||||||
      await db.close();
 | 
					      await db.close();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return db;
 | 
					    return db;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user