it

package module
v0.0.0-...-22c69f6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

README

Gateway Integration Tests

End-to-end integration tests for the API Gateway, validating API deployment, routing, policy enforcement, and service health.

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         Test Suite (Godog)                          │
│  ┌───────────────┐  ┌───────────────┐  ┌───────────────────────┐    │
│  │ Feature Files │  │ Step Defs     │  │ Test State            │    │
│  │ (Gherkin)     │  │ (Go)          │  │ (HTTP Client/Context) │    │
│  └───────────────┘  └───────────────┘  └───────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────┐
│                      Docker Compose Environment                     │
│  ┌───────────────────┐  ┌─────────────────┐  ┌──────────────────┐   │
│  │ gateway-controller│  │  router         │  │  policy-engine   │   │
│  │  :9090 (REST)     │  │   :8080 (HTTP)  │  │    :9002         │   │
│  │  :18000 (xDS)     │  │   :9901 (Admin) │  │                  │   │
│  └───────────────────┘  └─────────────────┘  └──────────────────┘   │
│                              │                                      │
│                              ▼                                      │
│                      ┌────────────────┐                             │
│                      │ sample-backend │                             │
│                      │     :9080      │                             │
│                      └────────────────┘                             │
└─────────────────────────────────────────────────────────────────────┘

Components:

  • Godog - BDD test framework (Go implementation of Cucumber)
  • Docker Compose - Orchestrates gateway services for testing
  • Coverage Collector - Gathers code coverage from instrumented binaries
  • Test Reporter - Generates JSON/formatted test reports

Prerequisites

  • Go 1.25.1 or later
  • Docker and Docker Compose
  • Built gateway-controller:coverage image

Quick Start

# Build coverage-instrumented image and run tests
make test-all

# Or run separately:
make build-coverage-image
make test

Project Structure

gateway/it/
├── features/               # Gherkin feature files (.feature)
│   ├── health.feature      # Health check scenarios
│   └── api_deploy.feature  # API deployment scenarios
├── steps/                  # Reusable step definitions
│   ├── http_steps.go       # HTTP request steps
│   └── assert_steps.go     # Response assertion steps
├── steps_health.go         # Health-specific steps
├── steps_api.go            # API deployment steps
├── state.go                # Test state management
├── setup.go                # Docker Compose lifecycle
├── coverage.go             # Coverage collection
├── reporter.go             # Test reporting
├── suite_test.go           # Main test entry point
├── docker-compose.test.yaml
├── Makefile
├── CONTRIBUTING.md         # Guide for writing new tests
└── README.md

Make Commands

Command Description
make test Run integration tests
make test-postgres Run integration tests with Postgres backend
make test-all Build coverage images + run tests
make test-verbose Run tests with verbose output
make build-coverage Build coverage-instrumented images
make clean Remove containers, volumes, and artifacts
make check-docker Verify Docker is available
make coverage-report Open coverage HTML report in browser

Running Tests

# Run all tests
make test-all

# Run tests with Postgres-backed gateway-controller
make test-postgres

# Run tests with an explicit compose file
COMPOSE_FILE=docker-compose.test.postgres.yaml make test

# Run with Go directly (with extended timeout for coverage builds)
COMPOSE_FILE=docker-compose.test.yaml go test -v -timeout 30m ./...

# Run specific scenario (use @tag)
COMPOSE_FILE=docker-compose.test.postgres.yaml go test -v -timeout 30m ./... -godog.tags="@wip"

Note: The default Go test timeout is 10 minutes. The full integration test suite with coverage instrumentation typically takes longer to complete, so a 30-minute timeout is recommended. Note: The Postgres test compose keeps the database internal to the Docker network and does not expose a host port.

Test Reports

After running tests, reports are available at:

Report Location
Test Results (JSON) reports/integration-test-results.json
Coverage Summary coverage/integration-test-coverage.txt
Coverage HTML coverage/integration-test-coverage.html
Coverage JSON coverage/integration-test-coverage-report.json

