use crate::client::AgentTrustClient;
use crate::error::Result;
use crate::models::AgentCard;
pub struct AgentCards<'a> {
pub(crate) client: &'a AgentTrustClient,
}
impl<'a> AgentCards<'a> {
pub fn generate(&self, agent_id: &str) -> Result<AgentCard> {
let path = format!("/api/v1/agents/{}/card", agent_id);
self.client.request("POST", &path, None::<&()>)
}
pub fn get(&self, agent_id: &str) -> Result<AgentCard> {
let path = format!("/api/v1/agents/{}/card", agent_id);
self.client.request("GET", &path, None::<&()>)
}
pub fn publish(&self, agent_id: &str) -> Result<AgentCard> {
let path = format!("/api/v1/agents/{}/card/publish", agent_id);
self.client.request("PUT", &path, None::<&()>)
}
pub fn get_public(&self, agent_id: &str) -> Result<AgentCard> {
let path = format!("/a2a/agents/{}/agent.json", agent_id);
self.client.request("GET", &path, None::<&()>)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::AgentTrustError;
use mockito::Server;
fn card_body() -> &'static str {
r#"{
"name": "test-agent",
"description": "A test agent",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://ati.example/a2a/agents/agent-123",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"provider": {"organization": "ATI", "url": "https://agenttrust.id"},
"capabilities": {
"streaming": true,
"pushNotifications": false,
"extensions": [
{
"uri": "https://agenttrust.id/ext/trust/v1",
"description": "ATI trust posture",
"params": {"ati_trust_score": 92, "ati_guardian_tier": "fast"}
}
]
},
"securitySchemes": {"bearer": {"type": "http", "scheme": "bearer"}},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{"id": "search", "name": "Search", "description": "Web search", "tags": ["web", "lookup"]}
],
"signatures": [
{"protected": "eyJhbGciOiJFUzI1NiJ9", "signature": "c2ln"}
]
}"#
}
#[test]
fn test_generate_success() {
let mut srv = Server::new();
let mock = srv
.mock("POST", "/api/v1/agents/agent-123/card")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(card_body())
.create();
let client = AgentTrustClient::builder()
.base_url(&srv.url())
.build()
.unwrap();
let card = client.agentcards().generate("agent-123").unwrap();
assert_eq!(card.name, "test-agent");
assert!(card.capabilities.streaming);
assert_eq!(card.trust_score(), 92.0);
mock.assert();
}
#[test]
fn test_card_deserializes_v1_card() {
let card: AgentCard = serde_json::from_str(card_body()).unwrap();
assert_eq!(card.name, "test-agent");
assert_eq!(card.version, "1.0.0");
assert_eq!(
card.primary_url(),
Some("https://ati.example/a2a/agents/agent-123")
);
assert_eq!(card.supported_interfaces[0].protocol_binding, "JSONRPC");
assert_eq!(card.trust_score(), 92.0);
assert_eq!(card.skills.len(), 1);
assert_eq!(card.skills[0].id, "search");
assert_eq!(card.skills[0].tags, vec!["web", "lookup"]);
assert_eq!(card.signatures.len(), 1);
assert_eq!(card.signatures[0].protected, "eyJhbGciOiJFUzI1NiJ9");
let schemes = card.security_schemes.as_ref().unwrap();
assert_eq!(schemes["bearer"].r#type, "http");
assert_eq!(schemes["bearer"].scheme.as_deref(), Some("bearer"));
assert_eq!(card.provider.as_ref().unwrap().organization, "ATI");
}
#[test]
fn test_legacy_card_still_parses() {
let legacy = r#"{
"name": "legacy-agent",
"url": "https://ati.example/a2a/agents/legacy/agent.json",
"version": "0.9"
}"#;
let card: AgentCard = serde_json::from_str(legacy).unwrap();
assert_eq!(card.name, "legacy-agent");
assert_eq!(
card.primary_url(),
Some("https://ati.example/a2a/agents/legacy/agent.json")
);
assert_eq!(card.trust_score(), 0.0);
assert!(card.skills.is_empty());
}
#[test]
fn test_get_not_found() {
let mut srv = Server::new();
let mock = srv
.mock("GET", "/api/v1/agents/missing/card")
.with_status(404)
.with_body(r#"{"message":"agent not found"}"#)
.create();
let client = AgentTrustClient::builder()
.base_url(&srv.url())
.build()
.unwrap();
let err = client.agentcards().get("missing").unwrap_err();
assert!(matches!(err, AgentTrustError::NotFound { .. }));
mock.assert();
}
#[test]
fn test_publish_server_error() {
let mut srv = Server::new();
let mock = srv
.mock("PUT", "/api/v1/agents/agent-123/card/publish")
.with_status(500)
.with_body(r#"{"message":"internal error"}"#)
.create();
let client = AgentTrustClient::builder()
.base_url(&srv.url())
.build()
.unwrap();
let err = client.agentcards().publish("agent-123").unwrap_err();
match err {
AgentTrustError::Api { status, .. } => assert_eq!(status, 500),
other => panic!("expected Api error, got {:?}", other),
}
mock.assert();
}
#[test]
fn test_get_public_success() {
let mut srv = Server::new();
let mock = srv
.mock("GET", "/a2a/agents/agent-123/agent.json")
.with_status(200)
.with_body(card_body())
.create();
let client = AgentTrustClient::builder()
.base_url(&srv.url())
.build()
.unwrap();
let card = client.agentcards().get_public("agent-123").unwrap();
assert_eq!(card.skills.len(), 1);
mock.assert();
}
}