diff --git a/tests/test_full_stack_contract.py b/tests/test_full_stack_contract.py index f6a0dd4..e8100bc 100644 --- a/tests/test_full_stack_contract.py +++ b/tests/test_full_stack_contract.py @@ -1,7 +1,18 @@ +import asyncio +import base64 from pathlib import Path +from a2a_pack.auth import PlatformUserAuth from a2a_pack.cli.local import load_local_project +from a2a_pack.context import LocalRunContext +from agent import ( + BrowserDocument, + DocumentRequest, + HighUtilityOnePageStartupsWE3c73, + OUTPUT_FILENAME, + _decode_browser_document, +) ROOT = Path(__file__).resolve().parents[1] @@ -11,20 +22,79 @@ def test_full_stack_product_contract(): source = (ROOT / "agent.py").read_text(encoding="utf-8") frontend = (ROOT / "frontend" / "src" / "App.jsx").read_text(encoding="utf-8") frontend_client = (ROOT / "frontend" / "src" / "a2a.js").read_text(encoding="utf-8") + migration = (ROOT / "db" / "migrations" / "001_app_records.sql").read_text(encoding="utf-8") assert "frontend:" in manifest and "mount: /app" in manifest + assert "auth: inherit" in manifest assert "resources:" in manifest and "databases:" in manifest assert "migrations:" in manifest and "db/migrations" in manifest + assert "runtime:" in manifest and "account_access:" in manifest assert "PlatformUserAuth" in source + assert "AccountAccess(required=True, platform_skill_calls=3, after_trial=\"byok\")" in source assert "AgentPlatformResources" in source and "AgentDatabase(" in source + assert "ctx.write_artifact" in source and "ctx.emit_artifact" in source assert "Skill runner" not in frontend, "replace the generic scaffold with the product workflow" + assert "data-testid=\"agent-app\"" in frontend + assert "data-testid=\"agent-submit\"" in frontend + assert "data-testid=\"agent-result\"" in frontend + assert "data-testid=\"agent-download\"" in frontend assert "unwrapInvokeResponse" in frontend_client, "unwrap the /invoke result envelope before rendering" - - migrations = list((ROOT / "db" / "migrations").glob("*.sql")) - assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations) + assert "DATABASE_URL" not in frontend and "A2A_LITELLM" not in frontend + assert "CREATE TABLE IF NOT EXISTS execution_receipts" in migration card = load_local_project(ROOT).agent_cls().card() databases = card.runtime.platform_resources.databases assert databases, "live Agent Card must declare its managed database" assert databases[0].migrations is not None assert databases[0].migrations.path == "db/migrations" + assert card.runtime.account_access.required is True + assert card.runtime.account_access.platform_skill_calls == 3 + skill_names = {skill.name for skill in card.skills} + assert {"generate_document", "validate_browser_upload", "generate_document_from_upload"}.issubset(skill_names) + + +def test_upload_bridge_validation_success_and_failure(): + good = BrowserDocument( + filename="notes.txt", + media_type="text/plain", + data_base64=base64.b64encode(b"leasing traffic steady; vendor delay on two turns").decode("ascii"), + ) + parsed = _decode_browser_document(good) + assert parsed.status == "ok" + assert "leasing traffic" in parsed.extracted_text + + bad = BrowserDocument(filename="notes.txt", media_type="text/plain", data_base64="not-base64!!!") + rejected = _decode_browser_document(bad) + assert rejected.status == "validation_error" + assert "base64" in rejected.message + + +def test_generate_document_smoke_returns_preview_and_artifact(monkeypatch): + monkeypatch.delenv("DATABASE_URL", raising=False) + agent = HighUtilityOnePageStartupsWE3c73() + ctx = LocalRunContext( + auth=PlatformUserAuth(sub="user-123", user_id=123, email="pm@example.com"), + task_id="test-task", + ) + request = DocumentRequest( + client_name="Northstar Property Group", + property_name="Maple Ridge Apartments", + property_type="multifamily", + city="Austin", + goal="owner update", + intake=( + "Owner wants a concise update on leasing momentum, delayed make-readies, " + "and resident communication. Traffic is steady but follow-up is inconsistent." + ), + tone="professional", + ) + + result = asyncio.run(agent.generate_document(ctx, request)) + + assert result.document is not None + assert result.document.filename == OUTPUT_FILENAME + assert result.document.media_type == "text/markdown" + assert result.document_preview.startswith("# Owner Update: Maple Ridge Apartments") + assert OUTPUT_FILENAME in ctx.artifacts + assert result.persisted_receipt is False + assert result.status == "persistence_warning"