import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { readJSON, writeJSON } from '../src/atomicJson.js'; function tmpFile(name) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'atomicjson-')); return path.join(dir, name); } test('writeJSON persists and readJSON reads it back', async () => { const file = tmpFile('a.json'); await writeJSON(file, { hello: 'world', n: 1 }); assert.deepEqual(readJSON(file), { hello: 'world', n: 1 }); }); test('concurrent writeJSON calls all land, none corrupt the target', async () => { const file = tmpFile('b.json'); const writers = []; for (let i = 0; i < 25; i++) { writers.push(writeJSON(file, { i, at: Date.now() })); } await Promise.all(writers); // File must be valid JSON with one of the written objects. const parsed = readJSON(file); assert.equal(typeof parsed.i, 'number'); assert.ok(parsed.i >= 0 && parsed.i < 25); }); test('writeJSON removes tmp files on rename failure', async () => { const file = tmpFile('c.json'); await writeJSON(file, { ok: true }); // Force a rename failure by pointing at a path whose parent does not exist. const bad = path.join(file, 'nested', 'not', 'there.json'); await assert.rejects(writeJSON(bad, { x: 1 })); // No tmp files should be left in the parent dir of `file`. const dir = path.dirname(file); const stray = fs.readdirSync(dir).filter(n => n.endsWith('.tmp')); assert.deepEqual(stray, []); }); test('writes to different paths do not block each other', async () => { const f1 = tmpFile('d.json'); const f2 = tmpFile('e.json'); await Promise.all([writeJSON(f1, { a: 1 }), writeJSON(f2, { b: 2 })]); assert.deepEqual(readJSON(f1), { a: 1 }); assert.deepEqual(readJSON(f2), { b: 2 }); });