Testing
clauderon uses a comprehensive testing strategy across all components: Rust backend/TUI, Web, and Mobile.
Quick Start
Run all tests by component:
# Rust (Backend + TUI/CLI)cargo nextest run # All tests (faster than cargo test)cargo nextest run --run-ignored all # Include E2E testsmise run test-fast # via mise
# Web (TypeScript/React)cd web && bun run test # All web tests
# Mobile (React Native)cd mobile && bun test # Jest tests
# Full build + test (CI simulation)dagger call ciRust (Backend + TUI/CLI)
Test Organization
44 test files across the codebase:
tests/directory: Integration and E2E tests (24 files)src/directory: Unit tests with#[cfg(test)](colocated with implementation)
Commands
cargo nextest run # Run all testscargo nextest run --run-ignored all # Include E2E testscargo nextest run -E 'test(/pattern/)' # Filter by patternmise run test-fast # Run via miseIf nextest is not installed: mise run setup-tools
Frameworks and Dependencies
- tokio-test: Async testing runtime
- proptest: Property-based testing
- tempfile: Temporary file/directory creation
- assert_cmd: CLI testing
- predicates: Assertion helpers
Test Patterns
Unit Tests
Colocated with implementation using #[cfg(test)]:
#[cfg(test)]mod tests { use super::*;
#[test] fn test_function() { // Test logic }}Example: src/utils/random.rs:46-76
Integration Tests
Located in tests/ directory with #[tokio::test]:
Example: tests/api_tests.rs
Conditional E2E Tests
Tests marked with #[ignore] skip when dependencies (Docker, Kubernetes) are unavailable:
Example: tests/e2e_docker.rs:44-77
#[tokio::test]#[ignore]async fn test_docker_session() { skip_if_no_docker!(); // Test logic}Mock Implementations
Mock backends for testing without external dependencies:
MockGitBackend: Simulates git operationsMockApiClient: Simulates API calls
Example: src/backends/mock.rs
Test Helpers
Located in tests/common/mod.rs:
Availability Checks
Functions to check if required dependencies are available:
pub fn docker_available() -> boolpub fn kubernetes_available() -> boolSkip Macros
Gracefully skip tests when dependencies are unavailable:
skip_if_no_docker!()skip_if_no_kubernetes!()Git Repository Initialization
pub fn init_git_repo(path: &Path)RAII Cleanup Guards
Automatic cleanup using Drop trait:
pub struct SpriteCleanupGuardReference: tests/common/mod.rs
Web (TypeScript/React)
Test Organization
9 test files across web packages:
web/frontend/src/: React component and utility testsweb/client/src/: Client library tests
Commands
cd web && bun run test # All web testscd web/frontend && bun test # Frontend onlycd web/client && bun test # Client onlyFramework
Uses Bun’s native bun:test runner:
- Fast execution
- Built-in TypeScript support
- Native mocking capabilities
Test Patterns
Describe/Test Blocks
import { describe, test, expect } from "bun:test";
describe("Component", () => { test("should render correctly", () => { // Test logic });});Mock Implementations
Browser API Mocks
Example: web/frontend/src/components/ThemeToggle.test.tsx:7-24
Mocking localStorage and matchMedia:
const mockLocalStorage = { getItem: () => null, setItem: () => {},};WebSocket Mocking
Example: web/client/src/ConsoleClient.test.ts:12-50
Controlled WebSocket behavior for testing:
class MockWebSocket { readyState = WebSocket.CONNECTING; onopen?: () => void; onerror?: () => void; // ...}Async Testing with Controlled Timing
Example: web/client/src/ConsoleClient.test.ts:90-123
Using await new Promise(resolve => setTimeout(resolve, 0)) for async state updates.
Factory Functions
Example: web/frontend/src/lib/claudeParser.test.ts
Creating test data with factory functions for consistency.
Mobile (React Native)
Test Organization
1 test file currently:
mobile/src/lib/historyParser.test.ts
Infrastructure ready for component tests (react-test-renderer installed).
Commands
cd mobile && bun test # Jest testscd mobile && bun test:windows # Windows-specificFramework
Jest with @rnx-kit/jest-preset:
- React Native preset configuration
- TypeScript transformation
- Module name mapping
Example: mobile/src/lib/historyParser.test.ts
CI/CD Integration
Dagger Pipeline
Located in /.dagger/src/index.ts (see clauderonCi function).
Pipeline Steps:
- Build docs package (
cd docs && bun run build) - Build web package (
cd web && bun run build) - Build Rust binary (
cargo build) - Run clippy (
cargo clippy) - Run tests (
cargo nextest run) - Create release artifacts
Caching
Optimized caching for faster builds:
- Cargo registry
- Git dependencies
- Target directory
- sccache (compiler cache)
Local Execution
dagger call ciTest Types
Unit Tests
- Purpose: Test individual functions/modules in isolation
- Location:
- Rust: In-module with
#[cfg(test)] - Web/Mobile: Colocated
.test.ts(x)files
- Rust: In-module with
- Characteristics: Fast, no external dependencies
Integration Tests
- Purpose: Test component interactions
- Location:
- Rust:
tests/directory - Web/Mobile: Colocated with source files
- Rust:
- Characteristics: May use test helpers, mock dependencies
E2E Tests
- Purpose: Test complete workflows with real dependencies
- Location: Rust
tests/directory - Characteristics:
- Marked with
#[ignore]attribute - Conditional execution based on environment
- Require Docker, Kubernetes, or other services
- Marked with
Common Patterns
Conditional Test Execution (Rust)
Check availability before running tests requiring external dependencies:
#[tokio::test]#[ignore]async fn test_requires_docker() { skip_if_no_docker!(); // Test logic that requires Docker}Reference: tests/common/mod.rs for availability checks and skip macros.
Mock Patterns (Web)
Browser API Mocking
Reference: web/frontend/src/components/ThemeToggle.test.tsx:7-24
Mock localStorage, matchMedia, and other browser APIs:
const mockLocalStorage = { getItem: vi.fn(), setItem: vi.fn(),};
Object.defineProperty(window, 'localStorage', { value: mockLocalStorage,});WebSocket Mocking
Reference: web/client/src/ConsoleClient.test.ts:12-50
Create controlled WebSocket instances for testing connection states:
class MockWebSocket { readyState = WebSocket.CONNECTING;
simulateOpen() { this.readyState = WebSocket.OPEN; this.onopen?.(); }
simulateError() { this.onerror?.(new Event('error')); }}Cleanup and Resource Management
RAII Pattern (Rust)
Use Drop trait for automatic cleanup:
pub struct TestCleanup { path: PathBuf,}
impl Drop for TestCleanup { fn drop(&mut self) { // Cleanup logic }}Example: SpriteCleanupGuard in tests/common/mod.rs
Troubleshooting
nextest not found
Install test tools:
mise run setup-toolsTests fail in CI but pass locally
Check Docker/Kubernetes availability:
docker ps # Check Dockerkubectl cluster-info # Check KubernetesE2E tests are designed to skip gracefully when dependencies are unavailable.
E2E tests always skip
This is expected behavior when:
- Docker is not running
- Kubernetes cluster is not configured
- Required credentials are not available
To run E2E tests:
cargo nextest run --run-ignored allTest timing issues (Web)
Use controlled async timing:
// Wait for async updatesawait new Promise(resolve => setTimeout(resolve, 0));Reference: web/client/src/ConsoleClient.test.ts