Testing Instructions¶
Running Tests¶
Full Test Suite¶
With Coverage¶
Run Specific Test Category¶
# Unit tests only
uv run pytest tests/unittest/ -v
# Integration tests only
uv run pytest tests/integrationtest/ -v
# Functional tests only
uv run pytest tests/functionaltest/ -v
Test Structure¶
Tests are organized into three categories:
tests/
├── conftest.py # Shared fixtures and test environment setup
├── unittest/ # Isolated unit tests with mocked dependencies
├── integrationtest/ # Tests verifying service + repository interaction
└── functionaltest/ # End-to-end API tests using FastAPI TestClient
Unit Tests (tests/unittest/)¶
Test isolated logic with mocked external dependencies.
- Mock infrastructure boundaries (Firebase, file system, HTTP)
- Test individual functions and classes
- Fast execution, no external service requirements
Integration Tests (tests/integrationtest/)¶
Test service + repository interaction.
- Verify interaction between services, handlers, and adapters
- May use temporary databases or file systems
- Test data flow across module boundaries
Functional Tests (tests/functionaltest/)¶
Test full API endpoints through FastAPI TestClient.
- Exercise HTTP request/response cycle
- Verify status codes, response bodies, and error handling
- Test authentication flow end-to-end
Platform-Specific Notes¶
- On some Windows environments, symlink-escape hardening tests can be skipped when the process lacks symlink creation privilege. This is expected and reported deterministically by
tests/unittest/test_security_hardening.py.
Test Configuration¶
Environment Variables¶
Tests automatically set the following environment variables (via conftest.py):
PYTEST_RUNNING=1: Enables test mode, skips Firebase initializationFIREBASE_API_KEY=test-api-key: Provides a mock API keyAPP_ALLOW_UNSAFE_DESERIALIZE=true: Allows pickle deserialization in tests
Coverage Configuration¶
Coverage settings are defined in pyproject.toml:
[tool.coverage.run]
branch = true
source = ["app"]
omit = ["tests/*"]
[tool.coverage.report]
show_missing = true
skip_covered = true
Writing New Tests¶
Guidelines¶
- Maintain existing coverage — do not remove or weaken existing tests
- Update tests if logic changes — keep tests in sync with code
- Ensure deterministic behavior — avoid reliance on timing or ordering
- Mock infrastructure boundaries only — do not mock business logic
- Add tests for new edge cases — when introducing changes
Example Unit Test¶
from unittest.mock import MagicMock
from app.services.data_service import list_pickles_for_user
from pathlib import Path
def test_list_pickles_empty_dir(tmp_path: Path):
result = list_pickles_for_user(tmp_path, "nonexistent_user")
assert result == []