'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const FileBackend = require('../../src/registry/backends/FileBackend'); function tmpdir(prefix) { return fs.mkdtempSync(path.join(os.tmpdir(), `evolv-fb-${prefix}-`)); } test('per-id layout: one file per id, lowercased keys', () => { const dir = tmpdir('perid'); fs.writeFileSync(path.join(dir, 'AlphaModel.json'), JSON.stringify({ kind: 'pump' })); fs.writeFileSync(path.join(dir, 'beta.json'), JSON.stringify({ kind: 'valve' })); const b = new FileBackend({ baseDir: dir, layout: 'per-id' }); const m = b.loadAll(); assert.equal(m.get('alphamodel').kind, 'pump'); assert.equal(m.get('beta').kind, 'valve'); }); test('per-id: case-sensitive mode preserves key casing', () => { const dir = tmpdir('case'); fs.writeFileSync(path.join(dir, 'Mixed.json'), JSON.stringify({ ok: true })); const b = new FileBackend({ baseDir: dir, layout: 'per-id', caseInsensitive: false }); const m = b.loadAll(); assert.ok(m.has('Mixed')); assert.ok(!m.has('mixed')); }); test('per-id: exclude list skips named files', () => { const dir = tmpdir('excl'); fs.writeFileSync(path.join(dir, 'good.json'), '{}'); fs.writeFileSync(path.join(dir, 'bad.json'), '{}'); const b = new FileBackend({ baseDir: dir, layout: 'per-id', exclude: ['bad'] }); const m = b.loadAll(); assert.ok(m.has('good')); assert.ok(!m.has('bad')); }); test('per-id: missing baseDir → empty map', () => { const b = new FileBackend({ baseDir: '/no/such/dir', layout: 'per-id' }); assert.equal(b.loadAll().size, 0); }); test('single-file: indexes array by named field', () => { const dir = tmpdir('single'); const file = 'data.json'; fs.writeFileSync(path.join(dir, file), JSON.stringify({ samples: [ { code: '001', desc: 'one' }, { code: '002', desc: 'two' }, ], })); const b = new FileBackend({ baseDir: dir, layout: 'single-file', filePath: file, arrayKey: 'samples', indexField: 'code', }); const m = b.loadAll(); assert.equal(m.get('001').desc, 'one'); assert.equal(m.get('002').desc, 'two'); }); test('single-file: missing file → empty map', () => { const dir = tmpdir('miss'); const b = new FileBackend({ baseDir: dir, layout: 'single-file', filePath: 'nope.json', arrayKey: 'samples', indexField: 'code', }); assert.equal(b.loadAll().size, 0); }); test('single-file: bad shape throws', () => { const dir = tmpdir('bad'); fs.writeFileSync(path.join(dir, 'data.json'), JSON.stringify({ samples: 'not-array' })); const b = new FileBackend({ baseDir: dir, layout: 'single-file', filePath: 'data.json', arrayKey: 'samples', indexField: 'code', }); assert.throws(() => b.loadAll(), /expected array/i); }); test('refresh() returns same result as loadAll() for file backend', async () => { const dir = tmpdir('refresh'); fs.writeFileSync(path.join(dir, 'a.json'), JSON.stringify({ v: 1 })); const b = new FileBackend({ baseDir: dir, layout: 'per-id' }); const r = await b.refresh(); assert.equal(r.get('a').v, 1); }); test('constructor validates layout + filePath combinations', () => { assert.throws(() => new FileBackend({}), /baseDir/); assert.throws(() => new FileBackend({ baseDir: '/tmp', layout: 'weird' }), /layout/); assert.throws(() => new FileBackend({ baseDir: '/tmp', layout: 'single-file' }), /filePath/); });