Files
geo/apps/desktop-client/src/main/adapters/deepseek.test.ts
T
root 93cf734673 test(desktop-client/deepseek): mock electron ipcMain to unblock vitest import
The deepseek adapter pulls in electron/main when registering ipc listeners,
which made the test fail at import time outside the electron runtime. Stub
ipcMain with vi.mock so the unit test can load the module under vitest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:34:28 +08:00

359 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it, vi } from "vitest";
vi.mock("electron/main", () => ({
ipcMain: {
on: vi.fn(),
},
}));
import { __deepseekTestUtils } from "./deepseek";
import { getMonitorAdapter } from "./index";
const {
extractQuestionText,
buildSourceItem,
buildDeepSeekPayload,
detectDeepSeekBusySignals,
dedupeSourceItems,
classifyDeepseekSources,
mergeDeepSeekRawSourceLinks,
isObservationComplete,
isDeepSeekToggleSelected,
isDeepSeekWebpagesTriggerText,
} = __deepseekTestUtils;
describe("deepseek adapter helpers", () => {
it("extracts question text from monitoring payload", () => {
expect(extractQuestionText({ question_text: "DeepSeek 怎么看 GEO" })).toBe("DeepSeek 怎么看 GEO");
});
it("dedupes sources by normalized url without hash fragments", () => {
const items = dedupeSourceItems([
{
url: "https://example.com/a#top",
title: "A",
normalized_url: "https://example.com/a#top",
},
{
url: "https://example.com/a#bottom",
title: "A later",
normalized_url: "https://example.com/a#bottom",
},
]);
expect(items).toHaveLength(1);
expect(items[0]?.normalized_url).toBe("https://example.com/a");
expect(items[0]?.title).toBe("A");
});
it("treats revealed search cards as citation sources and inline links as content citation candidates", () => {
const classified = classifyDeepseekSources({
inlineLinks: [
{
url: "https://example.com/citation",
title: "Citation",
normalized_url: "https://example.com/citation",
},
],
sourcePanelLinks: [],
searchPanelLinks: [
{
url: "https://example.com/search",
title: "Search",
normalized_url: "https://example.com/search",
},
],
});
expect(classified.citations).toHaveLength(1);
expect(classified.inlineCitationCandidates).toHaveLength(1);
expect(classified.searchResults).toHaveLength(1);
expect(classified.citations[0]?.url).toBe("https://example.com/search");
expect(classified.inlineCitationCandidates[0]?.url).toBe("https://example.com/citation");
expect(classified.searchResults[0]?.url).toBe("https://example.com/search");
});
it("enriches numeric inline citations with source metadata from revealed search cards", () => {
const classified = classifyDeepseekSources({
inlineLinks: [
{
url: "https://example.com/citation",
title: null,
text: "-1",
siteName: null,
kind: "inline",
},
],
sourcePanelLinks: [],
searchPanelLinks: [
{
url: "https://example.com/citation",
title: "DeepSeek source article",
text: "Snippet",
siteName: "Example",
kind: "search",
},
],
});
expect(classified.inlineCitationCandidates).toHaveLength(1);
expect(classified.inlineCitationCandidates[0]?.title).toBe("DeepSeek source article");
expect(classified.inlineCitationCandidates[0]?.site_name).toBe("Example");
expect(classified.citations).toHaveLength(1);
expect(classified.searchResults).toHaveLength(1);
});
it("keeps reasoning search pages as citation sources separate from answer content citations", () => {
const classified = classifyDeepseekSources({
inlineLinks: [
{
url: "https://example.com/body-citation",
title: "Answer body citation",
text: "[15]",
siteName: "Body Site",
kind: "inline",
},
],
sourcePanelLinks: [],
searchPanelLinks: [
{
url: "https://example.com/reasoning-search",
title: "Reasoning searched page",
text: "浏览页面",
siteName: "Search Site",
kind: "search",
},
],
});
expect(classified.inlineCitationCandidates.map((item) => item.url)).toEqual([
"https://example.com/body-citation",
]);
expect(classified.citations.map((item) => item.url)).toEqual([
"https://example.com/reasoning-search",
]);
expect(classified.searchResults.map((item) => item.url)).toEqual([
"https://example.com/reasoning-search",
]);
});
it("retains all revealed DeepSeek search results instead of truncating after 32 items", () => {
const searchPanelLinks = Array.from({ length: 78 }, (_, index) => ({
url: `https://example.com/search-${index + 1}`,
title: `Search ${index + 1}`,
text: `Snippet ${index + 1}`,
siteName: "Example",
kind: "search" as const,
}));
const classified = classifyDeepseekSources({
inlineLinks: [],
sourcePanelLinks: [],
searchPanelLinks,
});
expect(classified.citations).toHaveLength(78);
expect(classified.searchResults).toHaveLength(78);
});
it("merges revealed sidebar search links with the snapshot search links by normalized url", () => {
const merged = mergeDeepSeekRawSourceLinks(
[
{
url: "https://example.com/report#top",
title: null,
text: "Visible title",
siteName: null,
kind: "search",
},
],
[
{
url: "https://example.com/report#bottom",
title: "Merged title",
text: "Merged snippet",
siteName: "Example",
kind: "search",
},
{
url: "https://example.com/extra",
title: "Extra result",
text: "Extra snippet",
siteName: "Example",
kind: "search",
},
],
);
expect(merged).toHaveLength(2);
expect(merged[0]).toEqual(expect.objectContaining({
url: "https://example.com/report",
title: "Merged title",
text: "Visible title",
siteName: "Example",
}));
expect(merged[1]?.url).toBe("https://example.com/extra");
});
it("normalizes deepseek raw source links into monitoring source items", () => {
const item = buildSourceItem({
url: "https://example.com/report#cite-1",
title: null,
text: "DeepSeek source title",
siteName: "Example",
kind: "source",
});
expect(item).toEqual(expect.objectContaining({
url: "https://example.com/report",
normalized_url: "https://example.com/report",
title: "DeepSeek source title",
site_name: "Example",
host: "example.com",
}));
});
it("unwraps DeepSeek redirect URLs before storing source items", () => {
const item = buildSourceItem({
url: "https://chat.deepseek.com/api/redirect?url=https%3A%2F%2Fexample.com%2Fsource%23ref",
title: "Redirected source",
text: null,
siteName: null,
kind: "search",
});
expect(item).toEqual(expect.objectContaining({
url: "https://example.com/source",
normalized_url: "https://example.com/source",
host: "example.com",
}));
});
it("does not use numeric citation markers as source titles", () => {
const item = buildSourceItem({
url: "https://example.com/report#cite-1",
title: null,
text: "-1",
siteName: null,
kind: "inline",
});
expect(item?.title).toBeNull();
expect(item?.site_name).toBe("example.com");
});
it("treats a stable answer as complete even without citations", () => {
expect(isObservationComplete({
answer: "这是最终答案",
busy: false,
loginRequired: false,
challengeRequired: false,
signature: "sig-1",
})).toBe(true);
});
it("ignores source-logo loading placeholders when computing busy signals", () => {
expect(detectDeepSeekBusySignals({
bodyText: "已阅读 10 个网页",
descriptors: [
"site_logo_loading site_logo_fallback",
"site_logo_loading site_logo_fallback",
],
})).toEqual([]);
});
it("recognizes deepseek toggle selected state from class names", () => {
expect(isDeepSeekToggleSelected("ds-toggle-button ds-toggle-button--selected")).toBe(true);
expect(isDeepSeekToggleSelected("ds-toggle-button")).toBe(false);
});
it("only treats the bottom 网页 pill as the DeepSeek source trigger", () => {
expect(isDeepSeekWebpagesTriggerText("57 个网页")).toBe(true);
expect(isDeepSeekWebpagesTriggerText("已阅读 57 个网页")).toBe(true);
expect(isDeepSeekWebpagesTriggerText("搜索到 39 个网页")).toBe(false);
});
it("normalizes DeepSeek model labels without mistaking search toggles for models", () => {
const snapshot = {
url: "https://chat.deepseek.com/",
title: "DeepSeek",
loginRequired: false,
loginReason: null,
challengeRequired: false,
challengeReason: null,
busy: false,
busySignals: [],
composerValue: null,
sendDisabled: false,
answer: "Final answer",
answerText: "Final answer",
reasoning: null,
signature: "sig-1",
currentModelLabel: "深度思考 (R1)",
providerRequestID: null,
requestID: null,
inlineLinks: [],
sourcePanelLinks: [],
searchPanelLinks: [],
};
const sources = {
citations: [],
inlineCitationCandidates: [],
searchResults: [],
};
const r1Payload = buildDeepSeekPayload(snapshot, sources, "Final answer");
const searchPayload = buildDeepSeekPayload({
...snapshot,
currentModelLabel: "联网搜索",
}, sources, "Final answer");
expect(r1Payload.provider_model).toBe("DeepSeek-R1");
expect(searchPayload.provider_model).toBeNull();
});
it("builds json-safe payloads with null placeholders instead of undefined", () => {
const payload = buildDeepSeekPayload({
url: "https://chat.deepseek.com/",
title: "DeepSeek",
loginRequired: false,
loginReason: null,
challengeRequired: false,
challengeReason: null,
busy: false,
busySignals: [],
composerValue: null,
sendDisabled: false,
answer: "Final answer",
answerText: "Final answer",
reasoning: null,
signature: "sig-1",
currentModelLabel: null,
providerRequestID: null,
requestID: null,
inlineLinks: [],
sourcePanelLinks: [],
searchPanelLinks: [],
}, {
citations: [],
inlineCitationCandidates: [],
searchResults: [],
}, "Final answer");
expect(payload.provider_model).toBeNull();
expect(payload.provider_request_id).toBeNull();
expect(payload.request_id).toBeNull();
expect((payload.raw_response_json as Record<string, unknown>).provider_model).toBeNull();
expect((payload.raw_response_json as Record<string, unknown>).provider_request_id).toBeNull();
expect((payload.raw_response_json as Record<string, unknown>).request_id).toBeNull();
expect((payload.raw_response_json as Record<string, unknown>).signature).toBe("sig-1");
expect((payload.raw_response_json as Record<string, unknown>).inline_citation_candidates).toEqual([]);
});
});
describe("monitor adapter registry", () => {
it("registers deepseek as a monitor adapter", () => {
expect(getMonitorAdapter("deepseek")?.provider).toBe("deepseek");
});
});