fix(ui): show search tool errors instead of empty results (#48478)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
moe-berri 2026-10-26 15:42:17 -07:00 • committed by GitHub
parent 7045139c3a
commit 4da24fa01e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 5 deletions

View file

@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { SearchToolTester } from "./SearchToolTester";
@ -271,7 +271,15 @@ describe("SearchToolTester", () => {
});
it("should handle search errors and show notification", async () => {
const error = new Error("Search failed");
const error = new Error(
JSON.stringify({
error: {
message: "User not allowed to access search tool: exa-search-2",
type: "user_search_tool_access_denied",
code: "403",
},
}),
);
vi.mocked(networking.searchToolQueryCall).mockRejectedValue(error);
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const user = userEvent.setup();
@ -281,11 +289,29 @@ describe("SearchToolTester", () => {
const searchButton = screen.getByRole("button", { name: /search/i });
await user.click(searchButton);
await waitFor(() => {
expect(toast.fromError).toHaveBeenCalledWith("Failed to query search tool");
expect(toast.fromError).toHaveBeenCalledWith(error);
});
consoleSpy.mockRestore();
});
it("should not show empty results or add history when search fails", async () => {
vi.mocked(networking.searchToolQueryCall).mockRejectedValue(new Error("Search failed"));
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const user = userEvent.setup();
render(<SearchToolTester {...defaultProps} />);
const input = screen.getByPlaceholderText("Enter your search query...");
fireEvent.change(input, { target: { value: "test query" } });
await user.click(screen.getByRole("button", { name: /search/i }));
await waitFor(() => {
expect(toast.fromError).toHaveBeenCalled();
});
expect(screen.queryByText("No results found")).not.toBeInTheDocument();
expect(screen.queryByText("test query")).not.toBeInTheDocument();
expect(screen.getByText("Test your search tool")).toBeInTheDocument();
consoleSpy.mockRestore();
});
it("should maintain search history", async () => {
const user = userEvent.setup();
render(<SearchToolTester {...defaultProps} />);

View file

@ -60,7 +60,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
setSearchHistory((prev) => [historyEntry, ...prev]);
} catch (error) {
console.error("Error querying search tool:", error);
toast.fromError("Failed to query search tool");
toast.fromError(error);
} finally {
setIsLoading(false);
}

View file

@ -1178,3 +1178,29 @@ describe("getUiConfig runtime backend discovery", () => {
});
});
});
describe("searchToolQueryCall", () => {
const originalFetch = global.fetch;
afterEach(() => {
global.fetch = originalFetch;
});
it("rejects with the backend error message for a denied search", async () => {
const errorMessage = "User not allowed to access search tool: exa-search-2";
global.fetch = vi.fn().mockResolvedValue(
new Response(
JSON.stringify({
error: {
message: errorMessage,
type: "user_search_tool_access_denied",
code: "403",
},
}),
{ status: 403 },
),
);
await expect(Networking.searchToolQueryCall("token", "exa-search-2", "litellm")).rejects.toThrow(errorMessage);
});
});

View file

@ -6860,7 +6860,7 @@ export const searchToolQueryCall = async (
if (!response.ok) {
const errorData = await response.text();
await handleError(errorData);
return null;
throw new Error(errorData);
}
const data = await response.json();