/** * write.ts — atomic disk writer (② torn-write hardening). * * The aggregator overwrites two shared files that N parallel `/ba-develop` module * subagents also write. Each write must be atomic (tmp + rename) so a reader — or * a racing aggregator — never observes a half-written file, and no temp residue is * left behind. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { writeGeneratedFilesAtomic } from '../write.js' let root: string beforeEach(() => { root = fs.mkdtempSync(path.join(os.tmpdir(), 'agg-write-')) }) afterEach(() => { fs.rmSync(root, { recursive: true, force: true }) }) describe('writeGeneratedFilesAtomic', () => { it('writes each file to its path and returns the absolute paths', () => { const written = writeGeneratedFilesAtomic(root, [ { path: 'src/extensions/componentRegistry.generated.ts', content: 'A' }, { path: 'src/extensions/moduleResources.generated.ts', content: 'B' }, ]) expect(written).toHaveLength(2) expect(fs.readFileSync(path.join(root, 'src/extensions/componentRegistry.generated.ts'), 'utf-8')).toBe('A') expect(fs.readFileSync(path.join(root, 'src/extensions/moduleResources.generated.ts'), 'utf-8')).toBe('B') }) it('creates missing parent directories', () => { writeGeneratedFilesAtomic(root, [{ path: 'a/b/c/deep.ts', content: 'x' }]) expect(fs.existsSync(path.join(root, 'a/b/c/deep.ts'))).toBe(true) }) it('leaves no .tmp-* residue after writing (rename cleans up)', () => { writeGeneratedFilesAtomic(root, [ { path: 'src/extensions/moduleResources.generated.ts', content: 'B' }, ]) const leftovers = fs .readdirSync(path.join(root, 'src/extensions')) .filter((f) => f.includes('.tmp-')) expect(leftovers).toEqual([]) }) it('overwrites an existing file in place', () => { writeGeneratedFilesAtomic(root, [{ path: 'src/extensions/x.ts', content: 'old' }]) writeGeneratedFilesAtomic(root, [{ path: 'src/extensions/x.ts', content: 'new' }]) expect(fs.readFileSync(path.join(root, 'src/extensions/x.ts'), 'utf-8')).toBe('new') }) })