deer-flow/frontend/tests/unit/core/threads/stream-options.test.ts
zhangwei-way b6503e9a35
feat(knowledge): add per-message RAGFlow retrieval scope (#5238)
* feat(knowledge): integrate RAGFlow retrieval and management

* test(knowledge): cover merged listing tool

* feat(knowledge): add per-message retrieval scope

* chore(docs): remove unrelated document

* docs(knowledge): add interaction screenshots

* feat(knowledge): simplify scope selector trigger

* docs(knowledge): refresh selector screenshot

* feat(knowledge): defer standalone management

* docs(knowledge): show chat-only scope UI

* fix(knowledge): honor scope on clarification replies

* fix(knowledge): harden scoped replay validation

* docs(knowledge): clarify replay scope precedence

* fix(knowledge): keep provider settings on tools

* fix(config): preserve tools-only knowledge settings

* fix(knowledge): submit custom assistant identity

* refactor(knowledge): trim PR scope changes

* fix(knowledge): sanitize document scope display

* feat(knowledge): enable scope selection in main chat

* fix(knowledge): emphasize active scope icon without button frame

* fix(knowledge): close context scrubbing and refresh e2e checks

* fix(knowledge): preserve idempotent canonical retries

* fix(knowledge): accept promptless conversation runs

* style(knowledge): format backend regression tests

* chore(knowledge): trim PR scope and fix frontend format

* fix(knowledge): remove shared-scope notice

* fix(knowledge): remove scope persistence notice

* docs(knowledge): include main chat in catalog scope

* fix(knowledge): preserve scope recovery and upgrades

* fix(config): preserve LightRAG knowledge upgrades

---------

Co-authored-by: foreleven <for-eleven@hotmail.com>
2026-09-18 16:59:31 +08:00

107 lines
2.9 KiB
TypeScript

import { afterEach, expect, test, rs } from "@rstest/core";
async function captureThreadStreamOptions(assistantId?: string) {
let capturedOptions: Record<string, unknown> | undefined;
rs.resetModules();
rs.doMock("react", () => ({
useCallback: <T extends (...args: never[]) => unknown>(callback: T) =>
callback,
useEffect: () => undefined,
useMemo: <T>(factory: () => T) => factory(),
useRef: <T>(initialValue: T) => ({ current: initialValue }),
useState: <T>(initialValue: T | (() => T)) => [
typeof initialValue === "function"
? (initialValue as () => T)()
: initialValue,
rs.fn(),
],
}));
rs.doMock("@tanstack/react-query", () => ({
useInfiniteQuery: () => ({
data: { pages: [] },
error: null,
fetchNextPage: rs.fn(),
hasNextPage: false,
isFetchingNextPage: false,
isLoading: false,
}),
useMutation: rs.fn(),
useQuery: rs.fn(),
useQueryClient: () => ({
invalidateQueries: rs.fn(),
setQueriesData: rs.fn(),
}),
}));
rs.doMock("@langchain/langgraph-sdk/react", () => ({
useStream: (options: Record<string, unknown>) => {
capturedOptions = options;
return {
isLoading: false,
messages: [],
stop: rs.fn(),
submit: rs.fn(),
values: { title: "", messages: [] },
};
},
}));
rs.doMock("@/core/api", () => ({
getAPIClient: () => ({}),
}));
rs.doMock("@/core/i18n/hooks", () => ({
useI18n: () => ({
t: {
pages: { newChat: "New chat" },
uploads: { uploadingFiles: "Uploading files" },
},
}),
}));
rs.doMock("@/core/tasks/context", () => ({
useSubtaskContext: () => ({
tasksRef: { current: {} },
setTasks: rs.fn(),
}),
useUpdateSubtask: () => rs.fn(),
}));
const { useThreadStream } = await import("@/core/threads/hooks");
function ThreadStreamCapture() {
useThreadStream({
context: {
mode: "flash",
},
assistantId,
isMock: true,
} as never);
return null;
}
ThreadStreamCapture();
return capturedOptions;
}
afterEach(() => {
rs.doUnmock("react");
rs.doUnmock("@tanstack/react-query");
rs.doUnmock("@langchain/langgraph-sdk/react");
rs.doUnmock("@/core/api");
rs.doUnmock("@/core/i18n/hooks");
rs.doUnmock("@/core/tasks/context");
rs.resetModules();
});
test("does not subscribe to unsupported LangGraph events mode", async () => {
const options = await captureThreadStreamOptions();
expect(options).toBeDefined();
expect(options).not.toHaveProperty("onLangChainEvent");
expect(options).toHaveProperty("onUpdateEvent");
expect(options).toHaveProperty("onCustomEvent");
});
test("forwards the custom-agent assistant identity to the stream", async () => {
const options = await captureThreadStreamOptions("researcher");
expect(options).toHaveProperty("assistantId", "researcher");
});