cli: avoid data-dir initialization for version; create db parent dirs; redact generated passwords in CLI output
- Prevent 'nx9-wg version' from creating data directories by avoiding database initialization. - Create parent directories when an explicit --database path is provided. - Redact printed generated administrator passwords; announce file path or redact instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
commit
2ac6c81dfe
140 files changed
+31342
No files matched your search
@@ -0,0 +1,196 @@
|
||||
//! 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.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());
|
||||
}
|
||||
Reference in new issue
Block a user