from fastapi.testclient import TestClient

from grantflow.api.app import app

client = TestClient(app)


def test_agent_capabilities_manifest_exposes_agent_native_workflow():
    response = client.get("/.well-known/agent-capabilities.json")

    assert response.status_code == 200
    body = response.json()
    assert body["schema_version"] == "2026-04-01"
    assert body["service"] == "GrantFlow"
    assert body["openapi_url"] == "/openapi.json"
    assert body["registration_url"] == "/agents/register"
    assert body["onboarding_url"] == "/agents/onboarding"
    assert body["session_url"] == "/agents/session"
    assert body["agent_descriptor_url"] == "/.well-known/agent.json"
    assert body["ai_plugin_url"] == "/.well-known/ai-plugin.json"
    assert body["agent_policy_url"] == "/.well-known/agent-policy.json"
    assert body["tool_manifest_url"] == "/.well-known/agent-tools.json"
    assert body["agent_recipes_url"] == "/.well-known/agent-recipes.json"
    assert body["auth"]["mode"] == "api_key_header"
    assert body["auth"]["oauth_client_credentials"]["supported"] is True
    assert body["docs"]["agent_quickstart"] == "/docs/agents/quickstart.md"
    assert body["docs"]["agents_md"] == "/AGENTS.md"
    assert body["docs"]["policy"] == "/.well-known/agent-policy.json"
    assert body["docs"]["tool_manifest"] == "/.well-known/agent-tools.json"
    assert body["docs"]["recipes"] == "/.well-known/agent-recipes.json"

    endpoints = {(item["method"], item["path"]) for item in body["endpoints"]}
    assert ("POST", "/agents/register") in endpoints
    assert ("POST", "/agents/onboarding") in endpoints
    assert ("POST", "/agents/session") in endpoints
    assert ("POST", "/agents/credentials/rotate") in endpoints
    assert ("POST", "/agents/credentials/revoke") in endpoints
    assert ("POST", "/generate/preflight") in endpoints
    assert ("POST", "/generate") in endpoints
    assert ("GET", "/status/{job_id}") in endpoints

    workflow = next(item for item in body["workflows"] if item["name"] == "sandbox_generate")
    assert workflow["steps"][0] == "POST /agents/register"
    assert "POST /agents/onboarding" in workflow["steps"]
    assert "POST /agents/session" in workflow["steps"]
    assert "GET /status/{job_id}/events" in workflow["steps"]


def test_agent_descriptor_exposes_machine_readable_entrypoints():
    response = client.get("/.well-known/agent.json")

    assert response.status_code == 200
    body = response.json()
    assert body["name"] == "GrantFlow"
    assert body["api"]["url"] == "/openapi.json"
    assert body["links"]["policy"] == "/.well-known/agent-policy.json"
    assert body["links"]["tools"] == "/.well-known/agent-tools.json"
    assert body["links"]["recipes"] == "/.well-known/agent-recipes.json"
    assert body["entrypoints"]["onboard"] == "POST /agents/onboarding"
    assert body["entrypoints"]["session"] == "POST /agents/session"
    assert body["entrypoints"]["introspect"] == "POST /agents/introspect"
    assert body["entrypoints"]["token"] == "POST /agents/oauth/token"
    assert body["entrypoints"]["rotate_credential"] == "POST /agents/credentials/rotate"
    assert body["entrypoints"]["revoke_credential"] == "POST /agents/credentials/revoke"

    plugin_response = client.get("/.well-known/ai-plugin.json")
    assert plugin_response.status_code == 200
    assert plugin_response.json()["entrypoints"] == body["entrypoints"]


def test_agent_policy_exposes_runtime_limits_and_webhook_contract():
    response = client.get("/.well-known/agent-policy.json")

    assert response.status_code == 200
    body = response.json()
    assert body["schema_version"] == "2026-04-01"
    assert body["credentials"]["self_serve_api_keys"]["session_default_ttl_seconds"] == 3600
    assert body["credentials"]["self_serve_api_keys"]["tenant_bound"] is True
    assert body["credentials"]["self_serve_api_keys"]["rotation_endpoint"] == "/agents/credentials/rotate"
    assert body["credentials"]["self_serve_api_keys"]["revoke_endpoint"] == "/agents/credentials/revoke"
    assert body["credentials"]["oauth_client_credentials"]["supported"] is True
    assert body["credentials"]["oauth_client_credentials"]["grant_type"] == "client_credentials"
    assert body["credentials"]["oauth_client_credentials"]["self_serve_token_endpoint"] == "/agents/oauth/token"
    assert body["credentials"]["oauth_client_credentials"]["introspection_endpoint"] == "/agents/introspect"
    assert body["runtime_rules"]["terminal_statuses"] == ["done", "error", "pending_hitl"]
    assert body["webhooks"]["signature_header"] == "X-GrantFlow-Signature"
    assert body["webhooks"]["timestamp_header"] == "X-GrantFlow-Timestamp"
    assert body["webhooks"]["delivery_header"] == "X-GrantFlow-Delivery"
    assert body["export_rules"]["inspect_quality_before_export"] is True


