name: Tests
permissions:
contents: read
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
# Default test credentials for Couchbase
CB_USERNAME: Administrator
CB_PASSWORD: password
CB_MCP_TEST_BUCKET: travel-sample
jobs:
# ============================================
# Integration Tests - All Transport Modes
# ============================================
integration-tests:
name: Integration (${{ matrix.transport }} transport)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
transport: ["stdio", "http", "sse"]
services:
couchbase:
image: couchbase:enterprise-8.0.0
ports:
- 8091:8091
- 8092:8092
- 8093:8093
- 8094:8094
- 8095:8095
- 8096:8096
- 9102:9102
- 11210:11210
- 11207:11207
options: >-
--health-cmd "curl -s http://localhost:8091/pools || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"
- name: Set up Python (latest)
run: uv python install 3.13
- name: Install dependencies
run: uv sync --extra dev
- name: Wait for Couchbase to be ready
run: |
echo "Waiting for Couchbase to be fully ready..."
for i in {1..60}; do
if curl -s http://localhost:8091/pools > /dev/null 2>&1; then
echo "Couchbase REST API is responding"
break
fi
echo "Waiting for Couchbase... ($i/60)"
sleep 2
done
- name: Initialize Couchbase cluster
run: |
echo "Initializing Couchbase cluster..."
# Initialize node
curl -s -X POST http://localhost:8091/nodes/self/controller/settings \
-d 'path=/opt/couchbase/var/lib/couchbase/data' \
-d 'index_path=/opt/couchbase/var/lib/couchbase/data'
# Set up services
curl -s -X POST http://localhost:8091/node/controller/setupServices \
-d 'services=kv,n1ql,index,fts'
# Set memory quotas
curl -s -X POST http://localhost:8091/pools/default \
-d 'memoryQuota=512' \
-d 'indexMemoryQuota=256' \
-d 'ftsMemoryQuota=256'
# Set credentials
curl -s -X POST http://localhost:8091/settings/web \
-d "password=${{ env.CB_PASSWORD }}" \
-d "username=${{ env.CB_USERNAME }}" \
-d 'port=SAME'
echo "Cluster initialization complete"
- name: Load travel-sample bucket
run: |
echo "Loading travel-sample bucket..."
# Wait for cluster to be fully initialized
sleep 5
# Load the travel-sample sample bucket (includes inventory scope with airline, route, hotel, airport collections)
curl -s -X POST http://localhost:8091/sampleBuckets/install \
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
-H "Content-Type: application/json" \
-d '["travel-sample"]'
# Wait for bucket to be ready (sample buckets take longer to load)
echo "Waiting for travel-sample bucket to be ready..."
for i in {1..60}; do
if curl -s -u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
http://localhost:8091/pools/default/buckets/travel-sample 2>/dev/null | grep -q '"status":"healthy"'; then
echo "Bucket is healthy"
break
fi
echo "Waiting for bucket... ($i/60)"
sleep 3
done
# Additional wait for data to be fully loaded
echo "Waiting for sample data to be fully loaded..."
sleep 10
- name: Set indexer storage mode
run: |
echo "Setting indexer storage mode..."
# Wait for indexer service to be ready
sleep 5
# Set storage mode to plasma (Couchbase 7.1+)
# Use cluster settings API which is more reliable
curl -s -X POST http://localhost:8091/settings/indexes \
-u "${{ env.CB_USERNAME }}:${{ env.CB_PASSWORD }}" \
-d 'storageMode=plasma' \
|| echo "Storage mode may already be set or indexer not ready"
# Wait for storage mode to be applied
echo "Waiting for storage mode to be applied..."
sleep 5
- name: Setup test data (indexes and queries)
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_USERNAME: ${{ env.CB_USERNAME }}
CB_PASSWORD: ${{ env.CB_PASSWORD }}
CB_MCP_TEST_BUCKET: ${{ env.CB_MCP_TEST_BUCKET }}
PYTHONPATH: src
run: |
echo "Running setup_test_data.py to create indexes and populate test data..."
uv run python scripts/setup_test_data.py
# ============================================
# STDIO Transport Tests
# ============================================
- name: Run STDIO integration tests
if: matrix.transport == 'stdio'
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_MCP_TRANSPORT: stdio
CB_MCP_TEST_SCOPE: inventory
CB_MCP_TEST_COLLECTION: airline
PYTHONPATH: src
run: |
echo "Running tests with STDIO transport..."
uv run pytest tests/ -v --tb=short
# ============================================
# HTTP Transport Tests
# ============================================
- name: Start MCP server (HTTP)
if: matrix.transport == 'http'
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_MCP_TRANSPORT: http
CB_MCP_HOST: 127.0.0.1
CB_MCP_PORT: 8000
PYTHONPATH: src
run: |
echo "Starting MCP server with HTTP transport..."
uv run python -m mcp_server &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
# Wait for server to be ready (check if port is listening)
echo "Waiting for HTTP server to be ready..."
for i in {1..30}; do
if nc -z 127.0.0.1 8000 2>/dev/null; then
echo "HTTP server is ready"
break
fi
echo "Waiting for HTTP server... ($i/30)"
sleep 1
done
- name: Run HTTP transport tests
if: matrix.transport == 'http'
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_MCP_TRANSPORT: http
CB_MCP_HOST: 127.0.0.1
CB_MCP_PORT: 8000
MCP_SERVER_URL: http://127.0.0.1:8000/mcp
CB_MCP_TEST_SCOPE: inventory
CB_MCP_TEST_COLLECTION: airline
PYTHONPATH: src
run: |
echo "Running tests with HTTP transport..."
uv run pytest tests/ -v --tb=short
- name: Stop HTTP server
if: matrix.transport == 'http' && always()
run: |
if [ -n "$SERVER_PID" ]; then
kill $SERVER_PID 2>/dev/null || true
fi
# ============================================
# SSE Transport Tests
# ============================================
- name: Start MCP server (SSE)
if: matrix.transport == 'sse'
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_MCP_TRANSPORT: sse
CB_MCP_HOST: 127.0.0.1
CB_MCP_PORT: 8000
PYTHONPATH: src
run: |
echo "Starting MCP server with SSE transport..."
uv run python -m mcp_server &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
# Wait for server to be ready (check if port is listening)
echo "Waiting for SSE server to be ready..."
for i in {1..30}; do
if nc -z 127.0.0.1 8000 2>/dev/null; then
echo "SSE server is ready"
break
fi
echo "Waiting for SSE server... ($i/30)"
sleep 1
done
- name: Run SSE transport tests
if: matrix.transport == 'sse'
env:
CB_CONNECTION_STRING: couchbase://localhost
CB_MCP_TRANSPORT: sse
CB_MCP_HOST: 127.0.0.1
CB_MCP_PORT: 8000
MCP_SERVER_URL: http://127.0.0.1:8000/sse
CB_MCP_TEST_SCOPE: inventory
CB_MCP_TEST_COLLECTION: airline
PYTHONPATH: src
run: |
echo "Running tests with SSE transport..."
uv run pytest tests/ -v --tb=short
- name: Stop SSE server
if: matrix.transport == 'sse' && always()
run: |
if [ -n "$SERVER_PID" ]; then
kill $SERVER_PID 2>/dev/null || true
fi
# ============================================
# Test Summary
# ============================================
test-summary:
name: Test Summary
permissions: {}
runs-on: ubuntu-latest
needs: [integration-tests]
if: always()
steps:
- name: Check test results
run: |
echo "=== Test Results Summary ==="
echo "Integration Tests: ${{ needs.integration-tests.result }}"
echo ""
if [ "${{ needs.integration-tests.result }}" == "failure" ]; then
echo "❌ Some tests failed"
exit 1
elif [ "${{ needs.integration-tests.result }}" == "cancelled" ]; then
echo "⚠️ Tests were cancelled"
exit 1
else
echo "✅ All tests passed!"
fi