mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-03 04:19:27 -07:00
Route stats background mode through isolated daemon and defer in-app startup to live daemon (#58)
This commit is contained in:
86
src/main/runtime/stats-server-routing.test.ts
Normal file
86
src/main/runtime/stats-server-routing.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { createEnsureStatsServerUrlHandler } from './stats-server-routing';
|
||||
|
||||
function createHarness(options?: {
|
||||
state?: { pid: number; port: number; startedAtMs: number } | null;
|
||||
processAlive?: boolean;
|
||||
localServerStarted?: boolean;
|
||||
}) {
|
||||
const calls: string[] = [];
|
||||
let localServerStarted = options?.localServerStarted ?? false;
|
||||
const handler = createEnsureStatsServerUrlHandler({
|
||||
currentPid: 100,
|
||||
readBackgroundState: () => {
|
||||
calls.push('readBackgroundState');
|
||||
return options?.state ?? null;
|
||||
},
|
||||
removeBackgroundState: () => {
|
||||
calls.push('removeBackgroundState');
|
||||
},
|
||||
isProcessAlive: () => {
|
||||
calls.push('isProcessAlive');
|
||||
return options?.processAlive ?? true;
|
||||
},
|
||||
hasLocalStatsServer: () => localServerStarted,
|
||||
startLocalStatsServer: () => {
|
||||
calls.push('startLocalStatsServer');
|
||||
localServerStarted = true;
|
||||
},
|
||||
getConfiguredPort: () => 6969,
|
||||
});
|
||||
|
||||
return {
|
||||
calls,
|
||||
handler,
|
||||
};
|
||||
}
|
||||
|
||||
test('stats server routing defers to a live background daemon from another process', () => {
|
||||
const { calls, handler } = createHarness({
|
||||
state: { pid: 200, port: 7979, startedAtMs: 1 },
|
||||
processAlive: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(handler(), { url: 'http://127.0.0.1:7979', source: 'foreign' });
|
||||
assert.deepEqual(calls, ['readBackgroundState', 'isProcessAlive']);
|
||||
});
|
||||
|
||||
test('stats server routing clears dead daemon state and starts local server', () => {
|
||||
const { calls, handler } = createHarness({
|
||||
state: { pid: 200, port: 7979, startedAtMs: 1 },
|
||||
processAlive: false,
|
||||
});
|
||||
|
||||
assert.deepEqual(handler(), { url: 'http://127.0.0.1:6969', source: 'local' });
|
||||
assert.deepEqual(calls, [
|
||||
'readBackgroundState',
|
||||
'isProcessAlive',
|
||||
'removeBackgroundState',
|
||||
'startLocalStatsServer',
|
||||
]);
|
||||
});
|
||||
|
||||
test('stats server routing clears self-owned stale state and starts local server', () => {
|
||||
const { calls, handler } = createHarness({
|
||||
state: { pid: 100, port: 7979, startedAtMs: 1 },
|
||||
processAlive: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(handler(), { url: 'http://127.0.0.1:6969', source: 'local' });
|
||||
assert.deepEqual(calls, [
|
||||
'readBackgroundState',
|
||||
'removeBackgroundState',
|
||||
'startLocalStatsServer',
|
||||
]);
|
||||
});
|
||||
|
||||
test('stats server routing reuses a started local stats server', () => {
|
||||
const { calls, handler } = createHarness({
|
||||
state: null,
|
||||
localServerStarted: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(handler(), { url: 'http://127.0.0.1:6969', source: 'local' });
|
||||
assert.deepEqual(calls, ['readBackgroundState', 'removeBackgroundState']);
|
||||
});
|
||||
41
src/main/runtime/stats-server-routing.ts
Normal file
41
src/main/runtime/stats-server-routing.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { BackgroundStatsServerState } from './stats-daemon';
|
||||
|
||||
type EnsureStatsServerUrlDeps = {
|
||||
currentPid: number;
|
||||
readBackgroundState: () => BackgroundStatsServerState | null;
|
||||
removeBackgroundState: () => void;
|
||||
isProcessAlive: (pid: number) => boolean;
|
||||
hasLocalStatsServer: () => boolean;
|
||||
startLocalStatsServer: () => void;
|
||||
getConfiguredPort: () => number;
|
||||
};
|
||||
|
||||
function formatStatsServerUrl(port: number): string {
|
||||
return `http://127.0.0.1:${port}`;
|
||||
}
|
||||
|
||||
export type EnsureStatsServerUrlResult =
|
||||
| { url: string; source: 'foreign' }
|
||||
| { url: string; source: 'local' };
|
||||
|
||||
export function createEnsureStatsServerUrlHandler(
|
||||
deps: EnsureStatsServerUrlDeps,
|
||||
): () => EnsureStatsServerUrlResult {
|
||||
return () => {
|
||||
const state = deps.readBackgroundState();
|
||||
if (!state) {
|
||||
deps.removeBackgroundState();
|
||||
} else if (state.pid === deps.currentPid && !deps.hasLocalStatsServer()) {
|
||||
deps.removeBackgroundState();
|
||||
} else if (!deps.isProcessAlive(state.pid)) {
|
||||
deps.removeBackgroundState();
|
||||
} else if (state.pid !== deps.currentPid) {
|
||||
return { url: formatStatsServerUrl(state.port), source: 'foreign' };
|
||||
}
|
||||
|
||||
if (!deps.hasLocalStatsServer()) {
|
||||
deps.startLocalStatsServer();
|
||||
}
|
||||
return { url: formatStatsServerUrl(deps.getConfiguredPort()), source: 'local' };
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user