def test_agent_registration_returns_sandbox_scope_and_next_actions():
    response = client.post(
        "/agents/register",
        json={
            "agent_name": "Proposal Worker",
            "agent_type": "workflow_agent",
            "purpose": "Run grant generation",
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["status"] == "registered"
    assert body["agent_id"] == "agent_proposal_worker"
    assert body["tenant_id"] == "sandbox_proposal_worker"
    assert body["auth_mode"] == "api_key_header"
    assert body["links"]["capabilities"] == "/.well-known/agent-capabilities.json"
    assert any("/generate/preflight" in action for action in body["next_actions"])
    assert body["sample_requests"]["preflight"]["tenant_id"] == "sandbox_proposal_worker"
    assert body["sample_requests"]["generate"]["client_metadata"]["agent_id"] == "agent_proposal_worker"


def test_agent_onboarding_returns_pending_oauth_metadata_by_default():
    response = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Proposal Worker",
            "auth_type": "oauth_client_credentials",
            "requested_scopes": ["generate:write", "status:read"],
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["status"] == "pending_operator"
    assert body["agent_id"] == "agent_proposal_worker"
    assert body["tenant_id"] == "sandbox_proposal_worker"
    assert body["auth_type"] == "oauth_client_credentials"
    assert body["oauth_client"]["grant_type"] == "client_credentials"
    assert body["oauth_client"]["requested_scopes"] == ["generate:write", "status:read"]
    assert body["oauth_client"]["token_endpoint"] == "gateway-provided"
    assert body["links"]["agents_md"] == "/AGENTS.md"


def test_agent_onboarding_can_issue_self_serve_oauth_client_and_bearer_token(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "OAuth Proposal Worker",
            "auth_type": "oauth_client_credentials",
            "requested_scopes": ["generate:write", "status:read"],
            "ttl_seconds": 3600,
        },
    )

    assert onboarding.status_code == 200
    body = onboarding.json()
    assert body["status"] == "ready"
    oauth_client = body["oauth_client"]
    assert oauth_client["token_endpoint"] == "/agents/oauth/token"
    assert oauth_client["client_id"] == "gf_client_agent_oauth_proposal_worker"
    assert oauth_client["client_secret"].startswith("gf_oauth_client_")

    token = client.post(
        "/agents/oauth/token",
        json={
            "client_id": oauth_client["client_id"],
            "client_secret": oauth_client["client_secret"],
            "scope": "generate:write status:read",
            "ttl_seconds": 900,
        },
    )

    assert token.status_code == 200
    token_body = token.json()
    assert token_body["token_type"] == "Bearer"
    assert token_body["scope"] == "generate:write status:read"
    assert token_body["tenant_id"] == body["tenant_id"]

    protected = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "tenant_id": body["tenant_id"], "architect_rag_enabled": False},
        headers={"Authorization": f"Bearer {token_body['access_token']}"},
    )

    assert protected.status_code == 200

    introspection = client.post(
        "/agents/introspect",
        headers={"Authorization": f"Bearer {token_body['access_token']}"},
    )
    assert introspection.status_code == 200
    assert introspection.json()["active"] is True


def test_agent_oauth_token_rejects_unapproved_scope(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Scoped OAuth Worker",
            "auth_type": "oauth_client_credentials",
            "requested_scopes": ["status:read"],
        },
    )
    oauth_client = onboarding.json()["oauth_client"]

    token = client.post(
        "/agents/oauth/token",
        json={
            "client_id": oauth_client["client_id"],
            "client_secret": oauth_client["client_secret"],
            "scope": "generate:write",
        },
    )

    assert token.status_code == 401
    assert token.json()["detail"]["code"] == "invalid_client_credentials"


