Ctrl K
ComunidadGuías…

test/README.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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.