import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; const source = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); const sendSource = source.slice(source.indexOf(" async function send("), source.indexOf("\n\n consultationReplay.current")); const stopSource = source.slice(source.indexOf(" async function stopResponse("), source.indexOf("\n\n function completeConsultationInterface")); test("consultation persists the optimistic user message before generation starts", () => { const undo = sendSource.indexOf("await waitForUndoWindow(controller.signal)"); const persist = sendSource.indexOf("await persistSession(userSession)"); const consult = sendSource.indexOf('fetch("/api/consult"'); assert.ok(undo >= 0 && undo < persist && persist < consult); assert.match(sendSource, /问题保存失败,未开始生成;问题已放回输入框。[\s\S]*?return false;[\s\S]*?fetch\("\/api\/consult"/); }); test("only explicit stop can request consultation cancellation", () => { const streamCatch = sendSource.indexOf(" } catch (caught) {", sendSource.indexOf('fetch("/api/consult"')); assert.equal(source.match(/confirmCancellation\(/g)?.length, 2); assert.match(stopSource, /await confirmCancellation\(/); assert.ok(streamCatch >= 0); assert.doesNotMatch(sendSource.slice(streamCatch), /confirmCancellation\(|\/api\/consult\/cancel/); }); test("durable partial stop cancels before preserving content and recovers completed conflicts", () => { const partialStop = stopSource.slice( stopSource.indexOf(" if (pending.partialReply)"), stopSource.indexOf("\n updateSession(pending.sessionId, () => pending.previousSession)"), ); const cancel = partialStop.indexOf("await requestCancellation(pending.requestId)"); const persist = partialStop.indexOf("await persistSession(stoppedSession)"); const conflict = partialStop.indexOf("error.status === 409"); const recoveryPersist = partialStop.slice(conflict, persist); assert.ok(cancel >= 0 && cancel < persist); assert.ok(conflict >= 0 && conflict < persist); assert.doesNotMatch(recoveryPersist, /persistSession\(stoppedSession\)/); assert.match(recoveryPersist, /fetchConsultationStatus\(pending.sessionId, pending.requestId\)/); assert.match(recoveryPersist, /status.status === "completed"[\s\S]*?fetchSessions\(\)[\s\S]*?readSessions\(payload, modelCatalog\)/); assert.match(partialStop, /已停止回答,现有内容已保留,本次点数已退回。/); assert.match(source, /停止回答,保留已生成内容并退回本次点数/); assert.match(source, /停止回答,保留现有内容并申请退回本次点数/); }); test("explicit consultation HTTP failures unlock instead of entering recovery", () => { assert.match(sendSource, /!response\.ok[\s\S]*throw new ConsultationResponseError\([\s\S]*response\.status/); const explicitFailure = sendSource.slice( sendSource.indexOf("caught instanceof ConsultationResponseError"), sendSource.indexOf("if (!cancelled && ownsInterface && pendingConsultation.current)"), ); assert.match(explicitFailure, /caught\.message === "request_conflict"[\s\S]*setConsultationPhase\("recovering"\)/); const genericFailure = explicitFailure.slice(explicitFailure.indexOf("setRequestError({ sessionId, message: caught.message })")); assert.match(genericFailure, /setRequestError\(\{ sessionId, message: caught\.message \}\)/); assert.match(genericFailure, /completeConsultationInterface\(requestId\)/); assert.doesNotMatch(genericFailure, /phase: "recovering"|setConsultationPhase\("recovering"\)/); }); test("reserved consultations recover through the status endpoint", () => { assert.match(source, /\/api\/consult\/status\?sessionId=\$\{encodeURIComponent\(sessionId\)\}&requestId=\$\{encodeURIComponent\(requestId\)\}/); assert.match(source, /fetch\("\/api\/consult\/status", \{ signal, cache: "no-store" \}\)/); assert.match(source, /reservedConsultation\?\.status === "reserved"[\s\S]*nextSessions\.find\(\(session\) => session\.id === reservedConsultation\.sessionId\)/); assert.match(source, /status\?\.status !== "reserved"[\s\S]*sessions\.find\(\(item\) => item\.id === status\.sessionId\)/); assert.match(source, /status: "reserved" \| "completed" \| "cancelled"/); assert.match(source, /readonly responseMessage\?: unknown/); assert.match(source, /phase: "recovering"/); assert.match(source, /window\.setTimeout\(\(\) => void poll\(\), 1_750\)/); assert.match(source, /status\.status === "completed"[\s\S]*?fetchSessions\(controller\.signal\)[\s\S]*?readSessions\(payload, modelCatalog\)/); assert.match(source, /window\.addEventListener\("online", onOnline\)/); assert.match(source, /window\.addEventListener\("pageshow", onPageShow\)/); assert.match(source, /网络已断开,回答仍在后台生成;联网后会自动恢复。/); }); test("a missing reservation is replayed once instead of being polled until the question is lost", () => { assert.match(source, /class ConsultationStatusError extends Error[\s\S]*readonly status: number/); assert.match(source, /throw new ConsultationStatusError\([\s\S]*response\.status/); const recoveryEffect = source.slice( source.indexOf('if (consultationPhase !== "recovering"'), source.indexOf("}, [consultationPhase, modelCatalog, pendingRequestId, pendingSessionId])"), ); assert.match(source, /const consultationStatusMissingCount = useRef\(0\)/); assert.match(source, /const consultationReplayStarted = useRef\(null\)/); assert.match(recoveryEffect, /status\.status === "reserved"[\s\S]*consultationStatusMissingCount\.current = 0/); assert.match(recoveryEffect, /caught instanceof ConsultationStatusError && caught\.status === 404[\s\S]*consultationStatusMissingCount\.current \+= 1/); assert.match(recoveryEffect, /consultationStatusMissingCount\.current === 1[\s\S]*正在确认本次咨询请求是否已开始/); assert.match(recoveryEffect, /consultationReplayStarted\.current !== pendingRequestId[\s\S]*consultationReplay\.current\(\)/); assert.match(recoveryEffect, /consultationStatusMissingCount\.current >= 4[\s\S]*setPendingSessionId\(null\)[\s\S]*setPendingRequestId\(null\)[\s\S]*setConsultationPhase\(null\)/); assert.match(recoveryEffect, /后台未找到本次咨询请求,已停止恢复,请重新发送。/); assert.match(recoveryEffect, /consultationStatusMissingCount\.current = 0;[\s\S]*回答仍在后台生成,正在自动恢复。/); assert.match(recoveryEffect, /consultationStatusMissingCount\.current > 0[\s\S]*window\.setTimeout\(\(\) => void poll\(\), 1_750\)[\s\S]*else \{[\s\S]*void poll\(\)/); }); test("tab-local pending ids drive strict bootstrap recovery before the global fallback", () => { const bootstrap = source.slice( source.indexOf("let reservedConsultation: ConsultationStatus | null = null"), source.indexOf("if (controller.signal.aborted) return;", source.indexOf("let reservedConsultation: ConsultationStatus | null = null")), ); const storageSync = source.slice( source.indexOf("if (!hydrated || uiPreview.current) return;", source.indexOf("}, []);")), source.indexOf('if (consultationPhase !== "recovering"'), ); assert.match(bootstrap, /readStoredPendingConsultation\([\s\S]*pendingConsultationStorageKey[\s\S]*nextSessions\.map\(\(session\) => session\.id\)/); assert.match(bootstrap, /if \(!storedPending && sessionStorage\.getItem\(pendingConsultationStorageKey\)\) \{\s*sessionStorage\.removeItem\(pendingConsultationStorageKey\)/); assert.ok(bootstrap.indexOf("if (storedPending)") < bootstrap.indexOf("fetchActiveConsultationStatus(controller.signal)")); assert.match(bootstrap, /if \(storedPending\) \{[\s\S]*fetchConsultationStatus\([\s\S]*storedPending\.sessionId,[\s\S]*storedPending\.requestId,[\s\S]*\} else \{[\s\S]*fetchActiveConsultationStatus/); assert.match(bootstrap, /status\.status === "reserved"[\s\S]*reservedConsultation = status;[\s\S]*else \{[\s\S]*sessionStorage\.removeItem\(pendingConsultationStorageKey\)/); assert.match(bootstrap, /caught instanceof ConsultationStatusError && caught\.status === 404 \? 1 : 0/); assert.match(storageSync, /pendingSessionId && pendingRequestId[\s\S]*sessionStorage\.setItem\(pendingConsultationStorageKey,[\s\S]*sessionId: pendingSessionId,[\s\S]*requestId: pendingRequestId,[\s\S]*question:/); assert.match(storageSync, /else \{[\s\S]*sessionStorage\.removeItem\(pendingConsultationStorageKey\)/); }); test("pending storage keeps the question so a refresh during the undo window can replay", () => { assert.match(source, /function readStoredPendingConsultation\(/); assert.match(source, /typeof parsedPending\.question === "string" \? parsedPending\.question : ""/); assert.match(sendSource, /sessionStorage\.setItem\(pendingConsultationStorageKey, JSON\.stringify\(\{[\s\S]*sessionId,[\s\S]*requestId,[\s\S]*question: originalQuestion/); assert.match(source, /restoreConsultationRecovery\(recoverySession, reservedConsultation\.requestId, storedPending\)/); assert.match(source, /stored\?\.question\?\.trim\(\)/); assert.match(sendSource, /resumeRequestId/); assert.match(sendSource, /questionAlreadyPresent/); assert.match(source, /consultationReplay\.current = \(\) => \{[\s\S]*resumeRequestId: pending\.requestId/); }); test("the first default consultation title is persisted with the user question", () => { const userSessionBlock = sendSource.slice( sendSource.indexOf("const userSession: ChatSession"), sendSource.indexOf("const requestId = resumeRequestId ?? globalThis.crypto.randomUUID()"), ); assert.match(userSessionBlock, /currentSession\.messages\.length === 0 && currentSession\.title === "新对话"[\s\S]*resolveSessionTitle\(question\)/); assert.ok(sendSource.indexOf("await persistSession(userSession)") < sendSource.indexOf('fetch("/api/consult"')); assert.doesNotMatch(sendSource, /persistSession\(completedSession\)/); assert.match(sendSource, /const completedSession: ChatSession = \{[\s\S]*title: userSession\.title/); assert.doesNotMatch(sendSource, /resolveSessionTitle\(question, reply\.title\)/); }); test("a truncated generation keeps the partial answer and does not wait for a successful run", () => { const stream = sendSource.slice(sendSource.indexOf('fetch("/api/consult"')); assert.match(stream, /event\.code === "answer_truncated"/); assert.match(stream, /truncatedFailure = event/); assert.match(stream, /const truncatedSession: ChatSession = \{[\s\S]*role: "assistant"[\s\S]*text: reply\.text/); assert.match(stream, /await persistSession\(truncatedSession\)/); assert.match(stream, /setComposerNotice\(truncatedFailure\.message\)/); assert.match(stream, /if \(!runCompleted && !truncatedFailure\) throw new ConsultationResponseError/); assert.doesNotMatch(stream.slice(stream.indexOf("if (truncatedFailure)")), /runCompleted = true/); });