def test_agent_onboarding_can_issue_self_serve_api_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Proposal Worker",
            "auth_type": "api_key",
            "requested_scopes": ["generate:write", "status:read"],
            "ttl_seconds": 3600,
        },
    )

    assert onboarding.status_code == 200
    body = onboarding.json()
    assert body["status"] == "ready"
    assert body["auth_type"] == "api_key"
    api_key = body["credential"]["value"]
    assert api_key.startswith("gf_agent_")

    protected = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "tenant_id": body["tenant_id"], "architect_rag_enabled": False},
        headers={"X-API-Key": api_key},
    )

    assert protected.status_code == 200


def test_agent_session_returns_pending_operator_when_self_serve_keys_disabled(monkeypatch):
    monkeypatch.delenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", raising=False)

    response = client.post(
        "/agents/session",
        json={
            "agent_id": "agent_proposal_worker",
            "tenant_id": "sandbox_proposal_worker",
            "requested_scopes": ["generate:write", "status:read"],
            "ttl_seconds": 7200,
        },
    )

    assert response.status_code == 200
    body = response.json()
    assert body["status"] == "pending_operator"
    assert body["session_id"].startswith("ags_")
    assert body["agent_id"] == "agent_proposal_worker"
    assert body["tenant_id"] == "sandbox_proposal_worker"
    assert body["requested_scopes"] == ["generate:write", "status:read"]
    assert body["constraints"]["tenant_bound"] is True
    assert body["links"]["policy"] == "/.well-known/agent-policy.json"
    assert "credential" not in body


def test_agent_session_can_issue_short_lived_self_serve_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    session = client.post(
        "/agents/session",
        json={
            "agent_id": "agent_proposal_worker",
            "tenant_id": "sandbox_proposal_worker",
            "requested_scopes": ["generate:write", "status:read"],
            "ttl_seconds": 3600,
        },
    )

    assert session.status_code == 200
    body = session.json()
    assert body["status"] == "ready"
    assert body["credential"]["value"].startswith("gf_agent_")
    assert body["credential"]["scopes"] == ["generate:write", "status:read"]
    api_key = body["credential"]["value"]

    protected = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "tenant_id": "sandbox_proposal_worker", "architect_rag_enabled": False},
        headers={"X-API-Key": api_key},
    )

    assert protected.status_code == 200


def test_agent_introspection_reports_operator_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    response = client.post("/agents/introspect", headers={"X-API-Key": "operator-secret"})

    assert response.status_code == 200
    body = response.json()
    assert body["active"] is True
    assert body["credential_type"] == "operator"
    assert body["scopes"] == ["*"]
    assert "agent_id" not in body
    assert "tenant_id" not in body


def test_agent_introspection_reports_self_serve_session_claims(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    session = client.post(
        "/agents/session",
        json={
            "agent_id": "agent_proposal_worker",
            "tenant_id": "sandbox_proposal_worker",
            "requested_scopes": ["generate:write", "status:read"],
            "ttl_seconds": 3600,
        },
    )
    api_key = session.json()["credential"]["value"]

    response = client.post("/agents/introspect", headers={"X-API-Key": api_key})

    assert response.status_code == 200
    body = response.json()
    assert body["active"] is True
    assert body["credential_type"] == "self_serve_agent"
    assert body["agent_id"] == "agent_proposal_worker"
    assert body["tenant_id"] == "sandbox_proposal_worker"
    assert body["scopes"] == ["generate:write", "status:read"]
    assert body["expires_at"] == session.json()["expires_at"]
    assert body["jti"].startswith("agt_")


def test_agent_recipes_endpoint_exposes_use_case_recipes():
    response = client.get("/.well-known/agent-recipes.json")

    assert response.status_code == 200
    body = response.json()
    assert body["schema_version"] == "2026-04-01"
    recipe_names = {recipe["name"] for recipe in body["recipes"]}
    assert {"sandbox_generate", "agent_credential_rotation", "grant_lifecycle_export"}.issubset(recipe_names)


def test_agent_introspection_reports_inactive_without_valid_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    response = client.post("/agents/introspect", headers={"X-API-Key": "not-valid"})

    assert response.status_code == 200
    body = response.json()
    assert body["active"] is False
    assert body["next_action"] == "Request a fresh agent credential with /agents/session or /agents/onboarding."


def test_self_serve_agent_key_rejects_wrong_tenant(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Tenant Runner",
            "auth_type": "api_key",
            "requested_tenant_id": "tenant_alpha",
            "requested_scopes": ["generate:write", "status:read", "quality:read"],
        },
    )

    api_key = onboarding.json()["credential"]["value"]
    protected = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "tenant_id": "tenant_beta", "architect_rag_enabled": False},
        headers={"X-API-Key": api_key},
    )

    assert protected.status_code == 403
    assert protected.json()["detail"]["code"] == "agent_tenant_mismatch"


