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] 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") 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" 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"