Skip to content

Commit aaafef5

Browse files
committed
test(cli): move the #4095 e2e onto the shared serve harness main introduced
#4110 extracted the spawn-the-real-`os serve` harness this test had inlined into `test/helpers/serve-process.ts`; use it instead of carrying a second copy. The `os compile` step stays, but its justification inverts. It was there because config-boot could not start at all without `dist/objectstack.json` (#4085) — now fixed — and it is kept for a positive reason: the artifact is what makes `createStandaloneStack()` contribute the artifact-derived AppPlugin whose missing code this regression is about. Without it serve wraps the config directly, `onEnable` runs for the trivial reason, and the test would pass while exercising nothing. Comment says so, so nobody removes it as leftover #4085 scaffolding. Re-verified against merged main: with `packages/cli/src/commands/serve.ts` taken from origin/main the case still fails ("the action's handler went unregistered"), so #4095 is live on the new base and this remains a real guard. Full CLI suite 88 files / 909 tests; eslint src clean. Refs #4095 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HrRNgrWaRtggzmrHpbomyh
1 parent 001c01e commit aaafef5

1 file changed

Lines changed: 15 additions & 80 deletions

File tree

packages/cli/test/serve-app-runtime-hooks.e2e.test.ts

Lines changed: 15 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,25 @@
1111
* the module's `onEnable` — was skipped. A JSON artifact cannot hold a function,
1212
* so the app booted with all of its metadata and none of its code.
1313
*
14-
* Only a test that drives the real command can catch that: the loss happens in
15-
* the join between the artifact-derived stack and the loaded module, and neither
16-
* half is wrong on its own. So this boots a real stack through `bin/run-dev.js`
17-
* and reads its stdout.
14+
* Only a test that drives the real command can catch it: the loss happens in the
15+
* join between the artifact-derived stack and the loaded module, and neither half
16+
* is wrong on its own.
1817
*
1918
* The oracle is the ADR-0110 D5 inventory (visible on the CLI since #4012): a
2019
* `script` action whose handler is registered in `onEnable` reconciles as BOUND
21-
* when the hook ran, and is reported under `[action-governance]` when it did
22-
* not. Confirmed to fail against the pre-fix command, naming
23-
* `hookfix_task:do_thing`.
20+
* when the hook ran, and is reported under `[action-governance]` when it did not.
21+
* Confirmed to fail against the pre-fix command, naming `hookfix_task:do_thing`.
2422
*/
2523

2624
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
27-
import { execFile, spawn } from 'node:child_process';
25+
import { execFile } from 'node:child_process';
2826
import { promisify } from 'node:util';
2927
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3028
import { tmpdir } from 'node:os';
31-
import { join, resolve } from 'node:path';
32-
import { fileURLToPath } from 'node:url';
29+
import { join } from 'node:path';
30+
import { runServe, randomPort, CLI, TSX } from './helpers/serve-process.js';
3331

3432
const execFileP = promisify(execFile);
35-
const HERE = resolve(fileURLToPath(import.meta.url), '..');
36-
const CLI = resolve(HERE, '../bin/run-dev.js');
37-
const TSX = resolve(HERE, '../../../node_modules/.bin/tsx');
3833

3934
/**
4035
* An app whose action is only executable if its module-level `onEnable` runs:
@@ -72,76 +67,17 @@ export default {
7267
};
7368
`;
7469

75-
interface ServeRun {
76-
stdout: string;
77-
stderr: string;
78-
}
79-
80-
/** Boot `os serve` in `cwd`, collect output until the banner, then stop it. */
81-
function runServe(
82-
cwd: string,
83-
args: string[],
84-
opts: { waitFor: RegExp; timeoutMs?: number },
85-
): Promise<ServeRun> {
86-
return new Promise((resolveRun, rejectRun) => {
87-
const child = spawn(TSX, [CLI, 'serve', 'objectstack.config.ts', ...args], {
88-
cwd,
89-
env: {
90-
...process.env,
91-
NO_COLOR: '1',
92-
OS_DATABASE_URL: ':memory:',
93-
OS_LOG_LEVEL: '',
94-
OS_DISABLE_CONSOLE: '1',
95-
},
96-
});
97-
98-
let stdout = '';
99-
let stderr = '';
100-
let settled = false;
101-
102-
const finish = (err?: Error) => {
103-
if (settled) return;
104-
settled = true;
105-
clearTimeout(timer);
106-
try {
107-
child.kill('SIGTERM');
108-
} catch {
109-
/* already gone */
110-
}
111-
if (err) rejectRun(err);
112-
else resolveRun({ stdout, stderr });
113-
};
114-
115-
const timer = setTimeout(
116-
() =>
117-
finish(
118-
new Error(
119-
`serve did not reach ${opts.waitFor} in time.\n--- stdout ---\n${stdout}\n--- stderr ---\n${stderr}`,
120-
),
121-
),
122-
opts.timeoutMs ?? 180_000,
123-
);
124-
125-
child.stdout.on('data', (d) => {
126-
stdout += String(d);
127-
if (opts.waitFor.test(stdout)) finish();
128-
});
129-
child.stderr.on('data', (d) => {
130-
stderr += String(d);
131-
});
132-
child.on('error', (err) => finish(err));
133-
child.on('exit', () => finish());
134-
});
135-
}
136-
13770
let dir: string;
13871

13972
beforeAll(async () => {
14073
dir = mkdtempSync(join(tmpdir(), 'os-runtime-hooks-e2e-'));
14174
writeFileSync(join(dir, 'objectstack.config.ts'), CONFIG, 'utf8');
142-
// The artifact is what makes this test meaningful — it is the metadata-only
143-
// stack `createStandaloneStack()` boots from, and the reason the module's code
144-
// needed re-attaching. (It is also currently required to boot at all: #4085.)
75+
// The artifact is REQUIRED by this test, and for a positive reason rather than
76+
// as a workaround: it is what makes `createStandaloneStack()` contribute an
77+
// artifact-derived `AppPlugin`, which is the bundle whose missing code this
78+
// regression is about. Without it serve wraps the config directly (#4085/#4110
79+
// made that path boot), `onEnable` runs for the trivial reason, and the test
80+
// would pass while exercising nothing.
14581
await execFileP(TSX, [CLI, 'compile'], {
14682
cwd: dir,
14783
maxBuffer: 16 * 1024 * 1024,
@@ -155,8 +91,7 @@ afterAll(() => {
15591

15692
describe('os serve — an app booted from its artifact keeps its module code (#4095)', () => {
15793
it('runs onEnable, so the declared script action has a handler', async () => {
158-
const port = String(40000 + Math.floor(Math.random() * 20000));
159-
const { stdout, stderr } = await runServe(dir, ['--port', port], {
94+
const { stdout, stderr } = await runServe(dir, ['--port', randomPort()], {
16095
waitFor: /Press Ctrl\+C to stop/,
16196
});
16297

0 commit comments

Comments
 (0)