|
1 | 1 | import { execFileSync } from 'node:child_process'; |
2 | | -import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'; |
| 2 | +import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'; |
3 | 3 | import { tmpdir } from 'node:os'; |
4 | 4 | import { join, resolve } from 'node:path'; |
5 | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest'; |
@@ -209,6 +209,24 @@ describe('preserveWorktree → createWorktree resume roundtrip (INT-2503)', () = |
209 | 209 | expect(existsSync(info.worktreePath)).toBe(false); |
210 | 210 | }); |
211 | 211 |
|
| 212 | + it('does NOT crash when the preserve-marker write fails (ENOSPC/EACCES) — reports NOT preserved (INT-2521)', async () => { |
| 213 | + const info = await createWorktree(repo, 'INT-9', 'swarm/INT-9-test'); |
| 214 | + writeFileSync(join(info.worktreePath, 'app.py'), 'base\npartial-impl\n'); // dirty work |
| 215 | + // A read-only worktree dir makes the marker writeFileSync throw — exactly what a |
| 216 | + // full disk (ENOSPC) did in production, where an unguarded write crashed the whole |
| 217 | + // daemon via executePipeline. preserveWorktree must swallow it and honestly report |
| 218 | + // NOT preserved (false): the marker is the only thing that would protect the tree |
| 219 | + // from the next createWorktree()/sweep, so it must never claim a preservation it |
| 220 | + // can't back. It also never leaves a marker behind. |
| 221 | + chmodSync(info.worktreePath, 0o555); |
| 222 | + try { |
| 223 | + await expect(preserveWorktree(info, 'disk full')).resolves.toBe(false); |
| 224 | + expect(existsSync(join(info.worktreePath, '.openswarm-preserved'))).toBe(false); // marker never written |
| 225 | + } finally { |
| 226 | + if (existsSync(info.worktreePath)) chmodSync(info.worktreePath, 0o755); // restore for cleanup |
| 227 | + } |
| 228 | + }); |
| 229 | + |
212 | 230 | it('PRESERVES (does not delete) when git status FAILS — cannot confirm clean (INT-2521)', async () => { |
213 | 231 | const info = await createWorktree(repo, 'INT-9', 'swarm/INT-9-test'); |
214 | 232 | writeFileSync(join(info.worktreePath, 'app.py'), 'base\nreal-partial-work\n'); |
|
0 commit comments