Files
nx9-wg/crates/nx9-wireguard/tests/test_wireguard_engine.rs
T
2026-08-18 17:32:56 +05:30

200 lines
6.4 KiB
Rust

//! Integration tests for Phase 4 WireGuard Engine, Client Config Generator, and QR Code system.
use chrono::Utc;
use ipnet::IpNet;
use nx9_wg_core::crypto::{generate_keypair, generate_preshared_key};
use nx9_wg_core::types::wireguard::{Interface, Peer, PeerProfile, PeerState, PeerType};
use nx9_wireguard::{
ClientConfigBuilder, SimulatedWireGuardEngine, WireGuardEngine, generate_qr_ascii,
generate_qr_data_url, generate_qr_png_bytes, generate_qr_svg,
};
use std::str::FromStr;
use uuid::Uuid;
#[tokio::test]
async fn test_wireguard_engine_lifecycle_and_telemetry() {
let engine = SimulatedWireGuardEngine::new();
let (srv_priv, srv_pub) = generate_keypair();
let (peer1_priv, peer1_pub) = generate_keypair();
let (peer2_priv, peer2_pub) = generate_keypair();
let now = Utc::now().naive_utc();
let iface = Interface {
id: Uuid::new_v4(),
name: "wg0".to_string(),
private_key: srv_priv,
public_key: srv_pub.clone(),
listen_port: 51820,
address_v4: IpNet::from_str("10.0.0.1/24").unwrap(),
address_v6: None,
mtu: Some(1420),
dns: Some("1.1.1.1".to_string()),
enabled: true,
pre_up: None,
post_up: None,
pre_down: None,
post_down: None,
created_at: now,
updated_at: now,
};
let p1 = Peer {
id: Uuid::new_v4(),
interface_id: iface.id,
name: "laptop-user".to_string(),
peer_type: PeerType::RoadWarrior,
state: PeerState::Active,
public_key: peer1_pub.clone(),
private_key: Some(peer1_priv),
preshared_key: Some(generate_preshared_key()),
endpoint: Some("198.51.100.2:45000".to_string()),
allowed_ips: "10.0.0.2/32".to_string(),
server_allowed_ips: None,
address_v4: Some(IpNet::from_str("10.0.0.2/32").unwrap()),
address_v6: None,
dns: None,
mtu: None,
persistent_keepalive: Some(25),
profile: PeerProfile::FullTunnel,
expires_at: None,
last_handshake_at: None,
created_at: now,
updated_at: now,
};
let p2 = Peer {
id: Uuid::new_v4(),
interface_id: iface.id,
name: "phone-user".to_string(),
peer_type: PeerType::RoadWarrior,
state: PeerState::Disabled, // disabled peer should NOT be in active kernel set
public_key: peer2_pub.clone(),
private_key: Some(peer2_priv),
preshared_key: None,
endpoint: None,
allowed_ips: "10.0.0.3/32".to_string(),
server_allowed_ips: None,
address_v4: Some(IpNet::from_str("10.0.0.3/32").unwrap()),
address_v6: None,
dns: None,
mtu: None,
persistent_keepalive: None,
profile: PeerProfile::FullTunnel,
expires_at: None,
last_handshake_at: None,
created_at: now,
updated_at: now,
};
// 1. Sync interface with engine
engine
.sync_interface(&iface, &[p1.clone(), p2.clone()])
.await
.expect("sync interface");
// 2. Read live interface stats
let stats = engine
.get_interface_stats("wg0")
.await
.expect("get stats")
.expect("interface exists");
assert_eq!(stats.name, "wg0");
assert_eq!(stats.public_key, srv_pub.as_str());
assert_eq!(stats.addresses, vec!["10.0.0.1/24".to_string()]);
assert_eq!(stats.mtu, Some(1420));
assert!(stats.is_up);
assert_eq!(
stats.peers.len(),
1,
"only active peers should be synchronized"
);
assert_eq!(stats.peers[0].public_key, peer1_pub.as_str());
// 3. Simulate peer traffic activity
engine
.simulate_peer_activity("wg0", peer1_pub.as_str(), 1024, 2048)
.await
.expect("simulate activity");
let updated_stats = engine.get_interface_stats("wg0").await.unwrap().unwrap();
assert_eq!(updated_stats.peers[0].rx_bytes, 1024);
assert_eq!(updated_stats.peers[0].tx_bytes, 2048);
assert!(updated_stats.peers[0].last_handshake_at.is_some());
// 4. Delete interface
engine.delete_interface("wg0").await.expect("delete");
let after_del = engine.get_interface_stats("wg0").await.expect("query");
assert!(after_del.is_none());
}
#[test]
fn test_client_config_and_qr_codes() {
let (srv_priv, srv_pub) = generate_keypair();
let (peer_priv, peer_pub) = generate_keypair();
let psk = generate_preshared_key();
let now = Utc::now().naive_utc();
let iface = Interface {
id: Uuid::new_v4(),
name: "wg0".to_string(),
private_key: srv_priv,
public_key: srv_pub,
listen_port: 51820,
address_v4: IpNet::from_str("10.0.0.1/24").unwrap(),
address_v6: None,
mtu: Some(1420),
dns: Some("1.1.1.1".to_string()),
enabled: true,
pre_up: None,
post_up: None,
pre_down: None,
post_down: None,
created_at: now,
updated_at: now,
};
let peer = Peer {
id: Uuid::new_v4(),
interface_id: iface.id,
name: "ipad-charlie".to_string(),
peer_type: PeerType::RoadWarrior,
state: PeerState::Active,
public_key: peer_pub,
private_key: Some(peer_priv),
preshared_key: Some(psk),
endpoint: None,
allowed_ips: "10.0.0.5/32".to_string(),
server_allowed_ips: None,
address_v4: Some(IpNet::from_str("10.0.0.5/32").unwrap()),
address_v6: None,
dns: None,
mtu: None,
persistent_keepalive: Some(25),
profile: PeerProfile::FullTunnel,
expires_at: None,
last_handshake_at: None,
created_at: now,
updated_at: now,
};
let config_str = ClientConfigBuilder::build(&peer, &iface, "203.0.113.10").unwrap();
assert!(config_str.contains("[Interface]"));
assert!(config_str.contains("[Peer]"));
assert!(config_str.contains("Endpoint = 203.0.113.10:51820"));
// Verify all QR code output formats
let svg = generate_qr_svg(&config_str).unwrap();
assert!(svg.contains("<svg"));
let png_bytes = generate_qr_png_bytes(&config_str).unwrap();
assert!(!png_bytes.is_empty());
assert_eq!(&png_bytes[1..4], b"PNG");
let data_url = generate_qr_data_url(&config_str).unwrap();
assert!(data_url.starts_with("data:image/png;base64,"));
let ascii = generate_qr_ascii(&config_str).unwrap();
assert!(!ascii.is_empty());
}