diff --git a/frontend/__tests__/hooks/query/use-feedback-exists.test.tsx b/frontend/__tests__/hooks/query/use-feedback-exists.test.tsx new file mode 100644 index 0000000000..0935e0658c --- /dev/null +++ b/frontend/__tests__/hooks/query/use-feedback-exists.test.tsx @@ -0,0 +1,140 @@ +import { renderHook, waitFor } from "@testing-library/react"; +import { describe, expect, it, vi, beforeEach } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import OpenHands from "#/api/open-hands"; +import { useFeedbackExists } from "#/hooks/query/use-feedback-exists"; + +// Mock the useConfig hook +vi.mock("#/hooks/query/use-config", () => ({ + useConfig: vi.fn(), +})); + +// Mock the useConversationId hook +vi.mock("#/hooks/use-conversation-id", () => ({ + useConversationId: () => ({ conversationId: "test-conversation-id" }), +})); + +describe("useFeedbackExists", () => { + let queryClient: QueryClient; + const mockCheckFeedbackExists = vi.spyOn(OpenHands, "checkFeedbackExists"); + + beforeEach(() => { + queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + mockCheckFeedbackExists.mockClear(); + }); + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + it("should not call API when APP_MODE is not saas", async () => { + const { useConfig } = await import("#/hooks/query/use-config"); + vi.mocked(useConfig).mockReturnValue({ + data: { APP_MODE: "oss" }, + isLoading: false, + error: null, + } as ReturnType); + + const { result } = renderHook(() => useFeedbackExists(123), { + wrapper, + }); + + // Wait for any potential async operations + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + // Verify that the API was not called + expect(mockCheckFeedbackExists).not.toHaveBeenCalled(); + + // Verify that the query is disabled + expect(result.current.data).toBeUndefined(); + }); + + it("should call API when APP_MODE is saas", async () => { + const { useConfig } = await import("#/hooks/query/use-config"); + vi.mocked(useConfig).mockReturnValue({ + data: { APP_MODE: "saas" }, + isLoading: false, + error: null, + } as ReturnType); + + mockCheckFeedbackExists.mockResolvedValue({ + exists: true, + rating: 5, + reason: "Great job!", + }); + + const { result } = renderHook(() => useFeedbackExists(123), { + wrapper, + }); + + // Wait for the query to complete + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + // Verify that the API was called + expect(mockCheckFeedbackExists).toHaveBeenCalledWith( + "test-conversation-id", + 123, + ); + + // Verify that the data is returned + expect(result.current.data).toEqual({ + exists: true, + rating: 5, + reason: "Great job!", + }); + }); + + it("should not call API when eventId is not provided", async () => { + const { useConfig } = await import("#/hooks/query/use-config"); + vi.mocked(useConfig).mockReturnValue({ + data: { APP_MODE: "saas" }, + isLoading: false, + error: null, + } as ReturnType); + + const { result } = renderHook(() => useFeedbackExists(undefined), { + wrapper, + }); + + // Wait for any potential async operations + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + // Verify that the API was not called + expect(mockCheckFeedbackExists).not.toHaveBeenCalled(); + + // Verify that the query is disabled + expect(result.current.data).toBeUndefined(); + }); + + it("should not call API when config is not loaded yet", async () => { + const { useConfig } = await import("#/hooks/query/use-config"); + vi.mocked(useConfig).mockReturnValue({ + data: undefined, + isLoading: true, + error: null, + } as ReturnType); + + const { result } = renderHook(() => useFeedbackExists(123), { + wrapper, + }); + + // Wait for any potential async operations + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + // Verify that the API was not called + expect(mockCheckFeedbackExists).not.toHaveBeenCalled(); + + // Verify that the query is disabled + expect(result.current.data).toBeUndefined(); + }); +}); diff --git a/frontend/src/hooks/query/use-feedback-exists.ts b/frontend/src/hooks/query/use-feedback-exists.ts index 804417e433..281dbb9d07 100644 --- a/frontend/src/hooks/query/use-feedback-exists.ts +++ b/frontend/src/hooks/query/use-feedback-exists.ts @@ -1,6 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import OpenHands from "#/api/open-hands"; import { useConversationId } from "#/hooks/use-conversation-id"; +import { useConfig } from "#/hooks/query/use-config"; export interface FeedbackData { exists: boolean; @@ -10,6 +11,7 @@ export interface FeedbackData { export const useFeedbackExists = (eventId?: number) => { const { conversationId } = useConversationId(); + const { data: config } = useConfig(); return useQuery({ queryKey: ["feedback", "exists", conversationId, eventId], @@ -17,7 +19,7 @@ export const useFeedbackExists = (eventId?: number) => { if (!eventId) return { exists: false }; return OpenHands.checkFeedbackExists(conversationId, eventId); }, - enabled: !!eventId, + enabled: !!eventId && config?.APP_MODE === "saas", staleTime: 1000 * 60 * 5, // 5 minutes gcTime: 1000 * 60 * 15, // 15 minutes });