Example Test Scenario

Feature: API Deployment and Invocation

  Background:
    Given the gateway services are running

  Scenario: Deploy a simple HTTP API and invoke it successfully
    When I deploy this API configuration:
      """
      apiVersion: gateway.api-platform.wso2.com/v1
      kind: RestApi
      metadata:
        name: weather-api-v1.0
      spec:
        displayName: Weather-API
        version: v1.0
        context: /weather/$version
        upstream:
          main:
            url: http://sample-backend:9080/api/v2
        operations:
          - method: GET
            path: /{country_code}/{city}
      """
    Then the response should be successful
    And I wait for 2 seconds
    When I send a GET request to "http://localhost:8080/weather/v1.0/us/seattle"
    Then the response should be successful

Contributing

See CONTRIBUTING.md for detailed instructions on:

  • Writing feature files
  • Available step definitions
  • Adding new steps
  • Best practices

Documentation

Index

Constants

View Source
const (
	// DefaultStartupTimeout is the maximum time to wait for services to become healthy
	DefaultStartupTimeout = 60 * time.Second

	// HealthCheckInterval is how often to check service health
	HealthCheckInterval = 2 * time.Second

	// GatewayControllerPort is the REST API port for gateway-controller
	GatewayControllerPort = "9090"

	// GatewayControllerAdminPort is the controller admin HTTP port
	GatewayControllerAdminPort = "9092"

	// GatewayControllerRuntimeAdminPort is the host port mapped to the
	// runtime-facing controller's admin HTTP port (container 9092) in the
	// two-controller Postgres topology (docker-compose.test.postgres.yaml).
	// It is queried only for the policy-snapshot xDS-sync probe.
	GatewayControllerRuntimeAdminPort = "9093"

	// RouterPort is the HTTP traffic port for the router
	RouterPort = "8080"

	// EnvoyAdminPort is the admin port for Envoy
	EnvoyAdminPort = "9901"

	// SampleBackendPort is the port for sample-backend service
	SampleBackendPort = "9080"

	// EchoBackendPort is the port for echo-backend service
	EchoBackendPort = "9081"
)
View Source
const (
	// GatewayControllerMetricsPort is the port for gateway-controller metrics
	GatewayControllerMetricsPort = "9091"

	// PolicyEngineMetricsPort is the port for policy-engine metrics
	PolicyEngineMetricsPort = "9003"
)
View Source
const GatewayAdminAPIBasePath = "/api/admin/v1"

GatewayAdminAPIBasePath is the URL prefix under which the gateway-controller admin API is served. Must stay in sync with `servers.url` in gateway/gateway-controller/api/admin-openapi.yaml.

View Source
const GatewayManagementAPIBasePath = "/api/management/v1"

GatewayManagementAPIBasePath is the URL prefix under which the gateway-controller management API is served. Must stay in sync with `servers.url` in gateway/gateway-controller/api/management-openapi.yaml.

View Source
const MockAWSBedrockGuardrailPort = "8083"

MockAWSBedrockGuardrailPort is the port for mock-aws-bedrock-guardrail service

View Source
const MockAzureContentSafetyPort = "8084"

MockAzureContentSafetyPort is the port for mock-azure-content-safety service

View Source
const MockEmbeddingProviderPort = "8085"

MockEmbeddingProviderPort is the port for mock-embedding-provider service

View Source
const MockJWKSPort = "8082"

MockJWKSPort is the port for mock-jwks service

View Source
const MockOAuth2IdPPort = "8088"

MockOAuth2IdPPort is the port for mock-oauth2-idp service

View Source
const MockPlatformAPIPort = "9244"

MockPlatformAPIPort is the port for mock-platform-api inject endpoint

View Source
const RedisPort = "6379"

RedisPort is the port for redis service

Variables

View Source
var NewCoverageCollector = coverage.NewCoverageCollector

