use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::identity::AgentIdentity;
use crate::network::NetworkError;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FederationPolicy {
Open,
AllowList(Vec<Uuid>),
DenyList(Vec<Uuid>),
CapabilityBased(Vec<String>),
}
#[async_trait]
pub trait FederationGateway: Send + Sync {
async fn accept_peer(&mut self, peer: &AgentIdentity) -> Result<bool, NetworkError>;
fn policy(&self) -> &FederationPolicy;
fn list_federated_peers(&self) -> Vec<Uuid>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn federation_policy_equality() {
assert_eq!(FederationPolicy::Open, FederationPolicy::Open);
assert_ne!(FederationPolicy::Open, FederationPolicy::AllowList(vec![]));
}
#[test]
fn federation_policy_allow_list() {
let id1 = Uuid::new_v4();
let id2 = Uuid::new_v4();
let policy = FederationPolicy::AllowList(vec![id1, id2]);
if let FederationPolicy::AllowList(ids) = &policy {
assert_eq!(ids.len(), 2);
assert!(ids.contains(&id1));
assert!(ids.contains(&id2));
} else {
panic!("expected AllowList");
}
}
#[test]
fn federation_policy_deny_list() {
let id = Uuid::new_v4();
let policy = FederationPolicy::DenyList(vec![id]);
if let FederationPolicy::DenyList(ids) = &policy {
assert_eq!(ids.len(), 1);
assert_eq!(ids[0], id);
} else {
panic!("expected DenyList");
}
}
#[test]
fn federation_policy_capability_based() {
let policy =
FederationPolicy::CapabilityBased(vec!["inference".into(), "embedding".into()]);
if let FederationPolicy::CapabilityBased(caps) = &policy {
assert_eq!(caps.len(), 2);
assert!(caps.contains(&"inference".to_string()));
} else {
panic!("expected CapabilityBased");
}
}
#[test]
fn federation_policy_serde_roundtrip() {
let id = Uuid::new_v4();
let variants = vec![
FederationPolicy::Open,
FederationPolicy::AllowList(vec![id]),
FederationPolicy::DenyList(vec![id]),
FederationPolicy::CapabilityBased(vec!["tool_use".into()]),
];
for variant in variants {
let json = serde_json::to_string(&variant).unwrap();
let deserialized: FederationPolicy = serde_json::from_str(&json).unwrap();
assert_eq!(variant, deserialized);
}
}
#[test]
fn federation_policy_empty_collections() {
let empty_allow = FederationPolicy::AllowList(vec![]);
let empty_deny = FederationPolicy::DenyList(vec![]);
let empty_caps = FederationPolicy::CapabilityBased(vec![]);
assert_ne!(empty_allow, empty_deny);
assert_ne!(empty_deny, empty_caps);
assert_ne!(empty_allow, empty_caps);
for policy in [empty_allow, empty_deny, empty_caps] {
let json = serde_json::to_string(&policy).unwrap();
let deserialized: FederationPolicy = serde_json::from_str(&json).unwrap();
assert_eq!(policy, deserialized);
}
}
#[test]
fn federation_policy_clone() {
let id = Uuid::new_v4();
let original = FederationPolicy::AllowList(vec![id]);
let cloned = original.clone();
assert_eq!(original, cloned);
}
}