Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions .github/workflows/live-integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Live Integration Tests

on:
pull_request:
branches: [main]
paths:
- 'capiscio_mcp/**'
- 'tests/integration/**'
- 'docker-compose.test.yml'
- '.github/workflows/live-integration-tests.yml'
push:
branches: [main]
paths:
- 'capiscio_mcp/**'
- 'tests/integration/**'
workflow_dispatch:
repository_dispatch:
types: [run-e2e-tests]

permissions:
contents: read
pull-requests: write

jobs:
live-integration:
name: Live Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout MCP repository
uses: actions/checkout@v4

- name: Checkout capiscio-core
uses: actions/checkout@v4
with:
repository: capiscio/capiscio-core
path: _capiscio-core
token: ${{ secrets.REPO_ACCESS_TOKEN }}

- name: Checkout capiscio-server
uses: actions/checkout@v4
with:
repository: capiscio/capiscio-server
path: _capiscio-server
token: ${{ secrets.REPO_ACCESS_TOKEN }}

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'

- name: Build capiscio-core binary
working-directory: _capiscio-core
run: |
go build -o bin/capiscio ./cmd/capiscio
chmod +x bin/capiscio
echo "CAPISCIO_BINARY_PATH=${{ github.workspace }}/_capiscio-core/bin/capiscio" >> $GITHUB_ENV

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev,mcp]"

- name: Start PostgreSQL
run: |
docker run -d --name test-postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=capiscio \
-p 5435:5432 \
--health-cmd "pg_isready -U postgres" \
--health-interval 5s \
--health-timeout 5s \
--health-retries 5 \
postgres:15-alpine

# Wait for PostgreSQL
for i in $(seq 1 30); do
if docker exec test-postgres pg_isready -U postgres; then
echo "PostgreSQL ready"
break
fi
sleep 1
done

- name: Build and start capiscio-server
working-directory: _capiscio-server
run: |
go build -tags cloud -o bin/capiscio-server ./cmd/server
chmod +x bin/capiscio-server

export DATABASE_URL="postgres://postgres:postgres@localhost:5435/capiscio?sslmode=disable"
export PORT=8081
export LOG_LEVEL=debug
export CA_ISSUER_URL="http://localhost:8081"
export ALLOW_AGENT_REGISTRATION=true

./bin/capiscio-server &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV

# Wait for server health
for i in $(seq 1 30); do
if curl -sf http://localhost:8081/health > /dev/null 2>&1; then
echo "Server ready at :8081"
break
fi
sleep 1
done

- name: Start capiscio-core gRPC
run: |
export CAPISCIO_BINARY_PATH="${{ github.workspace }}/_capiscio-core/bin/capiscio"
$CAPISCIO_BINARY_PATH rpc --address localhost:50051 &
CORE_PID=$!
echo "CORE_PID=$CORE_PID" >> $GITHUB_ENV
echo "CAPISCIO_CORE_ADDR=localhost:50051" >> $GITHUB_ENV

Comment on lines +118 to +125
# Wait for gRPC health
sleep 3
echo "Core gRPC started on :50051"

- name: Run live integration tests
env:
CAPISCIO_SERVER_URL: http://localhost:8081
CAPISCIO_CORE_ADDR: localhost:50051
CAPISCIO_API_KEY: test-integration-key
run: |
pytest tests/integration/ -v --tb=short -m integration \
--junitxml=integration-results.xml

Comment on lines +130 to +138
echo "Integration test run complete"

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-results
path: integration-results.xml

- name: Cleanup
if: always()
run: |
kill ${{ env.SERVER_PID }} 2>/dev/null || true
kill ${{ env.CORE_PID }} 2>/dev/null || true
docker stop test-postgres 2>/dev/null || true
docker rm test-postgres 2>/dev/null || true
63 changes: 63 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
version: '3.8'

services:
# PostgreSQL for capiscio-server
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=capiscio
ports:
- "5435:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5

# capiscio-server REST API
server:
build:
context: ../capiscio-server
dockerfile: Dockerfile
ports:
- "8081:8080"
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/capiscio?sslmode=disable
- PORT=8080
- LOG_LEVEL=debug
- CA_ISSUER_URL=http://server:8080
- ALLOW_AGENT_REGISTRATION=true
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 5s
timeout: 5s
retries: 10

# MCP Python test runner
test-runner:
build:
context: .
dockerfile: Dockerfile.test
volumes:
- .:/workspace
working_dir: /workspace
environment:
- CAPISCIO_SERVER_URL=http://server:8080
- CAPISCIO_CORE_ADDR= # empty = embedded mode (auto-download binary)
- CAPISCIO_API_KEY=test-integration-key
- PYTHONPATH=/workspace
- PYTEST_ARGS=${PYTEST_ARGS:--v}
depends_on:
server:
condition: service_healthy
command: >
pytest tests/integration/ -v --tb=short
-m integration ${PYTEST_ARGS}
Comment on lines +49 to +60

volumes:
db-data:
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"integration: live integration tests requiring capiscio-core and/or capiscio-server",
]

[tool.coverage.run]
source = ["capiscio_mcp"]
Expand Down
Empty file added tests/integration/__init__.py
Empty file.
52 changes: 52 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Pytest fixtures for live integration tests.

These tests require:
- capiscio-core gRPC server (via CAPISCIO_CORE_ADDR or embedded binary)
- capiscio-server REST API (via CAPISCIO_SERVER_URL or default localhost:8080)

Skip automatically when infrastructure is unavailable.
"""

import os
import time

import pytest

CAPISCIO_SERVER_URL = os.getenv("CAPISCIO_SERVER_URL", "http://localhost:8080")
CAPISCIO_API_KEY = os.getenv("CAPISCIO_API_KEY", "test-integration-key")


@pytest.fixture(scope="session")
def server_url():
"""Base URL for capiscio-server."""
return CAPISCIO_SERVER_URL


@pytest.fixture(scope="session")
def api_key():
"""API key for capiscio-server SDK endpoints."""
return CAPISCIO_API_KEY


@pytest.fixture(scope="session")
def wait_for_server():
"""Wait for capiscio-server to be healthy (up to 30s)."""
import requests

for i in range(30):
try:
resp = requests.get(f"{CAPISCIO_SERVER_URL}/health", timeout=2)
if resp.status_code == 200:
return True
except requests.exceptions.RequestException:
pass
time.sleep(1)
pytest.skip(f"Server not available at {CAPISCIO_SERVER_URL} after 30s")


@pytest.fixture(autouse=True)
async def _reset_core_client():
"""Reset CoreClient singleton between tests to avoid stale connections."""
yield
from capiscio_mcp._core.client import CoreClient
await CoreClient.reset()
Loading
Loading