#![allow(unused)]
use rsclaw::{
channel::{DmPolicyEnforcer, PolicyResult},
config::schema::DmPolicy,
};
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_open() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Open, vec![]);
for peer in ["alice", "bob", "0123456789", "unknown_user_99"] {
let result = enforcer.check(peer).await;
assert_eq!(
result,
PolicyResult::Allow,
"Open policy should allow '{peer}', got: {result:?}"
);
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_allowlist() {
let allowed = vec!["alice".to_owned(), "charlie".to_owned()];
let enforcer = DmPolicyEnforcer::new(DmPolicy::Allowlist, allowed);
assert_eq!(
enforcer.check("alice").await,
PolicyResult::Allow,
"alice is in allowlist"
);
assert_eq!(
enforcer.check("charlie").await,
PolicyResult::Allow,
"charlie is in allowlist"
);
for denied in ["bob", "dave", "ALICE", " alice ", ""] {
let result = enforcer.check(denied).await;
assert_eq!(
result,
PolicyResult::Deny,
"'{denied}' should be denied, got: {result:?}"
);
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_allowlist_wildcard() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Allowlist, vec!["*".to_owned()]);
for peer in ["alice", "bob", "anyone"] {
assert_eq!(
enforcer.check(peer).await,
PolicyResult::Allow,
"wildcard allowlist should allow '{peer}'"
);
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_disabled() {
let enforcer =
DmPolicyEnforcer::new(DmPolicy::Disabled, vec!["alice".to_owned(), "*".to_owned()]);
for peer in ["alice", "bob", "admin", ""] {
let result = enforcer.check(peer).await;
assert_eq!(
result,
PolicyResult::Deny,
"Disabled policy should deny '{peer}', got: {result:?}"
);
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_pairing_issues_code() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let result = enforcer.check("new_user").await;
assert!(
matches!(result, PolicyResult::SendPairingCode(_)),
"first contact in Pairing mode should yield a code, got: {result:?}"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_pairing_approved_allows() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let code = match enforcer.check("peer_42").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected SendPairingCode, got {other:?}"),
};
let approved_peer = enforcer.approve_pairing(&code).await;
assert_eq!(
approved_peer.as_deref(),
Some("peer_42"),
"approve_pairing should return the peer ID"
);
assert_eq!(
enforcer.check("peer_42").await,
PolicyResult::Allow,
"approved peer should be allowed on subsequent checks"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_pairing_revoke() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let code = match enforcer.check("peer_rev").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected code, got {other:?}"),
};
enforcer.approve_pairing(&code).await;
assert_eq!(enforcer.check("peer_rev").await, PolicyResult::Allow);
enforcer.revoke("peer_rev").await;
let after_revoke = enforcer.check("peer_rev").await;
assert!(
matches!(after_revoke, PolicyResult::SendPairingCode(_)),
"revoked peer should require re-pairing, got: {after_revoke:?}"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_dm_policy_pairing_wrong_code_rejected() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let _ = enforcer.check("peer_x").await;
let result = enforcer.approve_pairing("0000-0000").await;
assert!(result.is_none(), "wrong code should not be approved");
let check = enforcer.check("peer_x").await;
assert_ne!(
check,
PolicyResult::Allow,
"peer_x should not be allowed after failed approval attempt"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_pairing_code_case_insensitive() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let code = match enforcer.check("peer_ci").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected SendPairingCode, got {other:?}"),
};
let lower_code = code.to_lowercase();
let approved = enforcer.approve_pairing(&lower_code).await;
assert_eq!(
approved.as_deref(),
Some("peer_ci"),
"approve should succeed with lowercase code"
);
assert_eq!(
enforcer.check("peer_ci").await,
PolicyResult::Allow,
"peer should be allowed after case-insensitive approval"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_same_peer_reuses_existing_code() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let code1 = match enforcer.check("repeat_user").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected code, got {other:?}"),
};
let code2 = match enforcer.check("repeat_user").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected code, got {other:?}"),
};
assert_eq!(
code1, code2,
"same peer should receive the same pairing code"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_pairing_code_character_set() {
const VALID_CHARS: &str = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
for i in 0..100 {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]);
let code = match enforcer.check(&format!("user_{i}")).await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected code, got {other:?}"),
};
let parts: Vec<&str> = code.split('-').collect();
assert_eq!(parts.len(), 2, "code should have 2 parts: {code}");
assert_eq!(parts[0].len(), 4, "first part should be 4 chars: {code}");
assert_eq!(parts[1].len(), 4, "second part should be 4 chars: {code}");
for ch in code.chars() {
if ch == '-' {
continue;
}
assert!(
VALID_CHARS.contains(ch),
"character '{ch}' in code '{code}' is not in valid charset"
);
}
}
}
#[tokio::test(flavor = "current_thread")]
async fn test_allowlist_empty_denies_all() {
let enforcer = DmPolicyEnforcer::new(DmPolicy::Allowlist, vec![]);
for peer in ["alice", "bob", "admin", "", "root"] {
assert_eq!(
enforcer.check(peer).await,
PolicyResult::Deny,
"empty allowlist should deny '{peer}'"
);
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_concurrent_pairing_requests() {
use std::sync::Arc;
let enforcer = Arc::new(DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]));
let mut handles = Vec::new();
for i in 0..10 {
let e = Arc::clone(&enforcer);
handles.push(tokio::spawn(async move {
e.check(&format!("concurrent_user_{i}")).await
}));
}
let mut codes = 0u32;
let mut fulls = 0u32;
for h in handles {
match h.await.unwrap() {
PolicyResult::SendPairingCode(_) => codes += 1,
PolicyResult::PairingQueueFull => fulls += 1,
other => panic!("unexpected result: {other:?}"),
}
}
assert_eq!(codes, 3, "exactly 3 pairing codes should be issued");
assert_eq!(fulls, 7, "remaining 7 should get PairingQueueFull");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_concurrent_approve_same_code() {
use std::sync::Arc;
let enforcer = Arc::new(DmPolicyEnforcer::new(DmPolicy::Pairing, vec![]));
let code = match enforcer.check("dup_approve_user").await {
PolicyResult::SendPairingCode(c) => c,
other => panic!("expected code, got {other:?}"),
};
let e1 = Arc::clone(&enforcer);
let e2 = Arc::clone(&enforcer);
let c1 = code.clone();
let c2 = code.clone();
let (r1, r2) = tokio::join!(
tokio::spawn(async move { e1.approve_pairing(&c1).await }),
tokio::spawn(async move { e2.approve_pairing(&c2).await }),
);
let results = [r1.unwrap(), r2.unwrap()];
let successes = results.iter().filter(|r| r.is_some()).count();
assert_eq!(
successes, 1,
"exactly one concurrent approval should succeed, got {successes}"
);
assert_eq!(
enforcer.check("dup_approve_user").await,
PolicyResult::Allow,
"peer should be allowed after approval"
);
}