Files
iptvnator/apps/electron-backend/src/app/api/xtream-preload-performance-capture.spec.ts
T
4gray a2fafcfc08 test(performance): add end-to-end Xtream benchmark harness (#1300)
* docs(performance): plan Xtream benchmark

* feat(xtream-mock-server): add deterministic 100k fixture

* style(xtream-mock-server): apply repository formatting

* fix(xtream-mock-server): harden performance fixture data

* feat(xtream-mock-server): add performance control plane

* docs(performance): correct Xtream capture plan

* fix(xtream-mock-server): harden performance controls

* fix(xtream-mock-server): harden control lifecycle

* feat(performance): add Xtream preload markers

* feat(performance): trace Xtream main phases

* feat(performance): mark Xtream store publications

* feat(performance): trace Xtream database phases

* feat(performance): trace Xtream delete cancellation

* feat(performance): capture Xtream phase attribution

* feat(performance): mark Sources Xtream refresh

* test(performance): define Xtream benchmark evidence contracts

* test(performance): add Xtream benchmark runner

* test(performance): surface failure evidence writes

* test(performance): align database read clock

* test(performance): preserve capture failure contracts
2026-07-28 08:08:07 +02:00

220 lines
6.7 KiB
TypeScript

import {
XTREAM_PRELOAD_PERFORMANCE_MARKER_CHANNEL,
XTREAM_PRELOAD_PERFORMANCE_SCHEMA_VERSION,
type XtreamPreloadPerformanceMarker,
} from '@iptvnator/shared/interfaces';
import { XtreamCodeActions } from '@iptvnator/shared/interfaces';
import { createXtreamPreloadPerformanceCapture } from './xtream-preload-performance-capture';
const MARKER_KEYS = [
'action',
'boundary',
'categoryType',
'contentType',
'ipcCallId',
'itemCount',
'method',
'operationId',
'outcome',
'playlistId',
'schemaVersion',
'sessionId',
'sourceEpochMs',
] as const;
const EMPTY_METADATA = {
action: null,
categoryType: null,
contentType: null,
itemCount: null,
operationId: null,
playlistId: null,
sessionId: null,
} as const;
describe('Xtream preload performance capture contract', () => {
it.each([
{
args: [
{
params: { action: XtreamCodeActions.GetLiveStreams },
sessionId: 'session-1',
},
],
expected: {
...EMPTY_METADATA,
action: XtreamCodeActions.GetLiveStreams,
sessionId: 'session-1',
},
method: 'xtreamRequest',
},
{
args: ['session-2'],
expected: { ...EMPTY_METADATA, sessionId: 'session-2' },
method: 'xtreamCancelSession',
},
{
args: ['playlist-1', 'movies'],
expected: {
...EMPTY_METADATA,
categoryType: 'movies',
playlistId: 'playlist-1',
},
method: 'dbGetCategories',
},
{
args: ['playlist-2', [{}, {}, {}], 'live', [99]],
expected: {
...EMPTY_METADATA,
categoryType: 'live',
itemCount: 3,
playlistId: 'playlist-2',
},
method: 'dbSaveCategories',
},
{
args: ['playlist-3', 'series'],
expected: {
...EMPTY_METADATA,
contentType: 'series',
playlistId: 'playlist-3',
},
method: 'dbGetContent',
},
{
args: ['playlist-4', [{}, {}], 'movie', 'operation-4'],
expected: {
...EMPTY_METADATA,
contentType: 'movie',
itemCount: 2,
operationId: 'operation-4',
playlistId: 'playlist-4',
},
method: 'dbSaveContent',
},
{
args: ['playlist-5', 'operation-5'],
expected: {
...EMPTY_METADATA,
operationId: 'operation-5',
playlistId: 'playlist-5',
},
method: 'dbDeleteXtreamContent',
},
{
args: ['playlist-6', 'operation-6'],
expected: {
...EMPTY_METADATA,
operationId: 'operation-6',
playlistId: 'playlist-6',
},
method: 'dbDeletePlaylist',
},
{
args: ['playlist-7', [{}, {}], [{}], 'operation-7'],
expected: {
...EMPTY_METADATA,
itemCount: 3,
operationId: 'operation-7',
playlistId: 'playlist-7',
},
method: 'dbRestoreXtreamUserData',
},
{
args: ['playlist-8', 'provider search', ['live'], true],
expected: {
...EMPTY_METADATA,
playlistId: 'playlist-8',
},
method: 'dbSearchContent',
},
{
args: ['provider search', ['live'], true, [{ id: 'source-1' }]],
expected: EMPTY_METADATA,
method: 'dbGlobalSearch',
},
{
args: ['operation-9'],
expected: { ...EMPTY_METADATA, operationId: 'operation-9' },
method: 'dbCancelOperation',
},
] as const)(
'extracts only safe positional metadata for $method',
({ args, expected, method }) => {
const sendMarker = jest.fn();
const capture = createXtreamPreloadPerformanceCapture(
true,
sendMarker,
() => 1_000.25
);
const call = capture.start(method, args);
expect(call).not.toBeNull();
expect(sendMarker).toHaveBeenCalledWith(
XTREAM_PRELOAD_PERFORMANCE_MARKER_CHANNEL,
{
...expected,
boundary: 'start',
ipcCallId: 1,
method,
outcome: null,
schemaVersion: XTREAM_PRELOAD_PERFORMANCE_SCHEMA_VERSION,
sourceEpochMs: 1_000.25,
}
);
}
);
it('emits fixed-key start and end markers with matching call metadata', () => {
const markers: XtreamPreloadPerformanceMarker[] = [];
const capture = createXtreamPreloadPerformanceCapture(
true,
(_channel, marker) => markers.push(marker),
() => 2_000.5
);
const successCall = capture.start('dbGetContent', [
'playlist-1',
'live',
]);
capture.success(successCall);
const errorCall = capture.start('dbCancelOperation', ['operation-2']);
capture.error(errorCall);
expect(
markers.map(({ boundary, ipcCallId, outcome }) => ({
boundary,
ipcCallId,
outcome,
}))
).toEqual([
{ boundary: 'start', ipcCallId: 1, outcome: null },
{ boundary: 'end', ipcCallId: 1, outcome: 'success' },
{ boundary: 'start', ipcCallId: 2, outcome: null },
{ boundary: 'end', ipcCallId: 2, outcome: 'error' },
]);
for (const marker of markers) {
expect(Object.keys(marker).sort()).toEqual([...MARKER_KEYS].sort());
}
});
it('allows every current Xtream action including the legacy alias', () => {
const markers: XtreamPreloadPerformanceMarker[] = [];
const capture = createXtreamPreloadPerformanceCapture(
true,
(_channel, marker) => markers.push(marker),
() => 3_000
);
const actions = Object.values(XtreamCodeActions);
for (const action of actions) {
capture.start('xtreamRequest', [{ params: { action } }]);
}
expect(markers.map(({ action }) => action)).toEqual(actions);
expect(actions).toContain('get_simple_data_table');
expect(actions).toContain('get_simple_date_table');
});
});