use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tap_agent::agent::{Agent, TapAgent};
use tap_agent::agent_key_manager::{AgentKeyManager, AgentKeyManagerBuilder};
use tap_agent::config::AgentConfig;
use tap_agent::key_manager::{Secret, SecretMaterial, SecretType};
use tap_msg::TapMessageBody;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct TestMessage {
content: String,
}
impl TapMessageBody for TestMessage {
fn message_type() -> &'static str {
"test.message.type"
}
fn validate(&self) -> Result<(), tap_msg::error::Error> {
Ok(())
}
}
fn create_test_key_manager() -> Arc<AgentKeyManager> {
let mut builder = AgentKeyManagerBuilder::new();
let secret = Secret {
id: "did:example:123".to_string(),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK {
private_key_jwk: serde_json::json!({
"kty": "OKP",
"crv": "Ed25519",
"x": "test1234",
"d": "test1234"
}),
},
};
builder = builder.add_secret("did:example:123".to_string(), secret);
let secret = Secret {
id: "did:example:456".to_string(),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK {
private_key_jwk: serde_json::json!({
"kty": "OKP",
"crv": "Ed25519",
"x": "test1234",
"d": "test1234"
}),
},
};
builder = builder.add_secret("did:example:456".to_string(), secret);
Arc::new(builder.build().unwrap())
}
#[tokio::test]
async fn test_agent_get_service_endpoint() {
let config = AgentConfig::new("did:example:123".to_string());
let key_manager = create_test_key_manager();
let agent = TapAgent::new(config, key_manager);
let endpoint = agent.get_service_endpoint("did:example:456").await.unwrap();
assert!(endpoint.is_some(), "Service endpoint should be found");
assert!(
endpoint
.unwrap()
.contains("https://example.com/did/did_example_456"),
"Service endpoint should use fallback URL format"
);
let endpoint = agent.get_service_endpoint("did:example:web").await.unwrap();
assert!(
endpoint.is_some(),
"Service endpoint should be found for web DID"
);
assert!(
endpoint
.unwrap()
.contains("https://example.com/did/did_example_web"),
"Service endpoint should use fallback URL format"
);
let endpoint = agent
.get_service_endpoint("https://direct.example.com")
.await
.unwrap();
assert!(
endpoint.is_some(),
"Service endpoint should be found for direct URL"
);
assert_eq!(
endpoint.unwrap(),
"https://direct.example.com",
"Direct URLs should be returned as-is"
);
}
#[tokio::test]
#[ignore = "Skipped until valid test keys are available for crypto operations"]
async fn test_send_message_to_multiple_recipients() {
let config = AgentConfig::new("did:example:123".to_string());
let key_manager = create_test_key_manager();
let agent = TapAgent::new(config, key_manager);
let test_message = TestMessage {
content: "test multiple recipients".to_string(),
};
let recipients = vec!["did:example:456"];
let (message_id, delivery_results) = agent
.send_message(&test_message, recipients.clone(), false)
.await
.unwrap();
assert!(!message_id.is_empty(), "Message ID should not be empty");
assert_eq!(delivery_results.len(), recipients.len());
for result in &delivery_results {
assert_eq!(result.did, "did:example:456");
assert!(
result
.endpoint
.contains("https://example.com/did/did_example_456"),
"Endpoint should use fallback URL format"
);
assert!(result.status.is_none()); assert!(result.error.is_none()); }
}
#[tokio::test]
async fn test_from_private_key_ed25519() {
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
use tap_agent::KeyType;
use tap_msg::message::Presentation;
use uuid::Uuid;
let mut csprng = OsRng;
let signing_key = SigningKey::generate(&mut csprng);
let private_key_bytes = signing_key.to_bytes();
let (agent, did) = TapAgent::from_private_key(&private_key_bytes, KeyType::Ed25519, true)
.await
.unwrap();
assert!(did.starts_with("did:key:z"), "DID should be a did:key");
assert_eq!(agent.get_agent_did(), &did);
let presentation = Presentation::new(
"test_challenge".to_string(),
vec![serde_json::json!({"test": "credential"})],
Some(Uuid::new_v4().to_string()),
);
let (packed, _) = agent
.send_message(&presentation, vec!["did:example:123"], false)
.await
.unwrap();
assert!(!packed.is_empty(), "Packed message should not be empty");
let (agent2, did2) = TapAgent::from_private_key(&private_key_bytes, KeyType::Ed25519, false)
.await
.unwrap();
assert_eq!(
did, did2,
"DIDs should be identical for the same private key"
);
let plain_message = agent2.receive_message(&packed).await.unwrap();
let received_presentation: Presentation = serde_json::from_value(plain_message.body).unwrap();
assert_eq!(presentation.id, received_presentation.id);
}
#[tokio::test]
#[cfg(feature = "crypto-p256")]
#[ignore = "P-256 key signature verification needs to be fixed"]
async fn test_from_private_key_p256() {
use p256::ecdsa::SigningKey as P256SigningKey;
use rand::rngs::OsRng;
use tap_agent::KeyType;
use tap_msg::message::Presentation;
use uuid::Uuid;
let mut rng = OsRng;
let signing_key = P256SigningKey::random(&mut rng);
let private_key_bytes = signing_key.to_bytes().to_vec();
let (agent, did) = TapAgent::from_private_key(&private_key_bytes, KeyType::P256, true)
.await
.unwrap();
assert!(did.starts_with("did:key:z"), "DID should be a did:key");
assert_eq!(agent.get_agent_did(), &did);
let presentation = Presentation::new(
"test_challenge".to_string(),
vec![serde_json::json!({"test": "credential"})],
Some(Uuid::new_v4().to_string()),
);
let (packed, _) = agent
.send_message(&presentation, vec!["did:example:123"], false)
.await
.unwrap();
assert!(!packed.is_empty(), "Packed message should not be empty");
let (agent2, did2) = TapAgent::from_private_key(&private_key_bytes, KeyType::P256, false)
.await
.unwrap();
assert_eq!(
did, did2,
"DIDs should be identical for the same private key"
);
let plain_message = agent2.receive_message(&packed).await.unwrap();
let received_presentation: Presentation = serde_json::from_value(plain_message.body).unwrap();
assert_eq!(presentation.id, received_presentation.id);
}