NewCoverageCollector creates a new CoverageCollector

Functions

func CheckDockerAvailable

func CheckDockerAvailable() error

CheckDockerAvailable verifies that Docker is running and accessible

func CheckPortsAvailable

func CheckPortsAvailable() error

CheckPortsAvailable checks if required ports are available

func DefaultCoverageConfig

func DefaultCoverageConfig() *coverage.CoverageConfig

DefaultCoverageConfig returns the default coverage configuration for gateway/it

func GetComposeFilePath

func GetComposeFilePath() string

GetComposeFilePath returns the path to the test docker-compose file

func GetStoredRestAPISourceConfiguration

func GetStoredRestAPISourceConfiguration(ctx context.Context, handle string) (string, error)

GetStoredRestAPISourceConfiguration returns the unrendered SourceConfiguration JSON blob for a RestApi handle. Used by IT scenarios to assert the DB persists the original templated body, not the rendered one.

func GetStoredRestAPISourceConfigurationWithRetry

func GetStoredRestAPISourceConfigurationWithRetry(ctx context.Context, handle string) (string, error)

GetStoredRestAPISourceConfigurationWithRetry retries a few times to give the controller a moment to flush the row to disk after a POST. The controller upserts synchronously on the request path, but in CI we occasionally see the row not visible to a separate sqlite3 process for a few hundred ms.

func GetStoredSourceConfigurationWithRetry

func GetStoredSourceConfigurationWithRetry(ctx context.Context, kind, table, handle string) (string, error)

GetStoredSourceConfigurationWithRetry generalises GetStoredRestAPISourceConfigurationWithRetry to any artifact kind/table pair so template-rendering ITs can assert DB persistence for LlmProvider, LlmProxy, and Mcp in addition to RestApi.

func RegisterAPISteps

func RegisterAPISteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterAPISteps registers all API deployment step definitions

func RegisterAnalyticsSteps

func RegisterAnalyticsSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterAnalyticsSteps registers all analytics step definitions

func RegisterAuthSteps

func RegisterAuthSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

Register auth steps

func RegisterComposeSteps

func RegisterComposeSteps(ctx *godog.ScenarioContext, composeManager *ComposeManager)

func RegisterDPToCPSteps

func RegisterDPToCPSteps(ctx *godog.ScenarioContext, state *TestState)

RegisterDPToCPSteps registers all DP->CP Gherkin steps.

func RegisterHealthSteps

func RegisterHealthSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterHealthSteps registers all health check step definitions

func RegisterJWTSteps

func RegisterJWTSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps, jwtSteps *JWTSteps)

RegisterJWTSteps registers JWT step definitions with the scenario context

func RegisterLLMSteps

func RegisterLLMSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterLLMSteps registers all LLM provider template and provider step definitions

func RegisterMCPSteps

func RegisterMCPSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps, jwtSteps *JWTSteps)

RegisterMCPSteps registers all MCP related step definitions

func RegisterMetricsSteps

func RegisterMetricsSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterMetricsSteps registers all metrics step definitions

func RegisterPolicyEngineSteps

func RegisterPolicyEngineSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterPolicyEngineSteps registers all policy-engine specific step definitions

func RegisterSecretSteps

func RegisterSecretSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterSecretSteps registers all secret management step definitions

func RegisterSubscriptionSteps

func RegisterSubscriptionSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterSubscriptionSteps registers step definitions for subscription validation tests.

func RegisterTemplateSteps

func RegisterTemplateSteps(ctx *godog.ScenarioContext, state *TestState, httpSteps *steps.HTTPSteps)

RegisterTemplateSteps registers all template-related Gherkin steps.

func RegisterTimeoutSteps

func RegisterTimeoutSteps(ctx *godog.ScenarioContext, state *TestState)

RegisterTimeoutSteps registers step definitions for upstream and HCM timeout scenarios

Types

type AnalyticsEvent

