mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-19 07:59:39 +00:00
enhance: add comprehensive unit tests PR223
This commit is contained in:
parent
1917d599e6
commit
7e72e6b115
6 changed files with 80 additions and 44 deletions
|
|
@ -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="",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue