165 lines
6.2 KiB
Dart
165 lines
6.2 KiB
Dart
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:plezy/connection/connection_registry.dart';
|
|
import 'package:plezy/database/app_database.dart';
|
|
import 'package:plezy/profiles/active_profile_binder.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_registry.dart';
|
|
import 'package:plezy/profiles/profile_registry.dart';
|
|
import 'package:plezy/providers/multi_server_provider.dart';
|
|
import 'package:plezy/services/data_aggregation_service.dart';
|
|
import 'package:plezy/services/multi_server_manager.dart';
|
|
import 'package:plezy/services/storage_service.dart';
|
|
|
|
import '../test_helpers/prefs.dart';
|
|
|
|
void main() {
|
|
late AppDatabase db;
|
|
late ConnectionRegistry connections;
|
|
late ProfileConnectionRegistry profileConnections;
|
|
late ProfileRegistry profiles;
|
|
late PlexHomeService plexHome;
|
|
late ActiveProfileProvider activeProfile;
|
|
late MultiServerManager manager;
|
|
late MultiServerProvider multiServerProvider;
|
|
late ActiveProfileBinder binder;
|
|
late StorageService storage;
|
|
late bool shouldDeferInitialBind;
|
|
|
|
setUp(() async {
|
|
resetSharedPreferencesForTest();
|
|
db = AppDatabase.forTesting(NativeDatabase.memory());
|
|
connections = ConnectionRegistry(db);
|
|
profileConnections = ProfileConnectionRegistry(db);
|
|
profiles = ProfileRegistry(db);
|
|
storage = await StorageService.getInstance();
|
|
plexHome = PlexHomeService(
|
|
connections: connections,
|
|
profileConnections: profileConnections,
|
|
storage: storage,
|
|
plexHomeUserFetcher: (_) async => const [],
|
|
);
|
|
activeProfile = ActiveProfileProvider(
|
|
registry: profiles,
|
|
plexHome: plexHome,
|
|
connections: connections,
|
|
storage: storage,
|
|
);
|
|
manager = MultiServerManager();
|
|
multiServerProvider = MultiServerProvider(manager, DataAggregationService(manager));
|
|
shouldDeferInitialBind = false;
|
|
binder = ActiveProfileBinder(
|
|
activeProfile: activeProfile,
|
|
connections: connections,
|
|
profileConnections: profileConnections,
|
|
serverManager: manager,
|
|
multiServerProvider: multiServerProvider,
|
|
pinPrompt: (_, {String? errorMessage}) async => null,
|
|
shouldDeferInitialBind: (_) async => shouldDeferInitialBind,
|
|
);
|
|
});
|
|
|
|
tearDown(() async {
|
|
binder.dispose();
|
|
multiServerProvider.dispose();
|
|
await activeProfile.resetForTesting();
|
|
activeProfile.dispose();
|
|
await plexHome.dispose();
|
|
await db.close();
|
|
});
|
|
|
|
Future<Profile> createActiveLocalProfile(String id) async {
|
|
final profile = Profile.local(id: id, displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
|
|
await profiles.upsert(profile);
|
|
await storage.setActiveProfileId(profile.id);
|
|
await activeProfile.initialize();
|
|
return profile;
|
|
}
|
|
|
|
test('local profile with no connections binds successfully with empty visibility', () async {
|
|
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
|
|
await profiles.upsert(profile);
|
|
await storage.setActiveProfileId(profile.id);
|
|
await activeProfile.initialize();
|
|
|
|
await binder.rebindActive();
|
|
|
|
expect(activeProfile.lastBindingSucceeded, isTrue);
|
|
expect(binder.debugLastBoundProfileId, profile.id);
|
|
expect(multiServerProvider.serverIds, isEmpty);
|
|
});
|
|
|
|
test('started binder does not loop forever after empty local bind', () async {
|
|
final profile = Profile.local(id: 'local-empty', displayName: 'Empty', createdAt: DateTime(2026, 1, 1));
|
|
await profiles.upsert(profile);
|
|
await storage.setActiveProfileId(profile.id);
|
|
await activeProfile.initialize();
|
|
|
|
var notifications = 0;
|
|
activeProfile.addListener(() => notifications++);
|
|
binder.start();
|
|
|
|
await Future<void>.delayed(const Duration(milliseconds: 50));
|
|
|
|
expect(activeProfile.isBinding, isFalse);
|
|
expect(activeProfile.lastBindingSucceeded, isTrue);
|
|
expect(binder.debugLastBoundProfileId, profile.id);
|
|
expect(notifications, lessThan(8));
|
|
});
|
|
|
|
test('initial bind can be deferred until profile selection', () async {
|
|
final profile = await createActiveLocalProfile('local-deferred');
|
|
shouldDeferInitialBind = true;
|
|
|
|
await binder.rebindActive();
|
|
|
|
expect(activeProfile.lastBindingSucceeded, isTrue);
|
|
expect(activeProfile.isBinding, isFalse);
|
|
expect(binder.debugLastBoundProfileId, isNull);
|
|
expect(binder.consumeUserInitiatedActivation(profile.id), isFalse);
|
|
expect(multiServerProvider.serverIds, isEmpty);
|
|
});
|
|
|
|
test('user initiated activation bypasses initial bind defer', () async {
|
|
final profile = await createActiveLocalProfile('local-user-initiated');
|
|
shouldDeferInitialBind = true;
|
|
binder.markUserInitiatedActivation(profile.id);
|
|
|
|
await binder.rebindActive();
|
|
|
|
expect(activeProfile.lastBindingSucceeded, isTrue);
|
|
expect(binder.debugLastBoundProfileId, profile.id);
|
|
expect(binder.consumeUserInitiatedActivation(profile.id), isFalse);
|
|
});
|
|
|
|
group('Plex Home token cache policy', () {
|
|
test('cold start uses cached token instead of forcing PIN revalidation', () {
|
|
expect(shouldUsePlexHomeTokenCache(preVerified: false, hasBoundOnce: false), isTrue);
|
|
});
|
|
|
|
test('preverified activation uses cache once regardless of setting', () {
|
|
expect(shouldUsePlexHomeTokenCache(preVerified: true, hasBoundOnce: false), isTrue);
|
|
});
|
|
|
|
test('user-initiated switches bypass cache after first bind', () {
|
|
expect(shouldUsePlexHomeTokenCache(preVerified: false, hasBoundOnce: true), isFalse);
|
|
});
|
|
|
|
test('preverified activation flag is consumed once per profile', () {
|
|
expect(binder.consumePlexHomePreVerified('plex-home-x'), isFalse);
|
|
binder.markPlexHomePreVerified('plex-home-x');
|
|
expect(binder.consumePlexHomePreVerified('plex-home-x'), isTrue);
|
|
expect(binder.consumePlexHomePreVerified('plex-home-x'), isFalse);
|
|
});
|
|
|
|
test('preverified activation flag isolates entries per profile id', () {
|
|
binder.markPlexHomePreVerified('plex-home-a');
|
|
binder.markPlexHomePreVerified('plex-home-b');
|
|
expect(binder.consumePlexHomePreVerified('plex-home-b'), isTrue);
|
|
expect(binder.consumePlexHomePreVerified('plex-home-a'), isTrue);
|
|
});
|
|
});
|
|
}
|