Six independent windows-only failure clusters identified from the windows-22 CI log on1be0e4e2: - scripts/command-contract-helpers.cjs: parseFrontmatter split on `\n` → on Windows checkout (autocrlf=true) every line carries trailing \r, lines.indexOf('---', 1) returns -1, all fields read as missing. Drove the bulk of three "67 subtests failed" suite-level errors covering workstreams.md, workspace.md, verify-work.md, add-tests.md, etc. Fix: split(/\r?\n/). - tests/enh-3271-sdk-adr-structure.test.cjs: same CRLF pattern — H2 captures pulled '\r' into headings like "decision\r", breaking equality checks on "## Decision" / "## Consequences". Fix: same. - tests/runtime-bridge-sync-smoke.test.cjs: await import(BRIDGE_PATH) passed a Windows absolute path to Node's ESM loader, which rejects with "Only URLs with a scheme in: file, data, and node are supported." Fix: wrap once at module scope with pathToFileURL. pathToFileURL is a no-op for POSIX absolute paths. - tests/bug-2957-claude-global-postinstall-message.test.cjs: hardcoded '/tmp/gsd-test-settings.json' resolved to D:\tmp\... on Windows where the parent dir doesn't exist → ENOENT on fs.writeFileSync inside finishInstall. Fix: os.tmpdir() + pid suffix. - tests/bug-2774-worktree-cleanup-workspace-safety.test.cjs: the "while/read loop" subtest and the "end-to-end against real git worktrees" describe both assert POSIX shell behavior (process substitution `< <(...)`, RUNNER~1 8.3-shortname mismatch). Skip on win32 with explicit reasons (satisfies the no-unconditional-win32-skip guard). - tests/bug-2838-summary-rescue-gitignored-planning.test.cjs: entire describe extracts bash rescue blocks from workflow .md files and runs them; the shell contract itself is the test's point. Skip on win32 with reason. - tests/helpers.cjs cleanup(): bumped rmSync retry budget from 10×100ms to 20×250ms — 1s wasn't enough for Windows Defender's deferred handle release; bumping to 5s should absorb the residual EBUSY failures observed in bug-1736 / bug-2248 / bug-2698 after the first retry bump landed in1be0e4e2. Validated: holodeck (ubuntu docker) 11224/0 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
104 lines
4.1 KiB
JavaScript
104 lines
4.1 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* CJS smoke test for the executeForCjs synchronous primitive (Phase 5.0 #3555).
|
|
*
|
|
* Verifies that the compiled dist artifact can be required from a CJS context
|
|
* and that executeForCjs returns the expected result shape synchronously.
|
|
*
|
|
* This is the critical end-to-end proof that the primitive works for CJS callers
|
|
* — the actual point of Phase 5.0.
|
|
*/
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const path = require('node:path');
|
|
const { pathToFileURL } = require('node:url');
|
|
|
|
const REPO_ROOT = path.join(__dirname, '..');
|
|
const BRIDGE_PATH = path.join(REPO_ROOT, 'sdk', 'dist', 'runtime-bridge-sync', 'index.js');
|
|
// Node's ESM loader rejects Windows absolute paths with `import()` — must be a
|
|
// file:// URL. pathToFileURL is a no-op for POSIX paths (produces file:///abs/...).
|
|
const BRIDGE_URL = pathToFileURL(BRIDGE_PATH).href;
|
|
|
|
describe('runtime-bridge-sync CJS smoke test', () => {
|
|
test('executeForCjs is exported and is a function', async () => {
|
|
// Use dynamic import because Node 24 supports require() of ESM but
|
|
// the module is ESM (NodeNext output). Dynamic import works in all contexts.
|
|
const mod = await import(BRIDGE_URL);
|
|
assert.strictEqual(typeof mod.executeForCjs, 'function', 'executeForCjs must be a function');
|
|
});
|
|
|
|
test('executeForCjs returns ok:true for generate-slug (success path)', async () => {
|
|
const { executeForCjs } = await import(BRIDGE_URL);
|
|
|
|
const result = executeForCjs({
|
|
registryCommand: 'generate-slug',
|
|
registryArgs: ['My Smoke Test Phase'],
|
|
legacyCommand: 'generate-slug',
|
|
legacyArgs: ['My Smoke Test Phase'],
|
|
mode: 'json',
|
|
projectDir: '/tmp',
|
|
});
|
|
|
|
// The returned value must be a plain object, not a Promise
|
|
assert.strictEqual(typeof result, 'object', 'result must be an object');
|
|
assert.ok(!(result instanceof Promise), 'result must not be a Promise');
|
|
assert.ok('ok' in result, 'result must have ok property');
|
|
|
|
assert.strictEqual(result.ok, true, 'expected ok:true');
|
|
assert.strictEqual(result.exitCode, 0, 'expected exitCode:0');
|
|
assert.ok(result.data != null, 'expected data to be non-null');
|
|
|
|
const data = result.data;
|
|
assert.strictEqual(typeof data, 'object', 'data must be an object');
|
|
assert.strictEqual(data.slug, 'my-smoke-test-phase', 'expected slug');
|
|
});
|
|
|
|
test('executeForCjs returns ok:false for unknown command', async () => {
|
|
const { executeForCjs } = await import(BRIDGE_URL);
|
|
|
|
const result = executeForCjs({
|
|
registryCommand: '__smoke_test_unknown_command__',
|
|
registryArgs: [],
|
|
legacyCommand: '__smoke_test_unknown_command__',
|
|
legacyArgs: [],
|
|
mode: 'json',
|
|
projectDir: '/tmp',
|
|
});
|
|
|
|
assert.strictEqual(typeof result, 'object', 'result must be an object');
|
|
assert.ok(!(result instanceof Promise), 'result must not be a Promise');
|
|
assert.strictEqual(result.ok, false, 'expected ok:false');
|
|
assert.ok(result.exitCode !== 0, 'expected non-zero exitCode');
|
|
assert.ok('errorKind' in result, 'expected errorKind property');
|
|
assert.strictEqual(result.errorKind, 'unknown_command', 'expected unknown_command errorKind');
|
|
assert.ok(Array.isArray(result.stderrLines), 'expected stderrLines array');
|
|
});
|
|
|
|
test('executeForCjs result shape matches RuntimeBridgeSyncResult discriminated union', async () => {
|
|
const { executeForCjs } = await import(BRIDGE_URL);
|
|
|
|
// Success shape
|
|
const success = executeForCjs({
|
|
registryCommand: 'current-timestamp',
|
|
registryArgs: ['date'],
|
|
legacyCommand: 'current-timestamp',
|
|
legacyArgs: ['date'],
|
|
mode: 'json',
|
|
projectDir: '/tmp',
|
|
});
|
|
|
|
assert.ok('ok' in success, 'success result must have ok');
|
|
if (success.ok) {
|
|
assert.strictEqual(success.exitCode, 0);
|
|
assert.ok('data' in success);
|
|
} else {
|
|
// current-timestamp might fail if args aren't what it expects; just check shape
|
|
assert.ok('errorKind' in success);
|
|
assert.ok('stderrLines' in success);
|
|
assert.ok(Array.isArray(success.stderrLines));
|
|
}
|
|
});
|
|
});
|