type AnalyticsEvent struct {
	Request struct {
		Time       string            `json:"time"`
		URI        string            `json:"uri"`
		Verb       string            `json:"verb"`
		Headers    map[string]string `json:"headers"`
		APIVersion string            `json:"api_version"`
		IPAddress  string            `json:"ip_address"`
	} `json:"request"`
	Response struct {
		Time    string            `json:"time"`
		Status  int               `json:"status"`
		Headers map[string]string `json:"headers"`
	} `json:"response"`
	Metadata map[string]interface{} `json:"metadata"`
}

AnalyticsEvent represents the structure of a Moesif analytics event

type AnalyticsSteps

type AnalyticsSteps struct {
	// contains filtered or unexported fields
}

AnalyticsSteps wraps TestState and HTTPSteps for analytics step definitions

type AuthUser

type AuthUser struct {
	Username string
	Password string
}

AuthUser holds credentials for a test user

type ComposeManager

type ComposeManager struct {
	// contains filtered or unexported fields
}

ComposeManager manages the Docker Compose lifecycle for integration tests Uses testcontainers-go compose module for reliable container management

func NewComposeManager

func NewComposeManager(composeFiles ...string) (*ComposeManager, error)

NewComposeManager creates a new ComposeManager from one or more compose files. Multiple files are layered in order (later files override earlier ones), so a base stack can be combined with a small override file (e.g. to flip a single env var) without duplicating the whole compose definition.

func (*ComposeManager) Cleanup

func (cm *ComposeManager) Cleanup()

Cleanup stops and removes all Docker Compose services

func (*ComposeManager) DumpLogs

func (cm *ComposeManager) DumpLogs(outputFile string) error

DumpLogs collects docker compose logs and writes them to the given file path

func (*ComposeManager) RestartService

func (cm *ComposeManager) RestartService(service string) error

RestartService restarts a single compose service and waits for its health endpoint if one is known.

func (*ComposeManager) Start

func (cm *ComposeManager) Start() error

Start starts all Docker Compose services and waits for them to be healthy

func (*ComposeManager) WaitForHealthy

func (cm *ComposeManager) WaitForHealthy(ctx context.Context) error

WaitForHealthy waits for all services to pass health checks

type Config

type Config struct {
	GatewayControllerURL      string
	GatewayControllerAdminURL string
	// PolicySnapshotControllerAdminURL is the admin base URL probed for the
	// policy-chain xDS-sync version. It targets the controller that feeds xDS to
	// gateway-runtime, which in the two-controller Postgres topology is
	// gateway-controller-xds rather than the management controller. When
	// empty, waitForPolicySnapshotSync falls back to GatewayControllerAdminURL
	// (single-controller topologies and unit tests).
	PolicySnapshotControllerAdminURL string
	RouterURL                        string
	PolicyEngineURL                  string
	SampleBackendURL                 string
	EchoBackendURL                   string
	MockJWKSURL                      string
	MockOAuth2IdPURL                 string
	MockAzureContentSafetyURL        string
	MockAWSBedrockGuardrailURL       string
	MockEmbeddingProviderURL         string
	MockPlatformAPIURL               string
	RedisURL                         string
	HTTPTimeout                      time.Duration
	Users                            map[string]AuthUser
}

Config holds configuration for the test suite

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default test configuration

type ConfigDumpResponse

type ConfigDumpResponse struct {
	LazyResources LazyResourcesDump `json:"lazy_resources"`
}

ConfigDumpResponse represents the policy engine config dump response structure

type CoverageCollector

type CoverageCollector = coverage.CoverageCollector

CoverageCollector is an alias to the common coverage collector

type DPToCPSteps

type DPToCPSteps struct {
	// contains filtered or unexported fields
}

