Files
iptvnator/libs/services/src/lib/server-storage/server-migration-plan.spec.ts
T
4grayandClaude Opus 5.5 676e3adcd4 fix(server-storage): keep Xtream data, ordered sync, sign-in and stale-cache fixes
- Moving browser playlists matches them to the server's by id or source and
  carries the legacy Xtream maps under the server id; it no longer goes
  through the backup format, whose PWA export has no Xtream user data. A
  partial move stays on offer.
- User-data batches go out in order, and a re-read keeps entries edited
  while it was in flight.
- A keepPayload write keeps its cached copy only when the server replaced
  the version its payload came from (new X-Replaced-ETag header).
- 502/503/504 from the proxy count as offline.
- The start-up screen lets the sign-in page through while a token is
  required.
- Settings → Server offers a reload after a move.
- The probe detects Electron through RuntimeCapabilitiesService.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:16:56 +02:00

80 lines
2.7 KiB
TypeScript

import type { Playlist } from '@iptvnator/shared/interfaces';
import { planServerMigration, sourceIdentity } from './server-migration-plan';
const playlist = (fields: Partial<Playlist> & { _id: string }) =>
({ title: fields._id, ...fields }) as Playlist;
describe('planServerMigration', () => {
it('uploads everything to an empty server', () => {
const browser = [
playlist({ _id: 'm', url: 'http://a.test/list.m3u' }),
playlist({ _id: 'file' }),
];
expect(planServerMigration(browser, [])).toEqual([
{ kind: 'upload', playlist: browser[0] },
{ kind: 'upload', playlist: browser[1] },
]);
});
it('merges by id first, then by source', () => {
const server = [
playlist({ _id: 'moved' }),
playlist({
_id: 's-x',
serverUrl: 'HTTP://Panel.test/',
username: 'u',
}),
playlist({
_id: 's-st',
portalUrl: 'http://portal.test/c',
macAddress: '00:1A:79:AA:BB:CC',
}),
playlist({ _id: 's-m', url: 'http://a.test/list.m3u' }),
];
const browser = [
playlist({ _id: 'moved' }),
playlist({
_id: 'b-x',
serverUrl: 'http://panel.test',
username: 'u',
}),
playlist({
_id: 'b-st',
url: 'http://portal.test/c/',
macAddress: '00:1a:79:aa:bb:cc',
}),
playlist({ _id: 'b-m', url: 'http://a.test/list.m3u' }),
playlist({
_id: 'b-other',
serverUrl: 'http://panel.test',
username: 'other',
}),
];
expect(planServerMigration(browser, server)).toEqual([
{ kind: 'merge', browserId: 'moved', serverId: 'moved' },
{ kind: 'merge', browserId: 'b-x', serverId: 's-x' },
{ kind: 'merge', browserId: 'b-st', serverId: 's-st' },
{ kind: 'merge', browserId: 'b-m', serverId: 's-m' },
{ kind: 'upload', playlist: browser[4] },
]);
});
it('collapses browser duplicates of one source onto the first', () => {
const browser = [
playlist({ _id: 'one', url: 'http://a.test/list.m3u' }),
playlist({ _id: 'two', url: 'http://A.test/list.m3u' }),
];
expect(planServerMigration(browser, [])).toEqual([
{ kind: 'upload', playlist: browser[0] },
{ kind: 'merge', browserId: 'two', serverId: 'one' },
]);
});
it('has no source for a file or pasted M3U', () => {
expect(sourceIdentity(playlist({ _id: 'file' }))).toBeNull();
});
});