refactor: type server identifiers

This commit is contained in:
edde746
2026-06-01 11:06:03 +02:00
parent 7b59dac26b
commit 74b8dc4561
258 changed files with 2296 additions and 2080 deletions
@@ -1,4 +1,5 @@
import 'package:drift/native.dart';
import 'package:plezy/media/ids.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
@@ -68,7 +69,7 @@ class _RecordingMediaClient implements MediaServerClient {
_RecordingMediaClient({required this.serverId, required this.backend});
@override
final String serverId;
final ServerId serverId;
@override
final MediaBackend backend;
@@ -200,7 +201,7 @@ void main() {
});
// No SettingsService initialized, no client registered → default 90/100.
expect(svc.getWatchedThreshold('unknown-server'), 0.9);
expect(svc.getWatchedThreshold(ServerId('unknown-server')), 0.9);
});
});
@@ -220,7 +221,7 @@ void main() {
var notifications = 0;
svc.addListener(() => notifications++);
await svc.queueMarkWatched(serverId: 'srv', itemId: '42');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '42');
expect(await svc.getPendingSyncCount(), 1);
// ChangeNotifier emission was synchronous in the queue helper.
@@ -242,7 +243,7 @@ void main() {
await db.close();
});
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '42');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '42');
final action = await db.getLatestWatchAction('srv:42');
expect(action, isNotNull);
@@ -257,13 +258,13 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '42');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '42');
expect(await svc.getPendingSyncCount(), 1);
// The DB layer's insertWatchAction deletes any prior entries for the
// same globalKey before inserting — so flipping watched/unwatched keeps
// a single row.
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '42');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '42');
expect(await svc.getPendingSyncCount(), 1);
final action = await db.getLatestWatchAction('srv:42');
@@ -278,9 +279,9 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkWatched(serverId: 'srv', itemId: '2');
await svc.queueMarkUnwatched(serverId: 'other', itemId: '1');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '2');
await svc.queueMarkUnwatched(serverId: ServerId('other'), itemId: '1');
expect(await svc.getPendingSyncCount(), 3);
@@ -299,7 +300,7 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '42');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '42');
await svc.syncPendingItems();
@@ -317,7 +318,7 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '42');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '42');
var action = await db.getLatestWatchAction('srv:42');
for (var i = 0; i < OfflineWatchSyncService.maxSyncAttempts; i++) {
await db.updateSyncAttempt(action!.id, 'server error');
@@ -340,9 +341,9 @@ void main() {
await db.close();
});
final client = _RecordingMediaClient(serverId: 'srv', backend: MediaBackend.plex);
final client = _RecordingMediaClient(serverId: ServerId('srv'), backend: MediaBackend.plex);
mgr.debugRegisterClientForTesting(client);
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 50000, duration: 100000);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 50000, duration: 100000);
final queued = await db.getLatestWatchAction('srv:42');
await svc.syncPendingItems();
@@ -365,9 +366,9 @@ void main() {
await db.close();
});
final client = _RecordingMediaClient(serverId: 'srv', backend: MediaBackend.plex);
final client = _RecordingMediaClient(serverId: ServerId('srv'), backend: MediaBackend.plex);
mgr.debugRegisterClientForTesting(client);
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 50000, duration: null);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 50000, duration: null);
await svc.syncPendingItems();
@@ -389,9 +390,9 @@ void main() {
await db.close();
});
final client = _RecordingMediaClient(serverId: 'srv', backend: MediaBackend.plex);
final client = _RecordingMediaClient(serverId: ServerId('srv'), backend: MediaBackend.plex);
mgr.debugRegisterClientForTesting(client);
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 95000, duration: 100000);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 95000, duration: 100000);
final queued = await db.getLatestWatchAction('srv:42');
await svc.syncPendingItems();
@@ -423,7 +424,7 @@ void main() {
});
// 50% progress → below default 0.9 threshold.
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 50, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 50, duration: 100);
final action = await db.getLatestWatchAction('srv:42');
expect(action, isNotNull);
@@ -441,7 +442,7 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 50, duration: null);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 50, duration: null);
final action = await db.getLatestWatchAction('srv:42');
expect(action, isNotNull);
@@ -459,7 +460,7 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 95, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 95, duration: 100);
final action = await db.getLatestWatchAction('srv:42');
expect(action!.shouldMarkWatched, isTrue);
@@ -473,8 +474,8 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 10, duration: 100);
await svc.queueProgressUpdate(serverId: 'srv', itemId: '42', viewOffset: 20, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 10, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '42', viewOffset: 20, duration: 100);
// upsertProgressAction merges by globalKey — only ONE row.
expect(await svc.getPendingSyncCount(), 1);
@@ -505,7 +506,7 @@ void main() {
mgr.dispose();
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
expect(await svc.getLocalWatchStatus('srv:1'), isTrue);
});
@@ -516,7 +517,7 @@ void main() {
mgr.dispose();
await db.close();
});
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '1');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '1');
expect(await svc.getLocalWatchStatus('srv:1'), isFalse);
});
@@ -529,11 +530,11 @@ void main() {
});
// Below threshold is resume-only; it must not override stale watched metadata.
await svc.queueProgressUpdate(serverId: 'srv', itemId: '1', viewOffset: 50, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '1', viewOffset: 50, duration: 100);
expect(await svc.getLocalWatchStatus('srv:1'), isNull);
// Above threshold → shouldMarkWatched=true → status=true.
await svc.queueProgressUpdate(serverId: 'srv', itemId: '2', viewOffset: 99, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '2', viewOffset: 99, duration: 100);
expect(await svc.getLocalWatchStatus('srv:2'), isTrue);
});
});
@@ -561,10 +562,10 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
expect(await svc.getLocalViewOffset('srv:1'), isNull);
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '2');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '2');
expect(await svc.getLocalViewOffset('srv:2'), isNull);
});
@@ -576,7 +577,7 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '1', viewOffset: 12345, duration: 60000);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '1', viewOffset: 12345, duration: 60000);
expect(await svc.getLocalViewOffset('srv:1'), 12345);
});
@@ -588,13 +589,13 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '1', viewOffset: 5000, duration: 10000);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '1', viewOffset: 5000, duration: 10000);
expect(await svc.getLocalViewOffset('srv:1'), 5000);
// Manual "watched" wipes the progress row (insertWatchAction deletes
// by globalKey first), so getLocalViewOffset reads the new row whose
// actionType != 'progress' → null.
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
expect(await svc.getLocalViewOffset('srv:1'), isNull);
});
});
@@ -614,9 +615,9 @@ void main() {
expect(await svc.getPendingSyncCount(), 0);
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '2');
await svc.queueProgressUpdate(serverId: 'srv', itemId: '3', viewOffset: 50, duration: 100);
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '2');
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '3', viewOffset: 50, duration: 100);
expect(await svc.getPendingSyncCount(), 3);
});
@@ -628,8 +629,8 @@ void main() {
await db.close();
});
await svc.queueProgressUpdate(serverId: 'srv', itemId: '1', viewOffset: 10, duration: 100);
await svc.queueProgressUpdate(serverId: 'srv', itemId: '1', viewOffset: 20, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '1', viewOffset: 10, duration: 100);
await svc.queueProgressUpdate(serverId: ServerId('srv'), itemId: '1', viewOffset: 20, duration: 100);
expect(await svc.getPendingSyncCount(), 1);
});
});
@@ -657,10 +658,10 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '2');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '2');
await svc.queueProgressUpdate(
serverId: 'srv',
serverId: ServerId('srv'),
itemId: '3',
viewOffset: 99,
duration: 100, // above threshold
@@ -691,7 +692,7 @@ void main() {
mgr.debugRegisterJellyfinClientForTesting(activeUserB);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -699,14 +700,14 @@ void main() {
status: 3,
);
await db.insertWatchAction(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
actionType: OfflineActionType.unwatched.id,
);
await Future<void>.delayed(const Duration(milliseconds: 2));
await db.insertWatchAction(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-b',
ratingKey: 'item-1',
actionType: OfflineActionType.watched.id,
@@ -725,14 +726,14 @@ void main() {
});
svc.setActiveProfileId('profile-a');
await svc.queueMarkWatched(serverId: 'plex-machine', itemId: 'item-1');
await svc.queueMarkWatched(serverId: ServerId('plex-machine'), itemId: 'item-1');
expect(await svc.getLocalWatchStatus('plex-machine:item-1'), isTrue);
expect(await svc.getPendingSyncCount(), 1);
svc.setActiveProfileId('profile-b');
expect(await svc.getLocalWatchStatus('plex-machine:item-1'), isNull);
expect(await svc.getPendingSyncCount(), 0);
await svc.queueMarkUnwatched(serverId: 'plex-machine', itemId: 'item-1');
await svc.queueMarkUnwatched(serverId: ServerId('plex-machine'), itemId: 'item-1');
expect(await svc.getLocalWatchStatus('plex-machine:item-1'), isFalse);
expect(await svc.getPendingSyncCount(), 1);
@@ -752,7 +753,7 @@ void main() {
});
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -760,7 +761,7 @@ void main() {
status: 3,
);
await svc.queueMarkWatched(serverId: 'jf-machine', itemId: 'item-1');
await svc.queueMarkWatched(serverId: ServerId('jf-machine'), itemId: 'item-1');
final queued = await db.getPendingWatchActions();
expect(queued.single.clientScopeId, 'jf-machine/user-a');
@@ -782,7 +783,7 @@ void main() {
mgr.debugRegisterJellyfinClientForTesting(activeUserB);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -790,7 +791,7 @@ void main() {
status: 3,
);
await db.upsertProgressAction(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
viewOffset: 5000,
@@ -799,7 +800,7 @@ void main() {
);
await Future<void>.delayed(const Duration(milliseconds: 2));
await db.upsertProgressAction(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-b',
ratingKey: 'item-1',
viewOffset: 90000,
@@ -822,7 +823,7 @@ void main() {
});
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -837,7 +838,7 @@ void main() {
addTearDown(activeUserB.close);
mgr.debugRegisterJellyfinClientForTesting(activeUserB);
final returnedScope = await svc.queueMarkWatched(serverId: 'jf-machine', itemId: 'item-1');
final returnedScope = await svc.queueMarkWatched(serverId: ServerId('jf-machine'), itemId: 'item-1');
final queued = await db.getPendingWatchActions();
expect(returnedScope, 'jf-machine/user-b');
@@ -876,7 +877,7 @@ void main() {
addTearDown(userB.close);
mgr.debugRegisterJellyfinClientForTesting(userA);
await svc.queueMarkWatched(serverId: 'jf-machine', itemId: 'item-1');
await svc.queueMarkWatched(serverId: ServerId('jf-machine'), itemId: 'item-1');
final queued = await db.getPendingWatchActions();
expect(queued.single.clientScopeId, 'jf-machine/user-a');
@@ -915,7 +916,11 @@ void main() {
addTearDown(client.close);
mgr.debugRegisterJellyfinClientForTesting(client);
await db.insertWatchAction(serverId: 'jf-machine', ratingKey: 'item-1', actionType: OfflineActionType.watched.id);
await db.insertWatchAction(
serverId: ServerId('jf-machine'),
ratingKey: 'item-1',
actionType: OfflineActionType.watched.id,
);
await svc.syncPendingItems();
@@ -955,14 +960,18 @@ void main() {
addTearDown(userB.close);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
type: 'movie',
status: 3,
);
await db.insertWatchAction(serverId: 'jf-machine', ratingKey: 'item-1', actionType: OfflineActionType.watched.id);
await db.insertWatchAction(
serverId: ServerId('jf-machine'),
ratingKey: 'item-1',
actionType: OfflineActionType.watched.id,
);
mgr.debugRegisterJellyfinClientForTesting(userA);
mgr.debugRegisterJellyfinClientForTesting(userB);
@@ -1009,7 +1018,7 @@ void main() {
addTearDown(sub.cancel);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -1058,7 +1067,7 @@ void main() {
addTearDown(sub.cancel);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-b',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -1119,7 +1128,7 @@ void main() {
addTearDown(userB.close);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'ep-1',
globalKey: 'jf-machine:ep-1',
@@ -1157,7 +1166,7 @@ void main() {
addTearDown(userB.close);
await db.insertDownload(
serverId: 'jf-machine',
serverId: ServerId('jf-machine'),
clientScopeId: 'jf-machine/user-a',
ratingKey: 'item-1',
globalKey: 'jf-machine:item-1',
@@ -1187,8 +1196,8 @@ void main() {
await db.close();
});
await svc.queueMarkWatched(serverId: 'srv', itemId: '1');
await svc.queueMarkUnwatched(serverId: 'srv', itemId: '2');
await svc.queueMarkWatched(serverId: ServerId('srv'), itemId: '1');
await svc.queueMarkUnwatched(serverId: ServerId('srv'), itemId: '2');
expect(await svc.getPendingSyncCount(), 2);
var notifications = 0;