import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smartdb from '../ts/index.js'; import { MongoClient } from 'mongodb'; import * as net from 'net'; let server: smartdb.SmartdbServer; let client: MongoClient; let port: number; async function getFreePort(): Promise { return await new Promise((resolve, reject) => { const probe = net.createServer(); probe.once('error', reject); probe.listen(0, '127.0.0.1', () => { const address = probe.address(); if (!address || typeof address === 'string') { probe.close(() => reject(new Error('Failed to allocate TCP port'))); return; } probe.close(() => resolve(address.port)); }); }); } tap.test('transactions: should start server and connect', async () => { port = await getFreePort(); server = new smartdb.SmartdbServer({ port }); await server.start(); client = new MongoClient(`mongodb://127.0.0.1:${port}`, { directConnection: true, serverSelectionTimeoutMS: 5000, }); await client.connect(); expect(server.running).toBeTrue(); }); tap.test('transactions: should still support explicit sessions', async () => { const result = await client.db('admin').command({ startSession: 1 }); expect(result.ok).toEqual(1); expect(result.id).toBeTruthy(); const end = await client.db('admin').command({ endSessions: [result.id] }); expect(end.ok).toEqual(1); }); tap.test('transactions: should reject transaction-scoped writes without txnNumber before mutation', async () => { const db = client.db('txntest'); const coll = db.collection('docs'); await coll.insertOne({ key: 'outside', value: 1 }); let threw = false; try { await db.command({ insert: 'docs', documents: [{ key: 'inside-raw', value: 2 }], startTransaction: true, autocommit: false, }); } catch (err: any) { threw = true; expect(err.code).toEqual(14); expect(err.codeName).toEqual('TypeMismatch'); } expect(threw).toBeTrue(); expect(await coll.countDocuments({ key: 'inside-raw' })).toEqual(0); expect(await coll.countDocuments({ key: 'outside' })).toEqual(1); }); tap.test('transactions: official driver transaction should commit buffered writes', async () => { const coll = client.db('txntest').collection('driverdocs'); await coll.insertOne({ key: 'outside-driver', value: 0 }); const session = client.startSession(); try { session.startTransaction(); await coll.insertOne({ key: 'inside-driver', value: 1 }, { session }); const inTxn = await coll.findOne({ key: 'inside-driver' }, { session }); expect(inTxn).toBeTruthy(); expect(await coll.countDocuments({ key: 'inside-driver' })).toEqual(0); await session.commitTransaction(); } finally { await session.endSession(); } expect(await coll.countDocuments({ key: 'inside-driver' })).toEqual(1); expect(await coll.countDocuments({ key: 'outside-driver' })).toEqual(1); }); tap.test('transactions: abort should discard buffered writes', async () => { const coll = client.db('txntest').collection('abortdocs'); const session = client.startSession(); try { session.startTransaction(); await coll.insertOne({ key: 'abort-me', value: 1 }, { session }); expect(await coll.findOne({ key: 'abort-me' }, { session })).toBeTruthy(); await session.abortTransaction(); } finally { await session.endSession(); } expect(await coll.findOne({ key: 'abort-me' })).toBeNull(); }); tap.test('transactions: update and delete should commit atomically', async () => { const coll = client.db('txntest').collection('mutations'); await coll.insertMany([ { key: 'update-me', value: 1 }, { key: 'delete-me', value: 2 }, ]); const session = client.startSession(); try { session.startTransaction(); await coll.updateOne({ key: 'update-me' }, { $set: { value: 10 } }, { session }); await coll.deleteOne({ key: 'delete-me' }, { session }); expect((await coll.findOne({ key: 'update-me' }, { session }))!.value).toEqual(10); expect(await coll.findOne({ key: 'delete-me' }, { session })).toBeNull(); expect((await coll.findOne({ key: 'update-me' }))!.value).toEqual(1); expect(await coll.findOne({ key: 'delete-me' })).toBeTruthy(); await session.commitTransaction(); } finally { await session.endSession(); } expect((await coll.findOne({ key: 'update-me' }))!.value).toEqual(10); expect(await coll.findOne({ key: 'delete-me' })).toBeNull(); }); tap.test('transactions: commit and abort without active transaction should be explicit errors', async () => { for (const command of [{ commitTransaction: 1 }, { abortTransaction: 1 }]) { let threw = false; try { await client.db('admin').command(command); } catch (err: any) { threw = true; expect(err.code).toEqual(251); expect(err.codeName).toEqual('NoSuchTransaction'); } expect(threw).toBeTrue(); } }); tap.test('transactions: serverStatus should expose transaction and oplog metrics', async () => { const status = await client.db('admin').command({ serverStatus: 1 }); expect(status.ok).toEqual(1); expect(status.transactions.currentActive).toEqual(0); expect(status.logicalSessionRecordCache.activeSessionsCount).toBeGreaterThanOrEqual(0); expect(status.oplog.totalEntries).toBeGreaterThan(0); }); tap.test('transactions: cleanup', async () => { await client.close(); await server.stop(); expect(server.running).toBeFalse(); }); export default tap.start();