mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor(launcher): split CLI flow into command modules
Isolate process-side effects behind adapter seams and keep wrapper behavior stable while improving command-level testability.
This commit is contained in:
90
launcher/commands/command-modules.test.ts
Normal file
90
launcher/commands/command-modules.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { parseArgs } from '../config.js';
|
||||
import type { ProcessAdapter } from '../process-adapter.js';
|
||||
import type { LauncherCommandContext } from './context.js';
|
||||
import { runConfigCommand } from './config-command.js';
|
||||
import { runDoctorCommand } from './doctor-command.js';
|
||||
import { runMpvPreAppCommand } from './mpv-command.js';
|
||||
|
||||
class ExitSignal extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number) {
|
||||
super(`exit:${code}`);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function createContext(overrides: Partial<LauncherCommandContext> = {}): LauncherCommandContext {
|
||||
const args = parseArgs([], 'subminer', {});
|
||||
const adapter: ProcessAdapter = {
|
||||
platform: () => 'linux',
|
||||
onSignal: () => {},
|
||||
writeStdout: () => {},
|
||||
exit: (code) => {
|
||||
throw new ExitSignal(code);
|
||||
},
|
||||
setExitCode: () => {},
|
||||
};
|
||||
|
||||
return {
|
||||
args,
|
||||
scriptPath: '/tmp/subminer',
|
||||
scriptName: 'subminer',
|
||||
mpvSocketPath: '/tmp/subminer.sock',
|
||||
appPath: '/tmp/subminer.app',
|
||||
launcherJellyfinConfig: {},
|
||||
processAdapter: adapter,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('config command writes newline-terminated path via process adapter', () => {
|
||||
const writes: string[] = [];
|
||||
const context = createContext();
|
||||
context.args.configPath = true;
|
||||
context.processAdapter = {
|
||||
...context.processAdapter,
|
||||
writeStdout: (text) => writes.push(text),
|
||||
};
|
||||
|
||||
const handled = runConfigCommand(context, {
|
||||
existsSync: () => true,
|
||||
readFileSync: () => '',
|
||||
resolveMainConfigPath: () => '/tmp/SubMiner/config.jsonc',
|
||||
});
|
||||
|
||||
assert.equal(handled, true);
|
||||
assert.deepEqual(writes, ['/tmp/SubMiner/config.jsonc\n']);
|
||||
});
|
||||
|
||||
test('doctor command exits non-zero for missing hard dependencies', () => {
|
||||
const context = createContext({ appPath: null });
|
||||
context.args.doctor = true;
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
runDoctorCommand(context, {
|
||||
commandExists: () => false,
|
||||
configExists: () => true,
|
||||
resolveMainConfigPath: () => '/tmp/SubMiner/config.jsonc',
|
||||
}),
|
||||
(error: unknown) => error instanceof ExitSignal && error.code === 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('mpv pre-app command exits non-zero when socket is not ready', async () => {
|
||||
const context = createContext();
|
||||
context.args.mpvStatus = true;
|
||||
|
||||
await assert.rejects(
|
||||
async () => {
|
||||
await runMpvPreAppCommand(context, {
|
||||
waitForUnixSocketReady: async () => false,
|
||||
launchMpvIdleDetached: async () => {},
|
||||
});
|
||||
},
|
||||
(error: unknown) => error instanceof ExitSignal && error.code === 1,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user