test(launcher): add e2e smoke suite and CI gates

This commit is contained in:
2026-02-21 16:56:12 -08:00
parent 16b8d80498
commit 2a5830c4c5
11 changed files with 392 additions and 17 deletions

View File

@@ -45,6 +45,17 @@ jobs:
- name: Test suite (source) - name: Test suite (source)
run: bun run test:fast run: bun run test:fast
- name: Launcher smoke suite (source)
run: bun run test:launcher:smoke:src
- name: Upload launcher smoke artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: launcher-smoke
path: .tmp/launcher-smoke/**
if-no-files-found: ignore
- name: Build (bundle) - name: Build (bundle)
run: bun run build run: bun run build

View File

@@ -47,6 +47,17 @@ jobs:
- name: Test suite (source) - name: Test suite (source)
run: bun run test:fast run: bun run test:fast
- name: Launcher smoke suite (source)
run: bun run test:launcher:smoke:src
- name: Upload launcher smoke artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: launcher-smoke
path: .tmp/launcher-smoke/**
if-no-files-found: ignore
- name: Build (bundle) - name: Build (bundle)
run: bun run build run: bun run build

View File

@@ -1,11 +1,11 @@
--- ---
id: TASK-82 id: TASK-82
title: Add end-to-end smoke suite for launcher mpv ipc and overlay runtime title: Add end-to-end smoke suite for launcher mpv ipc and overlay runtime
status: In Progress status: Done
assignee: assignee:
- codex-task82-smoke-20260222T002523Z-3j7u - codex-task82-smoke-20260222T002523Z-3j7u
created_date: '2026-02-18 11:43' created_date: '2026-02-18 11:43'
updated_date: '2026-02-22 00:27' updated_date: '2026-02-22 00:55'
labels: labels:
- testing - testing
- e2e - e2e
@@ -47,10 +47,10 @@ Current coverage is strong at unit/service level but thin on end-to-end behavior
## Acceptance Criteria ## Acceptance Criteria
<!-- AC:BEGIN --> <!-- AC:BEGIN -->
- [ ] #1 E2E smoke suite exists and runs in automated workflow - [x] #1 E2E smoke suite exists and runs in automated workflow
- [ ] #2 Core launcher/mpv/ipc/overlay startup scenarios are covered - [x] #2 Core launcher/mpv/ipc/overlay startup scenarios are covered
- [ ] #3 Failures produce actionable logs/artifacts - [x] #3 Failures produce actionable logs/artifacts
- [ ] #4 Smoke suite documented in development/release docs - [x] #4 Smoke suite documented in development/release docs
<!-- AC:END --> <!-- AC:END -->
## Implementation Plan ## Implementation Plan
@@ -94,10 +94,34 @@ Scope guardrails
<!-- SECTION:NOTES:BEGIN --> <!-- SECTION:NOTES:BEGIN -->
2026-02-22: Started execution with opencode-task82-smoke-20260222T002150Z-p5bp via writing-plans -> executing-plans workflow. 2026-02-22: Started execution with opencode-task82-smoke-20260222T002150Z-p5bp via writing-plans -> executing-plans workflow.
Implemented launcher e2e smoke suite in `launcher/smoke.e2e.test.ts` using local fake `mpv` + fake app binaries, temp config/runtime sockets, and assertions for mpv status readiness, launcher startup wiring (`--backend`, `--socket`), IPC socket forwarding, overlay env propagation (`SUBMINER_MPV_LOG`), and post-mpv overlay stop command.
Added automation wiring: `package.json` scripts (`test:launcher:smoke:src`) and CI/release quality-gate steps to run smoke suite plus upload `.tmp/launcher-smoke/**` artifacts on failure in `.github/workflows/ci.yml` and `.github/workflows/release.yml`.
Documented smoke workflow and artifact triage in `docs/development.md` and release/pre-release verification guidance in `docs/installation.md`.
Verification evidence: `bun run test:launcher:smoke:src` PASS, `bun run test:launcher` PASS, `bun run test:fast` PASS, `bun run docs:build` PASS. `bun run build && bun run test:smoke:dist` remains blocked by pre-existing unrelated TASK-80 IPC typing errors in `src/core/services/ipc.ts` + `src/core/services/ipc.test.ts`.
2026-02-22: Implemented launcher smoke suite at `launcher/smoke.e2e.test.ts` with deterministic fake mpv/app harness and preserved failure artifacts under `.tmp/launcher-smoke/<case-id>`.
2026-02-22: Wired source smoke lane into automation: `package.json` (`test:launcher:smoke:src`), `.github/workflows/ci.yml` and `.github/workflows/release.yml` now run the lane and upload `.tmp/launcher-smoke/**` artifacts on failure.
2026-02-22: Updated docs with smoke command + release gate reference and artifact guidance in `docs/development.md` and `docs/installation.md`.
2026-02-22 verification: `bun run test:launcher:smoke:src`, `bun run test:launcher`, `bun run test:fast`, `bun run build && bun run test:smoke:dist`, `bun run docs:build`.
2026-02-22 correction: `bun run build && bun run test:smoke:dist` passes in current workspace; prior note about TASK-80-related blockage is stale from earlier intermediate run.
<!-- SECTION:NOTES:END --> <!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added an end-to-end launcher smoke suite that exercises launcher->mpv IPC socket lifecycle plus overlay start/stop runtime wiring using deterministic local fake mpv/app fixtures and artifact-preserving failure output. Integrated the smoke lane into CI and release quality gates with failure artifact uploads, documented local/release usage, and verified all required test/build/docs lanes pass.
<!-- SECTION:FINAL_SUMMARY:END -->
## Definition of Done ## Definition of Done
<!-- DOD:BEGIN --> <!-- DOD:BEGIN -->
- [ ] #1 Smoke suite passes on baseline branch - [x] #1 Smoke suite passes on baseline branch
- [ ] #2 Release checklist references smoke suite - [x] #2 Release checklist references smoke suite
<!-- DOD:END --> <!-- DOD:END -->

View File

@@ -63,6 +63,7 @@ electron . --background # tray/background mode, minimal de
```bash ```bash
bun run test:config # Source-level config schema/validation tests bun run test:config # Source-level config schema/validation tests
bun run test:launcher # Launcher regression tests (config discovery + command routing) bun run test:launcher # Launcher regression tests (config discovery + command routing)
bun run test:launcher:smoke:src # Launcher e2e smoke: launcher -> mpv IPC -> overlay start/stop wiring
bun run test:core # Source-level core regression tests (default lane) bun run test:core # Source-level core regression tests (default lane)
bun run test:subtitle # Subtitle pipeline tests (build + run) bun run test:subtitle # Subtitle pipeline tests (build + run)
bun run test:fast # Source-level config + core lane (no build prerequisite) bun run test:fast # Source-level config + core lane (no build prerequisite)
@@ -70,6 +71,8 @@ bun run test:fast # Source-level config + core lane (no build prerequisi
Dist-level tests are now an explicit smoke lane used to validate compiled/runtime assumptions. Dist-level tests are now an explicit smoke lane used to validate compiled/runtime assumptions.
Launcher smoke artifacts are written to `.tmp/launcher-smoke` locally and uploaded by CI/release workflows when the smoke step fails.
Smoke and optional deep dist commands: Smoke and optional deep dist commands:
```bash ```bash

View File

@@ -224,4 +224,7 @@ SubMiner.AppImage --help # Show all CLI options
You should see the overlay appear over mpv. If subtitles are loaded in the video, they will appear as interactive text in the overlay. You should see the overlay appear over mpv. If subtitles are loaded in the video, they will appear as interactive text in the overlay.
Release/pre-release gate: run `bun run test:launcher:smoke:src` to validate launcher -> mpv IPC socket -> overlay start/stop wiring.
If the smoke lane fails, inspect `.tmp/launcher-smoke` artifacts locally; CI/release quality gates upload the same artifacts on failure.
Next: [Usage](/usage) — learn about the `subminer` wrapper, keybindings, and YouTube playback. Next: [Usage](/usage) — learn about the `subminer` wrapper, keybindings, and YouTube playback.

View File

@@ -55,5 +55,5 @@ Read first. Keep concise.
| `opencode-task79-runtime-reducers-20260221T235652Z-n4p7` | `opencode-task79-runtime-reducers` | `Execute TASK-79 explicit runtime state transitions/reducers in main via plan-first workflow` | `done` | `docs/subagents/agents/opencode-task79-runtime-reducers-20260221T235652Z-n4p7.md` | `2026-02-22T00:10:51Z` | | `opencode-task79-runtime-reducers-20260221T235652Z-n4p7` | `opencode-task79-runtime-reducers` | `Execute TASK-79 explicit runtime state transitions/reducers in main via plan-first workflow` | `done` | `docs/subagents/agents/opencode-task79-runtime-reducers-20260221T235652Z-n4p7.md` | `2026-02-22T00:10:51Z` |
| `opencode-task79-sliceb-20260222T000253Z-m2r7` | `opencode-task79-sliceb` | `Implement TASK-79 slice B invariants coverage/tests and composition-boundary docs updates without commit` | `done` | `docs/subagents/agents/opencode-task79-sliceb-20260222T000253Z-m2r7.md` | `2026-02-22T00:04:21Z` | | `opencode-task79-sliceb-20260222T000253Z-m2r7` | `opencode-task79-sliceb` | `Implement TASK-79 slice B invariants coverage/tests and composition-boundary docs updates without commit` | `done` | `docs/subagents/agents/opencode-task79-sliceb-20260222T000253Z-m2r7.md` | `2026-02-22T00:04:21Z` |
| `opencode-task80-ipc-contract-20260222T001728Z-obrv` | `opencode-task80-ipc-contract` | `Execute TASK-80 IPC contract typing + runtime payload validation end-to-end without commit` | `planning` | `docs/subagents/agents/opencode-task80-ipc-contract-20260222T001728Z-obrv.md` | `2026-02-22T00:17:28Z` | | `opencode-task80-ipc-contract-20260222T001728Z-obrv` | `opencode-task80-ipc-contract` | `Execute TASK-80 IPC contract typing + runtime payload validation end-to-end without commit` | `planning` | `docs/subagents/agents/opencode-task80-ipc-contract-20260222T001728Z-obrv.md` | `2026-02-22T00:17:28Z` |
| `opencode-task82-smoke-20260222T002150Z-p5bp` | `opencode-task82-smoke` | `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` | `planning` | `docs/subagents/agents/opencode-task82-smoke-20260222T002150Z-p5bp.md` | `2026-02-22T00:21:50Z` | | `opencode-task82-smoke-20260222T002150Z-p5bp` | `opencode-task82-smoke` | `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` | `done` | `docs/subagents/agents/opencode-task82-smoke-20260222T002150Z-p5bp.md` | `2026-02-22T00:54:29Z` |
| `codex-task82-smoke-20260222T002523Z-3j7u` | `codex-task82-smoke` | `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` | `planning` | `docs/subagents/agents/codex-task82-smoke-20260222T002523Z-3j7u.md` | `2026-02-22T00:25:23Z` | | `codex-task82-smoke-20260222T002523Z-3j7u` | `codex-task82-smoke` | `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` | `done` | `docs/subagents/agents/codex-task82-smoke-20260222T002523Z-3j7u.md` | `2026-02-22T00:53:25Z` |

View File

@@ -2,9 +2,9 @@
- alias: `codex-task82-smoke` - alias: `codex-task82-smoke`
- mission: `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` - mission: `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit`
- status: `planning` - status: `done`
- start_utc: `2026-02-22T00:25:23Z` - start_utc: `2026-02-22T00:25:23Z`
- last_update_utc: `2026-02-22T00:25:23Z` - last_update_utc: `2026-02-22T00:53:25Z`
## Intent ## Intent
@@ -30,3 +30,17 @@
## Log ## Log
- `2026-02-22T00:25:23Z` session started; reading backlog + subagent context; entering planning. - `2026-02-22T00:25:23Z` session started; reading backlog + subagent context; entering planning.
- `2026-02-22T00:53:25Z` implemented TASK-82 smoke suite + workflow/docs wiring; verified smoke/launcher/fast/docs lanes; finalized backlog TASK-82 Done (build/dist smoke blocked by unrelated TASK-80 IPC typing errors).
## Files Touched
- `launcher/smoke.e2e.test.ts`
- `package.json`
- `.github/workflows/ci.yml`
- `.github/workflows/release.yml`
- `docs/development.md`
- `docs/installation.md`
## Next Step
- Wait for user review; optional follow-up is rerunning build/dist smoke after TASK-80 IPC typing fixes land.

View File

@@ -2,9 +2,9 @@
- alias: `opencode-task82-smoke` - alias: `opencode-task82-smoke`
- mission: `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit` - mission: `Execute TASK-82 e2e smoke suite for launcher/mpv/ipc/overlay end-to-end without commit`
- status: `planning` - status: `done`
- start_utc: `2026-02-22T00:21:50Z` - start_utc: `2026-02-22T00:21:50Z`
- last_update_utc: `2026-02-22T00:21:50Z` - last_update_utc: `2026-02-22T00:54:29Z`
## Intent ## Intent
@@ -19,7 +19,8 @@
- `package.json` - `package.json`
- `.github/workflows/*.yml` - `.github/workflows/*.yml`
- `docs/development.md` - `docs/development.md`
- `docs/RELEASING.md` - `docs/installation.md`
- `backlog/tasks/task-82 - Add-end-to-end-smoke-suite-for-launcher-mpv-ipc-and-overlay-runtime.md`
## Assumptions ## Assumptions
@@ -29,3 +30,4 @@
## Log ## Log
- `2026-02-22T00:21:50Z` session started; backlog context loaded; moving to planning. - `2026-02-22T00:21:50Z` session started; backlog context loaded; moving to planning.
- `2026-02-22T00:54:29Z` completed TASK-82 implementation slice: stabilized `launcher/smoke.e2e.test.ts`, wired `test:launcher:smoke:src` into CI/release with artifact upload on failure, updated docs (`development`, `installation`), and verified lanes: `test:launcher:smoke:src`, `test:launcher`, `test:fast`, `build`, `test:smoke:dist`, `docs:build`.

View File

@@ -74,3 +74,5 @@ Shared notes. Append-only.
- [2026-02-22T00:17:28Z] [opencode-task80-ipc-contract-20260222T001728Z-obrv|opencode-task80-ipc-contract] starting TASK-80 via Backlog MCP + writing-plans/executing-plans; scope IPC contract typing/runtime payload validation + malformed payload tests; will parallelize independent slices where possible. - [2026-02-22T00:17:28Z] [opencode-task80-ipc-contract-20260222T001728Z-obrv|opencode-task80-ipc-contract] starting TASK-80 via Backlog MCP + writing-plans/executing-plans; scope IPC contract typing/runtime payload validation + malformed payload tests; will parallelize independent slices where possible.
- [2026-02-22T00:21:50Z] [opencode-task82-smoke-20260222T002150Z-p5bp|opencode-task82-smoke] starting TASK-82 via Backlog MCP + writing-plans/executing-plans; scope e2e smoke suite for launcher mpv ipc overlay startup + workflow/docs wiring, no commit. - [2026-02-22T00:21:50Z] [opencode-task82-smoke-20260222T002150Z-p5bp|opencode-task82-smoke] starting TASK-82 via Backlog MCP + writing-plans/executing-plans; scope e2e smoke suite for launcher mpv ipc overlay startup + workflow/docs wiring, no commit.
- [2026-02-22T00:25:23Z] [codex-task82-smoke-20260222T002523Z-3j7u|codex-task82-smoke] overlap note: taking active TASK-82 execution; reusing existing task context/plan artifact, scoping edits to launcher smoke test + workflow/docs wiring + backlog evidence updates only. - [2026-02-22T00:25:23Z] [codex-task82-smoke-20260222T002523Z-3j7u|codex-task82-smoke] overlap note: taking active TASK-82 execution; reusing existing task context/plan artifact, scoping edits to launcher smoke test + workflow/docs wiring + backlog evidence updates only.
- [2026-02-22T00:54:29Z] [opencode-task82-smoke-20260222T002150Z-p5bp|opencode-task82-smoke] completed TASK-82 implementation pass: launcher smoke e2e stabilized (`launcher/smoke.e2e.test.ts`), CI/release smoke + artifact upload wired, docs updated (`docs/development.md`, `docs/installation.md`), and verification lanes green (`test:launcher:smoke:src`, `test:launcher`, `test:fast`, `build`, `test:smoke:dist`, `docs:build`).
- [2026-02-22T00:53:25Z] [codex-task82-smoke-20260222T002523Z-3j7u|codex-task82-smoke] completed TASK-82: added `launcher/smoke.e2e.test.ts`, wired `test:launcher:smoke:src` + CI/release smoke gates with `.tmp/launcher-smoke` failure artifact upload, docs updated (`docs/development.md`, `docs/installation.md`), launcher/fast/docs lanes green; `build + test:smoke:dist` still blocked by unrelated TASK-80 IPC typing errors.

304
launcher/smoke.e2e.test.ts Normal file
View File

@@ -0,0 +1,304 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawn, spawnSync } from 'node:child_process';
type RunResult = {
status: number | null;
stdout: string;
stderr: string;
};
type SmokeCase = {
root: string;
artifactsDir: string;
binDir: string;
xdgConfigHome: string;
homeDir: string;
socketDir: string;
socketPath: string;
videoPath: string;
fakeAppPath: string;
fakeMpvPath: string;
mpvOverlayLogPath: string;
};
function writeExecutable(filePath: string, body: string): void {
fs.writeFileSync(filePath, body);
fs.chmodSync(filePath, 0o755);
}
function createSmokeCase(name: string): SmokeCase {
const baseDir = path.join(process.cwd(), '.tmp', 'launcher-smoke');
fs.mkdirSync(baseDir, { recursive: true });
const root = fs.mkdtempSync(path.join(baseDir, `${name}-`));
const artifactsDir = path.join(root, 'artifacts');
const binDir = path.join(root, 'bin');
const xdgConfigHome = path.join(root, 'xdg');
const homeDir = path.join(root, 'home');
const socketDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-smoke-sock-'));
const socketPath = path.join(socketDir, 'subminer.sock');
const videoPath = path.join(root, 'video.mkv');
const fakeAppPath = path.join(binDir, 'fake-subminer');
const fakeMpvPath = path.join(binDir, 'mpv');
const mpvOverlayLogPath = path.join(artifactsDir, 'mpv-overlay.log');
fs.mkdirSync(artifactsDir, { recursive: true });
fs.mkdirSync(binDir, { recursive: true });
fs.mkdirSync(path.join(xdgConfigHome, 'mpv', 'script-opts'), { recursive: true });
fs.writeFileSync(videoPath, 'fake video fixture');
fs.writeFileSync(
path.join(xdgConfigHome, 'mpv', 'script-opts', 'subminer.conf'),
`socket_path=${socketPath}\n`,
);
const fakeMpvLogPath = path.join(artifactsDir, 'fake-mpv.log');
const fakeAppLogPath = path.join(artifactsDir, 'fake-app.log');
const fakeAppStartLogPath = path.join(artifactsDir, 'fake-app-start.log');
const fakeAppStopLogPath = path.join(artifactsDir, 'fake-app-stop.log');
writeExecutable(
fakeMpvPath,
`#!/usr/bin/env node
const fs = require('node:fs');
const net = require('node:net');
const path = require('node:path');
const logPath = ${JSON.stringify(fakeMpvLogPath)};
const args = process.argv.slice(2);
const socketArg = args.find((arg) => arg.startsWith('--input-ipc-server='));
const socketPath = socketArg ? socketArg.slice('--input-ipc-server='.length) : '';
fs.appendFileSync(logPath, JSON.stringify({ argv: args, socketPath }) + '\\n');
if (!socketPath) {
process.exit(2);
}
try {
fs.rmSync(socketPath, { force: true });
} catch {}
fs.mkdirSync(path.dirname(socketPath), { recursive: true });
const server = net.createServer((socket) => socket.end());
server.on('error', (error) => {
fs.appendFileSync(logPath, JSON.stringify({ error: String(error) }) + '\\n');
process.exit(3);
});
server.listen(socketPath);
const closeAndExit = () => {
server.close(() => process.exit(0));
};
setTimeout(closeAndExit, 3000);
process.on('SIGTERM', closeAndExit);
`,
);
writeExecutable(
fakeAppPath,
`#!/usr/bin/env node
const fs = require('node:fs');
const logPath = ${JSON.stringify(fakeAppLogPath)};
const startPath = ${JSON.stringify(fakeAppStartLogPath)};
const stopPath = ${JSON.stringify(fakeAppStopLogPath)};
const entry = {
argv: process.argv.slice(2),
subminerMpvLog: process.env.SUBMINER_MPV_LOG || '',
};
fs.appendFileSync(logPath, JSON.stringify(entry) + '\\n');
if (entry.argv.includes('--start')) {
fs.appendFileSync(startPath, JSON.stringify(entry) + '\\n');
}
if (entry.argv.includes('--stop')) {
fs.appendFileSync(stopPath, JSON.stringify(entry) + '\\n');
}
process.exit(0);
`,
);
return {
root,
artifactsDir,
binDir,
xdgConfigHome,
homeDir,
socketDir,
socketPath,
videoPath,
fakeAppPath,
fakeMpvPath,
mpvOverlayLogPath,
};
}
function makeTestEnv(smokeCase: SmokeCase): NodeJS.ProcessEnv {
return {
...process.env,
HOME: smokeCase.homeDir,
XDG_CONFIG_HOME: smokeCase.xdgConfigHome,
SUBMINER_APPIMAGE_PATH: smokeCase.fakeAppPath,
SUBMINER_MPV_LOG: smokeCase.mpvOverlayLogPath,
PATH: `${smokeCase.binDir}${path.delimiter}${process.env.PATH || ''}`,
};
}
function runLauncher(
smokeCase: SmokeCase,
argv: string[],
env: NodeJS.ProcessEnv,
label: string,
): RunResult {
const result = spawnSync(
process.execPath,
['run', path.join(process.cwd(), 'launcher/main.ts'), ...argv],
{
env,
encoding: 'utf8',
timeout: 15000,
},
);
const stdout = result.stdout || '';
const stderr = result.stderr || '';
fs.writeFileSync(path.join(smokeCase.artifactsDir, `${label}.stdout.log`), stdout);
fs.writeFileSync(path.join(smokeCase.artifactsDir, `${label}.stderr.log`), stderr);
return {
status: result.status,
stdout,
stderr,
};
}
async function withSmokeCase(
name: string,
fn: (smokeCase: SmokeCase) => Promise<void>,
): Promise<void> {
const smokeCase = createSmokeCase(name);
let completed = false;
try {
await fn(smokeCase);
completed = true;
} catch (error) {
process.stderr.write(`[launcher-smoke] preserved artifacts: ${smokeCase.root}\n`);
throw error;
} finally {
if (completed) {
fs.rmSync(smokeCase.root, { recursive: true, force: true });
}
fs.rmSync(smokeCase.socketDir, { recursive: true, force: true });
}
}
function readJsonLines(filePath: string): Array<Record<string, unknown>> {
if (!fs.existsSync(filePath)) return [];
return fs
.readFileSync(filePath, 'utf8')
.split(/\r?\n/)
.filter((line) => line.trim().length > 0)
.map((line) => JSON.parse(line) as Record<string, unknown>);
}
async function waitForJsonLines(
filePath: string,
minCount: number,
timeoutMs = 1500,
): Promise<void> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (readJsonLines(filePath).length >= minCount) {
return;
}
await new Promise<void>((resolve) => setTimeout(resolve, 50));
}
}
test('launcher mpv status returns ready when socket is connectable', async () => {
await withSmokeCase('mpv-status', async (smokeCase) => {
const env = makeTestEnv(smokeCase);
const fakeMpv = spawn(smokeCase.fakeMpvPath, [`--input-ipc-server=${smokeCase.socketPath}`], {
env,
stdio: 'ignore',
});
try {
await new Promise<void>((resolve) => setTimeout(resolve, 120));
const result = runLauncher(
smokeCase,
['mpv', 'status', '--log-level', 'debug'],
env,
'mpv-status',
);
assert.equal(result.status, 0);
assert.match(result.stdout, /socket ready/i);
} finally {
if (fakeMpv.exitCode === null) {
await new Promise<void>((resolve) => {
fakeMpv.once('close', () => resolve());
});
}
}
});
});
test(
'launcher start-overlay run forwards socket/backend and stops overlay after mpv exits',
{ timeout: 20000 },
async () => {
await withSmokeCase('overlay-start-stop', async (smokeCase) => {
const env = makeTestEnv(smokeCase);
const result = runLauncher(
smokeCase,
['--backend', 'x11', '--start-overlay', smokeCase.videoPath],
env,
'overlay-start-stop',
);
assert.equal(result.status, 0);
assert.match(result.stdout, /Starting SubMiner overlay/i);
const appStartPath = path.join(smokeCase.artifactsDir, 'fake-app-start.log');
const appStopPath = path.join(smokeCase.artifactsDir, 'fake-app-stop.log');
await waitForJsonLines(appStartPath, 1);
await waitForJsonLines(appStopPath, 1);
const appStartEntries = readJsonLines(appStartPath);
const appStopEntries = readJsonLines(appStopPath);
const mpvEntries = readJsonLines(path.join(smokeCase.artifactsDir, 'fake-mpv.log'));
assert.equal(appStartEntries.length, 1);
assert.equal(appStopEntries.length, 1);
assert.equal(mpvEntries.length >= 1, true);
const appStartArgs = appStartEntries[0]?.argv;
assert.equal(Array.isArray(appStartArgs), true);
assert.equal((appStartArgs as string[]).includes('--start'), true);
assert.equal((appStartArgs as string[]).includes('--backend'), true);
assert.equal((appStartArgs as string[]).includes('x11'), true);
assert.equal((appStartArgs as string[]).includes('--socket'), true);
assert.equal((appStartArgs as string[]).includes(smokeCase.socketPath), true);
assert.equal(appStartEntries[0]?.subminerMpvLog, smokeCase.mpvOverlayLogPath);
const appStopArgs = appStopEntries[0]?.argv;
assert.deepEqual(appStopArgs, ['--stop']);
const mpvFirstArgs = mpvEntries[0]?.argv;
assert.equal(Array.isArray(mpvFirstArgs), true);
assert.equal(
(mpvFirstArgs as string[]).some(
(arg) => arg === `--input-ipc-server=${smokeCase.socketPath}`,
),
true,
);
assert.equal((mpvFirstArgs as string[]).includes(smokeCase.videoPath), true);
});
},
);

View File

@@ -23,9 +23,10 @@
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts src/config/definitions/domain-registry.test.ts", "test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts src/config/definitions/domain-registry.test.ts",
"test:config:dist": "node --test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js", "test:config:dist": "node --test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js",
"test:config:smoke:dist": "node --test dist/config/path-resolution.test.js", "test:config:smoke:dist": "node --test dist/config/path-resolution.test.js",
"test:launcher:src": "bun test launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts", "test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
"test:launcher:src": "bun test launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/smoke.e2e.test.ts",
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/config-hot-reload.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/renderer/error-recovery.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/x11-tracker.test.ts launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts", "test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/config-hot-reload.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/renderer/error-recovery.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/x11-tracker.test.ts launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts",
"test:core:dist": "node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/config-hot-reload.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/renderer/error-recovery.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js", "test:core:dist": "node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/anki-jimaku-ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/config-hot-reload.test.js dist/core/services/tokenizer.test.js dist/core/services/tokenizer/annotation-stage.test.js dist/core/services/tokenizer/parser-selection-stage.test.js dist/core/services/tokenizer/parser-enrichment-stage.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/renderer/error-recovery.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
"test:core:smoke:dist": "node --test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js", "test:core:smoke:dist": "node --test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist", "test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
"test:subtitle:dist": "echo \"Subtitle tests are currently not configured\"", "test:subtitle:dist": "echo \"Subtitle tests are currently not configured\"",