test(windows): name the B2, B4, B5 and B12 tests in the curated Windows lane

The runtime-root bundle tests lived in browse/test/config.test.ts and the
Copilot glossary test in test/host-config.test.ts, both excluded from the
Windows lane, so the two Windows-only fixes were never checked on Windows.
Move each into its own Windows-safe file (unchanged assertions) and list it,
the piped-stdin chain test and the /cso docker.exe discovery test as named
Windows coverage in scripts/lib/windows-curation.ts.
This commit is contained in:
garrytan committed 2026-10-06 20:42:38 +00:00
1 parent 981798f9bc
commit 9de8be3df1
5 files changed
+112 -90

No files matched your search

-80
View File
@@ -297,86 +297,6 @@ describe('resolveNodeServerScript', () => {
});
});
describe('#3026: a Windows runtime root without node_modules runs the source checkout bundle', () => {
const { resolveNodeServerScript } = require('../src/cli');
const fs = require('fs');
const os = require('os');
function layout(opts: { runtimeModules: boolean; sourcePath: boolean; sourceModules: boolean }) {
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'browse-runtime-root-'));
const source = path.join(base, 'gstack');
const runtime = path.join(base, 'home', '.codex', 'skills', 'gstack');
for (const dir of [source, runtime]) {
fs.mkdirSync(path.join(dir, 'browse', 'dist'), { recursive: true });
fs.writeFileSync(path.join(dir, 'browse', 'dist', 'server-node.mjs'), '');
}
const playwright = (root: string) => {
fs.mkdirSync(path.join(root, 'node_modules', 'playwright'), { recursive: true });
fs.writeFileSync(path.join(root, 'node_modules', 'playwright', 'package.json'), '{}');
};
if (opts.sourceModules) playwright(source);
if (opts.runtimeModules) playwright(runtime);
if (opts.sourcePath) fs.writeFileSync(path.join(runtime, '.source-path'), `${source}\n`);
return { base, source, runtime, exe: path.join(runtime, 'browse', 'dist', 'browse.exe') };
}
test('no reachable playwright beside the copy -> the checkout bundle named by .source-path', () => {
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('a runtime root that reaches playwright keeps its own bundle', () => {
const l = layout({ runtimeModules: true, sourcePath: true, sourceModules: true });
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.runtime, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('without .source-path, or a checkout without node_modules, the adjacent bundle is unchanged', () => {
for (const opts of [{ runtimeModules: false, sourcePath: false, sourceModules: true }, { runtimeModules: false, sourcePath: true, sourceModules: false }]) {
const l = layout(opts);
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.runtime, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
}
});
test('same build on both sides -> the checkout bundle; different builds -> refusal with the ./setup fix and anchor', () => {
const { BROWSE_VERSION_SKEW_ANCHOR } = require('../src/cli');
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
const version = (root: string, v: string) => fs.writeFileSync(path.join(root, 'browse', 'dist', '.version'), `${v}\n`);
try {
version(l.runtime, 'a'.repeat(40));
version(l.source, 'a'.repeat(40));
expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs'));
version(l.source, 'b'.repeat(40));
let message = '';
try { resolveNodeServerScript('/$bunfs/root', l.exe); } catch (err: any) { message = err.message; }
expect(message).toContain(`build ${'a'.repeat(12)}`);
expect(message).toContain(`build ${'b'.repeat(12)}`);
expect(message).toContain('are from different builds, so the server was not started');
expect(message).toContain(`Fix: cd "${l.source}" && ./setup`);
expect(message).toContain(BROWSE_VERSION_SKEW_ANCHOR);
expect(BROWSE_VERSION_SKEW_ANCHOR).toBe('https://github.com/garrytan/gstack/blob/main/docs/troubleshooting.md#browse-runtime-version-skew');
// resolveServerLaunch surfaces the same refusal on Windows.
expect(() => require('../src/cli').resolveServerLaunch('win32', {}, '/$bunfs/root', l.exe)).toThrow('different builds');
} finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('an unversioned build on either side is not called skew', () => {
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
try {
fs.writeFileSync(path.join(l.runtime, 'browse', 'dist', '.version'), 'abc\n');
expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs'));
} finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('setup records the source checkout in every runtime root it links', () => {
const setup = fs.readFileSync(path.resolve(__dirname, '../../setup'), 'utf8');
const fn = setup.slice(setup.indexOf('_link_runtime_dists() {'), setup.indexOf('\n}\n', setup.indexOf('_link_runtime_dists() {')));
expect(fn).toContain(`printf '%s\\n' "$1" > "$2/.source-path"`);
});
});
describe('version mismatch detection', () => {
test('detects when versions differ', () => {
const stateVersion = 'abc123';
@@ -0,0 +1,89 @@
/**
* A Windows host runtime root (~/.codex/skills/gstack and the other env-var
* hosts) holds a copy of browse/dist with no node_modules, so its CLI starts
* the source checkout's Node server bundle named by .source-path, and refuses
* when the two come from different builds. Windows-safe (os.tmpdir paths
* only), so the curated Windows lane runs it natively.
*/
import { describe, expect, test } from 'bun:test';
import * as path from 'path';
describe('#3026: a Windows runtime root without node_modules runs the source checkout bundle', () => {
const { resolveNodeServerScript } = require('../src/cli');
const fs = require('fs');
const os = require('os');
function layout(opts: { runtimeModules: boolean; sourcePath: boolean; sourceModules: boolean }) {
const base = fs.mkdtempSync(path.join(os.tmpdir(), 'browse-runtime-root-'));
const source = path.join(base, 'gstack');
const runtime = path.join(base, 'home', '.codex', 'skills', 'gstack');
for (const dir of [source, runtime]) {
fs.mkdirSync(path.join(dir, 'browse', 'dist'), { recursive: true });
fs.writeFileSync(path.join(dir, 'browse', 'dist', 'server-node.mjs'), '');
}
const playwright = (root: string) => {
fs.mkdirSync(path.join(root, 'node_modules', 'playwright'), { recursive: true });
fs.writeFileSync(path.join(root, 'node_modules', 'playwright', 'package.json'), '{}');
};
if (opts.sourceModules) playwright(source);
if (opts.runtimeModules) playwright(runtime);
if (opts.sourcePath) fs.writeFileSync(path.join(runtime, '.source-path'), `${source}\n`);
return { base, source, runtime, exe: path.join(runtime, 'browse', 'dist', 'browse.exe') };
}
test('no reachable playwright beside the copy -> the checkout bundle named by .source-path', () => {
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('a runtime root that reaches playwright keeps its own bundle', () => {
const l = layout({ runtimeModules: true, sourcePath: true, sourceModules: true });
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.runtime, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('without .source-path, or a checkout without node_modules, the adjacent bundle is unchanged', () => {
for (const opts of [{ runtimeModules: false, sourcePath: false, sourceModules: true }, { runtimeModules: false, sourcePath: true, sourceModules: false }]) {
const l = layout(opts);
try { expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.runtime, 'browse', 'dist', 'server-node.mjs')); }
finally { fs.rmSync(l.base, { recursive: true, force: true }); }
}
});
test('same build on both sides -> the checkout bundle; different builds -> refusal with the ./setup fix and anchor', () => {
const { BROWSE_VERSION_SKEW_ANCHOR } = require('../src/cli');
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
const version = (root: string, v: string) => fs.writeFileSync(path.join(root, 'browse', 'dist', '.version'), `${v}\n`);
try {
version(l.runtime, 'a'.repeat(40));
version(l.source, 'a'.repeat(40));
expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs'));
version(l.source, 'b'.repeat(40));
let message = '';
try { resolveNodeServerScript('/$bunfs/root', l.exe); } catch (err: any) { message = err.message; }
expect(message).toContain(`build ${'a'.repeat(12)}`);
expect(message).toContain(`build ${'b'.repeat(12)}`);
expect(message).toContain('are from different builds, so the server was not started');
expect(message).toContain(`Fix: cd "${l.source}" && ./setup`);
expect(message).toContain(BROWSE_VERSION_SKEW_ANCHOR);
expect(BROWSE_VERSION_SKEW_ANCHOR).toBe('https://github.com/garrytan/gstack/blob/main/docs/troubleshooting.md#browse-runtime-version-skew');
// resolveServerLaunch surfaces the same refusal on Windows.
expect(() => require('../src/cli').resolveServerLaunch('win32', {}, '/$bunfs/root', l.exe)).toThrow('different builds');
} finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('an unversioned build on either side is not called skew', () => {
const l = layout({ runtimeModules: false, sourcePath: true, sourceModules: true });
try {
fs.writeFileSync(path.join(l.runtime, 'browse', 'dist', '.version'), 'abc\n');
expect(resolveNodeServerScript('/$bunfs/root', l.exe)).toBe(path.join(l.source, 'browse', 'dist', 'server-node.mjs'));
} finally { fs.rmSync(l.base, { recursive: true, force: true }); }
});
test('setup records the source checkout in every runtime root it links', () => {
const setup = fs.readFileSync(path.resolve(__dirname, '../../setup'), 'utf8');
const fn = setup.slice(setup.indexOf('_link_runtime_dists() {'), setup.indexOf('\n}\n', setup.indexOf('_link_runtime_dists() {')));
expect(fn).toContain(`printf '%s\\n' "$1" > "$2/.source-path"`);
});
});
+6
View File
@@ -225,6 +225,12 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
// pattern hit is a false positive — the point of these files is Windows
// coverage, so auto-excluding them defeats the regression tests they carry.
export const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
// Named Windows coverage for fixes whose bug only bites on Windows: kept in
// the curated lane even if a future edit trips a content pattern.
{ file: 'browse/test/cli-chain-stdin.test.ts', reason: 'piped-stdin browse chain against a stub daemon, and no-flow usage errors before any daemon boots; spawns the CLI through Bun argv' },
{ file: 'browse/test/runtime-root-server-bundle.test.ts', reason: 'a runtime root copy without node_modules resolves the checkout bundle via .source-path and refuses on version skew; os.tmpdir layouts only' },
{ file: 'test/cso-windows-docker.test.ts', reason: '/cso docker.exe discovery under known-folder roots (real path, no reparse points) and the native-transport-unsupported outcome; pure win32 path logic' },
{ file: 'test/copilot-windows-bash.test.ts', reason: 'the Copilot glossary line that runs bash blocks in Git for Windows Bash; reads host config only' },
{ file: 'test/ship-hook-windows-paths.test.ts', reason: 'runs bin/ helpers through explicit bash and Bun argv with forward-slash paths; never executes a shebang; the path-spelling simulation is skipIf win32' },
{ file: 'test/state-root-parity.test.ts', reason: 'runs the bash twin and lib/state-root.ts over an env table with PATH empty; no shebang execution, raw-string comparison is platform-neutral' },
{ file: 'test/generator-eexist.test.ts',
+17
View File
@@ -0,0 +1,17 @@
/**
* Copilot CLI on Windows ran gstack's extensionless helpers straight from
* PowerShell. The Copilot tool glossary (rendered into every Copilot skill)
* names Git for Windows Bash by full path. Kept out of host-config.test.ts,
* which the curated Windows lane excludes, so Windows runs this check.
*/
import { describe, expect, test } from 'bun:test';
describe('#3047: Copilot on Windows runs bash blocks in Git for Windows Bash', () => {
test('the Copilot tool glossary names Git Bash by full path and forbids PowerShell translation', async () => {
const { default: copilot } = await import('../hosts/copilot');
const glossary = JSON.stringify(copilot.toolRewrites);
expect(glossary).toContain('Git for Windows Bash by its full path');
expect(glossary).toContain('bare `bash` can start WSL');
expect(glossary).toContain('Never translate a block into PowerShell');
});
});
-10
View File
@@ -790,13 +790,3 @@ describe('host renders name the host\'s own tools and identities (#2626, #2015,
}
});
});
describe('#3047: Copilot on Windows runs bash blocks in Git for Windows Bash', () => {
test('the Copilot tool glossary names Git Bash by full path and forbids PowerShell translation', async () => {
const { default: copilot } = await import('../hosts/copilot');
const glossary = JSON.stringify(copilot.toolRewrites);
expect(glossary).toContain('Git for Windows Bash by its full path');
expect(glossary).toContain('bare `bash` can start WSL');
expect(glossary).toContain('Never translate a block into PowerShell');
});
});