import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import LibDashStore from '../src/index.js'; import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js'; async function run() { console.log('--- Testing store.modelconversation / store.modelmessage ---'); const tempDir = mkdtempSync(join(tmpdir(), 'test-modelconv-')); const provider = new BetterSqlite3Provider({ filename: join(tempDir, 'test.db'), storageDir: join(tempDir, 'storage'), }); await provider.initialize(); const store = await new LibDashStore(provider); try { const userA = await store.google_auth.add('mc-a@example.com'); const company = await store.company.create({ name: 'MC Corp', iso639: 'de', taxrate: 19, reducedtaxrate: 7, googleAuthId: userA, }); await store.google_auth.setSelectedCompany(userA, company.id); const conv = await store.modelconversation.create(userA, { provider: 'openrouter', model: 'test/model', }); assert.equal(conv.state, 'idle'); assert.equal(conv.provider, 'openrouter'); await assert.rejects(store.modelconversation.create(userA, { model: 'x' }), /Provider required/); await assert.rejects(store.modelconversation.create(userA, { provider: 'x' }), /Model required/); const list = await store.modelconversation.list(userA); assert.equal(list.length, 1); assert.deepEqual(await store.modelmessage.list(userA, conv.id), []); const m1 = await store.modelmessage.add(userA, conv.id, { message: 'hello' }); assert.equal(m1.role, 'user'); assert.equal(m1.conversation_id, conv.id); await assert.rejects(store.modelmessage.add(userA, conv.id, { message: ' ' }), /Message required/); await assert.rejects(store.modelmessage.add(userA, 9999, { message: 'x' }), /Conversation not found/); // Non-idle conversation refuses user messages but allows assistant messages const busyConv = await store.modelconversation.setState(userA, conv.id, 'busy'); assert.equal(busyConv.state, 'busy'); const fetchedBusy = await store.modelconversation.get(userA, conv.id); assert.equal(fetchedBusy.state, 'busy'); await assert.rejects( store.modelmessage.add(userA, conv.id, { message: 'again' }), /Conversation is not idle/, ); // Assistant message can be added while busy const mAssistant = await store.modelmessage.add(userA, conv.id, { message: 'echo: hello', role: 'assistant' }); assert.equal(mAssistant.role, 'assistant'); assert.equal(mAssistant.message, 'echo: hello'); // Back to idle await store.modelconversation.setState(userA, conv.id, 'idle'); await store.modelmessage.add(userA, conv.id, { message: 'second' }); const msgs = await store.modelmessage.list(userA, conv.id); assert.deepEqual(msgs.map((m) => m.message), ['hello', 'echo: hello', 'second']); assert.deepEqual(msgs.map((m) => m.role), ['user', 'assistant', 'user']); // Other user cannot see or write const userB = await store.google_auth.add('mc-b@example.com'); await store.companyuser.add(company.id, userB); await store.google_auth.setSelectedCompany(userB, company.id); assert.equal((await store.modelconversation.list(userB)).length, 0); await assert.rejects(store.modelmessage.list(userB, conv.id), /Conversation not found/); await assert.rejects(store.modelmessage.add(userB, conv.id, { message: 'x' }), /Conversation not found/); console.log('PASS'); } finally { await provider.close?.(); rmSync(tempDir, { recursive: true, force: true }); } } run().catch((err) => { console.error('FAIL', err); process.exit(1); });