import test from "node:test"; import assert from "node:assert/strict"; import {createApp} from "../src/app.js"; import {DEFAULT_PERSONALITY_ID, getPersonality, listPersonalities} from "../src/services/personalityRegistry.js"; import {runMigrations} from "../src/db/schema.js"; import {createChatService} from "../src/services/chatService.js"; import {createModelService} from "../src/services/modelService.js"; test("personality metadata is curated and never exposes system prompts", () => { const personalities = listPersonalities(); assert.equal(personalities.length, 6); assert.equal(personalities.filter(item => item.isDefault).length, 1); assert.equal(personalities.find(item => item.isDefault).id, DEFAULT_PERSONALITY_ID); assert.deepEqual(personalities.map(item => item.name), ["Leonardo", "Shakespeare", "Ada", "Curie", "Socrates", "Turing"]); for (const item of personalities) { assert.ok(item.name && item.specialization && item.description && item.longDescription); assert.ok(item.historicalInspiration && item.icon && item.accentColor); assert.equal("systemPrompt" in item, false); } assert.match(getPersonality("creative-writer").systemPrompt, /narrative cohesion/); assert.match(getPersonality("creative-writer").systemPrompt, /do not default to archaic language/); assert.match(getPersonality("tutor").systemPrompt, /do not turn every exchange into a quiz or answer only with questions/); assert.match(getPersonality("programmer").systemPrompt, /smallest complete change/); }); test("chat prompt uses its stored personality and persists personality selection", async t => { const app = createApp({config:{dbPath:":memory:", testMode:true, eventLoopLagMonitorEnabled:false}}); t.after(() => app.locals.close()); const repositories = app.locals.repositories; app.locals.db.prepare("INSERT INTO users(username,password) VALUES(?,?)").run("writer", "unused"); const chat = repositories.createChat("writer", "Draft"); repositories.updateChatPersonality(chat.id, "writer", "creative-writer"); const restored = repositories.getChat(chat.id, "writer"); assert.equal(restored.personality_id, "creative-writer"); const chatService = createChatService(repositories, {generateReply:async()=>"", streamReply:async()=>""}, app.locals.config); const payload = chatService.buildMessagesPayload({user:"writer", chatId:chat.id, session:restored, message:"Continue"}); assert.match(payload.messagesPayload[0].content, /specialized creative-writing partner/); assert.doesNotMatch(payload.messagesPayload[0].content, /general assistant/i); assert.equal(payload.messagesPayload.at(-1).role, "user"); assert.equal(payload.messagesPayload.at(-1).content, "Continue"); let sent; const ollama = createModelService({modelApiBaseUrl:"http://ollama.test", modelRequestTimeoutMs:1000, modelUnloadAfterMs:1000}, { fetch:async (_url, options) => { sent = JSON.parse(options.body); return new Response('{"message":{"content":"A scene."},"done":true}\n', {status:200}); } }); await ollama.generateReply("configured:model", payload.messagesPayload); assert.match(sent.messages.find(item => item.role === "system" && item.content.includes("specialized creative-writing partner")).content, /Follow the user's genre/); assert.equal(sent.messages.at(-1).content, "Continue"); }); test("legacy conversations migrate to General Assistant without changing messages", async t => { const app = createApp({config:{dbPath:":memory:", testMode:true, eventLoopLagMonitorEnabled:false}}); t.after(() => app.locals.close()); const db = app.locals.db; db.prepare("INSERT INTO users(username,password) VALUES(?,?)").run("legacy", "unused"); const chatId = db.prepare("INSERT INTO chat_sessions(username,title) VALUES(?,?)").run("legacy", "Old chat").lastInsertRowid; db.prepare("INSERT INTO chat_messages(chat_id,role,content) VALUES(?,?,?)").run(chatId, "user", "Keep me"); db.exec("ALTER TABLE chat_sessions DROP COLUMN personality_id"); runMigrations(db); assert.equal(db.prepare("SELECT personality_id FROM chat_sessions WHERE id=?").get(chatId).personality_id, "general-assistant"); assert.equal(db.prepare("SELECT content FROM chat_messages WHERE chat_id=?").get(chatId).content, "Keep me"); });