/** * Nesting-guard tests — DC-077 (data/data recursive duplicate cleanup) * * The guard runs at app startup. Pre-fix, `src/config/paths.js` did NOT * re-export `dataDir`, so `paths.dataDir` resolved to `undefined`. The * outer try/catch swallowed the resulting `TypeError [ERR_INVALID_ARG_TYPE]` * and the entire guard became a silent no-op — every startup logged * `[nesting-guard] Skipped: The "path" argument must be of type string. * Received undefined`. Post-fix, paths.js exports `dataDir` and the guard * falls back to platform-paths directly if `paths.dataDir` is missing. * * Tests use jest.isolateModules() for clean module-cache isolation. * jest.doMock is intentionally avoided — it persists across tests in a * describe and is the root cause of subtle flakes. */ const fs = require('fs'); const path = require('path'); const os = require('os'); describe('nesting-guard (DC-077)', () => { const originalEnv = { ...process.env }; beforeEach(() => { jest.restoreAllMocks(); }); afterEach(() => { process.env = { ...originalEnv }; jest.restoreAllMocks(); }); function makeTmpTree() { return fs.mkdtempSync(path.join(os.tmpdir(), 'nest-guard-')); } function writeJson(p, obj) { fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, JSON.stringify(obj)); } it('removes a recursive data/data duplicate when present', () => { const tmp = makeTmpTree(); writeJson(path.join(tmp, 'config.json'), { x: 1 }); writeJson(path.join(tmp, 'data', 'config.json'), { x: 1 }); writeJson(path.join(tmp, 'data', 'services.json'), []); process.env.SERVICES_FILE = path.join(tmp, 'services.json'); process.env.CONFIG_FILE = path.join(tmp, 'config.json'); let cleanupLog = ''; let warnLog = ''; jest.isolateModules(() => { const guard = require('../src/utilities/nesting-guard'); jest.spyOn(console, 'log').mockImplementation((m) => { cleanupLog += String(m) + '\n'; }); jest.spyOn(console, 'warn').mockImplementation((m) => { warnLog += String(m) + '\n'; }); guard(); }); expect(fs.existsSync(path.join(tmp, 'data'))).toBe(false); expect(fs.existsSync(path.join(tmp, 'config.json'))).toBe(true); expect(cleanupLog).toMatch(/Removing recursive data nesting|Recursive nesting removed/); expect(warnLog).not.toMatch(/Skipped/); }); it('does nothing when no nested data/data directory exists', () => { const tmp = makeTmpTree(); writeJson(path.join(tmp, 'config.json'), { x: 1 }); process.env.SERVICES_FILE = path.join(tmp, 'services.json'); process.env.CONFIG_FILE = path.join(tmp, 'config.json'); let cleanupLog = ''; let warnLog = ''; jest.isolateModules(() => { const guard = require('../src/utilities/nesting-guard'); jest.spyOn(console, 'log').mockImplementation((m) => { cleanupLog += String(m) + '\n'; }); jest.spyOn(console, 'warn').mockImplementation((m) => { warnLog += String(m) + '\n'; }); guard(); }); expect(fs.existsSync(path.join(tmp, 'config.json'))).toBe(true); expect(warnLog).not.toMatch(/Skipped/); expect(cleanupLog).not.toMatch(/Removing recursive data nesting/); }); it('src/config/paths exports dataDir as a non-empty string', () => { let dataDir; jest.isolateModules(() => { const paths = require('../src/config/paths'); dataDir = paths.dataDir; }); expect(typeof dataDir).toBe('string'); expect(dataDir.length).toBeGreaterThan(0); }); it('src/config/paths.dataDir equals dirname(SERVICES_FILE) when SERVICES_FILE env is set', () => { const tmp = makeTmpTree(); process.env.SERVICES_FILE = path.join(tmp, 'services.json'); process.env.CONFIG_FILE = path.join(tmp, 'config.json'); let servicesFile, dataDir; jest.isolateModules(() => { const paths = require('../src/config/paths'); servicesFile = paths.SERVICES_FILE; dataDir = paths.dataDir; }); expect(dataDir).toBe(path.dirname(servicesFile)); expect(dataDir).toBe(tmp); }); });