This commit is contained in:
shenlong 2026-08-10 07:59:03 -07:00 committed by GitHub
commit 699243c98c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 85 additions and 132 deletions

View file

@ -1,10 +1,7 @@
import 'package:immich_mobile/domain/models/user.model.dart';
/// Key for each possible value in the `Store`.
/// Defines the data type for each value
enum StoreKey<T> {
version<int>._(0),
currentUser<UserDto>._(2),
deviceId<String>._(4),
advancedTroubleshooting<bool>._(114),
enableHapticFeedback<bool>._(126),

View file

@ -2,12 +2,11 @@ import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
@ -17,7 +16,7 @@ final syncLinkedAlbumServiceProvider = Provider(
ref.watch(localAlbumRepository),
ref.watch(remoteAlbumRepository),
ref.watch(driftAlbumApiRepositoryProvider),
ref.watch(storeServiceProvider),
ref.watch(authUserRepositoryProvider),
),
);
@ -25,13 +24,13 @@ class SyncLinkedAlbumService {
final DriftLocalAlbumRepository _localAlbumRepository;
final DriftRemoteAlbumRepository _remoteAlbumRepository;
final DriftAlbumApiRepository _albumApiRepository;
final StoreService _storeService;
final DriftAuthUserRepository _authUserRepository;
SyncLinkedAlbumService(
this._localAlbumRepository,
this._remoteAlbumRepository,
this._albumApiRepository,
this._storeService,
this._authUserRepository,
);
final _log = Logger("SyncLinkedAlbumService");
@ -119,11 +118,12 @@ class SyncLinkedAlbumService {
/// Creates a new remote album and links it to the local album
Future<void> _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async {
dPrint(() => "Creating new remote album for local album: ${localAlbum.name}");
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(
localAlbum.name,
_storeService.get(StoreKey.currentUser),
assetIds: [],
);
final currentUser = await _authUserRepository.get();
if (currentUser == null) {
_log.warning("No user logged in, skipping remote album creation for local album: ${localAlbum.name}");
return;
}
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, currentUser, assetIds: []);
await _remoteAlbumRepository.create(newRemoteAlbum, []);
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id);
}

View file

@ -1,29 +1,24 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
import 'package:logging/logging.dart';
class UserService {
final Logger _log = Logger("UserService");
final UserApiRepository _userApiRepository;
final StoreService _storeService;
final DriftAuthUserRepository _authUserRepository;
UserService({required this._userApiRepository, required this._storeService});
UserService({required this._userApiRepository, required this._authUserRepository});
UserDto getMyUser() {
return _storeService.get(StoreKey.currentUser);
}
UserDto? tryGetMyUser() {
return _storeService.tryGet(StoreKey.currentUser);
Future<UserDto?> tryGetMyUser() {
return _authUserRepository.get();
}
Stream<UserDto?> watchMyUser() {
return _storeService.watch(StoreKey.currentUser);
return _authUserRepository.watch();
}
Future<UserDto?> refreshMyUser() async {
@ -31,15 +26,17 @@ class UserService {
if (user == null) {
return null;
}
await _storeService.put(StoreKey.currentUser, user);
await _authUserRepository.upsert(user);
return user;
}
Future<String?> createProfileImage(String name, Uint8List image) async {
try {
final path = await _userApiRepository.createProfileImage(name: name, data: image);
final updatedUser = getMyUser();
await _storeService.put(StoreKey.currentUser, updatedUser);
final updatedUser = await tryGetMyUser();
if (updatedUser != null) {
await _authUserRepository.upsert(updatedUser);
}
return path;
} catch (e) {
_log.warning("Failed to upload profile image", e);

View file

@ -1,15 +1,14 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:logging/logging.dart';
Future<void> syncLinkedAlbumsIsolated(ProviderContainer ref) {
final user = Store.tryGet(StoreKey.currentUser);
Future<void> syncLinkedAlbumsIsolated(ProviderContainer ref) async {
final user = await ref.read(authUserRepositoryProvider).get();
if (user == null) {
Logger("SyncLinkedAlbum").warning("No user logged in, skipping linked album sync");
return Future.value();
return;
}
return ref
.read(syncLinkedAlbumServiceProvider)

View file

@ -1,9 +1,7 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
class DriftStoreRepository extends DriftDatabaseRepository {
final Drift _db;
@ -63,8 +61,6 @@ class DriftStoreRepository extends DriftDatabaseRepository {
const (String) => entity.stringValue,
const (bool) => entity.intValue == 1,
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
const (UserDto) =>
entity.stringValue == null ? null : await DriftAuthUserRepository(_db).get(entity.stringValue!),
_ => null,
}
as T?;
@ -75,7 +71,6 @@ class DriftStoreRepository extends DriftDatabaseRepository {
const (String) => (null, value as String),
const (bool) => ((value as bool) ? 1 : 0, null),
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
const (UserDto) => (null, (await DriftAuthUserRepository(_db).upsert(value as UserDto)).id),
_ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
};
return StoreEntityCompanion(id: Value(key.id), intValue: Value(intValue), stringValue: Value(strValue));

View file

@ -13,20 +13,20 @@ class UserRepository {
Stream<Iterable<User>> getAll() => _db.select(_db.userEntity).map(mapToUser).watch();
}
// TODO(shenlong): migrate this to return AuthUser instead
class DriftAuthUserRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftAuthUserRepository(super.db) : _db = db;
Future<UserDto?> get(String id) async {
final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
Selectable<UserDto?> get _authUserQuery => (_db.authUserEntity.select()..limit(1)).asyncMap(_toDto);
if (user == null) {
return null;
}
Future<UserDto?> get() => _authUserQuery.getSingleOrNull();
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
Stream<UserDto?> watch() => _authUserQuery.watchSingleOrNull();
Future<UserDto> _toDto(AuthUserEntityData user) async {
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(user.id));
final metadata = await query.map((row) => row.toDto()).get();
return user.toDto(metadata);
}

View file

@ -5,8 +5,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
@ -85,7 +83,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
final backupSyncManager = ref.watch(backgroundSyncProvider);
Future<void> startBackup() async {
final currentUser = Store.tryGet(StoreKey.currentUser);
final currentUser = ref.read(currentUserProvider);
if (currentUser == null) {
return;
}

View file

@ -8,16 +8,16 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/locales.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/session.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/session.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/view_intent/view_intent_handler.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
@ -311,6 +311,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
final backgroundManager = ref.read(backgroundSyncProvider);
final backupProvider = ref.read(driftBackupProvider.notifier);
final viewIntentHandler = ref.read(viewIntentHandlerProvider);
final authUserRepository = ref.read(authUserRepositoryProvider);
unawaited(
ref
@ -333,9 +334,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
if (syncSuccess) {
await Future.wait([
backgroundManager.hashAssets().then((_) {
unawaited(_resumeBackup(backupProvider));
unawaited(_resumeBackup(backupProvider, authUserRepository));
}),
_resumeBackup(backupProvider),
_resumeBackup(backupProvider, authUserRepository),
// TODO: Bring back when the soft freeze issue is addressed
// backgroundManager.syncCloudIds(),
]);
@ -375,11 +376,11 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
}
}
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
Future<void> _resumeBackup(DriftBackupNotifier notifier, DriftAuthUserRepository authUserRepository) async {
final isEnableBackup = SettingsRepository.instance.appConfig.backup.enabled;
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
final currentUser = await authUserRepository.get();
if (currentUser != null) {
unawaited(notifier.startForegroundBackup(currentUser.id));
}

View file

@ -1,9 +1,7 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
@ -14,6 +12,7 @@ import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/permission.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:logging/logging.dart';
@ -148,7 +147,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
final isEnableBackup = _ref.read(appConfigProvider).backup.enabled;
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
final currentUser = _ref.read(currentUserProvider);
if (currentUser != null) {
await _safeRun(
() => _ref.read(driftBackupProvider.notifier).startForegroundBackup(currentUser.id),

View file

@ -138,7 +138,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
// Get the deviceid from the store if it exists, otherwise generate a new one
final String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
UserDto? user = _userService.tryGetMyUser();
UserDto? user = await _userService.tryGetMyUser();
try {
final serverUser = await _userService.refreshMyUser().timeout(_timeoutDuration);

View file

@ -6,17 +6,18 @@ import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/repositories/partner_api.repository.dart';
final userRepositoryProvider = Provider((ref) => UserRepository(ref.watch(driftProvider)));
final authUserRepositoryProvider = Provider((ref) => DriftAuthUserRepository(ref.watch(driftProvider)));
final userApiRepositoryProvider = Provider((ref) => UserApiRepository(ref.watch(apiServiceProvider).usersApi));
final userServiceProvider = Provider(
(ref) => UserService(
userApiRepository: ref.watch(userApiRepositoryProvider),
storeService: ref.watch(storeServiceProvider),
authUserRepository: ref.watch(authUserRepositoryProvider),
),
);

View file

@ -7,7 +7,7 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
class CurrentUserProvider extends StateNotifier<UserDto?> {
CurrentUserProvider(this._userService) : super(null) {
state = _userService.tryGetMyUser();
unawaited(_userService.tryGetMyUser().then((user) => state = user ?? state));
streamSub = _userService.watchMyUser().listen((user) => state = user ?? state);
}

View file

@ -3,9 +3,7 @@ import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/session.model.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/utils/background_sync.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
@ -120,7 +118,6 @@ class AuthService {
await _backgroundSyncManager.cancel();
await Future.wait([
_authRepository.clearLocalData(),
Store.delete(StoreKey.currentUser),
SessionRepository.instance.clear([SessionKey.accessToken]),
SettingsRepository.instance.clear(const [
.networkAutoEndpointSwitching,

View file

@ -55,6 +55,8 @@ abstract final class Bootstrap {
await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates);
await SessionRepository.ensureInitialized(drift);
await SessionRepository.ensureInitialized(drift);
final settingsRepo = await SettingsRepository.ensureInitialized(drift);
final logDb = await _initLogger(settingsRepository: settingsRepo, shouldBufferLogs: shouldBufferLogs);

View file

@ -49,7 +49,7 @@ void main() {
expect(sut.tryGet(StoreKey.advancedTroubleshooting), _kAdvancedTroubleshooting);
expect(sut.tryGet(StoreKey.version), _kVersion);
// Other keys should be null
expect(sut.tryGet(StoreKey.currentUser), isNull);
expect(sut.tryGet(StoreKey.deviceId), isNull);
});
test('Listens to stream of store updates', () async {
@ -69,11 +69,11 @@ void main() {
});
test('Throws StoreKeyNotFoundException for nonexistent keys', () {
expect(() => sut.get(StoreKey.currentUser), throwsA(isA<StoreKeyNotFoundException>()));
expect(() => sut.get(StoreKey.deviceId), throwsA(isA<StoreKeyNotFoundException>()));
});
test('Returns the stored value for the given key or the defaultValue', () {
expect(sut.get(StoreKey.currentUser, 5), 5);
expect(sut.get(StoreKey.legacyBackupTriggerDelay, 5), 5);
});
});

View file

@ -2,11 +2,10 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import '../../infrastructure/repository.mock.dart';
import '../../service.mocks.dart';
void main() {
// A container with the service's deps overridden but cancellationProvider left
@ -18,7 +17,7 @@ void main() {
localAlbumRepository.overrideWithValue(MockLocalAlbumRepository()),
remoteAlbumRepository.overrideWithValue(MockRemoteAlbumRepository()),
driftAlbumApiRepositoryProvider.overrideWithValue(MockDriftAlbumApiRepository()),
storeServiceProvider.overrideWithValue(MockStoreService()),
authUserRepositoryProvider.overrideWithValue(MockDriftAuthUserRepository()),
],
);
addTearDown(container.dispose);

View file

@ -1,78 +1,62 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
import 'package:mocktail/mocktail.dart';
import '../../fixtures/user.stub.dart';
import '../../infrastructure/repository.mock.dart';
import '../../service.mocks.dart';
void main() {
late UserService sut;
late UserApiRepository mockUserApiRepo;
late StoreService mockStoreService;
late DriftAuthUserRepository mockAuthUserRepo;
setUp(() {
mockUserApiRepo = MockUserApiRepository();
mockStoreService = MockStoreService();
sut = UserService(userApiRepository: mockUserApiRepo, storeService: mockStoreService);
mockAuthUserRepo = MockDriftAuthUserRepository();
sut = UserService(userApiRepository: mockUserApiRepo, authUserRepository: mockAuthUserRepo);
registerFallbackValue(UserStub.admin);
when(() => mockStoreService.get(StoreKey.currentUser)).thenReturn(UserStub.admin);
when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(UserStub.admin);
});
group('getMyUser', () {
test('should return user from store', () {
final result = sut.getMyUser();
expect(result, UserStub.admin);
});
test('should handle user not found scenario', () {
when(() => mockStoreService.get(StoreKey.currentUser)).thenThrow(Exception('User not found'));
expect(() => sut.getMyUser(), throwsA(isA<Exception>()));
});
when(() => mockAuthUserRepo.get()).thenAnswer((_) async => UserStub.admin);
when(() => mockAuthUserRepo.upsert(any())).thenAnswer((_) async => UserStub.admin);
});
group('tryGetMyUser', () {
test('should return user from store', () {
final result = sut.tryGetMyUser();
test('should return the current user from the auth user repository', () async {
final result = await sut.tryGetMyUser();
expect(result, UserStub.admin);
});
test('should return null if user not found', () {
when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(null);
final result = sut.tryGetMyUser();
test('should return null if no user is logged in', () async {
when(() => mockAuthUserRepo.get()).thenAnswer((_) async => null);
final result = await sut.tryGetMyUser();
expect(result, isNull);
});
});
group('watchMyUser', () {
test('should return user stream from store', () {
when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => Stream.value(UserStub.admin));
test('should return the current user stream from the auth user repository', () {
when(() => mockAuthUserRepo.watch()).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());
test('should return an empty stream if no user is logged in', () {
when(() => mockAuthUserRepo.watch()).thenAnswer((_) => const Stream.empty());
final result = sut.watchMyUser();
expect(result, emitsInOrder([]));
});
});
group('refreshMyUser', () {
test('should return user from api and store it', () async {
test('should return user from api and persist it', () async {
when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => UserStub.admin);
when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).thenAnswer((_) async => true);
final result = await sut.refreshMyUser();
verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).called(1);
verify(() => mockAuthUserRepo.upsert(UserStub.admin)).called(1);
expect(result, UserStub.admin);
});
@ -80,7 +64,7 @@ void main() {
when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null);
final result = await sut.refreshMyUser();
verifyNever(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin));
verifyNever(() => mockAuthUserRepo.upsert(any()));
expect(result, isNull);
});
});
@ -88,29 +72,26 @@ void main() {
group('createProfileImage', () {
test('should return profile image path', () async {
const profileImagePath = 'profile.jpg';
final updatedUser = UserStub.admin;
when(
() => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)),
).thenAnswer((_) async => profileImagePath);
when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true);
final result = await sut.createProfileImage(profileImagePath, Uint8List(0));
verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).called(1);
verify(() => mockAuthUserRepo.upsert(UserStub.admin)).called(1);
expect(result, profileImagePath);
});
test('should return null if profile image creation fails', () async {
const profileImagePath = 'profile.jpg';
final updatedUser = UserStub.admin;
when(
() => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)),
).thenThrow(Exception('Failed to create profile image'));
final result = await sut.createProfileImage(profileImagePath, Uint8List(0));
verifyNever(() => mockStoreService.put(StoreKey.currentUser, updatedUser));
verifyNever(() => mockAuthUserRepo.upsert(any()));
expect(result, isNull);
});
});

View file

@ -4,17 +4,13 @@ import 'package:drift/drift.dart' hide isNull;
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import '../../fixtures/user.stub.dart';
const _kTestAccessToken = "#TestToken";
const _kTestVersion = 10;
const _kTestAdvancedTroubleshooting = false;
final _kTestUser = UserStub.admin;
Future<void> _populateStore(Drift db) async {
await db.batch((batch) async {
@ -82,14 +78,6 @@ void main() {
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
expect(advancedTroubleshooting, _kTestAdvancedTroubleshooting);
});
test('converts user', () async {
UserDto? user = await sut.tryGet(StoreKey.currentUser);
expect(user, isNull);
await sut.upsert(StoreKey.currentUser, _kTestUser);
user = await sut.tryGet(StoreKey.currentUser);
expect(user, _kTestUser);
});
});
group('Store Repository Deletes:', () {

View file

@ -48,6 +48,8 @@ class MockSyncMigrationRepository extends Mock implements SyncMigrationRepositor
class MockUserRepository extends Mock implements UserRepository {}
class MockDriftAuthUserRepository extends Mock implements DriftAuthUserRepository {}
class MockPartnerRepository extends Mock implements PartnerRepository {}
// API Repos

View file

@ -40,7 +40,7 @@ void main() {
userService = MockUserService();
when(() => memoryService.getMemoryLane('user-1')).thenAnswer((_) async => []);
when(() => userService.tryGetMyUser()).thenReturn(user());
when(() => userService.tryGetMyUser()).thenAnswer((_) async => user());
when(() => userService.watchMyUser()).thenAnswer((_) => const Stream.empty());
});
@ -49,7 +49,7 @@ void main() {
fakeAsync((async) {
final container = makeContainer();
container.listen(driftMemoryFutureProvider, (_, __) {});
async.flushMicrotasks();
async.elapse(Duration.zero);
verify(() => memoryService.getMemoryLane('user-1')).called(1);
@ -67,7 +67,7 @@ void main() {
fakeAsync((async) {
final container = makeContainer();
final subscription = container.listen(driftMemoryFutureProvider, (_, __) {});
async.flushMicrotasks();
async.elapse(Duration.zero);
verify(() => memoryService.getMemoryLane('user-1')).called(1);
subscription.close();
@ -79,7 +79,7 @@ void main() {
});
test('does not query or arm the timer when memories are disabled', () {
when(() => userService.tryGetMyUser()).thenReturn(user(memoryEnabled: false));
when(() => userService.tryGetMyUser()).thenAnswer((_) async => user(memoryEnabled: false));
fakeAsync((async) {
final container = makeContainer();

View file

@ -153,8 +153,7 @@ class ServiceMocks {
}
void _stubUserService() {
when(user.getMyUser).thenReturn(UserFactory.createDto());
when(user.tryGetMyUser).thenReturn(null);
when(user.tryGetMyUser).thenAnswer((_) async => UserFactory.createDto());
when(user.watchMyUser).thenAnswer((_) => const Stream.empty());
when(user.refreshMyUser).thenAnswer((_) async => null);
when(user.createProfileImage).thenAnswer((_) async => null);
@ -312,10 +311,7 @@ extension type const PartnerServiceStub(MockPartnerService service) implements S
}
extension type const UserServiceStub(MockUserService service) implements Stub<MockUserService> {
UserDto Function() get getMyUser =>
() => service.getMyUser();
UserDto? Function() get tryGetMyUser =>
Future<UserDto?> Function() get tryGetMyUser =>
() => service.tryGetMyUser();
Stream<UserDto?> Function() get watchMyUser =>

View file

@ -50,6 +50,7 @@ class PresentationContext {
List<Override> get overrides => [
currentUserProvider.overrideWith((ref) => CurrentUserProvider(service.user.service)),
authUserProvider.overrideWithValue(currentUser),
assetServiceProvider.overrideWithValue(service.asset.service),
cleanupServiceProvider.overrideWithValue(service.cleanup.service),
remoteAlbumServiceProvider.overrideWithValue(service.album.service),
@ -81,7 +82,7 @@ class PresentationContext {
}
void setup() {
when(service.user.tryGetMyUser).thenReturn(currentUser);
when(service.user.tryGetMyUser).thenAnswer((_) async => currentUser);
}
Future<void> dispose() async {