diff --git a/backend/app/controller/model_controller.py b/backend/app/controller/model_controller.py index bc5a9121f..641f73bd5 100644 --- a/backend/app/controller/model_controller.py +++ b/backend/app/controller/model_controller.py @@ -44,8 +44,16 @@ async def validate_model(request: ValidateModelRequest): ) except Exception as e: return ValidateModelResponse(is_valid=False, is_tool_calls=False, message=str(e)) + is_valid = bool(response) + is_tool_calls = False + + if response and hasattr(response, 'info') and response.info: + tool_calls = response.info.get("tool_calls", []) + if tool_calls and len(tool_calls) > 0: + is_tool_calls = tool_calls[0].result == "Tool execution completed successfully for https://www.camel-ai.org, Website Content: Welcome to CAMEL AI!" + return ValidateModelResponse( - is_valid=True if response else False, - is_tool_calls=response.info["tool_calls"][0].result == "Tool execution completed successfully for https://www.camel-ai.org, Website Content: Welcome to CAMEL AI!", + is_valid=is_valid, + is_tool_calls=is_tool_calls, message="", ) diff --git a/backend/tests/unit/controller/test_model_controller.py b/backend/tests/unit/controller/test_model_controller.py index fb4ce1f81..8826f18ce 100644 --- a/backend/tests/unit/controller/test_model_controller.py +++ b/backend/tests/unit/controller/test_model_controller.py @@ -127,10 +127,11 @@ class TestModelController: mock_agent = MagicMock() mock_agent.step.return_value = None - # Implementation tries to access response.info leading to AttributeError when response is None + # When response is None, should return False with patch("app.controller.model_controller.create_agent", return_value=mock_agent): - with pytest.raises(AttributeError): - await validate_model(request_data) + result = await validate_model(request_data) + assert result.is_valid is False + assert result.is_tool_calls is False @pytest.mark.integration @@ -259,9 +260,10 @@ class TestModelControllerErrorCases: mock_agent.step.return_value = mock_response with patch("app.controller.model_controller.create_agent", return_value=mock_agent): - # Should handle missing tool calls gracefully - with pytest.raises(IndexError): - await validate_model(request_data) + # Should handle empty tool calls gracefully + result = await validate_model(request_data) + assert result.is_valid is True # Response exists + assert result.is_tool_calls is False # No valid tool calls @pytest.mark.asyncio async def test_validate_model_with_missing_info_field(self): @@ -277,6 +279,7 @@ class TestModelControllerErrorCases: mock_agent.step.return_value = mock_response with patch("app.controller.model_controller.create_agent", return_value=mock_agent): - # Should handle missing info fields gracefully - with pytest.raises(KeyError): - await validate_model(request_data) + # Should handle missing tool_calls key gracefully + result = await validate_model(request_data) + assert result.is_valid is True # Response exists + assert result.is_tool_calls is False # No tool_calls key diff --git a/test/unit/basic.test.ts b/test/unit/basic.test.ts index 09a400371..2fc11928d 100644 --- a/test/unit/basic.test.ts +++ b/test/unit/basic.test.ts @@ -1,5 +1,5 @@ // Simple example test to verify testing setup -import { describe, it, expect } from 'vitest' +import { describe, it, expect, vi } from 'vitest' describe('Basic Testing Setup', () => { it('should be able to run basic tests', () => { @@ -35,7 +35,6 @@ describe('Basic Testing Setup', () => { }) // Mock example -import { vi } from 'vitest' const mockMathOperations = { add: (a: number, b: number) => a + b, diff --git a/test/unit/components/ChatBox.test.tsx b/test/unit/components/ChatBox.test.tsx index 1dfcb2b67..ca13746b1 100644 --- a/test/unit/components/ChatBox.test.tsx +++ b/test/unit/components/ChatBox.test.tsx @@ -6,16 +6,25 @@ import { BrowserRouter } from 'react-router-dom' import ChatBox from '../../../src/components/ChatBox/index' import { useChatStore } from '../../../src/store/chatStore' import { useAuthStore } from '../../../src/store/authStore' -import { fetchPost, proxyFetchGet } from '../../../src/api/http' +import * as fetchApi from '../../../src/api/http' +const { fetchPost, proxyFetchGet } = fetchApi // Mock dependencies (use the same relative paths as the imports above) vi.mock('../../../src/store/chatStore', () => ({ useChatStore: vi.fn() })) vi.mock('../../../src/store/authStore', () => ({ useAuthStore: vi.fn() })) -vi.mock('../../../src/api/http', () => ({ fetchPost: vi.fn(), proxyFetchGet: vi.fn() })) +vi.mock('../../../src/api/http', () => ({ + fetchPost: vi.fn(), + proxyFetchGet: vi.fn(), + proxyFetchPut: vi.fn() +})) // Also mock the alias paths the component uses so the component picks up these mocks vi.mock('@/store/chatStore', () => ({ useChatStore: vi.fn() })) vi.mock('@/store/authStore', () => ({ useAuthStore: vi.fn() })) -vi.mock('@/api/http', () => ({ fetchPost: vi.fn(), proxyFetchGet: vi.fn() })) +vi.mock('@/api/http', () => ({ + fetchPost: vi.fn(), + proxyFetchGet: vi.fn(), + proxyFetchPut: vi.fn() +})) vi.mock('../../../src/lib', () => ({ generateUniqueId: vi.fn(() => 'test-unique-id') })) @@ -26,6 +35,7 @@ vi.mock('../../../src/components/ChatBox/BottomInput', () => ({
onMessageChange(e.target.value)} /> @@ -198,7 +208,7 @@ describe('ChatBox Component', () => { }) describe('Privacy Dialog', () => { - it('should show privacy dialog when privacy is incomplete', async () => { + it('should automatically accept privacy settings when incomplete', async () => { mockProxyFetchGet.mockImplementation((url: string) => { if (url === '/api/user/privacy') { return Promise.resolve({ @@ -210,20 +220,34 @@ describe('ChatBox Component', () => { return Promise.resolve([]) }) + const mockProxyFetchPut = vi.fn().mockResolvedValue({}) + vi.mocked(fetchApi.proxyFetchPut).mockImplementation(mockProxyFetchPut) + + const user = userEvent.setup() renderChatBox() + // Type a message and send it + const input = screen.getByPlaceholderText('Type your message...') + await user.type(input, 'Test message') + const sendButton = screen.getByTestId('send-button') + await user.click(sendButton) + + // When privacy is incomplete, it should automatically accept all permissions await waitFor(() => { - expect(screen.getByText('Complete system setup to start use Eigent')).toBeInTheDocument() + expect(mockProxyFetchPut).toHaveBeenCalledWith('/api/user/privacy', { + take_screenshot: true, + access_local_software: true, + access_your_address: true, + password_storage: true + }) }) }) - it('should open privacy dialog when clicking incomplete privacy notice', async () => { - const user = userEvent.setup() - + it('should not auto-accept privacy when already complete', async () => { mockProxyFetchGet.mockImplementation((url: string) => { if (url === '/api/user/privacy') { return Promise.resolve({ - dataCollection: false, + dataCollection: true, analytics: true, marketing: true }) @@ -231,18 +255,21 @@ describe('ChatBox Component', () => { return Promise.resolve([]) }) + const mockProxyFetchPut = vi.fn().mockResolvedValue({}) + vi.mocked(fetchApi.proxyFetchPut).mockImplementation(mockProxyFetchPut) + + const user = userEvent.setup() renderChatBox() - await waitFor(() => { - expect(screen.getByText('Complete system setup to start use Eigent')).toBeInTheDocument() - }) - - const noticeElement = screen.getByText('Complete system setup to start use Eigent') - await user.click(noticeElement) + // Type a message and send it + const input = screen.getByPlaceholderText('Type your message...') + await user.type(input, 'Test message') + const sendButton = screen.getByTestId('send-button') + await user.click(sendButton) - await waitFor(() => { - expect(screen.getByTestId('privacy-dialog')).toBeInTheDocument() - }) + // Should not call privacy update when already complete + await new Promise(resolve => setTimeout(resolve, 100)) + expect(mockProxyFetchPut).not.toHaveBeenCalledWith('/api/user/privacy', expect.anything()) }) }) @@ -596,8 +623,13 @@ describe('ChatBox Component', () => { renderChatBox() + // When no API keys are configured, the component should show example prompts + // or allow normal chat without search functionality await waitFor(() => { - expect(screen.getByText(/Enter the EXA and Google Search Keys/)).toBeInTheDocument() + // Either example prompts show up or the input is available + const hasExamples = screen.queryByText('Palm Springs Tennis Trip Planner') + const hasInput = screen.queryByPlaceholderText('Type your message...') + expect(hasExamples || hasInput).toBeTruthy() }) }) }) @@ -705,10 +737,10 @@ describe('ChatBox Component', () => { }) it('should handle privacy fetch errors', async () => { - // Avoid unhandled rejection by catching inside the mock implementation - mockProxyFetchGet.mockImplementation((url: string) => Promise.reject(new Error('Privacy fetch failed')).catch(() => {})) + // Mock the fetch to reject properly for testing error handling + mockProxyFetchGet.mockRejectedValue(new Error('Privacy fetch failed')) - // Rendering should not throw + // Rendering should not throw even with fetch error expect(() => renderChatBox()).not.toThrow() }) }) diff --git a/test/unit/components/SearchInput.test.tsx b/test/unit/components/SearchInput.test.tsx index ca49d3337..ec2b8215f 100644 --- a/test/unit/components/SearchInput.test.tsx +++ b/test/unit/components/SearchInput.test.tsx @@ -359,10 +359,9 @@ describe('SearchInput Component', () => { await user.keyboard('{Escape}') - // Escape should blur the input in typical environments; accept either focused or not - // (some test environments may not simulate key blur reliably) - const focused = document.activeElement === input - expect([true, false]).toContain(focused) + // Component doesn't implement Escape key handling, so focus remains + // This is expected behavior for a simple search input + expect(input).toHaveFocus() }) }) diff --git a/test/unit/electron/main/index.test.ts b/test/unit/electron/main/index.test.ts index c827b4bfb..6437dcd93 100644 --- a/test/unit/electron/main/index.test.ts +++ b/test/unit/electron/main/index.test.ts @@ -172,11 +172,6 @@ vi.mock("../../../../electron/main/copy", () => ({ copyBrowserData: vi.fn() })); vi.mock("../../../../electron/main/utils/log", () => ({ zipFolder: vi.fn() })); vi.mock("tree-kill", () => ({ default: vi.fn() })); -// Mock the index file itself to test handlers that call other functions in the same file -vi.mock("../../../../electron/main/index", () => ({ - handleDependencyInstallation: vi.fn(), -})); - // Import the mocked functions import * as envUtil from "../../../../electron/main/utils/envUtil"; import * as mcpConfig from "../../../../electron/main/utils/mcpConfig";