import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smartdb from '../ts/index.js'; import { MongoClient, Db } from 'mongodb'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; // --------------------------------------------------------------------------- // Test: Missing data.rdb header recovery + startup logging // Covers: ensure_data_header, BuildStats, info-level startup logging // --------------------------------------------------------------------------- let tmpDir: string; let localDb: smartdb.LocalSmartDb; let client: MongoClient; let db: Db; function makeTmpDir(): string { return fs.mkdtempSync(path.join(os.tmpdir(), 'smartdb-header-test-')); } function cleanTmpDir(dir: string): void { if (fs.existsSync(dir)) { fs.rmSync(dir, { recursive: true, force: true }); } } // ============================================================================ // Setup: create data, then corrupt it // ============================================================================ tap.test('setup: start, insert data, stop', async () => { tmpDir = makeTmpDir(); localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const info = await localDb.start(); client = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('headertest'); const coll = db.collection('docs'); await coll.insertMany([ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 }, ]); await client.close(); await localDb.stop(); }); // ============================================================================ // Delete hint file and restart: should rebuild from data.rdb scan // ============================================================================ tap.test('header-recovery: delete hint file and restart', async () => { // Find and delete hint files const dbDir = path.join(tmpDir, 'headertest', 'docs'); const hintPath = path.join(dbDir, 'keydir.hint'); if (fs.existsSync(hintPath)) { fs.unlinkSync(hintPath); } localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const info = await localDb.start(); client = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('headertest'); }); tap.test('header-recovery: data intact after hint deletion', async () => { const coll = db.collection('docs'); const count = await coll.countDocuments(); expect(count).toEqual(3); const a = await coll.findOne({ key: 'a' }); expect(a!.val).toEqual(1); }); // ============================================================================ // Write new data after restart, stop, restart again // ============================================================================ tap.test('header-recovery: write after hint-less restart', async () => { const coll = db.collection('docs'); await coll.insertOne({ key: 'd', val: 4 }); expect(await coll.countDocuments()).toEqual(4); }); tap.test('header-recovery: restart and verify all data', async () => { await client.close(); await localDb.stop(); localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const info = await localDb.start(); client = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('headertest'); const coll = db.collection('docs'); const count = await coll.countDocuments(); expect(count).toEqual(4); const keys = (await coll.find({}).toArray()).map(d => d.key).sort(); expect(keys).toEqual(['a', 'b', 'c', 'd']); }); // ============================================================================ // Cleanup // ============================================================================ tap.test('header-recovery: cleanup', async () => { await client.close(); await localDb.stop(); cleanTmpDir(tmpDir); }); export default tap.start();