From f921412f0125ee516872d10395c657e149de8d4c Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 30 Dec 2025 19:11:04 -0500 Subject: [PATCH] feat: add test isolation scripts with dynamic port discovery - Add get-test-instance-id.sh for branch-based container isolation - Add run-integration-tests.sh for full test lifecycle management - Update integration tests to read service URLs from environment variables (GRIST_MCP_URL, MOCK_GRIST_URL) with fallback defaults --- scripts/get-test-instance-id.sh | 7 ++++ scripts/run-integration-tests.sh | 39 +++++++++++++++++++++ tests/integration/conftest.py | 5 +-- tests/integration/test_mcp_protocol.py | 3 +- tests/integration/test_tools_integration.py | 5 +-- 5 files changed, 54 insertions(+), 5 deletions(-) create mode 100755 scripts/get-test-instance-id.sh create mode 100755 scripts/run-integration-tests.sh diff --git a/scripts/get-test-instance-id.sh b/scripts/get-test-instance-id.sh new file mode 100755 index 0000000..911b8bc --- /dev/null +++ b/scripts/get-test-instance-id.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# scripts/get-test-instance-id.sh +# Generate a unique instance ID from git branch for parallel test isolation + +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") +# Sanitize: replace non-alphanumeric with dash, limit length +echo "$BRANCH" | sed 's/[^a-zA-Z0-9]/-/g' | cut -c1-20 diff --git a/scripts/run-integration-tests.sh b/scripts/run-integration-tests.sh new file mode 100755 index 0000000..82cc3b2 --- /dev/null +++ b/scripts/run-integration-tests.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# scripts/run-integration-tests.sh +# Run integration tests with branch isolation and dynamic port discovery +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Get branch-based instance ID +TEST_INSTANCE_ID=$("$SCRIPT_DIR/get-test-instance-id.sh") +export TEST_INSTANCE_ID + +echo "Test instance ID: $TEST_INSTANCE_ID" + +# Start containers +cd "$PROJECT_ROOT/deploy/test" +docker compose up -d --build --wait + +# Discover dynamic ports +GRIST_MCP_PORT=$(docker compose port grist-mcp 3000 | cut -d: -f2) +MOCK_GRIST_PORT=$(docker compose port mock-grist 8484 | cut -d: -f2) + +echo "grist-mcp available at: http://localhost:$GRIST_MCP_PORT" +echo "mock-grist available at: http://localhost:$MOCK_GRIST_PORT" + +# Export for tests +export GRIST_MCP_URL="http://localhost:$GRIST_MCP_PORT" +export MOCK_GRIST_URL="http://localhost:$MOCK_GRIST_PORT" + +# Run tests +cd "$PROJECT_ROOT" +TEST_EXIT=0 +uv run pytest tests/integration/ -v || TEST_EXIT=$? + +# Cleanup +cd "$PROJECT_ROOT/deploy/test" +docker compose down -v + +exit $TEST_EXIT diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 5560e55..4e1ff1f 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,13 +1,14 @@ """Fixtures for integration tests.""" +import os import time import httpx import pytest -GRIST_MCP_URL = "http://localhost:3000" -MOCK_GRIST_URL = "http://localhost:8484" +GRIST_MCP_URL = os.environ.get("GRIST_MCP_URL", "http://localhost:3000") +MOCK_GRIST_URL = os.environ.get("MOCK_GRIST_URL", "http://localhost:8484") MAX_WAIT_SECONDS = 30 diff --git a/tests/integration/test_mcp_protocol.py b/tests/integration/test_mcp_protocol.py index 0471b90..0e0d237 100644 --- a/tests/integration/test_mcp_protocol.py +++ b/tests/integration/test_mcp_protocol.py @@ -1,5 +1,6 @@ """Test MCP protocol compliance over SSE transport.""" +import os from contextlib import asynccontextmanager import pytest @@ -7,7 +8,7 @@ from mcp import ClientSession from mcp.client.sse import sse_client -GRIST_MCP_URL = "http://localhost:3000" +GRIST_MCP_URL = os.environ.get("GRIST_MCP_URL", "http://localhost:3000") @asynccontextmanager diff --git a/tests/integration/test_tools_integration.py b/tests/integration/test_tools_integration.py index 2f3e6ad..e7d7ff2 100644 --- a/tests/integration/test_tools_integration.py +++ b/tests/integration/test_tools_integration.py @@ -1,6 +1,7 @@ """Test tool calls through MCP client to verify Grist API interactions.""" import json +import os from contextlib import asynccontextmanager import httpx @@ -9,8 +10,8 @@ from mcp import ClientSession from mcp.client.sse import sse_client -GRIST_MCP_URL = "http://localhost:3000" -MOCK_GRIST_URL = "http://localhost:8484" +GRIST_MCP_URL = os.environ.get("GRIST_MCP_URL", "http://localhost:3000") +MOCK_GRIST_URL = os.environ.get("MOCK_GRIST_URL", "http://localhost:8484") @asynccontextmanager