Files
cleveragents-core/robot/helper_agent_card.py
T
HAL9000 e4224eb8ec fix(a2a): resolve JSON-RPC protocol conformance and code quality issues
- Fix malformed JSON parse error: wrap request.json() in try/except,
  return -32700 Parse error instead of unhandled HTTP 500
- Fix JSON-RPC error responses: add required 'id' field to all error
  responses per JSON-RPC 2.0 Section 5
- Fix HTTP status codes: return HTTP 200 for all JSON-RPC responses
  (error codes expressed in JSON body per JSON-RPC 2.0 over HTTP)
- Fix error message leakage: return generic messages instead of raw
  Pydantic ValidationError internals (CWE-209)
- Add catch-all exception handler returning -32603 Internal error
- Fix Agent Card url field: use base server URL without /a2a suffix;
  interfaces[0].url correctly uses endpoint URL with /a2a suffix
- Fix 0.0.0.0 host: substitute 127.0.0.1 for Agent Card URL
- Fix context typing: replace context: Any with context: Context in
  all step files per project convention
- Fix inline imports: move all imports to top of step files
- Fix _COMMANDS typing: use dict[str, Callable[[], None]] to avoid
  type: ignore suppression in helper files
- Add BDD scenarios for entity-sync and namespace-mgmt skill enumeration
- Update server_lifecycle.feature: correct HTTP status assertions to 200

ISSUES CLOSED: #867
2026-06-11 19:58:52 -04:00

147 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""Integration test helper for Agent Card discovery tests.
Each sub-command exercises a real import / instantiation path and prints
a unique sentinel on success. Robot tests parse the output for that
sentinel.
"""
from __future__ import annotations
import json
import sys
from collections.abc import Callable
def _test_build_card() -> None:
"""Build an Agent Card from a facade and verify structure."""
from cleveragents.a2a.agent_card import build_agent_card
from cleveragents.a2a.facade import A2aLocalFacade
facade = A2aLocalFacade()
card = build_agent_card(facade=facade)
assert card.name == "CleverAgents"
assert card.version == "1.0"
assert card.url
assert len(card.skills) > 0
assert len(card.extensions) > 0
assert len(card.supportedVersions) > 0
assert len(card.supportedOperations) > 0
print("build-card-ok")
def _test_conformance() -> None:
"""Validate Agent Card A2A conformance."""
from cleveragents.a2a.agent_card import (
build_agent_card,
validate_agent_card_conformance,
)
from cleveragents.a2a.facade import A2aLocalFacade
facade = A2aLocalFacade()
card = build_agent_card(facade=facade)
violations = validate_agent_card_conformance(card)
assert violations == [], f"Conformance violations: {violations}"
print("conformance-ok")
def _test_endpoint() -> None:
"""Exercise the /.well-known/agent.json endpoint via TestClient."""
from fastapi.testclient import TestClient
from cleveragents.a2a.facade import A2aLocalFacade
from cleveragents.infrastructure.server.asgi_app import create_asgi_app
facade = A2aLocalFacade()
app = create_asgi_app(facade=facade)
client = TestClient(app)
resp = client.get("/.well-known/agent.json")
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "CleverAgents"
assert "skills" in data
assert "supportedVersions" in data
assert "supportedOperations" in data
assert isinstance(data["skills"], list)
assert len(data["skills"]) > 0
assert isinstance(data["supportedOperations"], list)
assert len(data["supportedOperations"]) > 0
# Verify all operations have the correct prefix
for op in data["supportedOperations"]:
assert op.startswith("_cleveragents/"), f"Bad prefix: {op}"
print("endpoint-ok")
def _test_serialization() -> None:
"""Verify Agent Card serializes to valid JSON dict."""
from cleveragents.a2a.agent_card import (
agent_card_to_dict,
build_agent_card,
)
from cleveragents.a2a.facade import A2aLocalFacade
facade = A2aLocalFacade()
card = build_agent_card(facade=facade)
data = agent_card_to_dict(card)
# Round-trip through JSON to ensure serializability
serialized = json.dumps(data)
parsed = json.loads(serialized)
assert parsed["name"] == "CleverAgents"
assert "skills" in parsed
assert "supportedOperations" in parsed
print("serialization-ok")
def _test_skill_enumeration() -> None:
"""Verify skills are correctly enumerated from facade operations."""
from cleveragents.a2a.agent_card import build_agent_card
from cleveragents.a2a.facade import A2aLocalFacade
facade = A2aLocalFacade()
card = build_agent_card(facade=facade)
skill_ids = [s.id for s in card.skills]
assert "plan-lifecycle" in skill_ids, f"Missing plan-lifecycle: {skill_ids}"
assert "registry-crud" in skill_ids, f"Missing registry-crud: {skill_ids}"
assert "context-mgmt" in skill_ids, f"Missing context-mgmt: {skill_ids}"
assert "health-diagnostics" in skill_ids, f"Missing health-diagnostics: {skill_ids}"
print("skill-enumeration-ok")
def _test_version_info() -> None:
"""Verify version negotiation information in Agent Card."""
from cleveragents.a2a.agent_card import build_agent_card
from cleveragents.a2a.facade import A2aLocalFacade
from cleveragents.a2a.models import A2aVersion
facade = A2aLocalFacade()
card = build_agent_card(facade=facade)
assert card.version == A2aVersion.CURRENT
assert A2aVersion.CURRENT in card.supportedVersions
for v in A2aVersion.SUPPORTED:
assert v in card.supportedVersions
print("version-info-ok")
_COMMANDS: dict[str, Callable[[], None]] = {
"build-card": _test_build_card,
"conformance": _test_conformance,
"endpoint": _test_endpoint,
"serialization": _test_serialization,
"skill-enumeration": _test_skill_enumeration,
"version-info": _test_version_info,
}
def main() -> None:
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
sys.exit(2)
handler = _COMMANDS[sys.argv[1]]
handler()
if __name__ == "__main__":
main()