This directory contains comprehensive tests for the RoboSystems Service application. With 194+ test files covering all major components, the test suite ensures reliability across the platform.
# Run standard test suite (unit tests only)
just test
# Run with coverage report
just test-cov
# Run all tests including linting and formatting
just test-allFor complete end-to-end workflow validation, use the examples directory instead of traditional e2e tests:
# Run custom graph workflow
cd examples/custom_graph_demo
uv run main.py
# Run SEC data workflow
cd examples/sec_demo
uv run main.pyThese examples validate the entire stack (authentication, graph creation, data upload, ingestion, queries) in a production-like environment while also serving as user documentation.
The test suite is organized by component, mirroring the application structure:
adapters/- External service integrations (Arelle/XBRL, OpenFIGI, QuickBooks, S3, SEC)config/- Configuration validation and billing plan testsmiddleware/- Request/response middleware layersauth/- Authentication, cache validation, distributed locks (20+ test files)billing/- Credit consumption and subscription billing middlewaregraph/- Graph database routing and multi-tenancymcp/- Model Context Protocol integrationotel/- OpenTelemetry metrics and tracingrate_limits/- Rate limiting and burst protectionrobustness/- Circuit breakers, retries, health checkssse/- Server-sent events for real-time updates
models/- Database models and schemasapi/- API request/response modelsbilling/- Billing and subscription models (customer, subscription, invoice, audit log)iam/- Identity and access management models
operations/- Business logic servicesagents/- AI agent operations and orchestrationgraph/- Graph database operations (credit service, entity service)lbug/- LadybugDB-specific operations (backup, health monitoring)pipeline/- Data processing pipelinesproviders/- External provider integrations
adapters/- External service integrations- SEC adapter (XBRL processing, filings, taxonomies)
- QuickBooks adapter (transaction processing)
routers/- HTTP endpoint testsauth/- Authentication and authorization endpointsgraphs/- Graph database CRUD operationsuser/- User management and subscription endpoints
dagster/- Dagster pipeline testsassets/- Asset tests (SEC, etc.)jobs/- Job tests (billing, infrastructure)
graph_api/- Graph API services and routingclient/- Graph API client functionalityrouters/- Graph API HTTP endpoints
schemas/- Dynamic schema managementsecurity/- Security implementations and validatorsutils/- Utility functions and helpers
integration/- Cross-component integration testsunit/- Isolated unit tests for specific components
Tests are marked with pytest markers to categorize them. Use these markers to run specific test subsets:
@pytest.mark.unit- Fast, isolated unit tests (no external dependencies)@pytest.mark.integration- Integration tests (may use databases, create LadybugDB instances)@pytest.mark.slow- Long-running tests (XBRL processing, large datasets)@pytest.mark.security- Security-focused tests@pytest.mark.asyncio- Async operation tests (handled automatically)
# Only unit tests (fast, no external services)
uv run pytest -m unit
# Only integration tests
uv run pytest -m integration
# Only security tests
uv run pytest -m security
# Exclude slow tests
uv run pytest -m "not slow"
# Exclude slow tests (default)
uv run pytest -m "not slow"# All task tests
uv run pytest tests/tasks/
# Specific task category
uv run pytest tests/tasks/billing/
uv run pytest tests/tasks/data_sync/
# Business operations
uv run pytest tests/operations/
# Middleware components
uv run pytest tests/middleware/auth/
uv run pytest tests/middleware/credits/
# Adapters
uv run pytest tests/adapters/
# API endpoints
uv run pytest tests/routers/
# Graph API
uv run pytest tests/graph_api/# Specific test file
uv run pytest tests/tasks/billing/test_storage_billing.py
# Specific test class
uv run pytest tests/tasks/billing/test_storage_billing.py::TestDailyStorageBilling
# Specific test function
uv run pytest tests/tasks/billing/test_storage_billing.py::TestDailyStorageBilling::test_successful_billing
# Parametrized test case
uv run pytest tests/routers/auth/test_login.py::test_login[success]# Run tests matching a pattern
uv run pytest -k "storage and billing"
uv run pytest -k "test_auth or test_login"
# Exclude specific patterns
uv run pytest -k "not slow"
# Verbose output with test names
uv run pytest -v
# Stop on first failure
uv run pytest -x
# Show local variables on failure
uv run pytest -l
# Run last failed tests
uv run pytest --lf
# Run failed tests first, then others
uv run pytest --ff
# Parallel execution (requires pytest-xdist)
uv run pytest -n autoCommon test fixtures provide reusable test components. Fixtures are defined at multiple levels:
test_db(session scope) - Test PostgreSQL database, auto-migratedclient(module scope) - FastAPI TestClient with databaseclient_with_mocked_auth(function scope) - TestClient with mocked authenticationmock_get_current_user(module scope) - Mock authentication dependencytest_user(function scope) - Test user with API keysample_graph(function scope) - Sample graph databasetest_user_graph(function scope) - Graph owned by test usertest_graph_with_credits(function scope) - Graph with credit allocationdb_session(function scope) - Database session for direct queries
- Database model factories
- Sample model instances
- Relationship fixtures
def test_with_database(test_db):
"""Use test database directly."""
# test_db is already migrated and ready
pass
def test_with_client(client):
"""Make HTTP requests to the API."""
response = client.get("/api/health")
assert response.status_code == 200
def test_with_auth(client_with_mocked_auth):
"""Make authenticated requests."""
response = client_with_mocked_auth.get("/v1/user/profile")
assert response.status_code == 200
def test_with_user(test_user):
"""Use a test user with API key."""
assert test_user.api_key is not NoneTests run in an isolated environment with specific configuration:
- Test Database:
robosystems_testonlocalhost:5432 - Auto-Migration: Alembic migrations run automatically on
test_dbfixture - Isolation: Each test using
db_sessiongets a rolled-back transaction - Cleanup: Database state is cleaned between test modules
- LocalStack: AWS services (S3, etc.) on
http://localhost:4566 - Valkey/Redis: Cache and queues on
localhost:6379 - Graph API: LadybugDB service on
localhost:8001 - LadybugDB Databases: Test databases in
./data/lbug-dbs
Tests mock external services by default:
- SEC EDGAR API
- QuickBooks API
- Anthropic/Claude API
- OpenFIGI API
Key test environment variables (from pytest.ini):
ENVIRONMENT=test
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/robosystems_test
GRAPH_API_URL=http://localhost:8001
LBUG_DATABASE_PATH=./data/lbug-dbs
# Feature flags (mostly enabled for testing)
RATE_LIMIT_ENABLED=false # Disabled for easier testing
BILLING_ENABLED=true
SECURITY_AUDIT_ENABLED=true
SUBGRAPH_CREATION_ENABLED=true
BACKUP_CREATION_ENABLED=true
# Mock API keys
INTUIT_CLIENT_ID=test-intuit-client-id
OPENFIGI_API_KEY=test-openfigi-key- Unit tests should be fast (<100ms) and isolated (no external dependencies)
- Integration tests can use databases but should clean up after themselves
- E2E tests require full Docker stack and test complete user workflows
- Async tests use
@pytest.mark.asyncio(auto-detected) - Slow tests should be marked
@pytest.mark.slowfor selective exclusion
"""Tests for [component name]."""
import pytest
from unittest.mock import MagicMock, patch
# Import code under test
from robosystems.module import function_to_test
class TestComponentName:
"""Test cases for [specific component]."""
def test_success_case(self):
"""Test successful operation."""
# Arrange
# Act
# Assert
pass
def test_error_case(self):
"""Test error handling."""
# Arrange
# Act
# Assert
pass
class TestEdgeCases:
"""Test edge cases and boundary conditions."""
def test_empty_input(self):
"""Test handling of empty input."""
pass
def test_invalid_input(self):
"""Test handling of invalid input."""
pass# ✓ GOOD: Descriptive test name
def test_user_cannot_access_other_users_graphs():
pass
# ✗ BAD: Vague test name
def test_graphs():
pass
# ✓ GOOD: Test one thing
def test_credit_consumption_decrements_balance():
# Tests only credit balance change
pass
# ✗ BAD: Test multiple things
def test_credit_system():
# Tests consumption, refills, limits, history...
pass
# ✓ GOOD: Clear assertions
def test_authentication_returns_jwt_token():
response = login(username, password)
assert "access_token" in response
assert response["token_type"] == "bearer"
# ✗ BAD: Unclear assertions
def test_authentication():
response = login(username, password)
assert responsedef test_error_handling(self, mock_engine, mock_sessionmaker, mock_func):
mock_session = MagicMock()
mock_sessionmaker.return_value.return_value.__enter__.return_value = mock_session
mock_sessionmaker.return_value.return_value.__exit__.return_value = False
mock_func.side_effect = RuntimeError("Something failed")
with pytest.raises(RuntimeError) as exc_info:
your_task.apply(kwargs={}).get() # type: ignore[attr-defined]
assert "Something failed" in str(exc_info.value)def test_database_connection_failure(self, mock_engine, mock_sessionmaker, mock_func):
mock_session = MagicMock()
mock_sessionmaker.return_value.return_value.__enter__.return_value = mock_session
mock_sessionmaker.return_value.return_value.__exit__.return_value = False
from sqlalchemy.exc import OperationalError
mock_session.execute.side_effect = OperationalError("Connection failed", None, None)
with pytest.raises(OperationalError):
your_task() # type: ignore[call-arg]def test_logging_on_success(self, mock_engine, mock_sessionmaker, mock_func):
mock_session = MagicMock()
mock_sessionmaker.return_value.return_value.__enter__.return_value = mock_session
mock_sessionmaker.return_value.return_value.__exit__.return_value = False
mock_func.return_value = {"status": "success"}
with patch("path.to.task.logger") as mock_logger:
your_task() # type: ignore[call-arg]
mock_logger.info.assert_any_call("Starting task")
assert any("completed" in str(call) for call in mock_logger.info.call_args_list)def test_retry_behavior(self, mock_engine, mock_sessionmaker, mock_func):
mock_session = MagicMock()
mock_sessionmaker.return_value.return_value.__enter__.return_value = mock_session
mock_sessionmaker.return_value.return_value.__exit__.return_value = False
mock_func.side_effect = RuntimeError("Temporary error")
with patch.object(your_task, "retry") as mock_retry:
mock_retry.side_effect = RuntimeError("Temporary error")
with pytest.raises(RuntimeError):
your_task.apply(kwargs={}).get() # type: ignore[attr-defined]For tasks using asyncio.run():
@patch("path.to.task.asyncio")
def test_async_task(self, mock_asyncio):
mock_asyncio.run.return_value = {"status": "success"}
result = your_async_task() # type: ignore[call-arg]
assert result is None
mock_asyncio.run.assert_called_once()Solution: Your mock session isn't being used by the task. Make sure you're mocking the full chain:
# Wrong - only mocks one level
mock_sessionmaker.return_value = mock_session
# Correct - mocks the full chain
mock_sessionmaker.return_value.return_value.__enter__.return_value = mock_sessionSolution: Make sure you're properly mocking the __exit__ method:
mock_sessionmaker.return_value.return_value.__exit__.return_value = False# Coverage report in terminal
just test-cov
# Generate HTML coverage report
uv run pytest --cov=robosystems --cov-report=html
open htmlcov/index.html
# Show missing lines
uv run pytest --cov=robosystems --cov-report=term-missing
# Fail if coverage below threshold
uv run pytest --cov=robosystems --cov-fail-under=80# Run all quality checks (includes tests)
just test-all
# Individual checks
just lint # Ruff linting
just format # Ruff formatting
just typecheck # Pyright type checkingTests run automatically in GitHub Actions on:
- Every pull request
- Every push to
mainorstaging - Manual workflow dispatch
CI runs:
- Linting and formatting checks
- Type checking
- Unit tests (fast)
- Integration tests (with PostgreSQL)
- Coverage reporting
E2E tests run separately as they require the full Docker stack.
# Ensure you're using uv run
uv run pytest # ✓ Correct
pytest # ✗ Wrong - may use system Python# Check PostgreSQL is running
docker ps | grep postgres
# Verify test database exists
psql -h localhost -U postgres -l | grep robosystems_test# Check fixture scope and location
# Fixtures must be in conftest.py or imported
pytest --fixtures # List all available fixtures# Drop into debugger on failure
uv run pytest --pdb
# Drop into debugger on first failure
uv run pytest -x --pdb
# Print output even on success
uv run pytest -s
# Very verbose output
uv run pytest -vv
# Show local variables on failure
uv run pytest -l --tb=long# Run only fast tests
uv run pytest -m "unit and not slow"
# Run in parallel (requires pytest-xdist)
uv run pytest -n auto
# Run with minimal output
uv run pytest -q
# Skip slow fixtures
uv run pytest --no-cov # Skip coverage collection- Each test using
db_sessiongets a rolled-back transaction - Integration tests should clean up created resources
- Use unique identifiers (UUIDs) to avoid conflicts
- Parallel tests should not share mutable state
When adding new tests:
- Choose the right location - Mirror the source code structure
- Add appropriate markers -
@pytest.mark.unit,@pytest.mark.integration, etc. - Follow naming conventions -
test_*.py,Test*classes,test_*functions - Write descriptive docstrings - Explain what the test verifies
- Use existing fixtures - Don't duplicate fixture setup
- Clean up resources - Integration tests should clean up after themselves
- Run locally first - Ensure tests pass before committing
- Check coverage - New code should have corresponding tests