ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.36 вынос tryHandleLivingChat в отдельный runtime-handler чтобы убрать еще один большой локальный блок. новый living-chat handler-адаптер с try/catch + finalize + warn fallback
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { tryHandleAssistantLivingChatRuntime } from "../src/services/assistantLivingChatHandlerRuntimeAdapter";
|
||||
|
||||
function buildInput(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
sessionId: "asst-1",
|
||||
userMessage: "question",
|
||||
sessionItems: [],
|
||||
modeDecision: { mode: "chat", reason: "living_chat_signal_detected" },
|
||||
sessionScope: {
|
||||
knownOrganizations: [],
|
||||
selectedOrganization: null,
|
||||
activeOrganization: null
|
||||
},
|
||||
addressRuntimeMeta: null,
|
||||
traceIdFactory: () => "chat-trace-1",
|
||||
toNonEmptyString: (value: unknown) => (typeof value === "string" && value.trim() ? value.trim() : null),
|
||||
mergeKnownOrganizations: (values: unknown[]) => values.map((v) => String(v)),
|
||||
hasAssistantDataScopeMetaQuestionSignal: () => false,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
|
||||
hasDestructiveDataActionSignal: () => false,
|
||||
hasDangerOrCoercionSignal: () => false,
|
||||
hasOperationalAdminActionRequestSignal: () => false,
|
||||
hasOrganizationFactLookupSignal: () => false,
|
||||
hasOrganizationFactFollowupSignal: () => false,
|
||||
shouldEmitOrganizationSelectionReply: () => false,
|
||||
hasAssistantCapabilityQuestionSignal: () => false,
|
||||
resolveDataScopeProbe: async () => null,
|
||||
executeLlmChat: async () => "chat answer",
|
||||
applyScriptGuard: (text: string) => ({ text, applied: false, reason: null }),
|
||||
applyGroundingGuard: (payload: { chatText: string }) => ({
|
||||
text: payload.chatText,
|
||||
applied: false,
|
||||
reason: null
|
||||
}),
|
||||
buildAssistantSafetyRefusalReply: () => "safety",
|
||||
buildAssistantDataScopeContractReply: () => "scope",
|
||||
buildAssistantOrganizationFactBoundaryReply: () => "boundary",
|
||||
buildAssistantDataScopeSelectionReply: () => "selection",
|
||||
buildAssistantOperationalBoundaryReply: () => "operational",
|
||||
buildAssistantCapabilityContractReply: () => "capability",
|
||||
appendItem: () => {},
|
||||
getSession: () => ({ session_id: "asst-1", updated_at: "", items: [], investigation_state: null } as any),
|
||||
persistSession: () => {},
|
||||
cloneConversation: (items: any[]) => items,
|
||||
logEvent: vi.fn(),
|
||||
messageIdFactory: () => "msg-1",
|
||||
nowIso: () => "2026-04-10T00:00:00.000Z",
|
||||
...overrides
|
||||
} as any;
|
||||
}
|
||||
|
||||
describe("assistant living chat handler runtime adapter", () => {
|
||||
it("returns finalized response when runtime is handled", async () => {
|
||||
const runLivingChatRuntime = vi.fn(async () => ({
|
||||
handled: true,
|
||||
chatText: "chat",
|
||||
debug: { trace_id: "chat-1" }
|
||||
}));
|
||||
const finalizeLivingChatTurn = vi.fn(() => ({
|
||||
response: { ok: true, lane: "chat" }
|
||||
}));
|
||||
|
||||
const response = await tryHandleAssistantLivingChatRuntime(
|
||||
buildInput({
|
||||
runLivingChatRuntime,
|
||||
finalizeLivingChatTurn
|
||||
})
|
||||
);
|
||||
|
||||
expect(runLivingChatRuntime).toHaveBeenCalledTimes(1);
|
||||
expect(finalizeLivingChatTurn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
assistantReply: "chat",
|
||||
replyType: "factual_with_explanation"
|
||||
})
|
||||
);
|
||||
expect(response).toEqual({ ok: true, lane: "chat" });
|
||||
});
|
||||
|
||||
it("returns null when runtime is not handled", async () => {
|
||||
const finalizeLivingChatTurn = vi.fn();
|
||||
const response = await tryHandleAssistantLivingChatRuntime(
|
||||
buildInput({
|
||||
runLivingChatRuntime: async () => ({
|
||||
handled: false,
|
||||
chatText: "",
|
||||
debug: null
|
||||
}),
|
||||
finalizeLivingChatTurn
|
||||
})
|
||||
);
|
||||
|
||||
expect(response).toBeNull();
|
||||
expect(finalizeLivingChatTurn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("logs warn and returns null on runtime error", async () => {
|
||||
const logEvent = vi.fn();
|
||||
const response = await tryHandleAssistantLivingChatRuntime(
|
||||
buildInput({
|
||||
runLivingChatRuntime: async () => {
|
||||
throw new Error("boom");
|
||||
},
|
||||
logEvent
|
||||
})
|
||||
);
|
||||
|
||||
expect(response).toBeNull();
|
||||
expect(logEvent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
level: "warn",
|
||||
message: "assistant_living_chat_failed_fallback_to_deep"
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user