fix(rustdb-storage): detect stale hint files using data file size metadata and add restart persistence regression tests
This commit is contained in:
126
test/test.header-recovery.ts
Normal file
126
test/test.header-recovery.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user