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

This commit is contained in:
a2a-cloud
2026-07-18 02:32:44 +00:00
parent 4c13d9cfd7
commit d29538ae51

View File

@@ -1,9 +1,136 @@
import base64
import json
from pathlib import Path
import pytest
from a2a_pack import LocalRunContext, PlatformUserAuth
from a2a_pack.cli.local import load_local_project
from agent import QuoteJudgeStudioV1
ROOT = Path(__file__).resolve().parents[1]
SUCCESS_QUOTES = [
{"vendor": "Acme Bearings", "unit_price": 10.75, "quantity": 500, "delivery_days": 12, "warranty_months": 12},
{"vendor": "Beta Industrial", "unit_price": 9.1, "quantity": 500, "delivery_days": 8, "warranty_months": 18},
]
SUCCESS_WEIGHTS = {"price": 50, "delivery": 30, "warranty": 20}
class FakeCursor:
def __init__(self, row=None):
self._row = row
def fetchone(self):
return self._row
class FakeConn:
store = {}
receipts = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
def execute(self, sql, params):
normalized = " ".join(sql.lower().split())
if normalized.startswith("insert into quote_comparisons"):
tenant, comparison_id, vendor, score, payload_json = params
self.store[(tenant, comparison_id)] = json.loads(payload_json)
return FakeCursor()
if normalized.startswith("select payload from quote_comparisons"):
tenant, comparison_id = params
payload = self.store.get((tenant, comparison_id))
return FakeCursor((payload,) if payload else None)
if normalized.startswith("insert into quote_execution_receipts"):
self.receipts.append(params)
return FakeCursor()
raise AssertionError(f"unexpected SQL: {sql}")
@pytest.fixture(autouse=True)
def fake_db(monkeypatch):
FakeConn.store = {}
FakeConn.receipts = []
monkeypatch.setenv("DATABASE_URL", "postgresql://example.invalid/quotejudge")
monkeypatch.setattr("agent._connect", lambda: FakeConn())
@pytest.fixture
def ctx():
return LocalRunContext(
auth=PlatformUserAuth(sub="user-123", user_id=123, email="buyer@example.com", scopes=["agent:invoke"]),
task_id="test-task",
)
@pytest.mark.asyncio
async def test_success_failure_reload_and_receipt(ctx):
agent = QuoteJudgeStudioV1()
success = await agent.invoke_json(
"compare_quotes",
ctx,
{"comparison_id": "studio-quote-v1", "quotes": SUCCESS_QUOTES, "weights": SUCCESS_WEIGHTS},
)
assert success["ok"] is True
assert success["comparison_id"] == "studio-quote-v1"
assert success["recommendation"]["vendor"] == "Beta Industrial"
assert success["quotes"][0]["weighted_score"] == 100.0
assert FakeConn.receipts, "production execution receipt should be persisted"
failure = await agent.invoke_json(
"compare_quotes",
ctx,
{
"comparison_id": "studio-quote-invalid",
"quotes": [{"vendor": "Only Vendor", "unit_price": 10, "quantity": 1, "delivery_days": 5, "warranty_months": 6}],
"weights": SUCCESS_WEIGHTS,
},
)
assert failure["ok"] is False
assert failure["code"] == "at_least_two_quotes_required"
reload = await agent.invoke_json("get_comparison", ctx, {"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
@pytest.mark.asyncio
async def test_browser_upload_bridge(ctx):
agent = QuoteJudgeStudioV1()
csv_text = "vendor,unit_price,quantity,delivery_days,warranty_months\nAcme Bearings,10.75,500,12,12\nBeta Industrial,9.10,500,8,18\n"
result = await agent.invoke_json(
"compare_quotes_upload",
ctx,
{
"comparison_id": "upload-quote-v1",
"upload": {
"filename": "quotes.csv",
"media_type": "text/csv",
"data_base64": base64.b64encode(csv_text.encode()).decode(),
},
"weights": SUCCESS_WEIGHTS,
},
)
assert result["ok"] is True
assert result["recommendation"]["vendor"] == "Beta Industrial"
invalid = await agent.invoke_json(
"compare_quotes_upload",
ctx,
{
"comparison_id": "bad-upload",
"upload": {"filename": "quotes.csv", "media_type": "text/csv", "data_base64": "%%%not-base64%%%"},
"weights": SUCCESS_WEIGHTS,
},
)
assert invalid["ok"] is False
assert invalid["code"] == "invalid_base64"
def test_full_stack_product_contract():
@@ -12,17 +139,30 @@ def test_full_stack_product_contract():
frontend = (ROOT / "frontend" / "src" / "App.jsx").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 "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 "Skill runner" not in frontend
assert "QuoteJudge" in frontend
assert "data_base64" in frontend
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()
skill_names = {skill.name for skill in card.skills}
assert {"compare_quotes", "get_comparison", "compare_quotes_upload", "compare_quotes_file"} <= skill_names
compare_schema = next(skill.input_schema for skill in card.skills if skill.name == "compare_quotes")
assert set(compare_schema["required"]) == {"comparison_id", "quotes", "weights"}
upload_schema = next(skill.input_schema for skill in card.skills if skill.name == "compare_quotes_file")
assert upload_schema["properties"]["document"]["x-a2a-file-upload"]["max_bytes"] == 65536
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.mcp_endpoint == "/mcp"
assert card.runtime.pricing.caller_pays_llm is False