DPToCPSteps provides step definitions for the data-plane -> control-plane artifact push flow. The gateway-controller pushes every gateway-originated artifact (LLM provider template/provider/proxy, MCP proxy, REST API) to the control plane on create/update, undeploys them on delete, and re-pushes any pending/failed ones on (re)connect. In the integration test the control plane is stood in for by mock-platform-api, which records what it received; these steps drive and assert against that recorder plus the gateway's own DB bookkeeping (cp_sync_status / cp_artifact_id).

func NewDPToCPSteps

func NewDPToCPSteps(state *TestState) *DPToCPSteps

NewDPToCPSteps creates a new DPToCPSteps instance.

type EnvironmentInfo

type EnvironmentInfo struct {
	GoVersion    string            `json:"goVersion"`
	Platform     string            `json:"platform"`
	DockerImages map[string]string `json:"dockerImages,omitempty"`
}

EnvironmentInfo contains test environment details

type ErrorDetail

type ErrorDetail struct {
	Message    string `json:"message"`
	Type       string `json:"type,omitempty"`
	StackTrace string `json:"stackTrace,omitempty"`
}

ErrorDetail contains detailed error information for failed scenarios

type HealthSteps

type HealthSteps struct {
	// contains filtered or unexported fields
}

HealthSteps wraps TestState and HTTPSteps for health check step definitions

type JWTSteps

type JWTSteps struct {
	// contains filtered or unexported fields
}

JWTSteps provides JWT authentication specific step definitions

func NewJWTSteps

func NewJWTSteps(state *TestState, httpSteps *steps.HTTPSteps, mockJWKSURL string) *JWTSteps

NewJWTSteps creates a new JWTSteps instance

func (*JWTSteps) Register

func (j *JWTSteps) Register(ctx *godog.ScenarioContext)

Register registers all JWT step definitions

func (*JWTSteps) Reset

func (j *JWTSteps) Reset()

Reset clears JWT state between scenarios

type JsonRPCRequest

type JsonRPCRequest struct {
	JSONRPC string `json:"jsonrpc"`
	ID      int    `json:"id,omitempty"`
	Method  string `json:"method"`
	Params  any    `json:"params,omitempty"`
}

type LazyResourceInfo

type LazyResourceInfo struct {
	ID           string                 `json:"id"`
	ResourceType string                 `json:"resource_type"`
	Resource     map[string]interface{} `json:"resource"`
}

LazyResourceInfo represents a single lazy resource

type LazyResourcesDump

type LazyResourcesDump struct {
	TotalResources  int                           `json:"total_resources"`
	ResourcesByType map[string][]LazyResourceInfo `json:"resources_by_type"`
}

LazyResourcesDump represents the lazy resources section of config dump

type MetricsSteps

type MetricsSteps struct {
	// contains filtered or unexported fields
}

MetricsSteps wraps TestState and HTTPSteps for metrics step definitions

type PolicyEngineSteps

type PolicyEngineSteps struct {
	// contains filtered or unexported fields
}

PolicyEngineSteps wraps TestState and HTTPSteps for policy-engine specific step definitions

type ReporterConfig

type ReporterConfig struct {
	// OutputDir is the directory where reports are saved
	OutputDir string

	// ReportName is the base name for report files
	ReportName string
}

ReporterConfig holds test reporter configuration

func DefaultReporterConfig

func DefaultReporterConfig() *ReporterConfig

DefaultReporterConfig returns the default reporter configuration

type ScenarioResult

type ScenarioResult struct {
	ID          string       `json:"id"`
	Name        string       `json:"name"`
	URI         string       `json:"uri"`
	Tags        []string     `json:"tags,omitempty"`
	Status      string       `json:"status"`
	Duration    string       `json:"duration"`
	Error       string       `json:"error,omitempty"`
	ErrorDetail *ErrorDetail `json:"errorDetail,omitempty"`
	StartTime   time.Time    `json:"startTime"`
	EndTime     time.Time    `json:"endTime"`
}

ScenarioResult represents a single scenario's execution result

type SecretSteps

type SecretSteps struct {
	// contains filtered or unexported fields
}

SecretSteps provides step definitions for secret management

