refactor(electron-backend): split the last four files over the max-lines cap

Follow-up to #1278, which split four of the files the electron-backend lint
target had been silently skipping. Four were left over; this splits them, so
no file in the project sits above the 400-line cap outside the baseline.

None are added to tools/eslint/max-lines-baseline.mjs — the baseline only
shrinks. Shared setup moves to *.test-helpers.ts, the suffix
tsconfig.app.json already excludes, so the production build never sees jest
globals.

- remote-control.events.spec.ts 588 -> 188, plus remote-control-http.spec.ts.
  The mock registry moves to remote-control.test-helpers.ts; each spec keeps
  its own jest.mock() factories, which resolve the mock-prefixed exports.
- downloads.events.spec.ts 530 -> 182, plus downloads-actions.spec.ts. The
  jest.doMock setup is not hoisted, so the whole harness moves to
  downloads.test-helpers.ts behind setupDownloadsEventsHarness().
- http-server.spec.ts 448 -> 377, plus resolve-static-file-path.spec.ts. The
  resolveStaticFilePath cases were already self-contained.
- worker-performance-capture.spec.ts 408 -> 149, plus
  worker-performance-capture.resilience.spec.ts, matching the .concurrency
  and .histogram siblings.

Test bodies are unchanged; the helpers keep the same identifiers in scope so
the splits are a move, not a rewrite.

Verified with the glob quoted locally (that quoting is #1176's, not part of
this change): 245 files, 0 max-lines errors, and the only remaining lint
errors are the five #1176 fixes. Both tsconfig.app.json and
tsconfig.spec.json typecheck clean.

Note: worker-performance-capture.concurrency.spec.ts is flaky on master
independently of this change — it asserts a real 20ms event-loop block and
failed 4/4 clean-master runs here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 committed 2026-07-27 13:19:29 +02:00
1 parent 24f0dee6f0
commit e17bb39537
10 files changed
+1298 -1116

No files matched your search

