fix(stalker): reject replaced late edit targets

This commit is contained in:
4gray committed 2026-08-09 18:23:34 +02:00
1 parent e4bee6da40
commit fabffcc902
10 files changed
+102 -17

No files matched your search

@@ -87,13 +87,28 @@ describe('AppStalkerPlaylistConnectionEditorService', () => {
});
it('resolves a simple portal and clears the previous full session', async () => {
const sourcePlaylist = {
...draft,
portalUrl: 'https://old.example.com/server/load.php',
};
discovery.discover.mockResolvedValue({
status: 'resolved',
portalUrl: 'https://portal.example.com/portal.php',
isFullStalkerPortal: false,
});
const result = await service.resolveConnection(draft);
const result = await service.resolveConnection(draft, sourcePlaylist);
expect(stalkerSession.beginEditDiscovery).toHaveBeenCalledWith(
expect.objectContaining({
portalUrl: draft.portalUrl,
stalkerSerialNumber: 'SERIAL',
}),
expect.objectContaining({
portalUrl: sourcePlaylist.portalUrl,
stalkerSerialNumber: sourcePlaylist.stalkerSerialNumber,
})
);
expect(discovery.discover).toHaveBeenCalledWith(
draft.portalUrl,
@@ -88,7 +88,8 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
}
async resolveConnection(
playlist: PlaylistMeta
playlist: PlaylistMeta,
sourcePlaylist: PlaylistMeta = playlist
): Promise<StalkerPlaylistConnectionResult> {
const identity = normalizeStalkerPortalIdentity({
serialNumber: playlist.stalkerSerialNumber,
@@ -106,7 +107,8 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
stalkerSignature2: identity.signature2 ?? '',
};
const fence = await this.beginEditFence(
this.toRuntimePlaylist(normalizedPlaylist)
this.toRuntimePlaylist(normalizedPlaylist),
this.toRuntimePlaylist(sourcePlaylist)
);
let outcome: Awaited<
ReturnType<StalkerPortalDiscoveryService['discover']>
@@ -207,7 +209,8 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
}
private async beginEditFence(
playlist: Playlist
playlist: Playlist,
sourcePlaylist: Playlist = playlist
): Promise<StalkerEditFence> {
// Both fences are installed synchronously before either drain is
// awaited. No new authentication or lazy repair can start while the
@@ -217,7 +220,10 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
);
let fence: StalkerEditFence | undefined;
try {
fence = await this.stalkerSession.beginEditDiscovery(playlist);
fence = await this.stalkerSession.beginEditDiscovery(
playlist,
sourcePlaylist
);
await repairDrain;
this.editFences.set(playlist._id, fence);
return fence;