import { prisma } from "@kokorone/db"; const API_PORT = process.env.PORT_API ?? "3001"; const WEB_PORT = process.env.PORT_WEB ?? "3100"; const CHARACTER_ID = "seed-character-genki"; const USER_ID = "seed-user-primary"; async function post(path, body) { const res = await fetch(`http://localhost:${API_PORT}${path}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body ?? {}), }); if (!res.ok) { throw new Error(`POST ${path} 回傳 ${res.status}`); } return res.json(); } async function get(path) { const res = await fetch(`http://localhost:${API_PORT}${path}`); if (!res.ok) { throw new Error(`GET ${path} 回傳 ${res.status}`); } return res.json(); } export default async function smokeH() { const sessionId = "smoke-h-" + Date.now(); await prisma.emotionState.update({ where: { characterId: CHARACTER_ID }, data: { calm: 100, joy: 0, sad: 0, alert: 0, shy: 0, grumpy: 0, updatedAt: new Date() }, }); const before = await get(`/relationships/${CHARACTER_ID}/${USER_ID}`); const intimacyBefore = before.relationship.intimacy; // H-1 對話 API:一輪完整對話回傳回應、情緒狀態與親密度變化 const result = await post(`/chat/${CHARACTER_ID}`, { userId: USER_ID, sessionId, text: "謝謝你,我今天好開心!", }); if (!result.text || typeof result.text !== "string") { throw new Error("對話 API 未回傳文字內容"); } if (result.context.emotion.dominant !== "JOY") { throw new Error(`對話後情緒狀態應變為愉悅,實際為 ${result.context.emotion.dominant}`); } if (result.context.relationship.intimacy <= intimacyBefore) { throw new Error("對話後親密度應有變化(累積日常互動)"); } // H-1 session 生命週期:POST /session/:id/end 觸發睡眠固化,工作記憶應被清空 const contextBefore = await get(`/memory/${CHARACTER_ID}/sessions/${sessionId}/messages`); if (contextBefore.context.length === 0) { throw new Error("對話後工作記憶應保留本輪內容,session 結束前不應為空"); } await post(`/session/${sessionId}/end`, { characterId: CHARACTER_ID }); const contextAfter = await get(`/memory/${CHARACTER_ID}/sessions/${sessionId}/messages`); if (contextAfter.context.length !== 0) { throw new Error("session 結束後應觸發睡眠固化並清空工作記憶"); } // H-2~H-5:對話頁面應包含情緒晶片、親密度顯示、心跳波形、立繪佔位區塊的結構標記 const pageRes = await fetch(`http://localhost:${WEB_PORT}/chat`); if (!pageRes.ok) { throw new Error(`GET /chat 回傳 ${pageRes.status}`); } const html = await pageRes.text(); if (!html.includes("親密度")) { throw new Error("對話頁面缺少親密度顯示(H-2)"); } if (!html.includes("heartbeat-wave-line")) { throw new Error("對話頁面缺少心跳波形母題(H-4)"); } if (!html.includes("立繪")) { throw new Error("對話頁面缺少立繪佔位區塊(H-5)"); } // 清理本次測試造成的狀態變化,避免影響其他群組或人工檢視 await prisma.relationship.update({ where: { characterId_userId: { characterId: CHARACTER_ID, userId: USER_ID } }, data: { intimacy: 25 }, }); await prisma.emotionState.update({ where: { characterId: CHARACTER_ID }, data: { calm: 70, joy: 20, sad: 0, alert: 0, shy: 0, grumpy: 0, updatedAt: new Date() }, }); }