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'; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- let tmpDir: string; let localDb: smartdb.LocalSmartDb; let client: MongoClient; let db: Db; function makeTmpDir(): string { return fs.mkdtempSync(path.join(os.tmpdir(), 'smartdb-local-test-')); } function cleanTmpDir(dir: string): void { if (fs.existsSync(dir)) { fs.rmSync(dir, { recursive: true, force: true }); } } // ============================================================================ // LocalSmartDb: Lifecycle // ============================================================================ tap.test('localsmartdb: should start with just a folder path', async () => { tmpDir = makeTmpDir(); localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const info = await localDb.start(); expect(localDb.running).toBeTrue(); expect(info.socketPath).toBeTruthy(); expect(info.connectionUri).toBeTruthy(); expect(info.connectionUri.startsWith('mongodb://')).toBeTrue(); }); tap.test('localsmartdb: should connect via returned connectionUri', async () => { const info = localDb.getConnectionInfo(); client = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('localtest'); expect(db).toBeTruthy(); }); tap.test('localsmartdb: should reject double start', async () => { let threw = false; try { await localDb.start(); } catch { threw = true; } expect(threw).toBeTrue(); }); // ============================================================================ // LocalSmartDb: CRUD via Unix socket // ============================================================================ tap.test('localsmartdb: insert and find documents', async () => { const coll = db.collection('notes'); await coll.insertMany([ { title: 'Note 1', body: 'First note', priority: 1 }, { title: 'Note 2', body: 'Second note', priority: 2 }, { title: 'Note 3', body: 'Third note', priority: 3 }, ]); const all = await coll.find({}).toArray(); expect(all.length).toEqual(3); const high = await coll.findOne({ priority: 3 }); expect(high).toBeTruthy(); expect(high!.title).toEqual('Note 3'); }); tap.test('localsmartdb: update and verify', async () => { const coll = db.collection('notes'); await coll.updateOne( { title: 'Note 2' }, { $set: { body: 'Updated second note', edited: true } } ); const doc = await coll.findOne({ title: 'Note 2' }); expect(doc!.body).toEqual('Updated second note'); expect(doc!.edited).toBeTrue(); }); tap.test('localsmartdb: delete and verify', async () => { const coll = db.collection('notes'); await coll.deleteOne({ title: 'Note 1' }); const count = await coll.countDocuments(); expect(count).toEqual(2); const deleted = await coll.findOne({ title: 'Note 1' }); expect(deleted).toBeNull(); }); // ============================================================================ // LocalSmartDb: Persistence across restart // ============================================================================ tap.test('localsmartdb: stop for restart', async () => { await client.close(); await localDb.stop(); expect(localDb.running).toBeFalse(); }); tap.test('localsmartdb: restart with same folder', async () => { localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const info = await localDb.start(); expect(localDb.running).toBeTrue(); client = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('localtest'); }); tap.test('localsmartdb: data persists after restart', async () => { const coll = db.collection('notes'); const count = await coll.countDocuments(); expect(count).toEqual(2); // 3 inserted - 1 deleted const note2 = await coll.findOne({ title: 'Note 2' }); expect(note2!.body).toEqual('Updated second note'); expect(note2!.edited).toBeTrue(); const note3 = await coll.findOne({ title: 'Note 3' }); expect(note3!.priority).toEqual(3); }); // ============================================================================ // LocalSmartDb: Custom socket path // ============================================================================ tap.test('localsmartdb: works with custom socket path', async () => { await client.close(); await localDb.stop(); const customSocket = path.join(os.tmpdir(), `smartdb-custom-${Date.now()}.sock`); const tmpDir2 = makeTmpDir(); const localDb2 = new smartdb.LocalSmartDb({ folderPath: tmpDir2, socketPath: customSocket, }); const info = await localDb2.start(); expect(info.socketPath).toEqual(customSocket); const client2 = new MongoClient(info.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client2.connect(); const testDb = client2.db('customsock'); await testDb.collection('test').insertOne({ x: 1 }); const doc = await testDb.collection('test').findOne({ x: 1 }); expect(doc).toBeTruthy(); await client2.close(); await localDb2.stop(); cleanTmpDir(tmpDir2); // Reconnect original for remaining tests localDb = new smartdb.LocalSmartDb({ folderPath: tmpDir }); const origInfo = await localDb.start(); client = new MongoClient(origInfo.connectionUri, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); db = client.db('localtest'); }); // ============================================================================ // LocalSmartDb: getConnectionUri and getServer helpers // ============================================================================ tap.test('localsmartdb: getConnectionUri returns valid uri', async () => { const uri = localDb.getConnectionUri(); expect(uri.startsWith('mongodb://')).toBeTrue(); }); tap.test('localsmartdb: getServer returns the SmartdbServer', async () => { const srv = localDb.getServer(); expect(srv).toBeTruthy(); expect(srv.running).toBeTrue(); }); // ============================================================================ // LocalSmartDb: Data isolation between databases // ============================================================================ tap.test('localsmartdb: databases are isolated', async () => { const dbA = client.db('isoA'); const dbB = client.db('isoB'); await dbA.collection('shared').insertOne({ source: 'A', val: 1 }); await dbB.collection('shared').insertOne({ source: 'B', val: 2 }); const docsA = await dbA.collection('shared').find({}).toArray(); const docsB = await dbB.collection('shared').find({}).toArray(); expect(docsA.length).toEqual(1); expect(docsA[0].source).toEqual('A'); expect(docsB.length).toEqual(1); expect(docsB[0].source).toEqual('B'); await dbA.dropDatabase(); await dbB.dropDatabase(); }); // ============================================================================ // Cleanup // ============================================================================ tap.test('localsmartdb: cleanup', async () => { await client.close(); await localDb.stop(); expect(localDb.running).toBeFalse(); cleanTmpDir(tmpDir); }); export default tap.start();