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

This commit is contained in:
a2a-cloud
2026-07-18 05:12:24 +00:00
parent 931e2b81cd
commit 1e353f7a48

View File

@@ -1,28 +1,113 @@
import asyncio
import base64
from pathlib import Path
from a2a_pack import LocalRunContext, PlatformUserAuth
from a2a_pack.cli.local import load_local_project
from agent import QuoteJudgeStudioV1, _MEMORY_COMPARISONS, _MEMORY_RECEIPTS
ROOT = Path(__file__).resolve().parents[1]
SUCCESS_QUOTES = [
{"delivery_days": 12, "quantity": 500, "unit_price": 10.75, "vendor": "Acme Bearings", "warranty_months": 12},
{"delivery_days": 8, "quantity": 500, "unit_price": 9.1, "vendor": "Beta Industrial", "warranty_months": 18},
]
WEIGHTS = {"delivery": 30, "price": 50, "warranty": 20}
def _ctx():
return LocalRunContext(auth=PlatformUserAuth(sub="user-123", user_id=123, email="buyer@example.test"))
def _run(coro):
return asyncio.run(coro)
def test_full_stack_product_contract():
manifest = (ROOT / "a2a.yaml").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")
browser_client = (ROOT / "frontend" / "src" / "a2a.js").read_text(encoding="utf-8")
assert "frontend:" in manifest and "mount: /app" in manifest
assert "public: false" in manifest
assert "resources:" in manifest and "databases:" in manifest
assert "migrations:" in manifest and "db/migrations" in manifest
assert "runtime:" in manifest and "max_runtime_seconds: 120" in manifest
assert "PlatformUserAuth" in source
assert "AgentPlatformResources" in source and "AgentDatabase(" in source
assert "Skill runner" not in frontend, "replace the generic scaffold with the product workflow"
assert "LLMProvisioning" not in source
assert "ctx.llm" not in source
assert "A2A_LITELLM_KEY" not in source and "OPENAI_API_KEY" not in source
assert "Skill runner" not in frontend
assert "QuoteJudge Studio" in frontend
assert "compare_uploaded_quotes_browser" in frontend
assert "compare_uploaded_quote_file" in source
assert "config.json" in browser_client
assert "DATABASE_URL" not in frontend
migrations = list((ROOT / "db" / "migrations").glob("*.sql"))
assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations)
card = load_local_project(ROOT).agent_cls().card()
skills = {skill.name: skill for skill in card.skills}
for name in ["compare_quotes", "get_comparison", "compare_uploaded_quotes_browser", "compare_uploaded_quote_file"]:
assert name in skills
compare_schema = skills["compare_quotes"].input_schema
assert set(compare_schema["required"]) == {"comparison_id", "quotes", "weights"}
assert "quotes" in compare_schema["properties"]
upload_schema = skills["compare_uploaded_quote_file"].input_schema
assert upload_schema["properties"]["quote_file"]["x-a2a-file-upload"]["required_upload"] is True
databases = card.runtime.platform_resources.databases
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.path == "db/migrations"
assert card.runtime.pricing.caller_pays_llm is False
def test_success_failure_reload_and_receipt_memory_path(monkeypatch):
monkeypatch.delenv("DATABASE_URL", raising=False)
_MEMORY_COMPARISONS.clear()
_MEMORY_RECEIPTS.clear()
agent = QuoteJudgeStudioV1()
ctx = _ctx()
success = _run(agent.invoke("compare_quotes", ctx, comparison_id="studio-quote-v1", quotes=SUCCESS_QUOTES, weights=WEIGHTS))
assert success["ok"] is True
assert success["comparison_id"] == "studio-quote-v1"
assert success["recommendation"]["vendor"] == "Beta Industrial"
assert success["receipt"]["production"] is True
assert success["receipt"]["tool_name"] == "compare_quotes"
assert "api_key" not in str(success).lower()
reload = _run(agent.invoke("get_comparison", _ctx(), comparison_id="studio-quote-v1"))
assert reload["ok"] is True
assert reload["recommendation"]["vendor"] == "Beta Industrial"
assert reload["receipt"]["tool_name"] == "get_comparison"
failure = _run(agent.invoke("compare_quotes", _ctx(), comparison_id="studio-quote-invalid", quotes=[{"delivery_days": 5, "quantity": 1, "unit_price": 10, "vendor": "Only Vendor", "warranty_months": 6}], weights=WEIGHTS))
assert failure["ok"] is False
assert failure["code"] == "at_least_two_quotes_required"
assert failure["issues"][0]["action"]
assert len(_MEMORY_RECEIPTS) >= 3
def test_browser_upload_bridge_success_and_validation(monkeypatch):
monkeypatch.delenv("DATABASE_URL", raising=False)
_MEMORY_COMPARISONS.clear()
agent = QuoteJudgeStudioV1()
payload = base64.b64encode((
"vendor,unit_price,quantity,delivery_days,warranty_months\n"
"Acme Bearings,10.75,500,12,12\n"
"Beta Industrial,9.10,500,8,18\n"
).encode("utf-8")).decode("ascii")
result = _run(agent.invoke("compare_uploaded_quotes_browser", _ctx(), comparison_id="studio-quote-v1", documents=[{"filename": "quotes.csv", "media_type": "text/csv", "data_base64": payload}], weights=WEIGHTS))
assert result["ok"] is True
assert result["recommendation"]["vendor"] == "Beta Industrial"
invalid = _run(agent.invoke("compare_uploaded_quotes_browser", _ctx(), comparison_id="bad-upload", documents=[{"filename": "quotes.csv", "media_type": "text/csv", "data_base64": "not base64!!!"}], weights=WEIGHTS))
assert invalid["ok"] is False
assert invalid["code"] == "invalid_base64"