use crate::proto::auth_config::AuthType;
use crate::testing::mocks::{
MockGuardianService, create_mock_account_state, create_mock_delta, start_mock_server,
};
use crate::{
AccountRef, AuthConfig, ClientError, ConfigureResponse, FalconKeyStore,
GetAccountByKeyCommitmentResponse, GetDeltaProposalResponse, GetDeltaProposalsResponse,
GetDeltaResponse, GetDeltaSinceResponse, GetStateResponse, GuardianClient,
PushDeltaProposalResponse, PushDeltaResponse, SignDeltaProposalResponse, Signer,
};
use guardian_shared::ProposalSignature as JsonProposalSignature;
use miden_protocol::account::AccountId;
use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
use std::sync::Arc;
use tonic::Status;
fn create_test_account_id() -> AccountId {
AccountId::from_hex("0x7b7b7b7a7b7b7b017b7b7b7b7b7b7b").unwrap()
}
fn create_test_signer() -> Arc<dyn Signer> {
Arc::new(FalconKeyStore::new(SecretKey::new()))
}
#[tokio::test]
async fn test_get_pubkey_success() {
let service = MockGuardianService::default().with_get_pubkey(Ok("test_pubkey_123".to_string()));
let endpoint = start_mock_server(service).await.unwrap();
let mut client = GuardianClient::connect(endpoint).await.unwrap();
let result = client.get_pubkey(None).await;
assert!(result.is_ok());
assert_eq!(result.unwrap().0, "test_pubkey_123");
}
#[tokio::test]
async fn test_get_pubkey_error() {
let service =
MockGuardianService::default().with_get_pubkey(Err(Status::internal("Server error")));
let endpoint = start_mock_server(service).await.unwrap();
let mut client = GuardianClient::connect(endpoint).await.unwrap();
let result = client.get_pubkey(None).await;
assert!(result.is_err());
match result.unwrap_err() {
ClientError::Status(_) => {}
e => panic!("Expected Status error, got: {:?}", e),
}
}
#[tokio::test]
async fn test_configure_success() {
let service = MockGuardianService::default().with_configure(Ok(ConfigureResponse {
success: true,
message: "Account configured".to_string(),
ack_pubkey: "test_pubkey_123".to_string(),
ack_commitment: String::new(),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let initial_state = serde_json::json!({"balance": 1000});
let auth_config = AuthConfig {
auth_type: Some(AuthType::MidenFalconRpo(crate::proto::MidenFalconRpoAuth {
cosigner_commitments: vec!["0xabc".to_string()],
})),
};
let result = client
.configure(&account_id, auth_config, initial_state)
.await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.message, "Account configured");
assert_eq!(response.ack_pubkey, "test_pubkey_123");
}
#[tokio::test]
async fn test_configure_server_error() {
let service = MockGuardianService::default().with_configure(Ok(ConfigureResponse {
success: false,
message: "Account already exists".to_string(),
ack_pubkey: String::new(),
ack_commitment: String::new(),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let initial_state = serde_json::json!({"balance": 1000});
let auth_config = AuthConfig {
auth_type: Some(AuthType::MidenFalconRpo(crate::proto::MidenFalconRpoAuth {
cosigner_commitments: vec!["0xabc".to_string()],
})),
};
let result = client
.configure(&account_id, auth_config, initial_state)
.await;
assert!(result.is_err());
match result.unwrap_err() {
ClientError::ServerError(msg) => {
assert_eq!(msg, "Account already exists");
}
e => panic!("Expected ServerError, got: {:?}", e),
}
}
#[tokio::test]
async fn test_push_delta_proposal_success() {
let mock_delta = create_mock_delta();
let service =
MockGuardianService::default().with_push_delta_proposal(Ok(PushDeltaProposalResponse {
success: true,
message: String::new(),
commitment: "proposal_commitment_123".to_string(),
delta: Some(mock_delta.clone()),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let delta_payload = serde_json::json!({"tx_summary": {}, "signatures": []});
let result = client
.push_delta_proposal(&account_id, 1, delta_payload)
.await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.commitment, "proposal_commitment_123");
assert!(response.delta.is_some());
}
#[tokio::test]
async fn test_get_delta_proposals_success() {
let mock_delta1 = create_mock_delta();
let mut mock_delta2 = create_mock_delta();
mock_delta2.nonce = 2;
let service =
MockGuardianService::default().with_get_delta_proposals(Ok(GetDeltaProposalsResponse {
success: true,
message: String::new(),
proposals: vec![mock_delta1, mock_delta2],
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_delta_proposals(&account_id).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.proposals.len(), 2);
}
#[tokio::test]
async fn test_get_delta_proposals_empty() {
let service =
MockGuardianService::default().with_get_delta_proposals(Ok(GetDeltaProposalsResponse {
success: true,
message: String::new(),
proposals: vec![],
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_delta_proposals(&account_id).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.proposals.len(), 0);
}
#[tokio::test]
async fn test_get_delta_proposal_success() {
let mock_delta = create_mock_delta();
let service =
MockGuardianService::default().with_get_delta_proposal(Ok(GetDeltaProposalResponse {
success: true,
message: String::new(),
proposal: Some(mock_delta),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client
.get_delta_proposal(&account_id, "proposal_commitment_123")
.await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert!(response.proposal.is_some());
}
#[tokio::test]
async fn test_sign_delta_proposal_success() {
let mock_delta = create_mock_delta();
let service =
MockGuardianService::default().with_sign_delta_proposal(Ok(SignDeltaProposalResponse {
success: true,
message: "Signature added".to_string(),
delta: Some(mock_delta),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let signature = JsonProposalSignature::Falcon {
signature: "0xabcd".to_string(),
};
let result = client
.sign_delta_proposal(&account_id, "commitment_123", signature)
.await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.message, "Signature added");
assert!(response.delta.is_some());
}
#[tokio::test]
async fn test_push_delta_success() {
let mock_delta = create_mock_delta();
let service = MockGuardianService::default().with_push_delta(Ok(PushDeltaResponse {
success: true,
message: "Delta pushed".to_string(),
delta: Some(mock_delta),
ack_sig: Some("0xsig".to_string()),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let delta_payload = serde_json::json!({"updates": []});
let result = client
.push_delta(&account_id, 1, "0x123", delta_payload)
.await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert_eq!(response.message, "Delta pushed");
assert!(response.delta.is_some());
}
#[tokio::test]
async fn test_get_delta_success() {
let mock_delta = create_mock_delta();
let service = MockGuardianService::default().with_get_delta(Ok(GetDeltaResponse {
success: true,
message: String::new(),
delta: Some(mock_delta),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_delta(&account_id, 1).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert!(response.delta.is_some());
assert_eq!(response.delta.unwrap().nonce, 1);
}
#[tokio::test]
async fn test_get_delta_not_found() {
let service = MockGuardianService::default().with_get_delta(Ok(GetDeltaResponse {
success: false,
message: "Delta not found".to_string(),
delta: None,
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_delta(&account_id, 999).await;
assert!(result.is_err());
match result.unwrap_err() {
ClientError::ServerError(msg) => {
assert_eq!(msg, "Delta not found");
}
e => panic!("Expected ServerError, got: {:?}", e),
}
}
#[tokio::test]
async fn test_get_delta_since_success() {
let mock_delta = create_mock_delta();
let service = MockGuardianService::default().with_get_delta_since(Ok(GetDeltaSinceResponse {
success: true,
message: String::new(),
merged_delta: Some(mock_delta),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_delta_since(&account_id, 1).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert!(response.merged_delta.is_some());
}
#[tokio::test]
async fn test_get_state_success() {
let mock_state = create_mock_account_state();
let service = MockGuardianService::default().with_get_state(Ok(GetStateResponse {
success: true,
message: String::new(),
state: Some(mock_state),
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_state(&account_id).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.success);
assert!(response.state.is_some());
assert!(response.state.unwrap().state_json.contains("balance"));
}
#[tokio::test]
async fn test_get_state_not_found() {
let service = MockGuardianService::default().with_get_state(Ok(GetStateResponse {
success: false,
message: "State not found".to_string(),
state: None,
}));
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let account_id = create_test_account_id();
let result = client.get_state(&account_id).await;
assert!(result.is_err());
match result.unwrap_err() {
ClientError::ServerError(msg) => {
assert_eq!(msg, "State not found");
}
e => panic!("Expected ServerError, got: {:?}", e),
}
}
#[tokio::test]
async fn test_signer_pubkey_hex_without_signer() {
let service = MockGuardianService::default();
let endpoint = start_mock_server(service).await.unwrap();
let client = GuardianClient::connect(endpoint).await.unwrap();
let result = client.signer_pubkey_hex();
assert!(result.is_err());
match result.unwrap_err() {
ClientError::InvalidResponse(msg) => {
assert!(msg.contains("no signer configured"));
}
e => panic!("Expected InvalidResponse, got: {:?}", e),
}
}
#[tokio::test]
async fn test_signer_pubkey_hex_with_signer() {
let service = MockGuardianService::default();
let endpoint = start_mock_server(service).await.unwrap();
let signer = create_test_signer();
let expected_pubkey = signer.public_key_hex();
let client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let result = client.signer_pubkey_hex();
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected_pubkey);
}
fn lookup_test_signer() -> (Arc<dyn Signer>, String) {
let signer = Arc::new(FalconKeyStore::new(SecretKey::new())) as Arc<dyn Signer>;
let commitment_hex = signer.commitment_hex();
(signer, commitment_hex)
}
#[tokio::test]
async fn test_lookup_account_by_key_commitment_single_match() {
let service = MockGuardianService::default().with_get_account_by_key_commitment(Ok(
GetAccountByKeyCommitmentResponse {
accounts: vec![AccountRef {
account_id: "0x7b7b7b7a7b7b7b017b7b7b7b7b7b7b".to_string(),
}],
},
));
let endpoint = start_mock_server(service).await.unwrap();
let (signer, commitment_hex) = lookup_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let response = client
.lookup_account_by_key_commitment(&commitment_hex)
.await
.expect("happy path returns Ok");
assert_eq!(response.accounts.len(), 1);
assert_eq!(
response.accounts[0].account_id,
"0x7b7b7b7a7b7b7b017b7b7b7b7b7b7b"
);
}
#[tokio::test]
async fn test_lookup_account_by_key_commitment_empty_result() {
let service = MockGuardianService::default().with_get_account_by_key_commitment(Ok(
GetAccountByKeyCommitmentResponse { accounts: vec![] },
));
let endpoint = start_mock_server(service).await.unwrap();
let (signer, commitment_hex) = lookup_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let response = client
.lookup_account_by_key_commitment(&commitment_hex)
.await
.expect("empty list is a successful response");
assert!(response.accounts.is_empty());
}
#[tokio::test]
async fn test_lookup_account_by_key_commitment_multi_match() {
let service = MockGuardianService::default().with_get_account_by_key_commitment(Ok(
GetAccountByKeyCommitmentResponse {
accounts: vec![
AccountRef {
account_id: "0xaaaaaaaaaaaaaa012aaaaaaaaaaaaa".to_string(),
},
AccountRef {
account_id: "0xbbbbbbbabbbbbb013bbbbbbbbbbbbb".to_string(),
},
],
},
));
let endpoint = start_mock_server(service).await.unwrap();
let (signer, commitment_hex) = lookup_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let response = client
.lookup_account_by_key_commitment(&commitment_hex)
.await
.expect("multi-match returns all entries");
assert_eq!(response.accounts.len(), 2);
let ids: Vec<&str> = response
.accounts
.iter()
.map(|a| a.account_id.as_str())
.collect();
assert!(ids.contains(&"0xaaaaaaaaaaaaaa012aaaaaaaaaaaaa"));
assert!(ids.contains(&"0xbbbbbbbabbbbbb013bbbbbbbbbbbbb"));
}
#[tokio::test]
async fn test_lookup_account_by_key_commitment_unauthenticated_error_propagates() {
let service = MockGuardianService::default().with_get_account_by_key_commitment(Err(
Status::unauthenticated(
"submitted public key does not derive to the queried key_commitment",
),
));
let endpoint = start_mock_server(service).await.unwrap();
let (signer, commitment_hex) = lookup_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let result = client
.lookup_account_by_key_commitment(&commitment_hex)
.await;
let err = result.expect_err("unauthenticated must propagate as ClientError");
match err {
ClientError::Status(status) => {
assert_eq!(status.code(), tonic::Code::Unauthenticated);
}
other => panic!("expected Status error, got {other:?}"),
}
}
#[tokio::test]
async fn test_lookup_account_by_key_commitment_rejects_invalid_commitment_hex() {
let service = MockGuardianService::default();
let endpoint = start_mock_server(service).await.unwrap();
let (signer, _commitment_hex) = lookup_test_signer();
let mut client = GuardianClient::connect(endpoint)
.await
.unwrap()
.with_signer(signer);
let result = client.lookup_account_by_key_commitment("not-hex").await;
let err = result.expect_err("malformed hex must fail locally");
match err {
ClientError::InvalidResponse(msg) => {
assert!(msg.contains("Invalid key_commitment hex"), "{msg}");
}
other => panic!("expected InvalidResponse, got {other:?}"),
}
}