Skip to main content

Testing Overview

AxonFlow maintains a comprehensive testing infrastructure that ensures reliability, performance, and security across all components. This guide covers our testing philosophy and how to run tests locally.

Testing Pyramid​

Our testing strategy follows the industry-standard testing pyramid:

┌─────────────┐
│ E2E / │ ← Fewest tests
│ Load Tests │ (expensive, slow)
┌┴─────────────┴┐
│ Integration │ ← Medium coverage
│ Tests │ (API contracts)
┌┴───────────────┴┐
│ Unit Tests │ ← Most tests
└─────────────────┘ (fast, isolated)

Unit Tests​

Unit tests validate individual functions and business logic in isolation. Each Go package contains comprehensive unit tests.

Characteristics:

  • Fast execution (milliseconds)
  • No external dependencies (databases, APIs mocked)
  • High coverage of business logic paths
  • Run on every commit via CI

Integration Tests​

Integration tests validate interactions between components and external systems.

Characteristics:

  • Test database operations with real PostgreSQL
  • Verify API contracts between services
  • Test connector integrations
  • Run before deployments

Load Tests​

Load tests validate system performance under sustained traffic.

Characteristics:

  • Realistic client behavior (connection pooling, HTTP/2)
  • Multiple concurrent users
  • Percentile latency tracking (P50, P95, P99)
  • Run on-demand or scheduled

Performance Tests​

Performance tests track system benchmarks over time.

Characteristics:

  • Scheduled execution (hourly, daily, weekly)
  • Historical trend tracking
  • Regression detection
  • Cost-optimized (no LLM calls by default)

Running Tests Locally​

Prerequisites​

  • Go 1.25+ installed
  • Docker and Docker Compose (for integration tests)
  • Make (optional, for convenience)

Unit Tests​

Run all unit tests:

# Run all tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Run tests for a specific package
go test ./platform/agent/...

# Run tests with verbose output
go test -v ./platform/orchestrator/...

Integration Tests​

Integration tests require a running PostgreSQL instance:

# Start local infrastructure
docker compose up -d postgres redis mysql

# Run integration tests
go test -tags=integration ./...

# Clean up
docker compose down

Benchmarks​

Run performance benchmarks:

# Run all benchmarks
go test -bench=. ./...

# Run specific benchmark
go test -bench=BenchmarkPolicyEvaluation ./platform/agent/...

# Compare benchmark results
go test -bench=. ./... > new.txt
benchstat old.txt new.txt

Test Structure​

File Naming Conventions​

PatternPurposeExample
*_test.goUnit testspolicy_test.go
*_integration_test.goIntegration testsdb_integration_test.go
*_bench_test.goBenchmarksagent_bench_test.go

Test Organization​

Tests are co-located with the code they test:

platform/
├── agent/
│ ├── main.go
│ ├── main_test.go # Unit tests
│ ├── agent_bench_test.go # Benchmarks
│ └── db_auth.go
│ └── db_auth_test.go # Unit tests
├── orchestrator/
│ ├── main.go
│ └── main_test.go
└── connectors/
└── amadeus/
├── connector.go
└── connector_test.go

CI/CD Integration​

Tests run automatically in GitHub Actions in the main platform repositories. The public docs repo does not carry those workflows itself, so use this page as the operating summary and inspect the platform repo workflows when you need exact CI behavior.

Pipeline Overview​

  1. Core platform test suite (test.yml in the main platform repository):

    • parallel unit-test jobs for Agent, Orchestrator, Connectors, and Shared modules
    • coverage enforcement for the major Go modules
    • Postgres-backed setup for modules that require migrated schema state
  2. Community build test suite (test-community.yml):

    • community-only validation against the public build
    • Agent, Orchestrator, and Connector test runs with Postgres, Redis, and MySQL services
    • race-detector and integration-style coverage for community paths
  3. Lint and security workflows:

    • lint.yml runs golangci-lint across the Go modules
    • security.yml and related workflows handle security-oriented checks
    • docs-only pull requests should not be described as running the whole Go test matrix

Running the Full CI Suite Locally​

You can approximate the CI pipeline locally:

# Lint (matches the platform lint workflow)
golangci-lint run ./platform/...

# Unit tests with coverage
go test -cover -coverprofile=coverage.out ./platform/...

# Community integration-style test prep (requires Docker)
docker compose up -d postgres redis
go test -tags=integration ./platform/...
docker compose down

Coverage Requirements​

We enforce minimum coverage thresholds in CI. Pull requests that drop coverage below these targets will fail the test workflow.

Coverage Targets​

ModuleThresholdNotes
Orchestrator76%Core policy routing and LLM orchestration
Agent76%Policy enforcement and request handling
Connectors76%MCP connector implementations
Shared Policy80%Shared policy engine carries a higher bar

Checking Coverage Locally​

# Generate a coverage report for a specific module
go test -coverprofile=coverage.out ./platform/orchestrator/...
go tool cover -func=coverage.out | tail -1

# Open an HTML coverage report in the browser
go tool cover -html=coverage.out

Coverage reports are also generated during CI and uploaded as artifacts by the platform workflows.

Mock Usage​

We use standard Go mocking patterns:

Database Mocking (go-sqlmock)​

import "github.com/DATA-DOG/go-sqlmock"

func TestDatabaseQuery(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()

mock.ExpectQuery("SELECT").WillReturnRows(
sqlmock.NewRows([]string{"id", "name"}).
AddRow(1, "test"),
)

// Test your function
}

HTTP Mocking (httptest)​

import "net/http/httptest"

func TestExternalAPI(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "ok"}`))
}))
defer server.Close()

// Use server.URL in your tests
}

Next Steps​

From Local Work To Review​

When local development becomes a shared rollout, carry the same work into: