enhance: add comprehensive unit tests PR223

This commit is contained in:
Wendong-Fan 2025-09-06 06:17:32 +08:00
parent 1917d599e6
commit 7e72e6b115
6 changed files with 80 additions and 44 deletions

View file

@ -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="",
)

View file

@ -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

View file

@ -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,

View file

@ -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', () => ({
<div data-testid="bottom-input">
<input
data-testid="message-input"
placeholder="Type your message..."
value={message}
onChange={(e) => 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()
})
})

View file

@ -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()
})
})

View file

@ -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";