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
anthropic_provider_test.rspaladinlive-api (Anthropic key)llm-anthropic
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_integration_test.rspaladin, paladin-portsnone
citadel_integration_test.rspaladin, paladin-portsnone
cli_integration_test.rspaladinlive-apicli
cli_real_providers_test.rspaladinlive-apicli
cli_real_services_test.rspaladinRedis, MinIOcli
commander_integration_tests.rspaladin, paladin-portsnone
context_injection_test.rspaladin, paladin-portsnone
deepseek_provider_test.rspaladinlive-api (DeepSeek key)llm-deepseek
file_storage_integration_tests.rspaladin, paladin-portsMinIOs3-storage
herald_integration_test.rspaladin, paladin-portsnone
in_memory_sanctum_tests.rspaladin, paladin-portsnone
llm_live_api_tests.rspaladin, paladin-portslive-apilive-api-tests
mcp_sse_test.rspaladinnone
mcp_stdio_test.rspaladinnone
notification_system_integration_test.rspaladin, paladin-portsnone
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
paladin_garrison_integration_test.rspaladin, paladin-portsnone
paladin_integration_test.rspaladin, paladin-portsnone
qdrant_sanctum_tests.rspaladin, paladin-portsQdrantqdrant
rag_integration_tests.rspaladinQdrantqdrant
redis_queue_integration_test.rspaladinRedisredis-queue
scheduler_integration_test.rspaladin, paladin-portsnone
sqlite_garrison_integration_test.rspaladin, paladin-portsSQLite (temp file)
system_log_integration_test.rspaladin, paladin-portsnone
vision_integration_test.rspaladin, paladin-portslive-apivision+llm-openai+llm-anthropic

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
MinIOminio/minio:latestlocalhost: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.