use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameBody {
pub did: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameResultBody {
pub did: String,
pub name: String,
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameEntry {
pub name: String,
pub enabled: bool,
pub created_at: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameListBody {
pub did: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameListResultBody {
pub did: String,
pub names: Vec<AgentNameEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameCheckBody {
pub did: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AgentNameCheckResultBody {
pub name: String,
pub domain: String,
pub available: bool,
pub reserved: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_bodies_serialise_camel_case() {
let v = serde_json::to_value(AgentNameBody {
did: "did:webvh:QmScid:example.com".into(),
name: "ops".into(),
})
.unwrap();
assert_eq!(v["did"], "did:webvh:QmScid:example.com");
assert_eq!(v["name"], "ops");
let v = serde_json::to_value(AgentNameCheckBody {
did: "did:webvh:QmScid:example.com".into(),
name: "ops".into(),
})
.unwrap();
assert_eq!(v.as_object().unwrap().len(), 2, "no stray fields");
}
#[test]
fn created_at_is_camel_case_on_the_wire() {
let v = serde_json::to_value(AgentNameEntry {
name: "ops".into(),
enabled: true,
created_at: 1_700_000_000,
})
.unwrap();
assert!(
v.get("createdAt").is_some(),
"expected camelCase `createdAt`, got {v}"
);
assert!(v.get("created_at").is_none(), "snake_case leaked: {v}");
}
#[test]
fn a_parked_entry_deserialises_from_the_host_shape() {
let entry: AgentNameEntry = serde_json::from_value(serde_json::json!({
"name": "ops",
"enabled": false,
"createdAt": 1_700_000_000_u64,
}))
.unwrap();
assert!(
!entry.enabled,
"parked must survive the round trip — it is the difference \
between a name being reserved and being free"
);
}
#[test]
fn check_result_distinguishes_reserved_from_taken() {
let reserved: AgentNameCheckResultBody = serde_json::from_value(serde_json::json!({
"name": "admin", "domain": "example.com",
"available": false, "reserved": true,
}))
.unwrap();
assert!(reserved.reserved && !reserved.available);
let taken: AgentNameCheckResultBody = serde_json::from_value(serde_json::json!({
"name": "ops", "domain": "example.com",
"available": false, "reserved": false,
}))
.unwrap();
assert!(!taken.reserved && !taken.available);
}
#[test]
fn list_result_carries_an_empty_registry() {
let r: AgentNameListResultBody = serde_json::from_value(serde_json::json!({
"did": "did:webvh:QmScid:example.com",
"names": [],
}))
.unwrap();
assert!(r.names.is_empty());
}
#[test]
fn result_body_reports_whether_the_name_resolves() {
let v = serde_json::to_value(AgentNameResultBody {
did: "did:webvh:QmScid:example.com".into(),
name: "ops".into(),
enabled: false,
})
.unwrap();
assert_eq!(v["enabled"], false);
}
}