def test_self_serve_agent_key_rejects_missing_scope(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Read Only Runner",
            "auth_type": "api_key",
            "requested_scopes": ["status:read"],
        },
    )

    api_key = onboarding.json()["credential"]["value"]
    protected = client.post(
        "/generate/preflight",
        json={"donor_id": "usaid", "tenant_id": onboarding.json()["tenant_id"], "architect_rag_enabled": False},
        headers={"X-API-Key": api_key},
    )

    assert protected.status_code == 403
    assert protected.json()["detail"]["code"] == "insufficient_agent_scope"


def test_agent_credential_revoke_blocks_self_serve_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Revoked Runner",
            "auth_type": "api_key",
            "requested_scopes": ["generate:write", "status:read"],
        },
    )
    api_key = onboarding.json()["credential"]["value"]

    revoke = client.post(
        "/agents/credentials/revoke",
        json={"credential": api_key, "reason": "test rotation"},
    )

    assert revoke.status_code == 200
    body = revoke.json()
    assert body["status"] == "revoked"
    assert body["credential_type"] == "self_serve_agent"

    introspection = client.post("/agents/introspect", headers={"X-API-Key": api_key})
    assert introspection.status_code == 200
    assert introspection.json()["active"] is False


def test_agent_credential_rotate_replaces_self_serve_key(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")
    monkeypatch.setenv("GRANTFLOW_API_KEY", "operator-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Rotating Runner",
            "auth_type": "api_key",
            "requested_scopes": ["generate:write", "status:read"],
        },
    )
    old_key = onboarding.json()["credential"]["value"]

    rotate = client.post("/agents/credentials/rotate", json={"credential": old_key, "ttl_seconds": 900})

    assert rotate.status_code == 200
    body = rotate.json()
    assert body["status"] == "rotated"
    assert body["credential_type"] == "self_serve_agent"
    new_key = body["credential"]["value"]
    assert new_key.startswith("gf_agent_")

    assert client.post("/agents/introspect", headers={"X-API-Key": old_key}).json()["active"] is False
    assert client.post("/agents/introspect", headers={"X-API-Key": new_key}).json()["active"] is True


