Files
iptvnator/apps/electron-backend-e2e/src/performance/xtream-own-array-snapshot.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

120 lines
3.4 KiB
TypeScript

import type {
XtreamAttributionPhaseEvent,
XtreamIpcAttributionSpan,
XtreamPhaseAttributionInput,
} from './xtream-phase-attribution';
const MAX_CAPTURE_ARRAY_LENGTH = 512;
const EVENT_KEYS = [
'boundary',
'categoryType',
'contentType',
'correlationId',
'durationMs',
'epochMs',
'itemCount',
'phase',
'providerAction',
'semanticType',
] as const;
const IPC_SPAN_KEYS = [
'boundary',
'correlationId',
'endEpochMs',
'ipcCallId',
'method',
'outcome',
'sourceEpochMs',
'startEpochMs',
] as const;
export function snapshotXtreamPhaseInput(
input: XtreamPhaseAttributionInput
): XtreamPhaseAttributionInput {
return Object.freeze({
backgroundSearchMarkerObserved: input.backgroundSearchMarkerObserved,
events: snapshotArray(input.events, (event) =>
snapshotRecord<XtreamAttributionPhaseEvent>(event, EVENT_KEYS)
),
ipcSpans: snapshotArray(input.ipcSpans, (span) =>
snapshotRecord<XtreamIpcAttributionSpan>(span, IPC_SPAN_KEYS)
),
operationStartEpochMs: input.operationStartEpochMs,
scenarioId: input.scenarioId,
terminalEpochMs: input.terminalEpochMs,
uiPaintEpochMs: input.uiPaintEpochMs,
});
}
function snapshotArray<T>(
value: unknown,
snapshotElement: (element: unknown) => T
): readonly T[] {
if (!Array.isArray(value)) invalid();
const lengthDescriptor = Reflect.getOwnPropertyDescriptor(value, 'length');
const length = lengthDescriptor?.value;
if (
!Number.isSafeInteger(length) ||
Number(length) < 0 ||
Number(length) > MAX_CAPTURE_ARRAY_LENGTH
) {
invalid();
}
const expectedKeys = new Set<PropertyKey>(['length']);
const snapshot: T[] = [];
for (let index = 0; index < Number(length); index += 1) {
const key = String(index);
expectedKeys.add(key);
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
if (
!descriptor ||
!Object.prototype.hasOwnProperty.call(descriptor, 'value')
) {
invalid();
}
snapshot[index] = snapshotElement(descriptor.value);
}
const ownKeys = Reflect.ownKeys(value);
if (
ownKeys.length !== expectedKeys.size ||
ownKeys.some((key) => !expectedKeys.has(key))
) {
invalid();
}
return Object.freeze(snapshot);
}
function snapshotRecord<T>(value: unknown, keys: readonly string[]): T {
if (
typeof value !== 'object' ||
value === null ||
Array.isArray(value) ||
![Object.prototype, null].includes(Reflect.getPrototypeOf(value))
) {
invalid();
}
const ownKeys = Reflect.ownKeys(value);
if (
ownKeys.length !== keys.length ||
ownKeys.some((key) => typeof key !== 'string' || !keys.includes(key))
) {
invalid();
}
const snapshot: Record<string, unknown> = {};
for (const key of keys) {
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
if (
!descriptor ||
!Object.prototype.hasOwnProperty.call(descriptor, 'value')
) {
invalid();
}
snapshot[key] = descriptor.value;
}
return Object.freeze(snapshot) as T;
}
function invalid(): never {
throw new Error('invalid Xtream capture array');
}