const fs = require('fs'); const path = require('path'); const os = require('os'); let tmpDir; let dbPath; let server; let dbRef; function createFreshApp() { process.env.DB_PATH = dbPath; // Drop EVERY app module from the require cache so each test gets a fresh app + DB handle. // (A hand-picked list missed routes/*, which kept services bound to a previous, closed DB.) const root = path.resolve(__dirname, '..'); for (const id of Object.keys(require.cache)) { if (id.startsWith(path.join(root, 'src') + path.sep) || id.startsWith(path.join(root, 'scripts') + path.sep)) { delete require.cache[id]; } } const { runMigrations } = require('../scripts/migrate'); runMigrations(); // Capture the DB instance before creating the app (session store uses it) const dbModule = require('../src/db'); dbRef = dbModule.getDb(); const { createApp } = require('../src/app'); return createApp(); } async function setup() { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'emberfall-test-')); dbPath = path.join(tmpDir, 'test.db'); const app = createFreshApp(); return new Promise((resolve, reject) => { server = app.listen(0, () => { const port = server.address().port; resolve(`http://localhost:${port}`); }); server.on('error', reject); }); } function teardown() { return new Promise((resolve) => { if (server) { // Close server first to stop accepting connections try { server.closeAllConnections(); } catch (_) {} server.close(() => { server = null; // Then close DB to stop session store's cleanup interval if (dbRef) { try { dbRef.close(); } catch (_) {} dbRef = null; } resolve(); }); } else { // No server, just close DB if (dbRef) { try { dbRef.close(); } catch (_) {} dbRef = null; } resolve(); } }); } function cleanup() { if (tmpDir && fs.existsSync(tmpDir)) { try { fs.rmSync(tmpDir, { recursive: true }); } catch (_) {} } tmpDir = undefined; dbPath = undefined; } module.exports = { setup, teardown, cleanup };