Integration Tests

This document describes the integration test suite for the Paladin workspace: test ownership, service requirements, how to run tests locally, and how services are provisioned in CI.


1. Test Ownership and Service Requirements

All integration tests live at tests/integration/ (workspace root). Every file imports from at least the paladin facade crate, and most also import paladin-ports traits directly. No file is a candidate for relocation into a per-crate tests/ directory because all tests exercise cross-crate behaviour through the public API surface.

The tests/integration/battalion/ sub-module contains battalion-specific tests and is declared from tests/integration/mod.rs.

Main test files

Test FileCrate ScopeServices RequiredFeature Gate
aegis_retry_stress_test.rspaladin, paladin-portsSQLite (temp file)—
anthropic_provider_test.rspaladinlive-api (Anthropic key)llm-anthropic
arsenal_bridge_regression_test.rspaladin, paladin-portsnone—
arsenal_execution_integration_test.rspaladin, paladin-portsnone—
arsenal_registry_integration_test.rspaladin, paladin-portsnone—
autonomous_planning_test.rspaladin, paladin-portsnone—
battalion_campaign_integration_test.rspaladin, paladin-portsnone—
battalion_chain_of_command_herald_test.rspaladin, paladin-portsnone—
battalion_chain_of_command_integration_test.rspaladin, paladin-portsnone—
battalion_herald_end_to_end_test.rspaladin, paladin-portsnonecli
citadel_integration_test.rspaladin, paladin-portsnone—
cli_integration_test.rspaladinlive-apicli
cli_real_providers_test.rspaladinlive-apicli
cli_real_services_test.rspaladinRedis, MinIOcli
commander_error_paths_test.rspaladin, paladin-portsnone—
commander_integration_tests.rspaladin, paladin-portsnone—
context_injection_test.rspaladin, paladin-portsnone—
deepseek_provider_test.rspaladinlive-api (DeepSeek key)llm-deepseek
e2e_approval_gate_test.rspaladin, paladin-portsSQLite (temp file)—
e2e_compensation_chain_test.rspaladin, paladin-portsSQLite (temp file)—
e2e_crash_resume_test.rspaladin, paladin-portsSQLite (temp file)—
e2e_muster_defer_order_test.rspaladin, paladin-portsSQLite (temp file)—
e2e_platform_api_test.rspaladin, paladin-portsnone (mockito)web-server
file_storage_integration_tests.rspaladin, paladin-portsMinIOs3-storage
golden_bridge_equivalence_test.rspaladinnone—
herald_integration_test.rspaladin, paladin-portsnone—
in_memory_sanctum_tests.rspaladin, paladin-portsnone—
llm_live_api_tests.rspaladin, paladin-portslive-apilive-api-tests
mcp_stdio_test.rspaladinnone—
mcp_streamable_http_test.rspaladinnone (hermetic, in-process rmcp server)—
mcp_streamable_http_live_test.rspaladinlive-api (ETHERSCAN_API_KEY), #[ignore]'d—
middleware_under_engine_test.rspaladin, paladin-portsnone—
multi_parley_suspension_test.rspaladin, paladin-portsSQLite (temp file)—
notification_system_integration_test.rspaladin, paladin-portsnone—
ollama_docker_test.rspaladin, paladin-portsOllama (Docker)integration-tests+llm-ollama
openai_content_analysis_integration_test.rspaladin, paladin-portsnone (mock)llm-openai
openai_embedding_tests.rspaladin, paladin-portsnone (mock)openai-embeddings
openai_provider_test.rspaladinlive-api (OpenAI key)llm-openai
orchestrator_workflow_lifecycle_test.rspaladinnone—
otel_transport_test.rspaladin, paladin-portsnone (hermetic OTLP/HTTP)otel
paladin_garrison_integration_test.rspaladin, paladin-portsnone—
paladin_integration_test.rspaladin, paladin-portsnone—
parley_resume_stress_test.rspaladin, paladin-portsSQLite (temp file)—
provider_switching_test.rspaladin, paladin-portsnone (mockito)—
qdrant_sanctum_tests.rspaladin, paladin-portsQdrantqdrant
rag_commissary_test.rspaladin, paladin-portsnone (mock embedding)—
rag_integration_tests.rspaladinQdrantqdrant
reasoning_agent_test.rspaladin, paladin-portsnone—
redis_queue_integration_test.rspaladinRedisredis-queue
scheduler_integration_test.rspaladin, paladin-portsnone—
sqlite_garrison_integration_test.rspaladin, paladin-portsSQLite (temp file)—
structured_engine_node_test.rspaladin, paladin-portsnone—
subgraph_formation_in_campaign_test.rspaladin, paladin-portsnone—
system_log_integration_test.rspaladin, paladin-portsnone—
v0_9_config_boot_test.rspaladinnoneweb-server
vault_confinement_test.rspaladin, paladin-portsnone—
vision_integration_test.rspaladin, paladin-portslive-apivision+llm-openai+llm-anthropic
war_engine_tracer_test.rspaladin, paladin-portsnone—
waypoint_retention_fault_injection_test.rspaladin, paladin-portsnone—

