//! Integration test suite for Reconciliation Engine. use nx9_wg_api::reconciliation::ReconciliationEngine; use nx9_wg_api::state::AppState; use nx9_wg_core::crypto::generate_keypair; use nx9_wg_core::types::wireguard::{Interface, InterfaceRole}; use nx9_wg_core::validation::validate_cidr; use nx9_wg_db::Store; use nx9_wg_network::SimulatedNetworkEngine; use nx9_wireguard::{SimulatedWireGuardEngine, WireGuardEngine}; use std::sync::Arc; use tempfile::tempdir; use uuid::Uuid; #[tokio::test] async fn test_reconciliation_engine_drift_detection_and_apply() { let dir = tempdir().expect("create temp dir"); let db_path = dir.path().join("reconcile.db"); let store = Store::connect(&db_path.to_string_lossy()) .await .expect("connect to db"); store.migrate().await.expect("run migrations"); let state = AppState::new(store.clone()); let wg_engine = Arc::new(SimulatedWireGuardEngine::new()); let net_engine = Arc::new(SimulatedNetworkEngine::new()); let reconciler = ReconciliationEngine::new(state, wg_engine.clone(), net_engine.clone()); // 1. Create desired interface in SQLite let (priv_key, pub_key) = generate_keypair(); let iface = Interface { id: Uuid::new_v4(), name: "wg0".to_string(), role: InterfaceRole::Overlay, private_key: priv_key, public_key: pub_key, listen_port: Some(51820), address_v4: validate_cidr("10.0.0.1/24").unwrap(), address_v6: None, mtu: Some(1420), dns: None, enabled: true, pre_up: None, post_up: None, pre_down: None, post_down: None, created_at: chrono::Utc::now().naive_utc(), updated_at: chrono::Utc::now().naive_utc(), }; store .create_interface(&iface) .await .expect("create interface"); // 2. Compute plan: should detect missing wg0 in kernel let plan = reconciler.plan().await.expect("compute plan"); assert!(plan.has_drift); assert_eq!(plan.interface_changes, 1); assert!(!plan.actions.is_empty()); // 3. Apply reconciliation let report = reconciler.apply().await.expect("apply plan"); assert!(report.success); assert!(report.executed_actions > 0); // 4. Verify live WireGuard interface state let live_stats = wg_engine.get_interface_stats("wg0").await.unwrap(); assert!(live_stats.is_some()); let stats = live_stats.unwrap(); assert_eq!(stats.name, "wg0"); assert_eq!(stats.listen_port, 51820); // 5. Verify audit event was logged let audits = store .list_audit_events(&nx9_wg_db::AuditFilter::default(), 10, 0) .await .expect("list audits"); assert!(!audits.is_empty()); }