def test_agent_credential_rotate_replaces_oauth_client_secret(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_AGENT_SELF_SERVE_KEYS_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_AGENT_KEY_SIGNING_SECRET", "test-agent-signing-secret")

    onboarding = client.post(
        "/agents/onboarding",
        json={
            "agent_name": "Rotating OAuth Runner",
            "auth_type": "oauth_client_credentials",
            "requested_scopes": ["status:read"],
        },
    )
    oauth_client = onboarding.json()["oauth_client"]

    rotate = client.post(
        "/agents/credentials/rotate",
        json={"credential": oauth_client["client_secret"], "ttl_seconds": 3600},
    )

    assert rotate.status_code == 200
    body = rotate.json()
    assert body["status"] == "rotated"
    assert body["credential_type"] == "oauth_client_secret"
    assert body["oauth_client"]["client_secret"].startswith("gf_oauth_client_")

    old_token = client.post(
        "/agents/oauth/token",
        json={"client_id": oauth_client["client_id"], "client_secret": oauth_client["client_secret"]},
    )
    assert old_token.status_code == 401


def test_agent_tool_manifest_exposes_agent_ready_tools():
    response = client.get("/.well-known/agent-tools.json")

    assert response.status_code == 200
    body = response.json()
    assert body["schema_version"] == "2026-04-01"
    assert body["service"] == "GrantFlow"
    assert body["transport"] == "http"
    assert body["capabilities_url"] == "/.well-known/agent-capabilities.json"

    tools = {item["name"]: item for item in body["tools"]}
    assert set(tools) == {
        "onboard_agent",
        "create_session",
        "exchange_oauth_token",
        "introspect_agent",
        "rotate_credential",
        "revoke_credential",
        "register_agent",
        "run_preflight",
        "start_generation",
        "get_status",
        "get_quality",
        "get_events",
        "ingest_text",
        "hitl_approve",
        "hitl_list_pending",
        "get_export_payload",
    }
    assert tools["onboard_agent"]["path"] == "/agents/onboarding"
    assert tools["onboard_agent"]["auth_required"] is False
    assert tools["create_session"]["path"] == "/agents/session"
    assert tools["create_session"]["input_schema"]["required"] == ["agent_id", "tenant_id"]
    assert tools["introspect_agent"]["path"] == "/agents/introspect"
    assert tools["introspect_agent"]["auth_required"] is True
    assert tools["exchange_oauth_token"]["path"] == "/agents/oauth/token"
    assert tools["exchange_oauth_token"]["input_schema"]["required"] == ["client_id", "client_secret"]
    assert tools["rotate_credential"]["path"] == "/agents/credentials/rotate"
    assert tools["revoke_credential"]["path"] == "/agents/credentials/revoke"
    assert tools["register_agent"]["path"] == "/agents/register"
    assert tools["register_agent"]["auth_required"] is False
    assert tools["start_generation"]["idempotency_key_supported"] is True
    assert tools["start_generation"]["input_schema"]["required"] == ["donor_id", "input_context"]
    assert tools["get_status"]["input_schema"]["required"] == ["job_id"]
    assert body["recommended_workflows"]["sandbox_generate"] == [
        "onboard_agent",
        "create_session",
        "introspect_agent",
        "exchange_oauth_token",
        "rotate_credential",
        "revoke_credential",
        "ingest_text",
        "run_preflight",
        "start_generation",
        "get_status",
        "hitl_approve",
        "hitl_list_pending",
        "get_quality",
        "get_events",
        "get_export_payload",
    ]


def test_agent_capabilities_are_declared_in_openapi():
    response = client.get("/openapi.json")

    assert response.status_code == 200
    spec = response.json()
    assert spec["paths"]["/.well-known/agent-tools.json"]["get"]["responses"]["200"]["content"]["application/json"][
        "schema"
    ] == {"$ref": "#/components/schemas/AgentToolManifestPublicResponse"}
    assert spec["paths"]["/.well-known/agent-capabilities.json"]["get"]["responses"]["200"]["content"][
        "application/json"
    ]["schema"] == {"$ref": "#/components/schemas/AgentCapabilitiesPublicResponse"}
    assert spec["paths"]["/agents/register"]["post"]["responses"]["200"]["content"]["application/json"]["schema"] == {
        "$ref": "#/components/schemas/AgentRegistrationPublicResponse"
    }
    assert spec["paths"]["/agents/onboarding"]["post"]["responses"]["200"]["content"]["application/json"]["schema"] == {
        "$ref": "#/components/schemas/AgentOnboardingPublicResponse"
    }
    assert spec["paths"]["/agents/session"]["post"]["responses"]["200"]["content"]["application/json"]["schema"] == {
        "$ref": "#/components/schemas/AgentSessionPublicResponse"
    }
    assert spec["paths"]["/agents/introspect"]["post"]["responses"]["200"]["content"]["application/json"]["schema"] == {
        "$ref": "#/components/schemas/AgentIntrospectionPublicResponse"
    }
    assert spec["paths"]["/agents/oauth/token"]["post"]["responses"]["200"]["content"]["application/json"][
        "schema"
    ] == {"$ref": "#/components/schemas/AgentOAuthTokenPublicResponse"}
    assert spec["paths"]["/agents/credentials/revoke"]["post"]["responses"]["200"]["content"]["application/json"][
        "schema"
    ] == {"$ref": "#/components/schemas/AgentCredentialRevokePublicResponse"}
    assert spec["paths"]["/agents/credentials/rotate"]["post"]["responses"]["200"]["content"]["application/json"][
        "schema"
    ] == {"$ref": "#/components/schemas/AgentCredentialRotatePublicResponse"}
    assert "/.well-known/agent.json" in spec["paths"]
    assert "/.well-known/ai-plugin.json" in spec["paths"]
    assert "/.well-known/agent-policy.json" in spec["paths"]
    assert "/.well-known/agent-recipes.json" in spec["paths"]
    assert "x-grantflow-agent-recipes" in spec
    assert "examples" in spec["paths"]["/agents/onboarding"]["post"]["requestBody"]["content"]["application/json"]
    assert "examples" in spec["paths"]["/agents/oauth/token"]["post"]["requestBody"]["content"]["application/json"]
    assert (
        "examples" in spec["paths"]["/agents/credentials/rotate"]["post"]["requestBody"]["content"]["application/json"]
    )
    assert "examples" in spec["paths"]["/agents/session"]["post"]["requestBody"]["content"]["application/json"]
    assert (
        "agent_generation"
        in spec["paths"]["/generate"]["post"]["requestBody"]["content"]["application/json"]["examples"]
    )
    sandbox_recipe = next(item for item in spec["x-grantflow-agent-recipes"] if item["name"] == "sandbox_generate")
    assert {"method": "POST", "path": "/agents/introspect", "tool": "grantflow_introspect_agent"} in sandbox_recipe[
        "steps"
    ]
