use std::collections::HashMap;
use std::str::FromStr;
use tap_caip::AssetId;
use tap_msg::didcomm::PlainMessage;
use tap_msg::error::Result;
use tap_msg::message::tap_message_trait::{TapMessage, TapMessageBody, Transaction};
use tap_msg::message::{
AddAgents, Agent, Authorize, Party, Policy as TapPolicy, RequireAuthorization,
RequireProofOfControl, Transfer, UpdatePolicies,
};
#[allow(dead_code)]
const POLICY_ENGINE_DID: &str = "did:policy:engine";
fn create_test_transfer() -> Result<PlainMessage> {
let originator_did = "did:example:originator";
let beneficiary_did = "did:example:beneficiary";
let _sender_vasp_did = "did:example:sender_vasp";
let receiver_vasp_did = "did:example:receiver_vasp";
let transfer = Transfer {
transaction_id: Some(uuid::Uuid::new_v4().to_string()),
asset: AssetId::from_str("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
.unwrap(),
originator: Some(Party::new(originator_did)),
beneficiary: Some(Party::new(beneficiary_did)),
amount: "100.00".to_string(),
agents: vec![
Agent::new(
"did:example:sender_vasp",
"sender_vasp",
"did:example:sender_vasp",
),
Agent::new(receiver_vasp_did, "receiver_vasp", receiver_vasp_did),
],
settlement_id: None,
expiry: None,
transaction_value: None,
connection_id: None,
metadata: HashMap::new(),
memo: None,
};
transfer.to_didcomm_with_route(
originator_did,
[
beneficiary_did,
"did:example:sender_vasp",
receiver_vasp_did,
]
.iter()
.copied(),
)
}
#[test]
fn test_update_policies() -> Result<()> {
let transfer_message = create_test_transfer()?;
let _creator_did = "did:example:sender_vasp";
let auth_policy = RequireAuthorization {
from: Some(vec!["did:example:originator".to_string()]),
from_role: None,
from_agent: None,
purpose: Some("Authorization required".to_string()),
};
let proof_policy = RequireProofOfControl {
from: Some(vec!["did:example:beneficiary".to_string()]),
from_role: None,
from_agent: None,
address_id: "eip155:1:0x1234567890123456789012345678901234567890".to_string(),
purpose: Some("Please prove control of your account".to_string()),
};
let update_policies = UpdatePolicies {
transaction_id: transfer_message.id.clone(),
policies: vec![
TapPolicy::RequireAuthorization(auth_policy),
TapPolicy::RequireProofOfControl(proof_policy),
],
};
assert_eq!(update_policies.policies.len(), 2);
let didcomm_message = update_policies.to_didcomm("did:example:sender_vasp")?;
println!(
"DEBUG: Message body JSON: {}",
serde_json::to_string_pretty(&didcomm_message.body).unwrap()
);
assert_eq!(
didcomm_message.body_as::<UpdatePolicies>()?.policies.len(),
2
);
Ok(())
}
#[test]
fn test_add_agents() -> Result<()> {
let transfer_message = create_test_transfer()?;
let _creator_did = "did:example:sender_vasp";
let new_agent_did = "did:example:new_agent";
let new_agent = Agent::new(new_agent_did, "observer", new_agent_did);
let add_agents = AddAgents {
transaction_id: transfer_message.id.clone(),
agents: vec![new_agent.clone()],
};
assert_eq!(add_agents.agents.len(), 1);
assert_eq!(add_agents.agents[0].id, new_agent_did);
assert_eq!(add_agents.agents[0].role, Some("observer".to_string()));
let didcomm_message = add_agents.to_didcomm("did:example:sender_vasp")?;
let parsed_body = didcomm_message.body_as::<AddAgents>()?;
assert_eq!(parsed_body.agents.len(), 1);
assert_eq!(parsed_body.agents[0].id, new_agent_did);
Ok(())
}
#[test]
fn test_authorizable_trait_methods() -> Result<()> {
let transfer = create_test_transfer_struct()?;
let original_agent_did = "did:example:original_agent";
let replacement_agent_did = "did:example:replacement_agent";
let agent_to_remove = "did:example:agent_to_remove";
let replacement = Agent::new(
replacement_agent_did,
"replacement_agent",
replacement_agent_did,
);
let replace_agent_message = Transaction::replace_agent(
&transfer,
"test-transfer-123", original_agent_did,
replacement.clone(),
);
assert_eq!(
replace_agent_message.body.transaction_id,
transfer.transaction_id.clone().unwrap()
); assert_eq!(replace_agent_message.body.original, original_agent_did);
assert_eq!(
replace_agent_message.body.replacement.id,
replacement_agent_did
);
assert_eq!(
replace_agent_message.body.replacement.role,
Some("replacement_agent".to_string())
);
let remove_agent_message = Transaction::remove_agent(
&transfer,
"test-transfer-123", agent_to_remove,
);
assert_eq!(
remove_agent_message.body.transaction_id,
transfer.transaction_id.clone().unwrap()
); assert_eq!(remove_agent_message.body.agent, agent_to_remove);
Ok(())
}
#[test]
fn test_reply_creation_maintains_thread() -> Result<()> {
let transfer_message = create_test_transfer()?;
let creator_did = "did:example:sender_vasp";
let _participants = &[
"did:example:originator",
"did:example:beneficiary",
"did:example:sender_vasp",
"did:example:receiver_vasp",
];
let proof_policy = RequireProofOfControl {
from: Some(vec!["did:example:beneficiary".to_string()]),
from_role: None,
from_agent: None,
address_id: "eip155:1:0x1234567890123456789012345678901234567890".to_string(),
purpose: Some("Please prove control of your account".to_string()),
};
let update_policies = UpdatePolicies {
transaction_id: transfer_message.id.clone(),
policies: vec![TapPolicy::RequireProofOfControl(proof_policy)],
};
let mut reply = update_policies.to_didcomm("did:example:sender_vasp")?;
reply.thid = Some(transfer_message.id.clone());
assert_eq!(reply.thid, Some(transfer_message.id.clone()));
assert_eq!(reply.from, creator_did.to_string());
let body = reply.body_as::<UpdatePolicies>()?;
assert_eq!(body.transaction_id, transfer_message.id);
assert_eq!(body.policies.len(), 1);
Ok(())
}
#[test]
fn test_reply_chain() -> Result<()> {
let transfer_message = create_test_transfer()?;
let originator_did = "did:example:originator";
let beneficiary_did = "did:example:beneficiary";
let sender_vasp_did = "did:example:sender_vasp";
let _participants = &[originator_did, beneficiary_did, sender_vasp_did];
let update_policies = UpdatePolicies {
transaction_id: transfer_message.id.clone(),
policies: vec![TapPolicy::RequireAuthorization(RequireAuthorization {
from: Some(vec![beneficiary_did.to_string()]),
from_role: None,
from_agent: None,
purpose: Some("Please authorize".to_string()),
})],
};
let mut policies_message = update_policies.to_didcomm("did:example:sender_vasp")?;
policies_message.thid = Some(transfer_message.id.clone());
let authorize = Authorize {
transaction_id: transfer_message.id.clone(),
settlement_address: None,
expiry: None,
};
let mut authorize_message = authorize.to_didcomm("did:example:beneficiary")?;
authorize_message.thid = Some(transfer_message.id.clone());
let add_agents = AddAgents {
transaction_id: transfer_message.id.clone(),
agents: vec![Agent::new(
"did:example:compliance",
"compliance",
"did:example:compliance",
)],
};
let mut add_agents_message = add_agents.to_didcomm("did:example:sender_vasp")?;
add_agents_message.thid = Some(transfer_message.id.clone());
assert_eq!(policies_message.thid, Some(transfer_message.id.clone()));
assert_eq!(authorize_message.thid, Some(transfer_message.id.clone()));
assert_eq!(add_agents_message.thid, Some(transfer_message.id.clone()));
let body1 = policies_message.body_as::<UpdatePolicies>()?;
assert_eq!(body1.transaction_id, transfer_message.id);
let body2 = authorize_message.body_as::<Authorize>()?;
assert_eq!(body2.transaction_id, transfer_message.id);
let body3 = add_agents_message.body_as::<AddAgents>()?;
assert_eq!(body3.transaction_id, transfer_message.id);
assert_eq!(body3.agents[0].id, "did:example:compliance");
Ok(())
}
fn create_test_transfer_struct() -> Result<Transfer> {
let originator = Party::new("did:example:originator");
let beneficiary = Party::new("did:example:beneficiary");
let transfer = Transfer {
transaction_id: Some(uuid::Uuid::new_v4().to_string()),
asset: AssetId::from_str("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
.unwrap(),
originator: Some(originator),
beneficiary: Some(beneficiary),
amount: "100.00".to_string(),
agents: vec![
Agent::new(
"did:example:sender_vasp",
"sender_vasp",
"did:example:sender_vasp",
),
Agent::new(
"did:example:receiver_vasp",
"receiver_vasp",
"did:example:receiver_vasp",
),
],
settlement_id: None,
expiry: None,
transaction_value: None,
connection_id: None,
metadata: HashMap::new(),
memo: None,
};
Ok(transfer)
}