mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-04-28 19:50:34 +00:00
428 lines
11 KiB
Markdown
428 lines
11 KiB
Markdown
# Installation Flow Testing Environment
|
|
|
|
This comprehensive testing environment allows you to test all installation flows end-to-end with mocked `uv sync`, `uvicorn`, and Electron APIs. It simulates different system states and provides utilities to change the environment during tests.
|
|
|
|
## Overview
|
|
|
|
The testing environment consists of three main components:
|
|
|
|
1. **Electron API Mocks** (`test/mocks/electronMocks.ts`) - Mock Electron's preload APIs
|
|
1. **Environment State Mocks** (`test/mocks/environmentMocks.ts`) - Mock filesystem, processes, and system state
|
|
1. **Test Scenarios** - Predefined scenarios for different installation flows
|
|
|
|
## Quick Start
|
|
|
|
```typescript
|
|
import { setupElectronMocks, TestScenarios } from '../mocks/electronMocks';
|
|
import { setupMockEnvironment } from '../mocks/environmentMocks';
|
|
|
|
describe('My Installation Test', () => {
|
|
let electronAPI: MockedElectronAPI;
|
|
let mockEnv: ReturnType<typeof setupMockEnvironment>;
|
|
|
|
beforeEach(() => {
|
|
// Set up mocks
|
|
const { electronAPI: api } = setupElectronMocks();
|
|
electronAPI = api;
|
|
mockEnv = setupMockEnvironment();
|
|
});
|
|
|
|
it('should handle version update', async () => {
|
|
// Apply scenario
|
|
TestScenarios.versionUpdate(electronAPI);
|
|
|
|
// Your test code here
|
|
});
|
|
});
|
|
```
|
|
|
|
## Electron API Mocks
|
|
|
|
### Available Mock Methods
|
|
|
|
- `checkAndInstallDepsOnUpdate()` - Simulates dependency installation
|
|
- `getInstallationStatus()` - Returns current installation status
|
|
- `exportLog()` - Simulates log export functionality
|
|
- Event listeners for installation events
|
|
|
|
### Simulation Functions
|
|
|
|
```typescript
|
|
// Simulate installation events
|
|
electronAPI.simulateInstallationStart();
|
|
electronAPI.simulateInstallationLog('stdout', 'Installing packages...');
|
|
electronAPI.simulateInstallationComplete(true); // or false for failure
|
|
|
|
// Simulate system changes
|
|
electronAPI.simulateVersionChange('2.0.0');
|
|
electronAPI.simulateVenvRemoval();
|
|
electronAPI.simulateUvicornStartup();
|
|
```
|
|
|
|
### Mock State Control
|
|
|
|
```typescript
|
|
// Control the mock state directly
|
|
electronAPI.mockState.venvExists = false;
|
|
electronAPI.mockState.isInstalling = true;
|
|
electronAPI.mockState.toolInstalled = false;
|
|
```
|
|
|
|
## Environment State Mocks
|
|
|
|
### Filesystem Mock
|
|
|
|
Controls file system operations:
|
|
|
|
```typescript
|
|
// Control file existence
|
|
mockEnv.mockState.filesystem.venvExists = false;
|
|
mockEnv.mockState.filesystem.versionFileExists = true;
|
|
mockEnv.mockState.filesystem.installedLockExists = false;
|
|
|
|
// Control file contents
|
|
mockEnv.mockState.filesystem.versionFileContent = '0.9.0';
|
|
```
|
|
|
|
### Process Mock
|
|
|
|
Controls process spawning and execution:
|
|
|
|
```typescript
|
|
// Control tool availability
|
|
mockEnv.mockState.processes.uvAvailable = false;
|
|
mockEnv.mockState.processes.bunAvailable = true;
|
|
mockEnv.mockState.processes.uvicornRunning = false;
|
|
|
|
// Control network connectivity
|
|
mockEnv.mockState.network.canConnectToDefault = false;
|
|
mockEnv.mockState.network.canConnectToMirror = true;
|
|
```
|
|
|
|
## Predefined Test Scenarios
|
|
|
|
### Electron API Scenarios
|
|
|
|
Use `TestScenarios` from `electronMocks.ts`:
|
|
|
|
```typescript
|
|
// Fresh installation - no .venv, no version file
|
|
TestScenarios.freshInstall(electronAPI);
|
|
|
|
// Version update - version file exists but version changed
|
|
TestScenarios.versionUpdate(electronAPI);
|
|
|
|
// .venv removed - version file exists but .venv is missing
|
|
TestScenarios.venvRemoved(electronAPI);
|
|
|
|
// Installation in progress - when user opens app during installation
|
|
TestScenarios.installationInProgress(electronAPI);
|
|
|
|
// Installation error scenario
|
|
TestScenarios.installationError(electronAPI);
|
|
|
|
// Uvicorn startup with dependency installation
|
|
TestScenarios.uvicornDepsInstall(electronAPI);
|
|
|
|
// All good - no installation needed
|
|
TestScenarios.allGood(electronAPI);
|
|
```
|
|
|
|
### Environment Scenarios
|
|
|
|
Use `mockEnv.scenarios` from `environmentMocks.ts`:
|
|
|
|
```typescript
|
|
// Fresh installation
|
|
mockEnv.scenarios.freshInstall();
|
|
|
|
// Version update
|
|
mockEnv.scenarios.versionUpdate('0.9.0', '1.0.0');
|
|
|
|
// .venv removed
|
|
mockEnv.scenarios.venvRemoved();
|
|
|
|
// Network issues
|
|
mockEnv.scenarios.networkIssues();
|
|
|
|
// Complete failure
|
|
mockEnv.scenarios.completeFailure();
|
|
|
|
// Uvicorn startup installation
|
|
mockEnv.scenarios.uvicornStartupInstall();
|
|
|
|
// Installation in progress
|
|
mockEnv.scenarios.installationInProgress();
|
|
```
|
|
|
|
## Testing Different Installation States
|
|
|
|
### Installation Store States
|
|
|
|
Test all possible states from `installationStore.ts`:
|
|
|
|
- `'idle'` - Initial state
|
|
- `'checking-permissions'` - Checking system permissions
|
|
- `'showing-carousel'` - Showing onboarding carousel
|
|
- `'installing'` - Installation in progress
|
|
- `'error'` - Installation failed
|
|
- `'completed'` - Installation successful
|
|
|
|
```typescript
|
|
import { useInstallationStore } from '@/store/installationStore';
|
|
|
|
it('should transition through all states', () => {
|
|
const store = useInstallationStore.getState();
|
|
|
|
expect(store.state).toBe('idle');
|
|
|
|
store.startInstallation();
|
|
expect(store.state).toBe('installing');
|
|
|
|
store.setError('Installation failed');
|
|
expect(store.state).toBe('error');
|
|
|
|
store.retryInstallation();
|
|
expect(store.state).toBe('installing');
|
|
|
|
store.setSuccess();
|
|
expect(store.state).toBe('completed');
|
|
});
|
|
```
|
|
|
|
## Specific Test Cases
|
|
|
|
### 1. Testing .venv Removal
|
|
|
|
```typescript
|
|
it('should handle .venv removal', async () => {
|
|
// Simulate .venv being removed
|
|
TestScenarios.venvRemoved(electronAPI);
|
|
// or
|
|
mockEnv.scenarios.venvRemoved();
|
|
|
|
// Test your component/hook
|
|
const result = await electronAPI.checkAndInstallDepsOnUpdate();
|
|
expect(result.success).toBe(true);
|
|
});
|
|
```
|
|
|
|
### 2. Testing Version File Changes
|
|
|
|
```typescript
|
|
it('should handle version file changes', async () => {
|
|
// Simulate version change
|
|
TestScenarios.versionUpdate(electronAPI);
|
|
// or
|
|
mockEnv.scenarios.versionUpdate('0.9.0', '1.0.0');
|
|
|
|
// Your test assertions
|
|
});
|
|
```
|
|
|
|
### 3. Testing Uvicorn Startup Installation
|
|
|
|
```typescript
|
|
it('should handle uvicorn starting with dependency installation', async () => {
|
|
// Simulate uvicorn detecting missing dependencies
|
|
TestScenarios.uvicornDepsInstall(electronAPI);
|
|
|
|
// Trigger uvicorn startup
|
|
electronAPI.simulateUvicornStartup();
|
|
|
|
// Wait for installation events
|
|
await waitFor(() => {
|
|
expect(mockInstallationStore.startInstallation).toHaveBeenCalled();
|
|
});
|
|
});
|
|
```
|
|
|
|
### 4. Testing UI Installation States
|
|
|
|
```typescript
|
|
it('should show correct UI for each installation state', () => {
|
|
const { result } = renderHook(() => useInstallationStore());
|
|
|
|
// Test idle state
|
|
expect(result.current.state).toBe('idle');
|
|
expect(result.current.isVisible).toBe(false);
|
|
|
|
// Test installing state
|
|
act(() => result.current.startInstallation());
|
|
expect(result.current.state).toBe('installing');
|
|
expect(result.current.isVisible).toBe(true);
|
|
|
|
// Test error state
|
|
act(() => result.current.setError('Installation failed'));
|
|
expect(result.current.state).toBe('error');
|
|
expect(result.current.error).toBe('Installation failed');
|
|
|
|
// Test completed state
|
|
act(() => result.current.setSuccess());
|
|
expect(result.current.state).toBe('completed');
|
|
expect(result.current.progress).toBe(100);
|
|
});
|
|
```
|
|
|
|
## Advanced Testing Patterns
|
|
|
|
### Testing Event Sequences
|
|
|
|
```typescript
|
|
it('should handle complete installation flow', async () => {
|
|
const events: string[] = [];
|
|
|
|
// Set up event tracking
|
|
electronAPI.onInstallDependenciesStart(() => events.push('start'));
|
|
electronAPI.onInstallDependenciesLog(() => events.push('log'));
|
|
electronAPI.onInstallDependenciesComplete(() => events.push('complete'));
|
|
|
|
// Trigger installation
|
|
await electronAPI.checkAndInstallDepsOnUpdate();
|
|
|
|
// Verify event sequence
|
|
expect(events).toEqual(['start', 'log', 'log', 'complete']);
|
|
});
|
|
```
|
|
|
|
### Testing Error Recovery
|
|
|
|
```typescript
|
|
it('should recover from installation errors', async () => {
|
|
// Set up error scenario
|
|
TestScenarios.installationError(electronAPI);
|
|
|
|
const store = useInstallationStore.getState();
|
|
|
|
// Trigger installation
|
|
await store.performInstallation();
|
|
expect(store.state).toBe('error');
|
|
|
|
// Simulate retry
|
|
TestScenarios.allGood(electronAPI); // Fix the environment
|
|
store.retryInstallation();
|
|
|
|
await waitFor(() => {
|
|
expect(store.state).toBe('completed');
|
|
});
|
|
});
|
|
```
|
|
|
|
### Testing Concurrent Operations
|
|
|
|
```typescript
|
|
it('should handle concurrent installation attempts', async () => {
|
|
const store = useInstallationStore.getState();
|
|
|
|
// Start multiple installations
|
|
const promise1 = store.performInstallation();
|
|
const promise2 = store.performInstallation();
|
|
|
|
// Should handle gracefully
|
|
const [result1, result2] = await Promise.all([promise1, promise2]);
|
|
|
|
expect(store.state).toBe('completed');
|
|
});
|
|
```
|
|
|
|
## Debugging Tests
|
|
|
|
### Logging Mock State
|
|
|
|
```typescript
|
|
// Log current mock state
|
|
console.log('Electron API State:', electronAPI.mockState);
|
|
console.log('Environment State:', mockEnv.mockState);
|
|
|
|
// Check what functions were called
|
|
console.log(
|
|
'checkAndInstallDepsOnUpdate calls:',
|
|
electronAPI.checkAndInstallDepsOnUpdate.mock.calls
|
|
);
|
|
```
|
|
|
|
### Waiting for Async Operations
|
|
|
|
```typescript
|
|
import { waitForStateChange } from '../mocks/environmentMocks';
|
|
|
|
// Wait for specific state changes
|
|
await waitForStateChange(
|
|
() => mockEnv.mockState.processes.uvSyncInProgress,
|
|
true,
|
|
1000 // timeout
|
|
);
|
|
```
|
|
|
|
## Running the Tests
|
|
|
|
```bash
|
|
# Run all installation tests
|
|
npm test test/unit/store/installationStore.test.ts
|
|
npm test test/unit/hooks/useInstallationSetup.test.ts
|
|
npm test test/unit/electron/install-deps.test.ts
|
|
|
|
# Run with coverage
|
|
npm test -- --coverage
|
|
|
|
# Run in watch mode
|
|
npm test -- --watch
|
|
```
|
|
|
|
## Common Issues and Solutions
|
|
|
|
### 1. Mock Not Applied
|
|
|
|
**Problem**: Mock functions not being called
|
|
**Solution**: Ensure mocks are set up before importing modules
|
|
|
|
```typescript
|
|
beforeEach(async () => {
|
|
setupMocks(); // Set up first
|
|
const module = await import('./module'); // Import after
|
|
});
|
|
```
|
|
|
|
### 2. State Not Updating
|
|
|
|
**Problem**: Mock state changes not reflected
|
|
**Solution**: Use simulation functions instead of direct state mutation
|
|
|
|
```typescript
|
|
// Don't do this
|
|
electronAPI.mockState.isInstalling = true;
|
|
|
|
// Do this instead
|
|
electronAPI.simulateInstallationStart();
|
|
```
|
|
|
|
### 3. Async Operations Not Completing
|
|
|
|
**Problem**: Tests timeout waiting for async operations
|
|
**Solution**: Use proper wait functions and increase timeouts
|
|
|
|
```typescript
|
|
await vi.waitFor(
|
|
() => {
|
|
expect(condition).toBe(true);
|
|
},
|
|
{ timeout: 2000 }
|
|
);
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Reset State**: Always reset mock state between tests
|
|
1. **Use Scenarios**: Prefer predefined scenarios over manual state setup
|
|
1. **Test Edge Cases**: Include error conditions and edge cases
|
|
1. **Verify Events**: Check that the correct events are emitted
|
|
1. **Test Cleanup**: Verify that resources are properly cleaned up
|
|
1. **Integration Tests**: Test the complete flow, not just individual functions
|
|
|
|
## Example Test Files
|
|
|
|
- `test/unit/store/installationStore.test.ts` - Store state management
|
|
- `test/unit/hooks/useInstallationSetup.test.ts` - Hook behavior
|
|
- `test/unit/electron/install-deps.test.ts` - Backend installation logic
|
|
|
|
These test files demonstrate all the patterns and scenarios described in this README.
|