Files
plezy/test/providers/trakt_account_provider_test.dart
T

144 lines
4.7 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/providers/trakt_account_provider.dart';
import 'package:plezy/services/base_shared_preferences_service.dart';
import 'package:plezy/services/trackers/tracker_account_store.dart';
import 'package:plezy/services/trackers/tracker_constants.dart';
import 'package:plezy/services/trackers/tracker_session.dart';
import 'package:plezy/services/trakt/trakt_sync_service.dart';
import '../test_helpers/prefs.dart';
final _store = trackerAccountStore(TrackerService.trakt);
TrackerSession _session({String? username, String accessToken = 'at', String refreshToken = 'rt'}) {
return TrackerSession(
accessToken: accessToken,
refreshToken: refreshToken,
expiresAt: DateTime.now().millisecondsSinceEpoch ~/ 1000 + 3600,
scope: 'public',
createdAt: DateTime.now().millisecondsSinceEpoch ~/ 1000,
username: username,
);
}
void main() {
setUp(resetSharedPreferencesForTest);
group('TraktAccountProvider', () {
test('starts disconnected with null session', () {
final p = TraktAccountProvider();
expect(p.session, isNull);
expect(p.isConnected, isFalse);
expect(p.username, isNull);
expect(p.isConnecting, isFalse);
p.dispose();
});
test('onActiveProfileChanged loads stored session and notifies', () async {
// Pre-seed the store for a specific profile uuid.
const uuid = 'profile-1';
await _store.save(uuid, _session(username: 'alice'));
// Reset cached singletons so the provider reads fresh prefs state.
BaseSharedPreferencesService.resetForTesting();
final p = TraktAccountProvider();
var notified = 0;
p.addListener(() => notified++);
await p.onActiveProfileChanged(uuid);
await TraktSyncService.instance.flushQueue();
expect(p.isConnected, isTrue);
expect(p.username, 'alice');
expect(p.session?.accessToken, 'at');
// _setSessionAndRebind notifies once.
expect(notified, greaterThanOrEqualTo(1));
p.dispose();
});
test('onActiveProfileChanged with unknown uuid clears session', () async {
const uuid = 'profile-1';
await _store.save(uuid, _session(username: 'alice'));
BaseSharedPreferencesService.resetForTesting();
final p = TraktAccountProvider();
await p.onActiveProfileChanged(uuid);
expect(p.isConnected, isTrue);
// Switch to a profile with no stored session.
await p.onActiveProfileChanged('other-profile');
await TraktSyncService.instance.flushQueue();
expect(p.isConnected, isFalse);
expect(p.username, isNull);
p.dispose();
});
test('onActiveProfileChanged with null uuid loads from empty/global slot', () async {
final p = TraktAccountProvider();
await p.onActiveProfileChanged(null);
expect(p.isConnected, isFalse);
p.dispose();
});
test('disconnect with no session clears state and notifies', () async {
final p = TraktAccountProvider();
var notified = 0;
p.addListener(() => notified++);
await p.disconnect();
expect(p.isConnected, isFalse);
expect(p.session, isNull);
// _setSessionAndRebind always notifies.
expect(notified, 1);
p.dispose();
});
test('late refresh update after disconnect does not restore session', () async {
const uuid = 'profile-1';
await _store.save(uuid, _session(username: 'alice'));
BaseSharedPreferencesService.resetForTesting();
final p = TraktAccountProvider();
await p.onActiveProfileChanged(uuid);
final staleGeneration = p.debugBindingGenerationForTesting;
await p.disconnect();
await TraktSyncService.instance.flushQueue();
expect(p.isConnected, isFalse);
expect(await _store.load(uuid), isNull);
p.debugHandleSessionUpdatedForTesting(
uuid,
staleGeneration,
_session(accessToken: 'late-at', refreshToken: 'late-rt', username: 'alice'),
);
await Future<void>.delayed(Duration.zero);
expect(p.isConnected, isFalse);
expect(await _store.load(uuid), isNull);
p.dispose();
});
test('cancelConnect is a no-op when not connecting', () {
final p = TraktAccountProvider();
// Should not throw when no completer exists.
expect(() => p.cancelConnect(), returnsNormally);
expect(p.isConnecting, isFalse);
p.dispose();
});
test('safeNotifyListeners after dispose is a no-op', () async {
final p = TraktAccountProvider();
p.dispose();
// After dispose, calling onActiveProfileChanged still runs the rebind
// path; safeNotifyListeners must swallow the post-dispose notification
// without throwing.
await p.onActiveProfileChanged('any-uuid');
});
});
}