mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26: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
205 lines
7.0 KiB
TypeScript
205 lines
7.0 KiB
TypeScript
import type { AxiosRequestConfig } from 'axios';
|
|
import {
|
|
XTREAM_MAIN_PERFORMANCE_PHASE,
|
|
XTREAM_MAIN_PERFORMANCE_PHASE_EVENT_CHANNEL,
|
|
type PerformancePhaseEvent,
|
|
type XtreamMainPerformancePhase,
|
|
} from '@iptvnator/shared/interfaces';
|
|
import { channel } from 'node:diagnostics_channel';
|
|
import {
|
|
applyConfiguredTransforms,
|
|
axiosMock,
|
|
getHandler,
|
|
loadXtreamEvents,
|
|
mockParseTransform,
|
|
} from './xtream.events.performance.test-helpers';
|
|
|
|
type MainPhaseEvent = PerformancePhaseEvent<XtreamMainPerformancePhase>;
|
|
|
|
function expectFailedResponsePhases(events: MainPhaseEvent[]): void {
|
|
expect(events.map(({ boundary, phase }) => ({ boundary, phase }))).toEqual([
|
|
{
|
|
boundary: 'start',
|
|
phase: XTREAM_MAIN_PERFORMANCE_PHASE.NETWORK_TOTAL,
|
|
},
|
|
{
|
|
boundary: 'start',
|
|
phase: XTREAM_MAIN_PERFORMANCE_PHASE.JSON_TRANSFORM,
|
|
},
|
|
{
|
|
boundary: 'end',
|
|
phase: XTREAM_MAIN_PERFORMANCE_PHASE.JSON_TRANSFORM,
|
|
},
|
|
{
|
|
boundary: 'end',
|
|
phase: XTREAM_MAIN_PERFORMANCE_PHASE.NETWORK_TOTAL,
|
|
},
|
|
]);
|
|
expect(
|
|
events.some(
|
|
({ phase }) =>
|
|
phase === XTREAM_MAIN_PERFORMANCE_PHASE.RESPONSE_READY
|
|
)
|
|
).toBe(false);
|
|
}
|
|
|
|
describe('XtreamEvents failed-response performance phases', () => {
|
|
const originalCaptureValue = process.env['IPTVNATOR_PERF_CAPTURE'];
|
|
|
|
afterEach(() => {
|
|
if (originalCaptureValue === undefined) {
|
|
delete process.env['IPTVNATOR_PERF_CAPTURE'];
|
|
} else {
|
|
process.env['IPTVNATOR_PERF_CAPTURE'] = originalCaptureValue;
|
|
}
|
|
});
|
|
|
|
it('closes JSON and network phases without marking an Axios error response ready', async () => {
|
|
await loadXtreamEvents('1');
|
|
const events: MainPhaseEvent[] = [];
|
|
const phaseChannel = channel(
|
|
XTREAM_MAIN_PERFORMANCE_PHASE_EVENT_CHANNEL
|
|
);
|
|
const subscriber = (event: unknown) =>
|
|
events.push(event as MainPhaseEvent);
|
|
phaseChannel.subscribe(subscriber);
|
|
try {
|
|
let axiosError: Error & {
|
|
response?: {
|
|
data: unknown;
|
|
headers: object;
|
|
status: number;
|
|
statusText: string;
|
|
};
|
|
};
|
|
axiosMock.mockImplementation((config: AxiosRequestConfig) => {
|
|
axiosError = Object.assign(new Error('provider unavailable'), {
|
|
response: {
|
|
data: applyConfiguredTransforms(
|
|
config,
|
|
'{"message":"provider unavailable"}',
|
|
503
|
|
),
|
|
headers: {},
|
|
status: 503,
|
|
statusText: 'Unavailable',
|
|
},
|
|
});
|
|
axiosMock.isAxiosError.mockImplementation(
|
|
(error: unknown) => error === axiosError
|
|
);
|
|
return Promise.reject(axiosError);
|
|
});
|
|
|
|
await expect(
|
|
getHandler('XTREAM_REQUEST')(
|
|
{},
|
|
{
|
|
params: {
|
|
action: 'get_live_categories',
|
|
password: 'provider-password-secret',
|
|
username: 'provider-user-secret',
|
|
},
|
|
requestId: 'xtream-mabc1007-7',
|
|
sessionId: 'xtream-session-error',
|
|
suppressErrorLog: true,
|
|
url: 'http://localhost:3211',
|
|
}
|
|
)
|
|
).rejects.toEqual({
|
|
message: 'provider unavailable',
|
|
status: 503,
|
|
type: 'ERROR',
|
|
});
|
|
|
|
expectFailedResponsePhases(events);
|
|
} finally {
|
|
phaseChannel.unsubscribe(subscriber);
|
|
}
|
|
});
|
|
|
|
it('closes a fulfilled 4xx response without marking an IPC result ready', async () => {
|
|
await loadXtreamEvents('1');
|
|
const events: MainPhaseEvent[] = [];
|
|
const phaseChannel = channel(
|
|
XTREAM_MAIN_PERFORMANCE_PHASE_EVENT_CHANNEL
|
|
);
|
|
const subscriber = (event: unknown) =>
|
|
events.push(event as MainPhaseEvent);
|
|
phaseChannel.subscribe(subscriber);
|
|
try {
|
|
axiosMock.mockImplementation((config: AxiosRequestConfig) =>
|
|
Promise.resolve({
|
|
config,
|
|
data: applyConfiguredTransforms(
|
|
config,
|
|
'{"message":"not found"}',
|
|
404
|
|
),
|
|
headers: {},
|
|
status: 404,
|
|
statusText: 'Not Found',
|
|
})
|
|
);
|
|
|
|
await expect(
|
|
getHandler('XTREAM_REQUEST')(
|
|
{},
|
|
{
|
|
params: { action: 'get_live_categories' },
|
|
requestId: 'xtream-mabc1008-8',
|
|
suppressErrorLog: true,
|
|
url: 'http://localhost:3211',
|
|
}
|
|
)
|
|
).rejects.toEqual({
|
|
message: 'HTTP Error: Not Found',
|
|
status: 404,
|
|
});
|
|
expectFailedResponsePhases(events);
|
|
} finally {
|
|
phaseChannel.unsubscribe(subscriber);
|
|
}
|
|
});
|
|
|
|
it('preserves an exact default-transform error while closing JSON and network once', async () => {
|
|
await loadXtreamEvents('1');
|
|
const transformError = new Error('default transform failed');
|
|
mockParseTransform.mockImplementationOnce(() => {
|
|
throw transformError;
|
|
});
|
|
axiosMock.mockImplementation((config: AxiosRequestConfig) =>
|
|
Promise.resolve({
|
|
config,
|
|
data: applyConfiguredTransforms(config, '{"items":[]}', 200),
|
|
headers: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
})
|
|
);
|
|
const events: MainPhaseEvent[] = [];
|
|
const phaseChannel = channel(
|
|
XTREAM_MAIN_PERFORMANCE_PHASE_EVENT_CHANNEL
|
|
);
|
|
const subscriber = (event: unknown) =>
|
|
events.push(event as MainPhaseEvent);
|
|
phaseChannel.subscribe(subscriber);
|
|
try {
|
|
await expect(
|
|
getHandler('XTREAM_REQUEST')(
|
|
{},
|
|
{
|
|
params: { action: 'get_live_categories' },
|
|
requestId: 'xtream-mabc1009-9',
|
|
suppressErrorLog: true,
|
|
url: 'http://localhost:3211',
|
|
}
|
|
)
|
|
).rejects.toBe(transformError);
|
|
expectFailedResponsePhases(events);
|
|
} finally {
|
|
phaseChannel.unsubscribe(subscriber);
|
|
}
|
|
});
|
|
});
|