func NewSecretSteps

func NewSecretSteps(state *TestState, httpSteps *steps.HTTPSteps) *SecretSteps

NewSecretSteps creates a new SecretSteps instance

func (*SecretSteps) Reset

func (s *SecretSteps) Reset()

Reset clears the secret steps state between scenarios

type ServiceHealth

type ServiceHealth struct {
	Name    string
	Healthy bool
	Error   error
}

ServiceHealth represents the health status of a service

type TemplateSteps

type TemplateSteps struct {
	// contains filtered or unexported fields
}

TemplateSteps provides step definitions for asserting that template expressions ({{ env "..." }}, {{ secret "..." }}, {{ default ... }}) are resolved at runtime but persisted unrendered in the API response and DB.

func NewTemplateSteps

func NewTemplateSteps(state *TestState, httpSteps *steps.HTTPSteps) *TemplateSteps

NewTemplateSteps creates a new TemplateSteps instance.

type TestReport

type TestReport struct {
	// Metadata
	Name      string    `json:"name"`
	Timestamp time.Time `json:"timestamp"`
	Duration  string    `json:"duration"`

	// Summary
	Summary TestSummary `json:"summary"`

	// Scenarios
	Scenarios []ScenarioResult `json:"scenarios"`

	// Environment info
	Environment EnvironmentInfo `json:"environment"`
}

TestReport represents the complete test report

type TestReporter

type TestReporter struct {
	// contains filtered or unexported fields
}

TestReporter manages test report generation

func NewTestReporter

func NewTestReporter(config *ReporterConfig) *TestReporter

NewTestReporter creates a new TestReporter

func (*TestReporter) EndScenario

func (r *TestReporter) EndScenario(sc *godog.Scenario, err error)

EndScenario records the completion of a scenario

func (*TestReporter) GenerateReport

func (r *TestReporter) GenerateReport() error

GenerateReport generates the final test report

func (*TestReporter) GetSummary

func (r *TestReporter) GetSummary() TestSummary

GetSummary returns the current test summary

func (*TestReporter) Setup

func (r *TestReporter) Setup() error

Setup prepares the report output directory

func (*TestReporter) StartScenario

func (r *TestReporter) StartScenario(sc *godog.Scenario)

StartScenario records the start of a scenario

type TestState

type TestState struct {
	// Config holds the test configuration
	Config *Config

	// HTTPClient is the HTTP client for making requests
	HTTPClient *http.Client

	// LastRequest stores the most recent HTTP request
	LastRequest *http.Request

	// LastResponse stores the most recent HTTP response
	LastResponse *http.Response

	// LastError stores the most recent error
	LastError error

	// Context stores arbitrary key-value data for steps
	Context map[string]interface{}
	// contains filtered or unexported fields
}

TestState holds the shared state for BDD test scenarios

func NewTestState

func NewTestState() *TestState

NewTestState creates a new TestState with default configuration

func (*TestState) GetContextInt

func (s *TestState) GetContextInt(key string) (int, bool)

GetContextInt retrieves an int value from the context

func (*TestState) GetContextString

func (s *TestState) GetContextString(key string) (string, bool)

GetContextString retrieves a string value from the context

func (*TestState) GetContextValue

func (s *TestState) GetContextValue(key string) (interface{}, bool)

GetContextValue retrieves a value from the context

func (*TestState) Reset

func (s *TestState) Reset()

Reset clears all state between scenarios

func (*TestState) SetContextValue

func (s *TestState) SetContextValue(key string, value interface{})

SetContextValue stores a value in the context

type TestSummary

type TestSummary struct {
	Total   int `json:"total"`
	Passed  int `json:"passed"`
	Failed  int `json:"failed"`
	Skipped int `json:"skipped"`
}

TestSummary contains aggregate test statistics

Directories

Path Synopsis
Package steps provides common step definitions for BDD tests
Package steps provides common step definitions for BDD tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL