273 lines
9.0 KiB
Dart
273 lines
9.0 KiB
Dart
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:plezy/connection/connection.dart';
|
|
import 'package:plezy/connection/connection_registry.dart';
|
|
import 'package:plezy/database/app_database.dart';
|
|
import 'package:plezy/models/plex/plex_home_user.dart';
|
|
import 'package:plezy/profiles/active_profile_provider.dart';
|
|
import 'package:plezy/profiles/plex_home_service.dart';
|
|
import 'package:plezy/profiles/profile.dart';
|
|
import 'package:plezy/profiles/profile_connection.dart';
|
|
import 'package:plezy/profiles/profile_connection_registry.dart';
|
|
import 'package:plezy/profiles/profile_registry.dart';
|
|
import 'package:plezy/providers/user_profile_provider.dart';
|
|
import 'package:plezy/services/multi_server_manager.dart';
|
|
import 'package:plezy/services/storage_service.dart';
|
|
|
|
import '../test_helpers/prefs.dart';
|
|
|
|
void main() {
|
|
setUp(resetSharedPreferencesForTest);
|
|
|
|
group('UserProfileProvider (settings-only)', () {
|
|
test('starts with all-null state and no error', () {
|
|
final p = UserProfileProvider();
|
|
expect(p.profileSettings, isNull);
|
|
expect(p.isLoading, isFalse);
|
|
expect(p.error, isNull);
|
|
p.dispose();
|
|
});
|
|
|
|
test('refreshProfileSettings without a stored token is a no-op', () async {
|
|
final p = UserProfileProvider();
|
|
var notified = 0;
|
|
p.addListener(() => notified++);
|
|
await p.refreshProfileSettings();
|
|
// No token → no API call → no notify, no error.
|
|
expect(notified, 0);
|
|
expect(p.profileSettings, isNull);
|
|
p.dispose();
|
|
});
|
|
|
|
test('logout without initialization is safe', () async {
|
|
final p = UserProfileProvider();
|
|
await p.logout();
|
|
expect(p.profileSettings, isNull);
|
|
expect(p.error, isNull);
|
|
p.dispose();
|
|
});
|
|
|
|
test('safeNotifyListeners after dispose does not throw', () async {
|
|
final p = UserProfileProvider();
|
|
p.dispose();
|
|
await p.logout();
|
|
});
|
|
|
|
test('settings connection follows the profile default row', () async {
|
|
final db = AppDatabase.forTesting(NativeDatabase.memory());
|
|
final connections = ConnectionRegistry(db);
|
|
final profileConnections = ProfileConnectionRegistry(db);
|
|
final profiles = ProfileRegistry(db);
|
|
final storage = await StorageService.getInstance();
|
|
final plexHome = PlexHomeService(
|
|
connections: connections,
|
|
profileConnections: profileConnections,
|
|
storage: storage,
|
|
plexHomeUserFetcher: (_) async => const [],
|
|
);
|
|
final active = ActiveProfileProvider(
|
|
registry: profiles,
|
|
plexHome: plexHome,
|
|
connections: connections,
|
|
storage: storage,
|
|
);
|
|
final manager = MultiServerManager();
|
|
addTearDown(() async {
|
|
manager.dispose();
|
|
await active.resetForTesting();
|
|
active.dispose();
|
|
await plexHome.dispose();
|
|
await db.close();
|
|
});
|
|
|
|
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
|
|
final plex = PlexAccountConnection(
|
|
id: 'plex-a',
|
|
accountToken: 'plex-token',
|
|
clientIdentifier: 'client-a',
|
|
accountLabel: 'Plex',
|
|
createdAt: DateTime(2026, 1, 1),
|
|
);
|
|
final jellyfin = JellyfinConnection(
|
|
id: 'jf-machine/user-a',
|
|
baseUrl: 'https://jf.example.com',
|
|
serverName: 'Jellyfin',
|
|
serverMachineId: 'jf-machine',
|
|
userId: 'user-a',
|
|
userName: 'User A',
|
|
accessToken: 'jf-token',
|
|
deviceId: 'device-a',
|
|
createdAt: DateTime(2026, 1, 1),
|
|
);
|
|
await profiles.upsert(profile);
|
|
await connections.upsert(plex);
|
|
await connections.upsert(jellyfin);
|
|
await profileConnections.upsert(
|
|
ProfileConnection(
|
|
profileId: profile.id,
|
|
connectionId: plex.id,
|
|
userToken: 'plex-user-token',
|
|
userIdentifier: 'plex-user',
|
|
isDefault: true,
|
|
),
|
|
makeDefault: true,
|
|
);
|
|
await profileConnections.upsert(
|
|
ProfileConnection(profileId: profile.id, connectionId: jellyfin.id, userIdentifier: jellyfin.userId),
|
|
);
|
|
await storage.setActiveProfileId(profile.id);
|
|
await active.initialize();
|
|
|
|
final p = UserProfileProvider()
|
|
..attach(
|
|
connections: connections,
|
|
activeProfile: active,
|
|
profileConnections: profileConnections,
|
|
serverManager: manager,
|
|
);
|
|
addTearDown(p.dispose);
|
|
|
|
expect(await p.debugResolveActiveSettingsConnectionForTesting(), isA<PlexAccountConnection>());
|
|
|
|
await profileConnections.setDefault(profile.id, jellyfin.id);
|
|
|
|
expect(await p.debugResolveActiveSettingsConnectionForTesting(), isA<JellyfinConnection>());
|
|
});
|
|
|
|
test('watches Plex Home profile connection rows', () async {
|
|
final db = AppDatabase.forTesting(NativeDatabase.memory());
|
|
final connections = ConnectionRegistry(db);
|
|
final profileConnections = ProfileConnectionRegistry(db);
|
|
final profiles = ProfileRegistry(db);
|
|
final storage = await StorageService.getInstance();
|
|
final plexHome = PlexHomeService(
|
|
connections: connections,
|
|
profileConnections: profileConnections,
|
|
storage: storage,
|
|
plexHomeUserFetcher: (_) async => [_homeUser(uuid: 'home-user-a', title: 'Home User')],
|
|
);
|
|
final active = ActiveProfileProvider(
|
|
registry: profiles,
|
|
plexHome: plexHome,
|
|
connections: connections,
|
|
storage: storage,
|
|
);
|
|
final manager = MultiServerManager();
|
|
addTearDown(() async {
|
|
manager.dispose();
|
|
await active.resetForTesting();
|
|
active.dispose();
|
|
await plexHome.dispose();
|
|
await db.close();
|
|
});
|
|
|
|
final account = PlexAccountConnection(
|
|
id: 'plex-a',
|
|
accountToken: 'account-token-a',
|
|
clientIdentifier: 'client-a',
|
|
accountLabel: 'Plex A',
|
|
createdAt: DateTime(2026, 1, 1),
|
|
);
|
|
await connections.upsert(account);
|
|
await plexHome.refresh(account);
|
|
await storage.setActiveProfileId(plexHomeProfileId(accountConnectionId: account.id, homeUserUuid: 'home-user-a'));
|
|
await active.initialize();
|
|
|
|
final p = UserProfileProvider()
|
|
..attach(
|
|
connections: connections,
|
|
activeProfile: active,
|
|
profileConnections: profileConnections,
|
|
serverManager: manager,
|
|
);
|
|
addTearDown(p.dispose);
|
|
|
|
expect(p.debugWatchedProfileConnectionProfileId, active.activeId);
|
|
});
|
|
|
|
test('Plex token fallback uses the selected local profile account', () async {
|
|
final db = AppDatabase.forTesting(NativeDatabase.memory());
|
|
final connections = ConnectionRegistry(db);
|
|
final profileConnections = ProfileConnectionRegistry(db);
|
|
final profiles = ProfileRegistry(db);
|
|
final storage = await StorageService.getInstance();
|
|
final plexHome = PlexHomeService(
|
|
connections: connections,
|
|
profileConnections: profileConnections,
|
|
storage: storage,
|
|
plexHomeUserFetcher: (_) async => const [],
|
|
);
|
|
final active = ActiveProfileProvider(
|
|
registry: profiles,
|
|
plexHome: plexHome,
|
|
connections: connections,
|
|
storage: storage,
|
|
);
|
|
final manager = MultiServerManager();
|
|
addTearDown(() async {
|
|
manager.dispose();
|
|
await active.resetForTesting();
|
|
active.dispose();
|
|
await plexHome.dispose();
|
|
await db.close();
|
|
});
|
|
|
|
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
|
|
final accountA = PlexAccountConnection(
|
|
id: 'plex-a',
|
|
accountToken: 'wrong-owner-token',
|
|
clientIdentifier: 'client-a',
|
|
accountLabel: 'Plex A',
|
|
createdAt: DateTime(2026, 1, 1),
|
|
);
|
|
final accountB = PlexAccountConnection(
|
|
id: 'plex-b',
|
|
accountToken: 'selected-owner-token',
|
|
clientIdentifier: 'client-b',
|
|
accountLabel: 'Plex B',
|
|
createdAt: DateTime(2026, 1, 1),
|
|
);
|
|
await profiles.upsert(profile);
|
|
await connections.upsert(accountA);
|
|
await connections.upsert(accountB);
|
|
await profileConnections.upsert(
|
|
ProfileConnection(
|
|
profileId: profile.id,
|
|
connectionId: accountB.id,
|
|
userIdentifier: 'home-user-b',
|
|
isDefault: true,
|
|
),
|
|
makeDefault: true,
|
|
);
|
|
await storage.setActiveProfileId(profile.id);
|
|
await active.initialize();
|
|
|
|
final p = UserProfileProvider()
|
|
..attach(
|
|
connections: connections,
|
|
activeProfile: active,
|
|
profileConnections: profileConnections,
|
|
serverManager: manager,
|
|
);
|
|
addTearDown(p.dispose);
|
|
|
|
expect(await p.debugResolveActivePlexUserTokenForTesting(), 'selected-owner-token');
|
|
});
|
|
});
|
|
}
|
|
|
|
PlexHomeUser _homeUser({required String uuid, required String title}) {
|
|
return PlexHomeUser(
|
|
id: 1,
|
|
uuid: uuid,
|
|
title: title,
|
|
thumb: '',
|
|
hasPassword: false,
|
|
restricted: false,
|
|
updatedAt: null,
|
|
admin: true,
|
|
guest: false,
|
|
protected: false,
|
|
);
|
|
}
|