MiniSearch / client /modules /chatHelpers.test.ts
github-actions[bot]
Sync from https://github.com/felladrin/MiniSearch
abf1288
Raw
History Blame Contribute Delete
6.1 kB
import { beforeEach, describe, expect, it, vi } from "vitest";
beforeEach(() => vi.clearAllMocks());
vi.mock("./search", () => ({
searchText: vi.fn(),
searchImages: vi.fn(),
}));
vi.mock("./history", () => ({
getCurrentSearchRunId: vi.fn(() => "run-1"),
saveChatMessageForQuery: vi.fn(() => Promise.resolve()),
updateSearchResults: vi.fn(() => Promise.resolve()),
}));
vi.mock("./pubSub", () => ({
updateImageSearchResults: vi.fn(),
updateLlmTextSearchResults: vi.fn(),
updateTextSearchResults: vi.fn(),
updateTextSearchStale: vi.fn(),
}));
vi.mock("./relatedSearchQuery", () => ({
generateRelatedSearchQuery: vi.fn(() => Promise.resolve("")),
}));
vi.mock("./logEntries", () => ({
addLogEntry: vi.fn(),
}));
vi.mock("./textGenerationUtilities", () => ({
searchResultsToConsider: 6,
}));
import {
persistChatMessages,
refreshImageSearchResults,
refreshTextSearchResults,
runFollowUpSearch,
} from "./chatHelpers";
import { saveChatMessageForQuery } from "./history";
import {
updateImageSearchResults,
updateLlmTextSearchResults,
updateTextSearchResults,
updateTextSearchStale,
} from "./pubSub";
import { searchImages, searchText } from "./search";
import type { ImageSearchResults, TextSearchResults } from "./types";
const mockSearchText = vi.mocked(searchText);
const mockSearchImages = vi.mocked(searchImages);
const mockSaveChat = vi.mocked(saveChatMessageForQuery);
const mockUpdateText = vi.mocked(updateTextSearchResults);
const mockUpdateImage = vi.mocked(updateImageSearchResults);
const mockUpdateLlmText = vi.mocked(updateLlmTextSearchResults);
const mockUpdateTextStale = vi.mocked(updateTextSearchStale);
describe("refreshTextSearchResults", () => {
it("deduplicates by URL and appends fresh results", async () => {
const existing: TextSearchResults = [
["Existing", "snippet", "https://existing.com"],
];
const fresh: TextSearchResults = [
["New", "snippet", "https://new.com"],
["Duplicate", "snippet", "https://existing.com"],
];
mockSearchText.mockResolvedValueOnce({ results: fresh, stale: false });
await refreshTextSearchResults("query", 10, existing);
expect(mockUpdateText).toHaveBeenCalledWith([
["Existing", "snippet", "https://existing.com"],
["New", "snippet", "https://new.com"],
]);
expect(mockUpdateTextStale).toHaveBeenCalledWith(false);
});
it("skips updates when all fresh results are duplicates", async () => {
const existing: TextSearchResults = [
["Title", "snippet", "https://dup.com"],
];
const fresh: TextSearchResults = [["Title", "snippet", "https://dup.com"]];
mockSearchText.mockResolvedValueOnce({ results: fresh, stale: false });
await refreshTextSearchResults("query", 10, existing);
expect(mockUpdateText).not.toHaveBeenCalled();
});
it("skips updates when there are no fresh results", async () => {
mockSearchText.mockResolvedValueOnce({ results: [], stale: false });
await refreshTextSearchResults("query", 10, []);
expect(mockUpdateText).not.toHaveBeenCalled();
expect(mockUpdateLlmText).not.toHaveBeenCalled();
expect(mockUpdateTextStale).not.toHaveBeenCalled();
});
it("raises the stale banner when the fresh batch was served from the cache", async () => {
mockSearchText.mockResolvedValueOnce({
results: [["New", "snippet", "https://new.com"]],
stale: true,
});
await refreshTextSearchResults("query", 10, []);
expect(mockUpdateTextStale).toHaveBeenCalledWith(true);
});
});
describe("refreshImageSearchResults", () => {
it("deduplicates by URL and prepends fresh results", async () => {
const existing: ImageSearchResults = [
["Existing", "https://existing.com", "thumb", "source"],
];
const fresh: ImageSearchResults = [
["New", "https://new.com", "thumb", "source"],
["Duplicate", "https://existing.com", "thumb", "source"],
];
mockSearchImages.mockResolvedValueOnce({ results: fresh, stale: false });
await refreshImageSearchResults("query", 10, existing);
expect(mockUpdateImage).toHaveBeenCalledWith([
["New", "https://new.com", "thumb", "source"],
["Existing", "https://existing.com", "thumb", "source"],
]);
});
it("skips updates when there are no fresh results", async () => {
mockSearchImages.mockResolvedValueOnce({ results: [], stale: false });
await refreshImageSearchResults("query", 10, []);
expect(mockUpdateImage).not.toHaveBeenCalled();
});
});
describe("persistChatMessages", () => {
it("saves user and assistant messages", async () => {
await persistChatMessages("my query", "user msg", "assistant msg");
expect(mockSaveChat).toHaveBeenCalledWith("my query", "user", "user msg");
expect(mockSaveChat).toHaveBeenCalledWith(
"my query",
"assistant",
"assistant msg",
);
});
});
describe("runFollowUpSearch", () => {
const makeSettings = (overrides = {}) =>
({
enableTextSearch: true,
enableImageSearch: true,
searchResultsLimit: 10,
...overrides,
}) as unknown as Parameters<typeof runFollowUpSearch>[2];
it("refreshes text results when enabled", async () => {
mockSearchText.mockResolvedValueOnce({
results: [["Title", "snippet", "https://new.com"]],
stale: false,
});
await runFollowUpSearch(
[{ role: "user", content: "hello" }],
"hello",
makeSettings(),
[],
[],
);
expect(mockSearchText).toHaveBeenCalled();
});
it("skips text search when disabled", async () => {
await runFollowUpSearch(
[{ role: "user", content: "hello" }],
"hello",
makeSettings({ enableTextSearch: false }),
[],
[],
);
expect(mockSearchText).not.toHaveBeenCalled();
});
it("skips image search when disabled", async () => {
await runFollowUpSearch(
[{ role: "user", content: "hello" }],
"hello",
makeSettings({ enableImageSearch: false }),
[],
[],
);
expect(mockSearchImages).not.toHaveBeenCalled();
});
});