diff --git a/src/lib/state-store.js b/src/lib/state-store.js new file mode 100644 index 0000000..340b5ad --- /dev/null +++ b/src/lib/state-store.js @@ -0,0 +1,37 @@ +"use strict"; + +const fs = require("node:fs/promises"); +const path = require("node:path"); + +class JsonFileStateStore { + constructor(filePath) { + this.filePath = filePath; + } + + async load() { + try { + const raw = await fs.readFile(this.filePath, "utf8"); + return JSON.parse(raw); + } catch (error) { + if (error && error.code === "ENOENT") { + return null; + } + throw error; + } + } + + async save(state) { + const dir = path.dirname(this.filePath); + await fs.mkdir(dir, { recursive: true }); + + const tempPath = `${this.filePath}.tmp`; + const payload = JSON.stringify(state, null, 2); + + await fs.writeFile(tempPath, payload, "utf8"); + await fs.rename(tempPath, this.filePath); + } +} + +module.exports = { + JsonFileStateStore, +}; diff --git a/test/state-store.test.js b/test/state-store.test.js new file mode 100644 index 0000000..a60203b --- /dev/null +++ b/test/state-store.test.js @@ -0,0 +1,35 @@ +"use strict"; + +const test = require("node:test"); +const assert = require("node:assert/strict"); +const os = require("node:os"); +const path = require("node:path"); +const fs = require("node:fs/promises"); +const { JsonFileStateStore } = require("../src/lib/state-store"); + +test("load returns null when state file does not exist", async () => { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "xartaudio-state-test-")); + const store = new JsonFileStateStore(path.join(tempDir, "missing.json")); + + const data = await store.load(); + assert.equal(data, null); +}); + +test("save and load roundtrip state", async () => { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "xartaudio-state-test-")); + const filePath = path.join(tempDir, "state.json"); + const store = new JsonFileStateStore(filePath); + + const expected = { + version: 1, + engine: { + jobs: { "1": { id: "1", status: "completed" } }, + nextJobId: 2, + }, + }; + + await store.save(expected); + const actual = await store.load(); + + assert.deepEqual(actual, expected); +});