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

This commit is contained in:
a2a-cloud
2026-07-18 06:53:04 +00:00
parent 9d5a2b1373
commit 611d4675fc

View File

@@ -1,9 +1,7 @@
import asyncio import pytest
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
import pytest
from a2a_pack import LocalRunContext, PlatformUserAuth, UploadedFile from a2a_pack import LocalRunContext, PlatformUserAuth, UploadedFile
from a2a_pack.cli.local import load_local_project from a2a_pack.cli.local import load_local_project
from a2a_pack.workspace import LocalWorkspaceClient from a2a_pack.workspace import LocalWorkspaceClient
@@ -16,30 +14,15 @@ ROOT = Path(__file__).resolve().parents[1]
SUCCESS_ARGS = { SUCCESS_ARGS = {
"comparison_id": "studio-quote-v1", "comparison_id": "studio-quote-v1",
"quotes": [ "quotes": [
{ {"delivery_days": 12, "quantity": 500, "unit_price": 10.75, "vendor": "Acme Bearings", "warranty_months": 12},
"delivery_days": 12, {"delivery_days": 8, "quantity": 500, "unit_price": 9.1, "vendor": "Beta Industrial", "warranty_months": 18},
"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}, "weights": {"delivery": 30, "price": 50, "warranty": 20},
} }
def ctx() -> LocalRunContext[PlatformUserAuth]: def auth() -> PlatformUserAuth:
return LocalRunContext( return PlatformUserAuth(sub="user-123", user_id=123, email="buyer@example.com")
auth=PlatformUserAuth(sub="user-123", user_id=123, email="buyer@example.com"),
task_id="test-task",
)
def test_full_stack_product_contract(): def test_full_stack_product_contract():
@@ -68,13 +51,11 @@ def test_full_stack_product_contract():
card = load_local_project(ROOT).agent_cls().card() card = load_local_project(ROOT).agent_cls().card()
assert card.version == "0.1.0" assert card.version == "0.1.0"
skill_names = {skill.name for skill in card.skills} skill_names = {skill.name for skill in card.skills}
assert {"compare_quotes", "get_comparison", "compare_quotes_file", "compare_quotes_browser_upload"} <= skill_names assert {"compare_quotes", "get_comparison", "compare_quotes_file", "compare_quotes_upload", "compare_quotes_browser_upload"} <= 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 and databases[0].name == "quote-judge-studio-v1-data"
assert databases[0].name == "quote-judge-studio-v1-data"
assert databases[0].scope == "user" assert databases[0].scope == "user"
assert databases[0].migrations is not None assert databases[0].migrations and databases[0].migrations.path == "db/migrations"
assert databases[0].migrations.path == "db/migrations"
assert card.runtime.pricing.caller_pays_llm is False assert card.runtime.pricing.caller_pays_llm is False
compare_schema = next(skill.input_schema for skill in card.skills if skill.name == "compare_quotes") compare_schema = next(skill.input_schema for skill in card.skills if skill.name == "compare_quotes")
@@ -104,30 +85,24 @@ async def test_success_failure_reload_and_receipt(monkeypatch):
monkeypatch.setattr(quote_agent, "_persist_receipt_best_effort", fake_receipt) monkeypatch.setattr(quote_agent, "_persist_receipt_best_effort", fake_receipt)
app = QuoteJudgeStudioV1() app = QuoteJudgeStudioV1()
success = await app.local_invoke("compare_quotes", auth=ctx().auth, **SUCCESS_ARGS) success = await app.local_invoke("compare_quotes", auth=auth(), **SUCCESS_ARGS)
assert success["ok"] is True assert success["ok"] is True
assert success["comparison_id"] == "studio-quote-v1" assert success["comparison_id"] == "studio-quote-v1"
assert success["recommendation"]["vendor"] == "Beta Industrial" assert success["recommendation"]["vendor"] == "Beta Industrial"
assert success["execution_receipt"]["persisted"] is True assert success["execution_receipt"]["persisted"] is True
assert success["receipt"]["persisted"] is True
reload = await app.local_invoke("get_comparison", auth=ctx().auth, comparison_id="studio-quote-v1") reload = await app.local_invoke("get_comparison", auth=auth(), comparison_id="studio-quote-v1")
assert reload["ok"] is True assert reload["ok"] is True
assert reload["recommendation"]["vendor"] == "Beta Industrial" assert reload["recommendation"]["vendor"] == "Beta Industrial"
failure_args = { failure = await app.local_invoke(
"comparison_id": "studio-quote-invalid", "compare_quotes",
"quotes": [ auth=auth(),
{ comparison_id="studio-quote-invalid",
"delivery_days": 5, quotes=[{"delivery_days": 5, "quantity": 1, "unit_price": 10, "vendor": "Only Vendor", "warranty_months": 6}],
"quantity": 1, weights={"delivery": 30, "price": 50, "warranty": 20},
"unit_price": 10, )
"vendor": "Only Vendor",
"warranty_months": 6,
}
],
"weights": {"delivery": 30, "price": 50, "warranty": 20},
}
failure = await app.local_invoke("compare_quotes", auth=ctx().auth, **failure_args)
assert failure["ok"] is False assert failure["ok"] is False
assert failure["code"] == "at_least_two_quotes_required" assert failure["code"] == "at_least_two_quotes_required"
assert any(r["skill_name"] == "compare_quotes" and r["status"] == "ok" for r in receipts) assert any(r["skill_name"] == "compare_quotes" and r["status"] == "ok" for r in receipts)
@@ -150,23 +125,26 @@ async def test_browser_upload_bridge(monkeypatch):
app = QuoteJudgeStudioV1() app = QuoteJudgeStudioV1()
result = await app.local_invoke( result = await app.local_invoke(
"compare_quotes_browser_upload", "compare_quotes_browser_upload",
auth=ctx().auth, auth=auth(),
comparison_id="studio-quote-v1", comparison_id="studio-quote-v1",
weights={"delivery": 30, "price": 50, "warranty": 20}, weights={"delivery": 30, "price": 50, "warranty": 20},
documents=[ documents=[{"filename": "quotes.csv", "media_type": "text/csv", "data_base64": base64.b64encode(csv_text.encode()).decode()}],
{
"filename": "quotes.csv",
"media_type": "text/csv",
"data_base64": base64.b64encode(csv_text.encode()).decode(),
}
],
) )
assert result["ok"] is True assert result["ok"] is True
assert result["recommendation"]["vendor"] == "Beta Industrial" assert result["recommendation"]["vendor"] == "Beta Industrial"
one_file = await app.local_invoke(
"compare_quotes_upload",
auth=auth(),
comparison_id="studio-quote-v1",
weights={"delivery": 30, "price": 50, "warranty": 20},
upload={"filename": "quotes.csv", "media_type": "text/csv", "data_base64": base64.b64encode(csv_text.encode()).decode()},
)
assert one_file["ok"] is True
bad = await app.local_invoke( bad = await app.local_invoke(
"compare_quotes_browser_upload", "compare_quotes_browser_upload",
auth=ctx().auth, auth=auth(),
comparison_id="studio-quote-v1", comparison_id="studio-quote-v1",
weights={"delivery": 30, "price": 50, "warranty": 20}, weights={"delivery": 30, "price": 50, "warranty": 20},
documents=[{"filename": "bad.csv", "media_type": "text/csv", "data_base64": "not base64"}], documents=[{"filename": "bad.csv", "media_type": "text/csv", "data_base64": "not base64"}],
@@ -187,16 +165,10 @@ async def test_external_file_upload_path(monkeypatch):
monkeypatch.setattr(quote_agent, "_persist_receipt_best_effort", fake_receipt) monkeypatch.setattr(quote_agent, "_persist_receipt_best_effort", fake_receipt)
workspace = LocalWorkspaceClient( workspace = LocalWorkspaceClient(
{ {"uploads/quotes.csv": b"vendor,unit_price,quantity,delivery_days,warranty_months\nAcme Bearings,10.75,500,12,12\nBeta Industrial,9.1,500,8,18\n"},
"uploads/quotes.csv": b"vendor,unit_price,quantity,delivery_days,warranty_months\nAcme Bearings,10.75,500,12,12\nBeta Industrial,9.1,500,8,18\n"
},
access=QuoteJudgeStudioV1.workspace_access, access=QuoteJudgeStudioV1.workspace_access,
) )
local_ctx = LocalRunContext( local_ctx = LocalRunContext(auth=auth(), workspace=workspace, task_id="file-test")
auth=ctx().auth,
workspace=workspace,
task_id="file-test",
)
result = await QuoteJudgeStudioV1().invoke( result = await QuoteJudgeStudioV1().invoke(
"compare_quotes_file", "compare_quotes_file",
local_ctx, local_ctx,