mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
* 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
202 lines
7.5 KiB
TypeScript
202 lines
7.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { describe, it } from 'node:test';
|
|
|
|
import { XTREAM_SCENARIO_ID } from './xtream-benchmark-contract';
|
|
import { createXtreamAssemblerFixture } from './xtream-iteration-assembler.fixture';
|
|
import { assembleXtreamRawIteration } from './xtream-iteration-assembler';
|
|
import { summarizeXtreamUiActionSamples } from './xtream-ui-action-probe';
|
|
|
|
describe('Xtream raw iteration assembler background UI evidence', () => {
|
|
it('binds samples to the full refresh lifecycle after the delete prelude', () => {
|
|
const input = createXtreamAssemblerFixture(
|
|
XTREAM_SCENARIO_ID.BACKGROUND_UI
|
|
);
|
|
const request = input.mainCapture.requests.find(
|
|
({ operation }) => operation === 'DB_DELETE_XTREAM_CONTENT'
|
|
);
|
|
const background = input.terminal.backgroundProbe;
|
|
const operation = input.terminal.operation;
|
|
const storeTerminalEpochMs =
|
|
input.rendererCapture.probe.storeTerminalEpochMs;
|
|
assert.ok(request?.workStartedEpochMs);
|
|
assert.ok(request.workEndedEpochMs);
|
|
assert.ok(background);
|
|
assert.ok(operation);
|
|
assert.ok(storeTerminalEpochMs);
|
|
const step = (storeTerminalEpochMs - request.workEndedEpochMs) / 12;
|
|
assert.ok(step > 0);
|
|
const samples = background.result.samples.map((sample, index) => {
|
|
const startEpochMs =
|
|
Number(request.workEndedEpochMs) + step * (index * 2 + 1);
|
|
const endEpochMs = startEpochMs + step;
|
|
return {
|
|
...sample,
|
|
endEpochMs,
|
|
latencyMs: step,
|
|
startEpochMs,
|
|
};
|
|
});
|
|
const summary = summarizeXtreamUiActionSamples(samples);
|
|
|
|
assert.doesNotThrow(() =>
|
|
assembleXtreamRawIteration({
|
|
...input,
|
|
terminal: {
|
|
...input.terminal,
|
|
backgroundProbe: {
|
|
...background,
|
|
completedEpochMs: samples.at(-1)?.endEpochMs ?? 0,
|
|
result: {
|
|
...summary,
|
|
dashboardPaintVisibility: 'visible-without-overlay',
|
|
samples,
|
|
},
|
|
startedEpochMs: request.workStartedEpochMs,
|
|
},
|
|
operation: {
|
|
...operation,
|
|
terminalEpochMs: request.workEndedEpochMs,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
it('accepts samples that finish before the delete prelude completes', () => {
|
|
const input = createXtreamAssemblerFixture(
|
|
XTREAM_SCENARIO_ID.BACKGROUND_UI
|
|
);
|
|
const request = input.mainCapture.requests.find(
|
|
({ operation }) => operation === 'DB_DELETE_XTREAM_CONTENT'
|
|
);
|
|
const background = input.terminal.backgroundProbe;
|
|
const operation = input.terminal.operation;
|
|
assert.ok(request?.workStartedEpochMs);
|
|
assert.ok(request.workEndedEpochMs);
|
|
assert.ok(background);
|
|
assert.ok(operation);
|
|
const step =
|
|
(request.workEndedEpochMs - background.startedEpochMs) / 12;
|
|
assert.ok(step > 0);
|
|
const samples = background.result.samples.map((sample, index) => {
|
|
const startEpochMs =
|
|
background.startedEpochMs + step * (index * 2 + 1);
|
|
const endEpochMs = startEpochMs + step;
|
|
return {
|
|
...sample,
|
|
endEpochMs,
|
|
latencyMs: step,
|
|
startEpochMs,
|
|
};
|
|
});
|
|
const summary = summarizeXtreamUiActionSamples(samples);
|
|
assert.ok(
|
|
Number(samples.at(-1)?.endEpochMs) < request.workEndedEpochMs
|
|
);
|
|
|
|
assert.doesNotThrow(() =>
|
|
assembleXtreamRawIteration({
|
|
...input,
|
|
terminal: {
|
|
...input.terminal,
|
|
backgroundProbe: {
|
|
...background,
|
|
completedEpochMs: samples.at(-1)?.endEpochMs ?? 0,
|
|
result: {
|
|
...summary,
|
|
dashboardPaintVisibility: 'visible-without-overlay',
|
|
samples,
|
|
},
|
|
},
|
|
operation: {
|
|
...operation,
|
|
terminalEpochMs: request.workEndedEpochMs,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
it('rejects overlapping samples', () => {
|
|
const input = createXtreamAssemblerFixture(
|
|
XTREAM_SCENARIO_ID.BACKGROUND_UI
|
|
);
|
|
const background = input.terminal.backgroundProbe;
|
|
assert.ok(background);
|
|
const samples = background.result.samples.map((sample, index, all) => {
|
|
if (index !== 1) return sample;
|
|
const previous = all[0];
|
|
assert.ok(previous);
|
|
const startEpochMs = previous.endEpochMs - 0.1;
|
|
return {
|
|
...sample,
|
|
latencyMs: sample.endEpochMs - startEpochMs,
|
|
startEpochMs,
|
|
};
|
|
});
|
|
const summary = summarizeXtreamUiActionSamples(samples);
|
|
|
|
assert.throws(
|
|
() =>
|
|
assembleXtreamRawIteration({
|
|
...input,
|
|
terminal: {
|
|
...input.terminal,
|
|
backgroundProbe: {
|
|
...background,
|
|
result: {
|
|
...summary,
|
|
dashboardPaintVisibility:
|
|
'visible-without-overlay',
|
|
samples,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
/xtream-iteration-assembly-invalid/
|
|
);
|
|
});
|
|
|
|
it('rejects a sample after the final refresh store terminal', () => {
|
|
const input = createXtreamAssemblerFixture(
|
|
XTREAM_SCENARIO_ID.BACKGROUND_UI
|
|
);
|
|
const background = input.terminal.backgroundProbe;
|
|
const storeTerminalEpochMs =
|
|
input.rendererCapture.probe.storeTerminalEpochMs;
|
|
assert.ok(background);
|
|
assert.ok(storeTerminalEpochMs);
|
|
const samples = background.result.samples.map((sample, index, all) => {
|
|
if (index !== all.length - 1) return sample;
|
|
const endEpochMs = storeTerminalEpochMs + 0.1;
|
|
return {
|
|
...sample,
|
|
endEpochMs,
|
|
latencyMs: endEpochMs - sample.startEpochMs,
|
|
};
|
|
});
|
|
const summary = summarizeXtreamUiActionSamples(samples);
|
|
|
|
assert.throws(
|
|
() =>
|
|
assembleXtreamRawIteration({
|
|
...input,
|
|
terminal: {
|
|
...input.terminal,
|
|
backgroundProbe: {
|
|
...background,
|
|
completedEpochMs: samples.at(-1)?.endEpochMs ?? 0,
|
|
result: {
|
|
...summary,
|
|
dashboardPaintVisibility:
|
|
'visible-without-overlay',
|
|
samples,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
/xtream-iteration-assembly-invalid/
|
|
);
|
|
});
|
|
});
|