@@ -0,0 +1,196 @@
import {
expectManagedPathLookup,
getHandler,
MANAGED_PATH_STATE,
mockExistsSync,
mockManagedPath,
mockOpenPath,
mockPauseDownload,
mockResumeDownloadRequest,
mockShowItemInFolder,
setupDownloadsEventsHarness,
} from './downloads.test-helpers';
describe('downloads events: pause, resume, and reveal', () => {
beforeEach(async () => {
await setupDownloadsEventsHarness();
});
it('maps a successful runtime pause to a success response', async () => {
mockPauseDownload.mockResolvedValue(true);
const consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
try {
await expect(
getHandler('DOWNLOADS_PAUSE')(null, 42)
).resolves.toEqual({ success: true });
} finally {
consoleLog.mockRestore();
}
expect(mockPauseDownload).toHaveBeenCalledWith(42);
});
it('maps an unknown pause target to an error response', async () => {
mockPauseDownload.mockResolvedValue(false);
const consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
try {
await expect(
getHandler('DOWNLOADS_PAUSE')(null, 42)
).resolves.toEqual({
error: 'Download not found in queue',
success: false,
});
} finally {
consoleLog.mockRestore();
}
});
it('forwards resume requests with the download folder and returns the result', async () => {
mockResumeDownloadRequest.mockResolvedValue({
error: 'Can only resume paused downloads',
success: false,
});
await expect(
getHandler('DOWNLOADS_RESUME')(null, 42, '/downloads')
).resolves.toEqual({
error: 'Can only resume paused downloads',
success: false,
});
expect(mockResumeDownloadRequest).toHaveBeenCalledWith(
42,
'/downloads',
expect.anything()
);
});
describe.each([
{
channel: 'DOWNLOADS_REVEAL_FILE',
filePath: '/downloads/reveal-boundary.mp4',
operation: 'reveal',
},
{
channel: 'DOWNLOADS_PLAY_FILE',
filePath: '/downloads/play-boundary.mp4',
operation: 'play',
},
])('$operation managed-path boundary', ({ channel, filePath }) => {
it('rejects an unmanaged database path before accessing the filesystem', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.UNMANAGED);
mockExistsSync.mockReturnValue(true);
await expect(getHandler(channel)(null, filePath)).resolves.toEqual({
error: 'File not found',
success: false,
});
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).not.toHaveBeenCalled();
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
it('rejects a managed database path that is missing from disk', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(false);
await expect(getHandler(channel)(null, filePath)).resolves.toEqual({
error: 'File not found',
success: false,
});
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
it('fails closed when the managed-path database query rejects', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.ERROR);
mockExistsSync.mockReturnValue(true);
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => undefined);
try {
await expect(
getHandler(channel)(null, filePath)
).resolves.toEqual({
error: 'File not found',
success: false,
});
expect(consoleError).toHaveBeenCalledTimes(1);
expect(consoleError).toHaveBeenCalledWith(
'Error verifying managed download path:',
expect.objectContaining({
message: 'database unavailable',
})
);
} finally {
consoleError.mockRestore();
}
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).not.toHaveBeenCalled();
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
});
it('reveals a managed file that exists on disk', async () => {
const filePath = '/downloads/reveal-success.mp4';
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(true);
await expect(
getHandler('DOWNLOADS_REVEAL_FILE')(null, filePath)
).resolves.toEqual({ success: true });
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockShowItemInFolder).toHaveBeenCalledTimes(1);
expect(mockShowItemInFolder).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).not.toHaveBeenCalled();
});
it('waits for the native shell before reporting a managed file as played', async () => {
const filePath = '/downloads/play-success.mp4';
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(true);
let resolveOpenPath!: (value: string) => void;
const openPathResult = new Promise<string>((resolve) => {
resolveOpenPath = resolve;
});
mockOpenPath.mockReturnValue(openPathResult);
let responseSettled = false;
const response = getHandler('DOWNLOADS_PLAY_FILE')(null, filePath).then(
(result) => {
responseSettled = true;
return result;
}
);
await new Promise<void>((resolve) => setImmediate(resolve));
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).toHaveBeenCalledTimes(1);
expect(mockOpenPath).toHaveBeenCalledWith(filePath);
expect(mockShowItemInFolder).not.toHaveBeenCalled();
expect(responseSettled).toBe(false);
resolveOpenPath('');
await expect(response).resolves.toEqual({ success: true });
});
});
@@ -1,189 +1,19 @@
type IpcHandler = (_event: unknown, ...args: unknown[]) => Promise<unknown>;
import {
createDownloadRow,
getHandler,
mockBroadcastDownloadUpdate,
mockDownloadRow,
mockRemoveDownloadFromRuntime,
mockRemovePartialDownloadFile,
mockTerminalRows,
setupDownloadsEventsHarness,
} from './downloads.test-helpers';
const mockRegisteredHandlers = new Map<string, IpcHandler>();
const mockGetDatabase = jest.fn();
const mockRemoveDownloadFromRuntime = jest.fn();
const mockBroadcastDownloadUpdate = jest.fn();
const mockRemovePartialDownloadFile = jest.fn();
const mockPauseDownload = jest.fn();
const mockResumeDownloadRequest = jest.fn();
const mockExistsSync = jest.fn();
const mockOpenPath = jest.fn();
const mockShowItemInFolder = jest.fn();
const mockEq = jest.fn();
let downloadsFilePathColumn: unknown;
const MANAGED_PATH_STATE = {
ERROR: 'error',
MANAGED: 'managed',
UNMANAGED: 'unmanaged',
} as const;
type ManagedPathState =
(typeof MANAGED_PATH_STATE)[keyof typeof MANAGED_PATH_STATE];
function getHandler(channel: string): IpcHandler {
const handler = mockRegisteredHandlers.get(channel);
if (!handler) {
throw new Error(`Expected IPC handler for ${channel}`);
}
return handler;
}
function createDownloadRow(status: string) {
return {
filePath: '/downloads/resume.mp4',
status,
};
}
describe('downloads events', () => {
describe('downloads events: partial-file cleanup', () => {
beforeEach(async () => {
jest.resetModules();
mockRegisteredHandlers.clear();
mockGetDatabase.mockReset();
mockRemoveDownloadFromRuntime.mockReset();
mockBroadcastDownloadUpdate.mockReset();
mockRemovePartialDownloadFile.mockReset();
mockPauseDownload.mockReset();
mockResumeDownloadRequest.mockReset();
mockExistsSync.mockReset();
mockOpenPath.mockReset().mockResolvedValue('');
mockShowItemInFolder.mockReset();
mockEq.mockReset();
jest.doMock('node:fs', () => ({
...jest.requireActual<typeof import('node:fs')>('node:fs'),
existsSync: mockExistsSync,
}));
jest.doMock('drizzle-orm', () => {
const actual =
jest.requireActual<typeof import('drizzle-orm')>('drizzle-orm');
mockEq.mockImplementation(actual.eq);
return {
...actual,
eq: mockEq,
};
});
jest.doMock('electron', () => ({
app: {
getPath: jest.fn((name: string) =>
name === 'userData' ? '/user-data' : '/downloads'
),
},
dialog: {
showOpenDialog: jest.fn(),
},
ipcMain: {
handle: jest.fn((channel: string, handler: IpcHandler) => {
mockRegisteredHandlers.set(channel, handler);
}),
},
shell: {
openPath: mockOpenPath,
showItemInFolder: mockShowItemInFolder,
},
}));
jest.doMock('../../database/connection', () => ({
getDatabase: mockGetDatabase,
}));
jest.doMock('./download-file-path', () => ({
removePartialDownloadFile: mockRemovePartialDownloadFile,
}));
jest.doMock('./download-runtime', () => ({
broadcastDownloadUpdate: mockBroadcastDownloadUpdate,
cancelDownload: jest.fn(),
pauseDownload: mockPauseDownload,
removeDownloadFromRuntime: mockRemoveDownloadFromRuntime,
setMainWindow: jest.fn(),
}));
jest.doMock('./download-requests', () => ({
resumeDownloadRequest: mockResumeDownloadRequest,
retryDownloadRequest: jest.fn(),
startDownloadRequest: jest.fn(),
}));
jest.doMock('./download-recovery', () => ({
resetStaleDownloads: jest.fn(),
}));
await import('./downloads.events');
const schema = await import('../../database/schema');
downloadsFilePathColumn = schema.downloads.filePath;
await setupDownloadsEventsHarness();
});
function mockManagedPath(state: ManagedPathState) {
const limit = jest.fn(() => {
if (state === MANAGED_PATH_STATE.ERROR) {
return Promise.reject(new Error('database unavailable'));
}
return Promise.resolve(
state === MANAGED_PATH_STATE.MANAGED ? [{ id: 42 }] : []
);
});
const where = jest.fn((_predicate: unknown) => ({ limit }));
const from = jest.fn(() => ({ where }));
const select = jest.fn(() => ({ from }));
const db = { select };
mockGetDatabase.mockResolvedValue(db);
return { from, limit, select, where };
}
function expectManagedPathLookup(
lookup: ReturnType<typeof mockManagedPath>,
filePath: string
) {
expect(mockGetDatabase).toHaveBeenCalledTimes(1);
expect(lookup.select).toHaveBeenCalledTimes(1);
expect(lookup.from).toHaveBeenCalledTimes(1);
expect(lookup.where).toHaveBeenCalledTimes(1);
expect(mockEq).toHaveBeenCalledTimes(1);
expect(mockEq.mock.calls[0][0] === downloadsFilePathColumn).toBe(true);
expect(mockEq.mock.calls[0][1]).toBe(filePath);
expect(
lookup.where.mock.calls[0][0] === mockEq.mock.results[0].value
).toBe(true);
expect(lookup.limit).toHaveBeenCalledTimes(1);
expect(lookup.limit).toHaveBeenCalledWith(1);
}
function mockDownloadRow(row: { filePath: string | null; status: string }) {
const deleteWhere = jest.fn().mockResolvedValue(undefined);
const db = {
delete: jest.fn(() => ({ where: deleteWhere })),
select: jest.fn(() => ({
from: jest.fn(() => ({
where: jest.fn(() => ({
limit: jest.fn().mockResolvedValue([row]),
})),
})),
})),
};
mockGetDatabase.mockResolvedValue(db);
return { db, deleteWhere };
}
function mockTerminalRows(
rows: Array<{ filePath: string | null; status: string }>
) {
const deleteWhere = jest.fn().mockResolvedValue(undefined);
const selectWhere = jest
.fn()
.mockResolvedValue(
rows.map((row, index) => ({ id: index + 1, ...row }))
);
const db = {
delete: jest.fn(() => ({ where: deleteWhere })),
select: jest.fn(() => ({
from: jest.fn(() => ({
where: selectWhere,
})),
})),
};
mockGetDatabase.mockResolvedValue(db);
return { db, deleteWhere, selectWhere };
}
it('removes queued resumed partial files before deleting the row', async () => {
const { deleteWhere } = mockDownloadRow(createDownloadRow('queued'));
@@ -349,182 +179,4 @@ describe('downloads events', () => {
expect(mockRemovePartialDownloadFile).toHaveBeenCalledTimes(2);
expect(deleteWhere).toHaveBeenCalledTimes(1);
});
it('maps a successful runtime pause to a success response', async () => {
mockPauseDownload.mockResolvedValue(true);
const consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
try {
await expect(
getHandler('DOWNLOADS_PAUSE')(null, 42)
).resolves.toEqual({ success: true });
} finally {
consoleLog.mockRestore();
}
expect(mockPauseDownload).toHaveBeenCalledWith(42);
});
it('maps an unknown pause target to an error response', async () => {
mockPauseDownload.mockResolvedValue(false);
const consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
try {
await expect(
getHandler('DOWNLOADS_PAUSE')(null, 42)
).resolves.toEqual({
error: 'Download not found in queue',
success: false,
});
} finally {
consoleLog.mockRestore();
}
});
it('forwards resume requests with the download folder and returns the result', async () => {
mockResumeDownloadRequest.mockResolvedValue({
error: 'Can only resume paused downloads',
success: false,
});
await expect(
getHandler('DOWNLOADS_RESUME')(null, 42, '/downloads')
).resolves.toEqual({
error: 'Can only resume paused downloads',
success: false,
});
expect(mockResumeDownloadRequest).toHaveBeenCalledWith(
42,
'/downloads',
expect.anything()
);
});
describe.each([
{
channel: 'DOWNLOADS_REVEAL_FILE',
filePath: '/downloads/reveal-boundary.mp4',
operation: 'reveal',
},
{
channel: 'DOWNLOADS_PLAY_FILE',
filePath: '/downloads/play-boundary.mp4',
operation: 'play',
},
])('$operation managed-path boundary', ({ channel, filePath }) => {
it('rejects an unmanaged database path before accessing the filesystem', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.UNMANAGED);
mockExistsSync.mockReturnValue(true);
await expect(getHandler(channel)(null, filePath)).resolves.toEqual({
error: 'File not found',
success: false,
});
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).not.toHaveBeenCalled();
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
it('rejects a managed database path that is missing from disk', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(false);
await expect(getHandler(channel)(null, filePath)).resolves.toEqual({
error: 'File not found',
success: false,
});
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
it('fails closed when the managed-path database query rejects', async () => {
const lookup = mockManagedPath(MANAGED_PATH_STATE.ERROR);
mockExistsSync.mockReturnValue(true);
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => undefined);
try {
await expect(
getHandler(channel)(null, filePath)
).resolves.toEqual({
error: 'File not found',
success: false,
});
expect(consoleError).toHaveBeenCalledTimes(1);
expect(consoleError).toHaveBeenCalledWith(
'Error verifying managed download path:',
expect.objectContaining({
message: 'database unavailable',
})
);
} finally {
consoleError.mockRestore();
}
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).not.toHaveBeenCalled();
expect(mockOpenPath).not.toHaveBeenCalled();
expect(mockShowItemInFolder).not.toHaveBeenCalled();
});
});
it('reveals a managed file that exists on disk', async () => {
const filePath = '/downloads/reveal-success.mp4';
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(true);
await expect(
getHandler('DOWNLOADS_REVEAL_FILE')(null, filePath)
).resolves.toEqual({ success: true });
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockShowItemInFolder).toHaveBeenCalledTimes(1);
expect(mockShowItemInFolder).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).not.toHaveBeenCalled();
});
it('waits for the native shell before reporting a managed file as played', async () => {
const filePath = '/downloads/play-success.mp4';
const lookup = mockManagedPath(MANAGED_PATH_STATE.MANAGED);
mockExistsSync.mockReturnValue(true);
let resolveOpenPath!: (value: string) => void;
const openPathResult = new Promise<string>((resolve) => {
resolveOpenPath = resolve;
});
mockOpenPath.mockReturnValue(openPathResult);
let responseSettled = false;
const response = getHandler('DOWNLOADS_PLAY_FILE')(null, filePath).then(
(result) => {
responseSettled = true;
return result;
}
);
await new Promise<void>((resolve) => setImmediate(resolve));
expectManagedPathLookup(lookup, filePath);
expect(mockExistsSync).toHaveBeenCalledTimes(1);
expect(mockExistsSync).toHaveBeenCalledWith(filePath);
expect(mockOpenPath).toHaveBeenCalledTimes(1);
expect(mockOpenPath).toHaveBeenCalledWith(filePath);
expect(mockShowItemInFolder).not.toHaveBeenCalled();
expect(responseSettled).toBe(false);
resolveOpenPath('');
await expect(response).resolves.toEqual({ success: true });
});
});
@@ -0,0 +1,192 @@
type IpcHandler = (_event: unknown, ...args: unknown[]) => Promise<unknown>;
/**
* Shared harness for the downloads-events specs.
*
* The module registry is reset and re-mocked per test via `jest.doMock`, which
* is deliberately not hoisted — so the whole setup can live here and each spec
* just calls `setupDownloadsEventsHarness()` from its own `beforeEach`.
*/
export const mockRegisteredHandlers = new Map<string, IpcHandler>();
export const mockGetDatabase = jest.fn();
export const mockRemoveDownloadFromRuntime = jest.fn();
export const mockBroadcastDownloadUpdate = jest.fn();
export const mockRemovePartialDownloadFile = jest.fn();
export const mockPauseDownload = jest.fn();
export const mockResumeDownloadRequest = jest.fn();
export const mockExistsSync = jest.fn();
export const mockOpenPath = jest.fn();
export const mockShowItemInFolder = jest.fn();
export const mockEq = jest.fn();
let downloadsFilePathColumn: unknown;
export const MANAGED_PATH_STATE = {
ERROR: 'error',
MANAGED: 'managed',
UNMANAGED: 'unmanaged',
} as const;
export type ManagedPathState =
(typeof MANAGED_PATH_STATE)[keyof typeof MANAGED_PATH_STATE];
export function getHandler(channel: string): IpcHandler {
const handler = mockRegisteredHandlers.get(channel);
if (!handler) {
throw new Error(`Expected IPC handler for ${channel}`);
}
return handler;
}
export function createDownloadRow(status: string) {
return {
filePath: '/downloads/resume.mp4',
status,
};
}
export async function setupDownloadsEventsHarness(): Promise<void> {
jest.resetModules();
mockRegisteredHandlers.clear();
mockGetDatabase.mockReset();
mockRemoveDownloadFromRuntime.mockReset();
mockBroadcastDownloadUpdate.mockReset();
mockRemovePartialDownloadFile.mockReset();
mockPauseDownload.mockReset();
mockResumeDownloadRequest.mockReset();
mockExistsSync.mockReset();
mockOpenPath.mockReset().mockResolvedValue('');
mockShowItemInFolder.mockReset();
mockEq.mockReset();
jest.doMock('node:fs', () => ({
...jest.requireActual<typeof import('node:fs')>('node:fs'),
existsSync: mockExistsSync,
}));
jest.doMock('drizzle-orm', () => {
const actual =
jest.requireActual<typeof import('drizzle-orm')>('drizzle-orm');
mockEq.mockImplementation(actual.eq);
return {
...actual,
eq: mockEq,
};
});
jest.doMock('electron', () => ({
app: {
getPath: jest.fn((name: string) =>
name === 'userData' ? '/user-data' : '/downloads'
),
},
dialog: {
showOpenDialog: jest.fn(),
},
ipcMain: {
handle: jest.fn((channel: string, handler: IpcHandler) => {
mockRegisteredHandlers.set(channel, handler);
}),
},
shell: {
openPath: mockOpenPath,
showItemInFolder: mockShowItemInFolder,
},
}));
jest.doMock('../../database/connection', () => ({
getDatabase: mockGetDatabase,
}));
jest.doMock('./download-file-path', () => ({
removePartialDownloadFile: mockRemovePartialDownloadFile,
}));
jest.doMock('./download-runtime', () => ({
broadcastDownloadUpdate: mockBroadcastDownloadUpdate,
cancelDownload: jest.fn(),
pauseDownload: mockPauseDownload,
removeDownloadFromRuntime: mockRemoveDownloadFromRuntime,
setMainWindow: jest.fn(),
}));
jest.doMock('./download-requests', () => ({
resumeDownloadRequest: mockResumeDownloadRequest,
retryDownloadRequest: jest.fn(),
startDownloadRequest: jest.fn(),
}));
jest.doMock('./download-recovery', () => ({
resetStaleDownloads: jest.fn(),
}));
await import('./downloads.events');
const schema = await import('../../database/schema');
downloadsFilePathColumn = schema.downloads.filePath;
}
export function mockManagedPath(state: ManagedPathState) {
const limit = jest.fn(() => {
if (state === MANAGED_PATH_STATE.ERROR) {
return Promise.reject(new Error('database unavailable'));
}
return Promise.resolve(
state === MANAGED_PATH_STATE.MANAGED ? [{ id: 42 }] : []
);
});
const where = jest.fn((_predicate: unknown) => ({ limit }));
const from = jest.fn(() => ({ where }));
const select = jest.fn(() => ({ from }));
const db = { select };
mockGetDatabase.mockResolvedValue(db);
return { from, limit, select, where };
}
export function expectManagedPathLookup(
lookup: ReturnType<typeof mockManagedPath>,
filePath: string
) {
expect(mockGetDatabase).toHaveBeenCalledTimes(1);
expect(lookup.select).toHaveBeenCalledTimes(1);
expect(lookup.from).toHaveBeenCalledTimes(1);
expect(lookup.where).toHaveBeenCalledTimes(1);
expect(mockEq).toHaveBeenCalledTimes(1);
expect(mockEq.mock.calls[0][0] === downloadsFilePathColumn).toBe(true);
expect(mockEq.mock.calls[0][1]).toBe(filePath);
expect(
lookup.where.mock.calls[0][0] === mockEq.mock.results[0].value
).toBe(true);
expect(lookup.limit).toHaveBeenCalledTimes(1);
expect(lookup.limit).toHaveBeenCalledWith(1);
}
export function mockDownloadRow(row: { filePath: string | null; status: string }) {
const deleteWhere = jest.fn().mockResolvedValue(undefined);
const db = {
delete: jest.fn(() => ({ where: deleteWhere })),
select: jest.fn(() => ({
from: jest.fn(() => ({
where: jest.fn(() => ({
limit: jest.fn().mockResolvedValue([row]),
})),
})),
})),
};
mockGetDatabase.mockResolvedValue(db);
return { db, deleteWhere };
}
export function mockTerminalRows(
rows: Array<{ filePath: string | null; status: string }>
) {
const deleteWhere = jest.fn().mockResolvedValue(undefined);
const selectWhere = jest
.fn()
.mockResolvedValue(
rows.map((row, index) => ({ id: index + 1, ...row }))
);
const db = {
delete: jest.fn(() => ({ where: deleteWhere })),
select: jest.fn(() => ({
from: jest.fn(() => ({
where: selectWhere,
})),
})),
};
mockGetDatabase.mockResolvedValue(db);
return { db, deleteWhere, selectWhere };
}
@@ -0,0 +1,264 @@
import {
bootstrapRemoteControl as bootstrapRemoteControlWith,
createBodyAtByteLength,
getIpcListener,
invokeHttpHandler,
JSON_HEADERS,
METHOD_NOT_ALLOWED_RESPONSE,
mockFirstRendererSend,
mockGetAllWindows,
mockIpcHandle,
mockIpcOn,
mockRegisterRemoteControlHandler,
mockSecondRendererSend,
mockStartHttpServer,
mockStoreGet,
REMOTE_CONTROL_PATHS,
resetRemoteControlMocks,
SUCCESS_RESPONSE,
type RemoteControlSettings,
} from './remote-control.test-helpers';
jest.mock('electron', () => ({
BrowserWindow: {
getAllWindows: mockGetAllWindows,
},
ipcMain: {
handle: mockIpcHandle,
on: mockIpcOn,
},
}));
jest.mock('../server/http-server', () => ({
httpServer: {
registerRemoteControlHandler: mockRegisterRemoteControlHandler,
start: mockStartHttpServer,
},
}));
jest.mock('../services/store.service', () => ({
store: {
get: mockStoreGet,
},
}));
import { RemoteControlEvents } from './remote-control.events';
const bootstrapRemoteControl = (settings?: RemoteControlSettings) =>
bootstrapRemoteControlWith(RemoteControlEvents, settings);
describe('RemoteControlEvents HTTP endpoints', () => {
let consoleLog: jest.SpyInstance;
let consoleWarn: jest.SpyInstance;
beforeEach(() => {
resetRemoteControlMocks();
consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
consoleWarn = jest
.spyOn(console, 'warn')
.mockImplementation(() => undefined);
});
afterEach(() => {
jest.useRealTimers();
consoleLog.mockRestore();
consoleWarn.mockRestore();
});
it('rejects POST on the status endpoint', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(REMOTE_CONTROL_PATHS.STATUS, {
method: 'POST',
});
expect(result.response).toEqual(METHOD_NOT_ALLOWED_RESPONSE);
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('floors a finite positive channel number and dispatches it', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body: JSON.stringify({ number: 7.9 }),
}
);
expect(result.response).toEqual(SUCCESS_RESPONSE);
expect(mockFirstRendererSend).toHaveBeenCalledWith(
'REMOTE_CONTROL_COMMAND',
{
type: 'channel-select-number',
number: 7,
}
);
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it.each([
{ label: 'missing', body: '{}' },
{ label: 'zero', body: '{"number":0}' },
{ label: 'negative', body: '{"number":-2}' },
{ label: 'infinite', body: '{"number":1e309}' },
{ label: 'nonnumeric', body: '{"number":"seven"}' },
])('rejects a $label channel number', async ({ body }) => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
expect(result.response).toEqual({
statusCode: 400,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Invalid channel number' }),
});
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('rejects malformed JSON without dispatching', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body: '{"number":',
}
);
expect(result.response).toEqual({
statusCode: 400,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Invalid JSON payload' }),
});
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('accepts a valid JSON request at the 10,240-byte body limit', async () => {
bootstrapRemoteControl();
const body = createBodyAtByteLength(10 * 1024);
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
expect(Buffer.byteLength(body, 'utf8')).toBe(10_240);
expect(result.response).toEqual(SUCCESS_RESPONSE);
expect(mockFirstRendererSend).toHaveBeenCalledWith(
'REMOTE_CONTROL_COMMAND',
{
type: 'channel-select-number',
number: 7,
}
);
});
it('rejects and destroys a 10,241-byte request without dispatching', async () => {
bootstrapRemoteControl();
const body = Buffer.alloc(10 * 1024 + 1, 'x');
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
await result.requestClosed;
expect(body.byteLength).toBe(10_241);
expect(result.response).toEqual({
statusCode: 413,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Payload too large' }),
});
expect(result.request.destroyed).toBe(true);
expect(result.requestErrors).toEqual([]);
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('merges partial status updates and refreshes updatedAt', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2026-07-25T10:00:00.000Z'));
bootstrapRemoteControl();
const updateStatus = getIpcListener('REMOTE_CONTROL_STATUS_UPDATE');
jest.setSystemTime(new Date('2026-07-25T10:01:00.000Z'));
updateStatus(
{},
{
portal: 'm3u',
isLiveView: true,
channelName: 'News',
volume: 35,
}
);
jest.setSystemTime(new Date('2026-07-25T10:02:00.000Z'));
updateStatus({}, { channelName: 'Sports', muted: true });
const result = await invokeHttpHandler(REMOTE_CONTROL_PATHS.STATUS, {
method: 'GET',
});
expect(result.response).toEqual({
statusCode: 200,
headers: JSON_HEADERS,
body: JSON.stringify({
portal: 'm3u',
isLiveView: true,
supportsVolume: false,
updatedAt: '2026-07-25T10:02:00.000Z',
channelName: 'Sports',
volume: 35,
muted: true,
}),
});
});
it.each([
{
path: REMOTE_CONTROL_PATHS.CHANNEL_UP,
warning: 'No browser windows found to send channel change',
},
{
path: REMOTE_CONTROL_PATHS.VOLUME_UP,
warning: 'No browser windows found to send remote command',
},
])(
'returns success and warns instead of throwing when $path has no renderer',
async ({ path, warning }) => {
mockGetAllWindows.mockReturnValue([]);
bootstrapRemoteControl();
await expect(
invokeHttpHandler(path, { method: 'POST' })
).resolves.toMatchObject({
response: SUCCESS_RESPONSE,
});
expect(consoleWarn).toHaveBeenCalledWith(warning);
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
}
);
});
@@ -1,81 +1,23 @@
import type * as http from 'node:http';
import { PassThrough } from 'node:stream';
type HttpHandler = (
request: http.IncomingMessage,
response: http.ServerResponse
) => void;
type IpcCallback = (...args: unknown[]) => unknown;
interface RemoteControlSettings {
enabled: boolean;
port: number;
}
interface ResponseSnapshot {
statusCode: number;
headers: http.OutgoingHttpHeaders;
body: string;
}
interface ResponseRecorder {
response: http.ServerResponse;
completed: Promise<ResponseSnapshot>;
}
interface RequestInvocation {
response: ResponseSnapshot;
request: PassThrough;
requestClosed: Promise<void>;
requestErrors: Error[];
}
interface InvokeOptions {
method: string;
body?: string | Buffer;
}
const REMOTE_CONTROL_PATHS = {
STATUS: '/api/remote-control/status',
SELECT_NUMBER: '/api/remote-control/channel/select-number',
CHANNEL_UP: '/api/remote-control/channel/up',
CHANNEL_DOWN: '/api/remote-control/channel/down',
VOLUME_UP: '/api/remote-control/volume/up',
VOLUME_DOWN: '/api/remote-control/volume/down',
VOLUME_TOGGLE_MUTE: '/api/remote-control/volume/toggle-mute',
} as const;
const JSON_HEADERS = { 'Content-Type': 'application/json' } as const;
const SUCCESS_RESPONSE = {
statusCode: 200,
headers: JSON_HEADERS,
body: JSON.stringify({ success: true }),
} as const;
const METHOD_NOT_ALLOWED_RESPONSE = {
statusCode: 405,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Method not allowed' }),
} as const;
const mockHttpHandlers = new Map<string, HttpHandler>();
const mockIpcHandlers = new Map<string, IpcCallback>();
const mockIpcListeners = new Map<string, IpcCallback>();
const mockRegisterRemoteControlHandler = jest.fn(
(path: string, handler: HttpHandler) => {
mockHttpHandlers.set(path, handler);
}
);
const mockStartHttpServer = jest.fn();
const mockStoreGet = jest.fn();
const mockFirstRendererSend = jest.fn();
const mockSecondRendererSend = jest.fn();
const mockGetAllWindows = jest.fn();
const mockIpcHandle = jest.fn((channel: string, handler: IpcCallback) => {
mockIpcHandlers.set(channel, handler);
});
const mockIpcOn = jest.fn((channel: string, listener: IpcCallback) => {
mockIpcListeners.set(channel, listener);
});
import {
bootstrapRemoteControl as bootstrapRemoteControlWith,
getIpcHandler,
invokeHttpHandler,
METHOD_NOT_ALLOWED_RESPONSE,
mockFirstRendererSend,
mockGetAllWindows,
mockHttpHandlers,
mockIpcHandle,
mockIpcHandlers,
mockIpcOn,
mockRegisterRemoteControlHandler,
mockSecondRendererSend,
mockStartHttpServer,
mockStoreGet,
REMOTE_CONTROL_PATHS,
resetRemoteControlMocks,
SUCCESS_RESPONSE,
type RemoteControlSettings,
} from './remote-control.test-helpers';
jest.mock('electron', () => ({
BrowserWindow: {
@@ -102,162 +44,15 @@ jest.mock('../services/store.service', () => ({
import { RemoteControlEvents } from './remote-control.events';
function createResponseRecorder(): ResponseRecorder {
let statusCode = 0;
let headers: http.OutgoingHttpHeaders = {};
let response: http.ServerResponse;
let responseEnded = false;
let resolveCompleted: (snapshot: ResponseSnapshot) => void = () => {
throw new Error('Response completion promise is not initialized');
};
const completed = new Promise<ResponseSnapshot>((resolve) => {
resolveCompleted = resolve;
});
const bootstrapRemoteControl = (settings?: RemoteControlSettings) =>
bootstrapRemoteControlWith(RemoteControlEvents, settings);
response = {
writeHead: (
nextStatusCode: number,
nextHeaders?: http.OutgoingHttpHeaders
) => {
statusCode = nextStatusCode;
headers = { ...nextHeaders };
return response;
},
end: (chunk?: string | Uint8Array) => {
if (responseEnded) {
throw new Error('Response ended more than once');
}
responseEnded = true;
const body =
typeof chunk === 'string'
? chunk
: chunk
? Buffer.from(chunk).toString('utf8')
: '';
resolveCompleted({ statusCode, headers, body });
return response;
},
} as unknown as http.ServerResponse;
return { response, completed };
}
function getHttpHandler(path: string): HttpHandler {
const handler = mockHttpHandlers.get(path);
if (!handler) {
throw new Error(`Expected HTTP handler for ${path}`);
}
return handler;
}
function getIpcHandler(channel: string): IpcCallback {
const handler = mockIpcHandlers.get(channel);
if (!handler) {
throw new Error(`Expected IPC handler for ${channel}`);
}
return handler;
}
function getIpcListener(channel: string): IpcCallback {
const listener = mockIpcListeners.get(channel);
if (!listener) {
throw new Error(`Expected IPC listener for ${channel}`);
}
return listener;
}
function bootstrapRemoteControl(
settings: RemoteControlSettings = { enabled: false, port: 8765 }
): RemoteControlEvents {
mockStoreGet.mockImplementation(
(key: string, fallbackValue: unknown): unknown => {
if (key === 'remoteControl') {
return settings.enabled;
}
if (key === 'remoteControlPort') {
return settings.port;
}
return fallbackValue;
}
);
const events = new RemoteControlEvents();
events.bootstrapRemoteControlEvents();
return events;
}
async function invokeHttpHandler(
path: string,
options: InvokeOptions
): Promise<RequestInvocation> {
const request = new PassThrough();
const requestErrors: Error[] = [];
request.on('error', (error: Error) => {
requestErrors.push(error);
});
const requestClosed = new Promise<void>((resolve) => {
request.once('close', resolve);
});
const incomingMessage = Object.assign(request, {
method: options.method,
url: path,
}) as unknown as http.IncomingMessage;
const recorder = createResponseRecorder();
getHttpHandler(path)(incomingMessage, recorder.response);
request.end(options.body);
return {
response: await recorder.completed,
request,
requestClosed,
requestErrors,
};
}
function createBodyAtByteLength(byteLength: number): string {
const prefix = '{"number":7,"padding":"';
const suffix = '"}';
const paddingLength =
byteLength -
Buffer.byteLength(prefix, 'utf8') -
Buffer.byteLength(suffix, 'utf8');
if (paddingLength < 0) {
throw new Error(`Cannot create a JSON body at ${byteLength} bytes`);
}
const body = `${prefix}${'x'.repeat(paddingLength)}${suffix}`;
if (Buffer.byteLength(body, 'utf8') !== byteLength) {
throw new Error(`Expected a ${byteLength}-byte JSON body`);
}
return body;
}
describe('RemoteControlEvents', () => {
describe('RemoteControlEvents bootstrap and dispatch', () => {
let consoleLog: jest.SpyInstance;
let consoleWarn: jest.SpyInstance;
beforeEach(() => {
mockHttpHandlers.clear();
mockIpcHandlers.clear();
mockIpcListeners.clear();
mockRegisterRemoteControlHandler.mockClear();
mockStartHttpServer.mockReset();
mockStoreGet.mockReset();
mockFirstRendererSend.mockReset();
mockSecondRendererSend.mockReset();
mockGetAllWindows.mockReset();
mockIpcHandle.mockClear();
mockIpcOn.mockClear();
mockGetAllWindows.mockReturnValue([
{ webContents: { send: mockFirstRendererSend } },
{ webContents: { send: mockSecondRendererSend } },
]);
resetRemoteControlMocks();
consoleLog = jest
.spyOn(console, 'log')
.mockImplementation(() => undefined);
@@ -390,199 +185,4 @@ describe('RemoteControlEvents', () => {
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('rejects POST on the status endpoint', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(REMOTE_CONTROL_PATHS.STATUS, {
method: 'POST',
});
expect(result.response).toEqual(METHOD_NOT_ALLOWED_RESPONSE);
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('floors a finite positive channel number and dispatches it', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body: JSON.stringify({ number: 7.9 }),
}
);
expect(result.response).toEqual(SUCCESS_RESPONSE);
expect(mockFirstRendererSend).toHaveBeenCalledWith(
'REMOTE_CONTROL_COMMAND',
{
type: 'channel-select-number',
number: 7,
}
);
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it.each([
{ label: 'missing', body: '{}' },
{ label: 'zero', body: '{"number":0}' },
{ label: 'negative', body: '{"number":-2}' },
{ label: 'infinite', body: '{"number":1e309}' },
{ label: 'nonnumeric', body: '{"number":"seven"}' },
])('rejects a $label channel number', async ({ body }) => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
expect(result.response).toEqual({
statusCode: 400,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Invalid channel number' }),
});
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('rejects malformed JSON without dispatching', async () => {
bootstrapRemoteControl();
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body: '{"number":',
}
);
expect(result.response).toEqual({
statusCode: 400,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Invalid JSON payload' }),
});
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('accepts a valid JSON request at the 10,240-byte body limit', async () => {
bootstrapRemoteControl();
const body = createBodyAtByteLength(10 * 1024);
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
expect(Buffer.byteLength(body, 'utf8')).toBe(10_240);
expect(result.response).toEqual(SUCCESS_RESPONSE);
expect(mockFirstRendererSend).toHaveBeenCalledWith(
'REMOTE_CONTROL_COMMAND',
{
type: 'channel-select-number',
number: 7,
}
);
});
it('rejects and destroys a 10,241-byte request without dispatching', async () => {
bootstrapRemoteControl();
const body = Buffer.alloc(10 * 1024 + 1, 'x');
const result = await invokeHttpHandler(
REMOTE_CONTROL_PATHS.SELECT_NUMBER,
{
method: 'POST',
body,
}
);
await result.requestClosed;
expect(body.byteLength).toBe(10_241);
expect(result.response).toEqual({
statusCode: 413,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Payload too large' }),
});
expect(result.request.destroyed).toBe(true);
expect(result.requestErrors).toEqual([]);
expect(mockGetAllWindows).not.toHaveBeenCalled();
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
});
it('merges partial status updates and refreshes updatedAt', async () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2026-07-25T10:00:00.000Z'));
bootstrapRemoteControl();
const updateStatus = getIpcListener('REMOTE_CONTROL_STATUS_UPDATE');
jest.setSystemTime(new Date('2026-07-25T10:01:00.000Z'));
updateStatus(
{},
{
portal: 'm3u',
isLiveView: true,
channelName: 'News',
volume: 35,
}
);
jest.setSystemTime(new Date('2026-07-25T10:02:00.000Z'));
updateStatus({}, { channelName: 'Sports', muted: true });
const result = await invokeHttpHandler(REMOTE_CONTROL_PATHS.STATUS, {
method: 'GET',
});
expect(result.response).toEqual({
statusCode: 200,
headers: JSON_HEADERS,
body: JSON.stringify({
portal: 'm3u',
isLiveView: true,
supportsVolume: false,
updatedAt: '2026-07-25T10:02:00.000Z',
channelName: 'Sports',
volume: 35,
muted: true,
}),
});
});
it.each([
{
path: REMOTE_CONTROL_PATHS.CHANNEL_UP,
warning: 'No browser windows found to send channel change',
},
{
path: REMOTE_CONTROL_PATHS.VOLUME_UP,
warning: 'No browser windows found to send remote command',
},
])(
'returns success and warns instead of throwing when $path has no renderer',
async ({ path, warning }) => {
mockGetAllWindows.mockReturnValue([]);
bootstrapRemoteControl();
await expect(
invokeHttpHandler(path, { method: 'POST' })
).resolves.toMatchObject({
response: SUCCESS_RESPONSE,
});
expect(consoleWarn).toHaveBeenCalledWith(warning);
expect(mockFirstRendererSend).not.toHaveBeenCalled();
expect(mockSecondRendererSend).not.toHaveBeenCalled();
}
);
});
@@ -0,0 +1,249 @@
import type * as http from 'node:http';
import { PassThrough } from 'node:stream';
/**
* Shared harness for the remote-control specs.
*
* Every exported mock is `mock`-prefixed so the spec files can reference them
* from their hoisted `jest.mock()` factories.
*/
export type HttpHandler = (
request: http.IncomingMessage,
response: http.ServerResponse
) => void;
export type IpcCallback = (...args: unknown[]) => unknown;
export interface RemoteControlSettings {
enabled: boolean;
port: number;
}
export interface ResponseSnapshot {
statusCode: number;
headers: http.OutgoingHttpHeaders;
body: string;
}
export interface ResponseRecorder {
response: http.ServerResponse;
completed: Promise<ResponseSnapshot>;
}
export interface RequestInvocation {
response: ResponseSnapshot;
request: PassThrough;
requestClosed: Promise<void>;
requestErrors: Error[];
}
export interface InvokeOptions {
method: string;
body?: string | Buffer;
}
export const REMOTE_CONTROL_PATHS = {
STATUS: '/api/remote-control/status',
SELECT_NUMBER: '/api/remote-control/channel/select-number',
CHANNEL_UP: '/api/remote-control/channel/up',
CHANNEL_DOWN: '/api/remote-control/channel/down',
VOLUME_UP: '/api/remote-control/volume/up',
VOLUME_DOWN: '/api/remote-control/volume/down',
VOLUME_TOGGLE_MUTE: '/api/remote-control/volume/toggle-mute',
} as const;
export const JSON_HEADERS = { 'Content-Type': 'application/json' } as const;
export const SUCCESS_RESPONSE = {
statusCode: 200,
headers: JSON_HEADERS,
body: JSON.stringify({ success: true }),
} as const;
export const METHOD_NOT_ALLOWED_RESPONSE = {
statusCode: 405,
headers: JSON_HEADERS,
body: JSON.stringify({ error: 'Method not allowed' }),
} as const;
export const mockHttpHandlers = new Map<string, HttpHandler>();
export const mockIpcHandlers = new Map<string, IpcCallback>();
export const mockIpcListeners = new Map<string, IpcCallback>();
export const mockRegisterRemoteControlHandler = jest.fn(
(path: string, handler: HttpHandler) => {
mockHttpHandlers.set(path, handler);
}
);
export const mockStartHttpServer = jest.fn();
export const mockStoreGet = jest.fn();
export const mockFirstRendererSend = jest.fn();
export const mockSecondRendererSend = jest.fn();
export const mockGetAllWindows = jest.fn();
export const mockIpcHandle = jest.fn(
(channel: string, handler: IpcCallback) => {
mockIpcHandlers.set(channel, handler);
}
);
export const mockIpcOn = jest.fn((channel: string, listener: IpcCallback) => {
mockIpcListeners.set(channel, listener);
});
/** Restore every mock to the state each spec's `beforeEach` expects. */
export function resetRemoteControlMocks(): void {
mockHttpHandlers.clear();
mockIpcHandlers.clear();
mockIpcListeners.clear();
mockRegisterRemoteControlHandler.mockClear();
mockStartHttpServer.mockReset();
mockStoreGet.mockReset();
mockFirstRendererSend.mockReset();
mockSecondRendererSend.mockReset();
mockGetAllWindows.mockReset();
mockIpcHandle.mockClear();
mockIpcOn.mockClear();
mockGetAllWindows.mockReturnValue([
{ webContents: { send: mockFirstRendererSend } },
{ webContents: { send: mockSecondRendererSend } },
]);
}
export function createResponseRecorder(): ResponseRecorder {
let statusCode = 0;
let headers: http.OutgoingHttpHeaders = {};
let responseEnded = false;
let resolveCompleted: (snapshot: ResponseSnapshot) => void = () => {
throw new Error('Response completion promise is not initialized');
};
const completed = new Promise<ResponseSnapshot>((resolve) => {
resolveCompleted = resolve;
});
const response = {
writeHead: (
nextStatusCode: number,
nextHeaders?: http.OutgoingHttpHeaders
) => {
statusCode = nextStatusCode;
headers = { ...nextHeaders };
return response;
},
end: (chunk?: string | Uint8Array) => {
if (responseEnded) {
throw new Error('Response ended more than once');
}
responseEnded = true;
const body =
typeof chunk === 'string'
? chunk
: chunk
? Buffer.from(chunk).toString('utf8')
: '';
resolveCompleted({ statusCode, headers, body });
return response;
},
} as unknown as http.ServerResponse;
return { response, completed };
}
export function getHttpHandler(path: string): HttpHandler {
const handler = mockHttpHandlers.get(path);
if (!handler) {
throw new Error(`Expected HTTP handler for ${path}`);
}
return handler;
}
export function getIpcHandler(channel: string): IpcCallback {
const handler = mockIpcHandlers.get(channel);
if (!handler) {
throw new Error(`Expected IPC handler for ${channel}`);
}
return handler;
}
export function getIpcListener(channel: string): IpcCallback {
const listener = mockIpcListeners.get(channel);
if (!listener) {
throw new Error(`Expected IPC listener for ${channel}`);
}
return listener;
}
/**
* Construct and bootstrap the events class under test. The constructor is
* injected so this module never imports the subject — the spec files own that
* import order relative to their `jest.mock()` calls.
*/
export function bootstrapRemoteControl<
T extends { bootstrapRemoteControlEvents(): unknown },
>(
RemoteControlEvents: new () => T,
settings: RemoteControlSettings = { enabled: false, port: 8765 }
): T {
mockStoreGet.mockImplementation(
(key: string, fallbackValue: unknown): unknown => {
if (key === 'remoteControl') {
return settings.enabled;
}
if (key === 'remoteControlPort') {
return settings.port;
}
return fallbackValue;
}
);
const events = new RemoteControlEvents();
events.bootstrapRemoteControlEvents();
return events;
}
export async function invokeHttpHandler(
path: string,
options: InvokeOptions
): Promise<RequestInvocation> {
const request = new PassThrough();
const requestErrors: Error[] = [];
request.on('error', (error: Error) => {
requestErrors.push(error);
});
const requestClosed = new Promise<void>((resolve) => {
request.once('close', resolve);
});
const incomingMessage = Object.assign(request, {
method: options.method,
url: path,
}) as unknown as http.IncomingMessage;
const recorder = createResponseRecorder();
getHttpHandler(path)(incomingMessage, recorder.response);
request.end(options.body);
return {
response: await recorder.completed,
request,
requestClosed,
requestErrors,
};
}
export function createBodyAtByteLength(byteLength: number): string {
const prefix = '{"number":7,"padding":"';
const suffix = '"}';
const paddingLength =
byteLength -
Buffer.byteLength(prefix, 'utf8') -
Buffer.byteLength(suffix, 'utf8');
if (paddingLength < 0) {
throw new Error(`Cannot create a JSON body at ${byteLength} bytes`);
}
const body = `${prefix}${'x'.repeat(paddingLength)}${suffix}`;
if (Buffer.byteLength(body, 'utf8') !== byteLength) {
throw new Error(`Expected a ${byteLength}-byte JSON body`);
}
return body;
}
@@ -9,9 +9,9 @@ import {
import * as http from 'node:http';
import type { AddressInfo } from 'node:net';
import { tmpdir } from 'node:os';
import { join, posix, win32 } from 'node:path';
import { join } from 'node:path';
import { HttpServer, resolveStaticFilePath } from './http-server';
import { HttpServer } from './http-server';
interface CapturedServer {
listening: Promise<void>;
@@ -119,77 +119,6 @@ async function getAvailablePort(): Promise<number> {
return port;
}
describe('resolveStaticFilePath', () => {
const POSIX_STATIC_ROOT = '/opt/iptvnator/remote-control';
const WINDOWS_STATIC_ROOT = 'C:\\iptvnator\\remote-control';
it('rejects a Windows double-leading-slash traversal', () => {
expect(
resolveStaticFilePath(
WINDOWS_STATIC_ROOT,
'//../../outside-secret.txt',
win32
)
).toBeNull();
});
it('rejects encoded traversal segments after one decode', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/%2e%2e/outside-secret.txt',
posix
)
).toBeNull();
});
it('fails closed for malformed percent encoding without throwing', () => {
expect(
resolveStaticFilePath(POSIX_STATIC_ROOT, '/%E0%A4%A', posix)
).toBeNull();
});
it('rejects decoded NUL bytes', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/assets/%00secret.js',
posix
)
).toBeNull();
});
it('resolves a valid Windows-style asset inside the static root', () => {
expect(
resolveStaticFilePath(
WINDOWS_STATIC_ROOT,
'/assets\\app.js?version=1',
win32
)
).toBe('C:\\iptvnator\\remote-control\\assets\\app.js');
});
it('ignores query and fragment data for filesystem resolution', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/data.json?cache=1#ignored',
posix
)
).toBe('/opt/iptvnator/remote-control/data.json');
});
it('decodes the URL pathname exactly once', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/%252e%252e/asset.js',
posix
)
).toBe('/opt/iptvnator/remote-control/%2e%2e/asset.js');
});
});
describe('HttpServer', () => {
let capturedServers: CapturedServer[];
let distPath: string;
@@ -0,0 +1,74 @@
import { posix, win32 } from 'node:path';
import { resolveStaticFilePath } from './http-server';
describe('resolveStaticFilePath', () => {
const POSIX_STATIC_ROOT = '/opt/iptvnator/remote-control';
const WINDOWS_STATIC_ROOT = 'C:\\iptvnator\\remote-control';
it('rejects a Windows double-leading-slash traversal', () => {
expect(
resolveStaticFilePath(
WINDOWS_STATIC_ROOT,
'//../../outside-secret.txt',
win32
)
).toBeNull();
});
it('rejects encoded traversal segments after one decode', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/%2e%2e/outside-secret.txt',
posix
)
).toBeNull();
});
it('fails closed for malformed percent encoding without throwing', () => {
expect(
resolveStaticFilePath(POSIX_STATIC_ROOT, '/%E0%A4%A', posix)
).toBeNull();
});
it('rejects decoded NUL bytes', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/assets/%00secret.js',
posix
)
).toBeNull();
});
it('resolves a valid Windows-style asset inside the static root', () => {
expect(
resolveStaticFilePath(
WINDOWS_STATIC_ROOT,
'/assets\\app.js?version=1',
win32
)
).toBe('C:\\iptvnator\\remote-control\\assets\\app.js');
});
it('ignores query and fragment data for filesystem resolution', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/data.json?cache=1#ignored',
posix
)
).toBe('/opt/iptvnator/remote-control/data.json');
});
it('decodes the URL pathname exactly once', () => {
expect(
resolveStaticFilePath(
POSIX_STATIC_ROOT,
'/%252e%252e/asset.js',
posix
)
).toBe('/opt/iptvnator/remote-control/%2e%2e/asset.js');
});
});
@@ -0,0 +1,285 @@
import {
WORKER_PERFORMANCE_UNAVAILABLE_REASON,
armWorkerPerformanceCapture,
executeWithWorkerPerformanceCapture,
startWorkerPerformanceCapture,
} from './worker-performance-capture';
import { createFakeRuntime } from './worker-performance-capture.test-harness';
const PROFILING_ENV = 'IPTVNATOR_PERF_WORKER_PROFILING';
/**
* Capture must never change the business result. These cases drive the
* monotonic-clock timeouts and every runtime callback that can throw, and
* assert the awaited value survives each one.
*/
describe('worker performance capture resilience', () => {
const originalProfilingValue = process.env[PROFILING_ENV];
afterEach(() => {
if (originalProfilingValue === undefined) {
delete process.env[PROFILING_ENV];
} else {
process.env[PROFILING_ENV] = originalProfilingValue;
}
});
it('times out arming after a monotonic 50ms without blocking work metrics', async () => {
const harness = createFakeRuntime({
armHistogram: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(
harness.scheduledTimeouts.reduce(
(total, timeout) => total + timeout,
0
)
).toBe(50);
expect(execution.performance).toMatchObject({
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
});
});
it('cannot poll forever when the monotonic runtime clock stalls', async () => {
const harness = createFakeRuntime({
armHistogram: false,
stallMonotonicClock: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(harness.scheduledTimeouts).toHaveLength(50);
expect(execution).toMatchObject({
result: 'result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
},
});
});
it('times out flushing after a monotonic 50ms while preserving work CPU and ELU', async () => {
const harness = createFakeRuntime({
flushHistogram: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(
harness.scheduledTimeouts
.slice(1)
.reduce((total, timeout) => total + timeout, 0)
).toBe(50);
expect(execution.performance).toMatchObject({
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_FLUSH_TIMEOUT,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
});
});
it('reports unavailable thread CPU without falling back to process CPU usage', async () => {
const harness = createFakeRuntime({
threadCpuAvailable: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(execution.performance).toMatchObject({
eventLoopUtilization: 0.75,
threadCpuSystemMicros: null,
threadCpuUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.THREAD_CPU_USAGE_UNAVAILABLE,
threadCpuUserMicros: null,
});
expect(harness.lifecycle).not.toContain('cpu:1');
expect(harness.lifecycle).not.toContain('cpu:2');
});
it('preserves the business result when CPU and ELU runtime callbacks throw', async () => {
const harness = createFakeRuntime({
throwBoundaryCallbacks: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: {
maxMs: 24,
p95Ms: 18,
p99Ms: 22,
},
eventLoopUtilization: null,
eventLoopUtilizationUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_UTILIZATION_UNAVAILABLE,
threadCpuSystemMicros: null,
threadCpuUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.THREAD_CPU_USAGE_UNAVAILABLE,
threadCpuUserMicros: null,
},
});
});
it('preserves work metrics and the business result when timer scheduling throws', async () => {
const harness = createFakeRuntime({
throwScheduleTimeout: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
eventLoopUtilization: 0.75,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
});
it.each([
{
label: 'count getter during finish',
options: {
throwHistogramCountAtRead: 3,
},
reason: WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_CAPTURE_UNAVAILABLE,
},
{
label: 'metric getter after flush',
options: {
throwHistogramMetric: true,
},
reason: WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_INVALID,
},
])(
'preserves the business result when the histogram $label throws',
async ({ options, reason }) => {
const harness = createFakeRuntime(options);
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason: reason,
eventLoopUtilization: 0.75,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
}
);
it('keeps the business result and rejects delay metrics when histogram disable throws', async () => {
const harness = createFakeRuntime({
throwHistogramDisable: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_CAPTURE_UNAVAILABLE,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
});
});
@@ -146,263 +146,4 @@ describe('worker performance capture', () => {
harness.lifecycle.indexOf('epoch:145')
);
});
it('times out arming after a monotonic 50ms without blocking work metrics', async () => {
const harness = createFakeRuntime({
armHistogram: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(
harness.scheduledTimeouts.reduce(
(total, timeout) => total + timeout,
0
)
).toBe(50);
expect(execution.performance).toMatchObject({
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
});
});
it('cannot poll forever when the monotonic runtime clock stalls', async () => {
const harness = createFakeRuntime({
armHistogram: false,
stallMonotonicClock: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(harness.scheduledTimeouts).toHaveLength(50);
expect(execution).toMatchObject({
result: 'result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
},
});
});
it('times out flushing after a monotonic 50ms while preserving work CPU and ELU', async () => {
const harness = createFakeRuntime({
flushHistogram: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(
harness.scheduledTimeouts
.slice(1)
.reduce((total, timeout) => total + timeout, 0)
).toBe(50);
expect(execution.performance).toMatchObject({
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_FLUSH_TIMEOUT,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
});
});
it('reports unavailable thread CPU without falling back to process CPU usage', async () => {
const harness = createFakeRuntime({
threadCpuAvailable: false,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'result'
);
expect(execution.performance).toMatchObject({
eventLoopUtilization: 0.75,
threadCpuSystemMicros: null,
threadCpuUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.THREAD_CPU_USAGE_UNAVAILABLE,
threadCpuUserMicros: null,
});
expect(harness.lifecycle).not.toContain('cpu:1');
expect(harness.lifecycle).not.toContain('cpu:2');
});
it('preserves the business result when CPU and ELU runtime callbacks throw', async () => {
const harness = createFakeRuntime({
throwBoundaryCallbacks: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: {
maxMs: 24,
p95Ms: 18,
p99Ms: 22,
},
eventLoopUtilization: null,
eventLoopUtilizationUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_UTILIZATION_UNAVAILABLE,
threadCpuSystemMicros: null,
threadCpuUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.THREAD_CPU_USAGE_UNAVAILABLE,
threadCpuUserMicros: null,
},
});
});
it('preserves work metrics and the business result when timer scheduling throws', async () => {
const harness = createFakeRuntime({
throwScheduleTimeout: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_ARM_TIMEOUT,
eventLoopUtilization: 0.75,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
});
it.each([
{
label: 'count getter during finish',
options: {
throwHistogramCountAtRead: 3,
},
reason: WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_CAPTURE_UNAVAILABLE,
},
{
label: 'metric getter after flush',
options: {
throwHistogramMetric: true,
},
reason: WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_INVALID,
},
])(
'preserves the business result when the histogram $label throws',
async ({ options, reason }) => {
const harness = createFakeRuntime(options);
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason: reason,
eventLoopUtilization: 0.75,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
}
);
it('keeps the business result and rejects delay metrics when histogram disable throws', async () => {
const harness = createFakeRuntime({
throwHistogramDisable: true,
});
const capture = startWorkerPerformanceCapture({
enabled: true,
runtime: harness.runtime,
});
await armWorkerPerformanceCapture(capture);
const execution = await executeWithWorkerPerformanceCapture(
capture,
async () => 'business-result'
);
expect(execution).toMatchObject({
error: null,
result: 'business-result',
success: true,
performance: {
eventLoopDelay: null,
eventLoopDelayUnavailableReason:
WORKER_PERFORMANCE_UNAVAILABLE_REASON.EVENT_LOOP_DELAY_CAPTURE_UNAVAILABLE,
eventLoopUtilization: 0.75,
histogramFlushedEpochMs: null,
threadCpuSystemMicros: 30,
threadCpuUserMicros: 80,
},
});
});
});