e4224eb8ec
- 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
147 lines
4.7 KiB
Python
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()
|