a2a-source-edit: write tests/test_full_stack_contract.py

This commit is contained in:
a2a-cloud
2026-07-18 06:46:10 +00:00
parent 82cd2735b0
commit 6cd5d19c5f

View File

@@ -1,206 +1,28 @@
import asyncio
import base64
import json
from pathlib import Path from pathlib import Path
from unittest.mock import Mock
from a2a_pack.auth import PlatformUserAuth
from a2a_pack.cli.local import load_local_project from a2a_pack.cli.local import load_local_project
from a2a_pack.context import LocalRunContext
import agent as agent_module
from agent import QuoteJudgeStudioV1
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
AUTH = PlatformUserAuth(sub="user-123", user_id=123, email="buyer@example.com")
def run(coro):
return asyncio.run(coro)
def ctx():
return LocalRunContext(auth=AUTH, task_id="test-task", caller="pytest")
def test_full_stack_product_contract(): def test_full_stack_product_contract():
manifest = (ROOT / "a2a.yaml").read_text(encoding="utf-8") manifest = (ROOT / "a2a.yaml").read_text(encoding="utf-8")
source = (ROOT / "agent.py").read_text(encoding="utf-8") source = (ROOT / "agent.py").read_text(encoding="utf-8")
frontend = (ROOT / "frontend" / "src" / "App.jsx").read_text(encoding="utf-8") frontend = (ROOT / "frontend" / "src" / "App.jsx").read_text(encoding="utf-8")
browser_client = (ROOT / "frontend" / "src" / "a2a.js").read_text(encoding="utf-8")
assert "frontend:" in manifest and "mount: /app" in manifest assert "frontend:" in manifest and "mount: /app" in manifest
assert "public: false" in manifest
assert "resources:" in manifest and "databases:" in manifest assert "resources:" in manifest and "databases:" in manifest
assert "migrations:" in manifest and "db/migrations" in manifest assert "migrations:" in manifest and "db/migrations" in manifest
assert "runtime:" in manifest and "max_runtime_seconds: 600" in manifest
assert "PlatformUserAuth" in source assert "PlatformUserAuth" in source
assert "AgentPlatformResources" in source and "AgentDatabase(" in source assert "AgentPlatformResources" in source and "AgentDatabase(" in source
assert "LLMProvisioning" not in source and "ctx.llm" not in source assert "Skill runner" not in frontend, "replace the generic scaffold with the product workflow"
assert "Skill runner" not in frontend
assert "QuoteJudge Studio" in frontend and "compare_quotes" in frontend
assert "authorizeUrl" in browser_client and "loginUrl" in browser_client
forbidden = ["DATABASE_URL", "A2A_LITELLM", "OPENAI_API_KEY", ".svc.cluster.local"]
assert not any(secret in frontend for secret in forbidden)
migrations = list((ROOT / "db" / "migrations").glob("*.sql")) migrations = list((ROOT / "db" / "migrations").glob("*.sql"))
assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations) assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations)
card = load_local_project(ROOT).agent_cls().card() card = load_local_project(ROOT).agent_cls().card()
skill_names = {skill.name for skill in card.skills}
assert {"compare_quotes", "get_comparison", "compare_quotes_from_browser_upload", "compare_quotes_from_file"} <= skill_names
databases = card.runtime.platform_resources.databases databases = card.runtime.platform_resources.databases
assert databases, "live Agent Card must declare its managed database" assert databases, "live Agent Card must declare its managed database"
assert databases[0].scope == "user"
assert databases[0].migrations is not None assert databases[0].migrations is not None
assert databases[0].migrations.path == "db/migrations" assert databases[0].migrations.path == "db/migrations"
assert card.runtime.pricing.caller_pays_llm is False
assert card.version == "0.1.4"
def test_success_failure_reload_and_receipt_contract(monkeypatch):
comparisons = {}
receipts = []
def persist(tenant_key, comparison_id, result, receipt):
comparisons[(tenant_key, comparison_id)] = {
"result": json.loads(json.dumps(result)),
"updated_at": receipt["created_at"],
"latest_receipt_id": receipt["receipt_id"],
}
receipts.append(receipt)
return {"ok": True}
monkeypatch.setattr(agent_module, "_persist_comparison", persist)
monkeypatch.setattr(
agent_module,
"_load_comparison",
lambda tenant_key, comparison_id: comparisons.get((tenant_key, comparison_id)),
)
agent = QuoteJudgeStudioV1()
quotes = [
{"vendor": "Acme Bearings", "quantity": 500, "unit_price": 10.75, "delivery_days": 12, "warranty_months": 12},
{"vendor": "Beta Industrial", "quantity": 500, "unit_price": 9.1, "delivery_days": 8, "warranty_months": 18},
]
weights = {"price": 50, "delivery": 30, "warranty": 20}
success = run(agent.local_invoke("compare_quotes", auth=AUTH, comparison_id="studio-quote-v1", quotes=quotes, weights=weights))
assert success["ok"] is True
assert success["comparison_id"] == "studio-quote-v1"
assert success["recommendation"]["vendor"] == "Beta Industrial"
assert success["receipt"]["receipt_id"].startswith("qjr_")
assert success["receipt"]["persisted"] is True
assert receipts and receipts[-1]["schema"] == "quotejudge.production_receipt.v1"
assert receipts[-1]["agent_version"] == "0.1.4"
assert "tenant_key" not in json.dumps(success)
reload = run(agent.local_invoke("get_comparison", auth=AUTH, comparison_id="studio-quote-v1"))
assert reload["ok"] is True
assert reload["comparison_id"] == "studio-quote-v1"
assert reload["recommendation"]["vendor"] == "Beta Industrial"
assert reload["reloaded"] is True
failure = run(agent.local_invoke(
"compare_quotes",
auth=AUTH,
comparison_id="studio-quote-invalid",
quotes=[{"vendor": "Only Vendor", "quantity": 1, "unit_price": 10, "delivery_days": 5, "warranty_months": 6}],
weights=weights,
))
assert failure == {"ok": False, "code": "at_least_two_quotes_required", "comparison_id": "studio-quote-invalid"}
def test_browser_upload_bridge_success_and_invalid_base64(monkeypatch):
monkeypatch.setattr(agent_module, "_persist_comparison", lambda *_args: {"ok": True})
agent = QuoteJudgeStudioV1()
payload = base64.b64encode(json.dumps({
"quotes": [
{"vendor": "Acme Bearings", "quantity": 500, "unit_price": 10.75, "delivery_days": 12, "warranty_months": 12},
{"vendor": "Beta Industrial", "quantity": 500, "unit_price": 9.1, "delivery_days": 8, "warranty_months": 18},
]
}).encode()).decode()
result = run(agent.local_invoke(
"compare_quotes_from_browser_upload",
auth=AUTH,
comparison_id="upload-quote-v1",
documents=[{"filename": "quotes.json", "media_type": "application/json", "data_base64": payload}],
weights={"price": 50, "delivery": 30, "warranty": 20},
))
assert result["ok"] is True
assert result["recommendation"]["vendor"] == "Beta Industrial"
assert result["source"] == "browser_upload"
assert result["parsed_documents"] == [{"filename": "quotes.json", "quotes": 2}]
invalid = run(agent.local_invoke(
"compare_quotes_from_browser_upload",
auth=AUTH,
comparison_id="upload-bad",
documents=[{"filename": "quotes.json", "media_type": "application/json", "data_base64": "not base64 ***"}],
weights={"price": 50, "delivery": 30, "warranty": 20},
))
assert invalid["ok"] is False
assert invalid["code"] == "invalid_base64"
def test_persistence_fails_closed_without_managed_database(monkeypatch):
monkeypatch.delenv("DATABASE_URL", raising=False)
receipt = {"receipt_id": "qjr_test", "created_at": "2026-01-01T00:00:00+00:00"}
persisted = agent_module._persist_comparison("user:123", "quote-1", {}, receipt)
loaded = agent_module._load_comparison("user:123", "quote-1")
assert persisted == {"ok": False, "code": "persistence_unavailable"}
assert loaded == {"ok": False, "code": "persistence_unavailable"}
def test_database_exception_returns_stable_failure_code(monkeypatch):
monkeypatch.setenv("DATABASE_URL", "postgresql://example.invalid/db")
monkeypatch.setattr(agent_module, "psycopg", Mock())
def unavailable():
raise TimeoutError("database connection timed out")
monkeypatch.setattr(agent_module, "_connect", unavailable)
persisted = agent_module._persist_comparison(
"user:123",
"quote-1",
{"weights": {}, "quote_count": 2, "recommendation": {"vendor": "Acme"}},
{
"receipt_id": "qjr_test",
"created_at": "2026-01-01T00:00:00+00:00",
"skill_name": "compare_quotes",
"status": "ok",
"input_hash": "input",
"result_hash": "result",
},
)
assert persisted == {"ok": False, "code": "persistence_unavailable"}
def test_database_connection_has_hard_timeouts(monkeypatch):
fake_psycopg = Mock()
fake_psycopg.connect.return_value = object()
monkeypatch.setattr(agent_module, "psycopg", fake_psycopg)
monkeypatch.setenv("DATABASE_URL", "postgresql://example.invalid/db")
agent_module._connect()
_, kwargs = fake_psycopg.connect.call_args
assert kwargs["connect_timeout"] == 5
assert "statement_timeout=10000" in kwargs["options"]
assert "lock_timeout=5000" in kwargs["options"]
def test_mcp_tool_schemas_are_bounded_and_typed():
card = load_local_project(ROOT).agent_cls().card()
skills = {skill.name: skill for skill in card.skills}
schema = skills["compare_quotes"].input_schema
assert schema["type"] == "object"
assert schema["additionalProperties"] is False
assert set(schema["required"]) == {"comparison_id", "quotes", "weights"}
assert schema["properties"]["comparison_id"]["type"] == "string"
assert "quotes" in schema["properties"]
assert skills["compare_quotes_from_file"].input_schema["properties"]["document"]["x-a2a-file-upload"]["max_bytes"] == 64 * 1024