mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
test: the installer's own-temp-file cleanup is exercised, and the ratchet coverage test names setup and scripts
An mv shim first on PATH refuses only the launch-file target, so the temp file exists when the move fails: the run removes that file alone (a sibling from a concurrent run survives), says so, prints no GSTACK_LAUNCH_FILE line and keeps the earlier file, exit code and trailer. The ratchet coverage test asserts setup, scripts/build.sh and scripts/sandbox-doctor.sh are scanned. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
48cff6682f
commit
93b793c6ef
2 files changed
+29
-4
No files matched your search
@@ -477,6 +477,31 @@ describe('gstack-capy-install: launch PATH, Bun reuse, CI pins, setup skip, verd
|
||||
fs.chmodSync(dir, 0o755);
|
||||
}
|
||||
});
|
||||
|
||||
// protects: when the launch file's temp file IS created and the final mv fails, write_launch_file
|
||||
// removes its own temp file and nothing else (a sibling temp file from a concurrent run survives),
|
||||
// the run says so, prints no GSTACK_LAUNCH_FILE line and keeps the earlier launch file, exit code
|
||||
// and trailer. fails_when: the `|| { rm -f "$tmp"; return 1; }` cleanup goes (the temp file stays),
|
||||
// or a glob cleanup returns (the sibling disappears). why_existing_misses: the unwritable-directory
|
||||
// case above fails at mktemp, before any temp file exists. seam: an mv shim first on PATH that
|
||||
// refuses only the launch-file target.
|
||||
test('a launch-file move that fails after the temp file exists removes only that temp file', () => {
|
||||
const f = fixture();
|
||||
const first = run(f, ['--for', 'autoplan']);
|
||||
expect(first.status, first.stdout + first.stderr).toBe(0);
|
||||
const launchFile = first.stdout.match(/^GSTACK_LAUNCH_FILE: (.+)$/m)?.[1] ?? '';
|
||||
const before = fs.readFileSync(launchFile, 'utf8');
|
||||
const sibling = `${launchFile}.tmp.OTHER1`;
|
||||
fs.writeFileSync(sibling, '');
|
||||
script(path.join(f.stub, 'mv'), [`for a in "$@"; do [ "$a" = "${launchFile}" ] && exit 1; done`, 'exec /bin/mv "$@"']);
|
||||
const c = run(f, ['--check', '--for', 'autoplan']);
|
||||
expect(c.status, c.stdout + c.stderr).toBe(0);
|
||||
expect(c.stdout).toContain(`gstack-capy-install: could not write the launch file ${launchFile}`);
|
||||
expect(c.stdout).not.toMatch(/^GSTACK_LAUNCH_FILE:/m);
|
||||
expect(lastLine(c.stdout)).toBe('gstack: ok 1.2.3 for=autoplan');
|
||||
expect(fs.readdirSync(path.dirname(launchFile)).filter(n => n.includes('.tmp.'))).toEqual([path.basename(sibling)]);
|
||||
expect(fs.readFileSync(launchFile, 'utf8')).toBe(before);
|
||||
});
|
||||
});
|
||||
|
||||
// Plan A1/A3/A5/A6/A-f: install and --check resolve through one contract,
|
||||
|
||||
@@ -111,13 +111,13 @@ function markdownTargets(): string[] {
|
||||
/^capy\/skill\/gstack\/[^/]+\.md$/.test(rel));
|
||||
}
|
||||
|
||||
/** Every shell helper under bin/, the setup script and the install scripts callers run first. */
|
||||
/** Every shell helper under bin/, the setup script and the scripts/*.sh build and doctor scripts. */
|
||||
function shellTargets(): string[] {
|
||||
const candidates = [...fs.readdirSync(path.join(ROOT, 'bin')).map(name => path.join('bin', name)), 'setup',
|
||||
...fs.readdirSync(path.join(ROOT, 'scripts')).filter(name => name.endsWith('.sh')).map(name => path.join('scripts', name))];
|
||||
return candidates.filter(rel => {
|
||||
const full = path.join(ROOT, rel);
|
||||
return fs.existsSync(full) && fs.statSync(full).isFile() && isShell(rel, fs.readFileSync(full, 'utf8'));
|
||||
return fs.statSync(full).isFile() && isShell(rel, fs.readFileSync(full, 'utf8'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ describe('launch-env echo ratchet', () => {
|
||||
for (const want of ['ship/SKILL.md', 'ship/sections/documentation.md', 'review/SKILL.md', 'qa/SKILL.md',
|
||||
'document-release/SKILL.md', 'autoplan/DRIVER.md', 'capy/skill/gstack/SKILL.md']) expect(md).toContain(want);
|
||||
const sh = shellTargets();
|
||||
for (const want of ['bin/gstack-capy-install', 'bin/gstack-capy-session', 'bin/gstack-skill-start']) expect(sh).toContain(want);
|
||||
for (const want of ['bin/gstack-capy-install', 'bin/gstack-capy-session', 'bin/gstack-skill-start', 'setup', 'scripts/build.sh', 'scripts/sandbox-doctor.sh']) expect(sh).toContain(want);
|
||||
});
|
||||
|
||||
test('no skill fence prints the environment', () => {
|
||||
@@ -172,7 +172,7 @@ describe('launch-env echo ratchet', () => {
|
||||
expect(violations.map(v => `${v.file}:${v.line} [${v.rule}] ${v.text}`)).toEqual([]);
|
||||
});
|
||||
|
||||
test('no shell helper under bin/ prints the environment', () => {
|
||||
test('no shell helper under bin/, setup or install script prints the environment', () => {
|
||||
const violations = shellTargets().flatMap(file => scanText(file, fs.readFileSync(path.join(ROOT, file), 'utf8'), 'whole'));
|
||||
expect(violations.map(v => `${v.file}:${v.line} [${v.rule}] ${v.text}`)).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user