Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:
printf '%s' "$(openssl rand -hex 32)" > "$HOME/.config/egg/lifecycle-secret"
printf 'CLAUDE_CODE_OAUTH_TOKEN=dummy\nGATEWAY_BOT_NAME=ci\n' \
> "$HOME/.config/egg/secrets.env"
printf 'local_repos:\n paths: []\n' \
printf 'github_username: ci-test-user\nbot_username: ci-bot\nwritable_repos:\n - test-owner/test-repo\nrepo_settings:\n test-owner/test-repo:\n auth_mode: bot\nlocal_repos:\n paths: []\n' \
> "$HOME/.config/egg/repositories.yaml"
chmod 600 "$HOME/.config/egg"/*

Expand Down
35 changes: 35 additions & 0 deletions integration_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,41 @@ def orchestrator_url(egg_stack: EggStack) -> str:
return egg_stack.orchestrator_url


@pytest.fixture(scope="session")
def orchestrator_mcp_url(egg_stack: EggStack) -> str:
"""Streamable-HTTP URL for the orchestrator's MCP server.

The orchestrator pod runs the MCP sidecar on container port 9850
(see ``orchestrator/api.py::_start_mcp_server``). The base Service
(``k8s/base/orchestrator-service.yaml``) only exposes the API port
(9849); the MCP port is reached via the ``hostPort: 9850`` mapping
in ``k8s/overlays/local/patches/orchestrator-volumes.yaml`` (the
overlay used by ``make deploy`` in CI and locally). Tests reach
it via ``http://localhost:9850/mcp``.

Override at test time with ``EGG_MCP_URL`` if the cluster maps the
port elsewhere. The fixture skips if the ``/health`` sidecar
endpoint is unreachable so a missing hostPort produces a clear skip
rather than a confusing connection error mid-test.
"""
import urllib.error
import urllib.request

url = os.environ.get("EGG_MCP_URL", "http://localhost:9850/mcp")
health_url = url.rsplit("/mcp", 1)[0] + "/health"
try:
with urllib.request.urlopen(health_url, timeout=10) as resp:
if resp.status != 200:
pytest.skip(f"Orchestrator MCP /health at {health_url} returned {resp.status}")
except (urllib.error.URLError, TimeoutError, ConnectionError) as exc:
pytest.skip(
f"Orchestrator MCP server not reachable at {health_url}: {exc}. "
"Integration suite expects the local-overlay hostPort mapping "
"(k8s/overlays/local/patches/orchestrator-volumes.yaml)."
)
return url


@pytest.fixture
def gateway_session(egg_stack: EggStack) -> Generator[dict[str, Any]]:
"""Function-scoped fixture: create a gateway session for isolation.
Expand Down
8 changes: 7 additions & 1 deletion integration_tests/test_babysit_pr/test_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,14 @@ def test_tool_schema_has_required_fields(self):

tool = next(t for t in PIPELINE_TOOLS if t["name"] == "babysit_pr")
required = set(tool["inputSchema"]["required"])
assert "pr_number" in required
# pr_number is intentionally not in required so the handler can return
# a structured {"error": "pr_number must be a positive integer"} envelope
# when it is omitted, rather than Pydantic raising "Field required".
# The "pr_number not in required" assertion is the property under test;
# "repo in required" is the positive companion that locks in shape.
assert "repo" in required
assert "pr_number" not in required
props = tool["inputSchema"]["properties"]
assert "pr_number" in props
assert props["pr_number"]["type"] == "integer"
assert props["repo"]["type"] == "string"
Loading
Loading