diff --git a/client/test/public-surface.test.mjs b/client/test/public-surface.test.mjs index c0586bb..816c817 100644 --- a/client/test/public-surface.test.mjs +++ b/client/test/public-surface.test.mjs @@ -4,13 +4,50 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { execFileSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; import { dirtyWorktreeFiles, scanPublicSurface } from "../public-surface.mjs"; +const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); + function makeTempDir() { return fs.mkdtempSync(path.join(os.tmpdir(), "simbroker-public-surface-test-")); } +test("public surface scan accepts the runner-neutral spec on a hosted Ubuntu home", () => { + const hostedRunnerHome = path.posix.join(path.posix.sep, "home", "runner"); + + const report = scanPublicSurface({ + files: ["spec/build-and-test.md"], + homePath: hostedRunnerHome, + root: repositoryRoot, + }); + + assert.equal(report.ok, true, JSON.stringify(report.issues)); + assert.deepEqual(report.issues, []); + assert.equal(report.filesScanned, 1); +}); + +test("public surface scan still rejects an actual hosted Ubuntu home leak", () => { + const root = makeTempDir(); + const hostedRunnerHome = path.posix.join(path.posix.sep, "home", "runner"); + fs.writeFileSync(path.join(root, "README.md"), `artifact: ${hostedRunnerHome}/work/private.log\n`); + + const report = scanPublicSurface({ + files: ["README.md"], + homePath: hostedRunnerHome, + root, + }); + + assert.equal(report.ok, false); + assert.deepEqual(report.issues, [{ + line: 1, + path: "README.md", + rule: "local-home-path", + }]); + assert.equal(JSON.stringify(report).includes(hostedRunnerHome), false); +}); + test("public surface scan reports a local home path without echoing the matched value", () => { const root = makeTempDir(); const localHome = path.join(root, "private-home"); diff --git a/spec/build-and-test.md b/spec/build-and-test.md index f68b5df..e74bbd7 100644 --- a/spec/build-and-test.md +++ b/spec/build-and-test.md @@ -92,13 +92,14 @@ A first extracted implementation slice now exists: `test:harness-adoption` with a 10-minute budget; macOS runs `test:client` with a 15-minute budget. Neither job runs `test:app` or `verify:public-surface`. Home-path leak scanning - uses `os.homedir()` of the current machine, so GitHub-hosted runners would - search for `/home/runner` or `/Users/runner`, not the operator home. The - full-repo scan stays on local `npm test`. Do not raise those budgets to - hide a hung `simctl` or `brokerd` wait. Broker tests that build an - app snapshot, including in-process `startBrokerService`, must inject the - fixture `simctl` adapter (`SIMBROKER_SIMCTL_FIXTURE_STATE` or an explicit - adapter / snapshot writer). macOS CI runs client tests with + uses `os.homedir()` of the current machine, so each runner searches its own + account home, not the operator home. Keep runner-home examples symbolic: + an environment-specific absolute path can match the runner's real home and + correctly fail closed. The full-repo scan stays on local `npm test`. Do not + raise those budgets to hide a hung `simctl` or `brokerd` wait. Broker tests + that build an app snapshot, including in-process `startBrokerService`, must + inject the fixture `simctl` adapter (`SIMBROKER_SIMCTL_FIXTURE_STATE` or an + explicit adapter / snapshot writer). macOS CI runs client tests with `node --test --test-concurrency=1 --test-timeout=120000` so client files cannot interleave and a hung test cannot consume `serviceStartupTimeoutMs`. Do not add `--test-force-exit`: on Node 20 it