Battalion sub-module (tests/integration/battalion/)

Test FileServices Required
campaign_integration_test.rsnone
chain_of_command_integration_test.rsnone
council_integration_test.rsnone
formation_integration_test.rsnone
grove_integration_test.rsnone
load_test.rsnone
phalanx_integration_test.rsnone

Service legend

SymbolMeaning
noneIn-memory / mock only; no external process needed
RedisRequires a Redis 7 instance
MinIORequires MinIO (S3-compatible object storage)
SQLiteUses a tempfile::NamedTempFile; no external service needed
QdrantRequires a Qdrant vector-database instance
live-apiRequires real provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, or DEEPSEEK_API_KEY); skipped in normal CI

2. Running Integration Tests Locally

Prerequisites

  • Rust stable toolchain
  • Docker (for Redis / MinIO when running service-dependent tests)
  • docker compose v2 plugin (docker compose version must succeed)

Option A — All integration tests (mock/in-process only)

cargo test --workspace --features integration-tests -- --test-threads=1

This runs every test that does not require an external service. Tests gated behind live-api-tests, qdrant, etc. are excluded unless the corresponding feature is enabled.

Option B — With Redis and MinIO (docker-compose)

Start the test infrastructure, then run:

# Start services
docker compose -f docker/docker-compose.test.yml up -d redis-test minio-test minio-test-init

# Wait for minio-test-init to finish creating buckets
until docker inspect paladin-minio-test-init --format="{{.State.Status}}" 2>/dev/null | grep -q exited; do sleep 2; done

# Run tests (all features that need services are enabled by default)
USE_EXTERNAL_TEST_SERVICES=true \
TEST_REDIS_HOST=localhost TEST_REDIS_PORT=6380 \
TEST_MINIO_ENDPOINT=localhost:9010 \
TEST_MINIO_ACCESS_KEY=testuser TEST_MINIO_SECRET_KEY=testpass123 \
cargo test --workspace --features integration-tests -- --test-threads=1

# Tear down
docker compose -f docker/docker-compose.test.yml down -v

Or use the helper script which handles all of the above:

./scripts/run_integration_tests.sh -m docker -v

Option C — Specific test files or patterns

# Run only SQLite garrison tests
cargo test --workspace --features integration-tests sqlite_garrison -- --test-threads=1

# Run only Redis queue tests
cargo test --workspace --features integration-tests,redis-queue redis_queue -- --test-threads=1

# Run only MinIO file storage tests
cargo test --workspace --features integration-tests,s3-storage file_storage -- --test-threads=1

Option D — Per-crate test targets (Makefile)

make test-core          # paladin-core unit + integration tests
make test-ports         # paladin-ports
make test-battalion     # paladin-battalion
make test-llm           # paladin-llm
make test-memory        # paladin-memory
make test-storage       # paladin-storage
make test-notifications # paladin-notifications
make test-content       # paladin-content
make test-web           # paladin-web
make test-facade        # paladin (root crate / facade)

Makefile convenience targets

make test-integration          # local mode (uses testcontainers)
make test-integration-docker   # docker-compose mode (starts services automatically)
make test-integration-redis    # Redis tests only
make test-integration-minio    # MinIO tests only

3. CI Service Provisioning

Integration Tests job (.github/workflows/integration-tests.yml)

The integration-tests job uses GitHub-native service containers:

ServiceImagePort
Redisredis:7-alpinelocalhost:6379
MinIOquay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z.hotfix.7aa24e772localhost:9000

The job runs:

cargo test --workspace --features integration-tests --verbose -- --test-threads=1

Environment variables passed to the test binary:

VariableValue
REDIS_URLredis://localhost:6379
MINIO_ENDPOINTlocalhost:9000
MINIO_ACCESS_KEYminioadmin
MINIO_SECRET_KEYminioadmin
MINIO_USE_SSLfalse

Docker Integration Tests job

The docker-integration job builds the test image from docker/testserver/Dockerfile (test stage) and runs tests inside the container using docker/docker-compose.test.yml.

Services started:

ServiceContainer NamePurpose
redis-testpaladin-redis-testRedis 7 on port 6380 (host)
minio-testpaladin-minio-testMinIO on port 9010 (host)
minio-test-initpaladin-minio-test-initCreates test buckets, then exits

The test container (paladin-integration-tests) runs:

cargo test --features integration-tests -- --test-threads=1 --nocapture

The test image includes:

  • Cargo.toml / Cargo.lock
  • src/, crates/, tests/
  • migrations/ (required by SqliteGarrison at runtime via sqlx::migrate)
  • config.test.yml (required by test_load_from_file_regression)

Live-API tests

Tests guarded by live-api-tests, llm-openai, llm-anthropic, llm-deepseek, or qdrant features are not run in CI (API keys are not available in the public workflow). They are intended for manual verification or a separate secrets-aware workflow.