//! Tests for Network, Route, and Firewall Rule repositories. use chrono::Utc; use ipnet::IpNet; use nx9_wg_core::crypto::generate_keypair; use nx9_wg_core::types::firewall::{ FirewallAction, FirewallDirection, FirewallProtocol, FirewallRule, }; use nx9_wg_core::types::network::{Network, Route}; use nx9_wg_core::types::wireguard::{Interface, InterfaceRole}; use nx9_wg_db::Store; use std::net::IpAddr; use std::str::FromStr; use uuid::Uuid; #[tokio::test] async fn test_network_and_route_crud() { let store = Store::connect_in_memory().await.expect("connect"); store.migrate().await.expect("migrate"); let now = Utc::now().naive_utc(); let net_id = Uuid::new_v4(); let net = Network { id: net_id, name: "Home Lab".to_string(), cidr: IpNet::from_str("192.168.10.0/24").expect("cidr"), enabled: true, description: Some("Internal lab subnet".to_string()), created_at: now, updated_at: now, }; store.create_network(&net).await.expect("create_network"); let fetched_net = store .get_network(net_id) .await .expect("get") .expect("found"); assert_eq!(fetched_net.name, "Home Lab"); assert_eq!(fetched_net.cidr.to_string(), "192.168.10.0/24"); assert!(fetched_net.enabled); // Test routes let route_id = Uuid::new_v4(); let route = Route { id: route_id, network_id: Some(net_id), interface_id: None, destination: IpNet::from_str("192.168.10.0/24").expect("dest cidr"), gateway: Some(IpAddr::from_str("10.0.0.1").expect("gateway")), interface_name: None, metric: Some(100), enabled: true, description: Some("Lab route via wg gateway".to_string()), created_at: now, updated_at: now, }; store.create_route(&route).await.expect("create_route"); let fetched_route = store .get_route(route_id) .await .expect("get") .expect("route found"); assert_eq!(fetched_route.network_id, Some(net_id)); assert_eq!( fetched_route.gateway, Some(IpAddr::from_str("10.0.0.1").unwrap()) ); assert_eq!(fetched_route.metric, Some(100)); // Enable/disable route store .set_route_enabled(route_id, false) .await .expect("disable"); let disabled_route = store .get_route(route_id) .await .expect("get") .expect("route"); assert!(!disabled_route.enabled); // List routes for network let net_routes = store .list_routes_for_network(net_id) .await .expect("list net routes"); assert_eq!(net_routes.len(), 1); // Deleting network sets route's network_id to NULL (ON DELETE SET NULL) store.delete_network(net_id).await.expect("delete network"); let route_after_net_delete = store .get_route(route_id) .await .expect("get") .expect("route"); assert!( route_after_net_delete.network_id.is_none(), "network_id must be SET NULL when network is deleted" ); } #[tokio::test] async fn test_firewall_rule_crud_and_priority_ordering() { let store = Store::connect_in_memory().await.expect("connect"); store.migrate().await.expect("migrate"); let now = Utc::now().naive_utc(); let iface_id = Uuid::new_v4(); let (priv_k, pub_k) = generate_keypair(); let iface = Interface { id: iface_id, name: "wg0".to_string(), role: InterfaceRole::Overlay, private_key: priv_k, public_key: pub_k, listen_port: Some(51820), address_v4: IpNet::from_str("10.0.0.1/24").unwrap(), address_v6: None, mtu: None, dns: None, enabled: true, pre_up: None, post_up: None, pre_down: None, post_down: None, created_at: now, updated_at: now, }; store .create_interface(&iface) .await .expect("create interface"); let rule1_id = Uuid::new_v4(); let rule1 = FirewallRule { id: rule1_id, name: "Allow SSH".to_string(), interface_id: Some(iface_id), peer_id: None, direction: FirewallDirection::In, action: FirewallAction::Accept, protocol: FirewallProtocol::Tcp, source: None, destination: None, source_port: None, destination_port: Some(22), port_range: None, priority: 50, enabled: true, description: Some("SSH access".to_string()), created_at: now, updated_at: now, }; let rule2_id = Uuid::new_v4(); let rule2 = FirewallRule { id: rule2_id, name: "Drop All Other".to_string(), interface_id: Some(iface_id), peer_id: None, direction: FirewallDirection::In, action: FirewallAction::Drop, protocol: FirewallProtocol::Any, source: None, destination: None, source_port: None, destination_port: None, port_range: None, priority: 100, enabled: true, description: Some("Default drop".to_string()), created_at: now, updated_at: now, }; store .create_firewall_rule(&rule2) .await .expect("create rule2"); store .create_firewall_rule(&rule1) .await .expect("create rule1"); // List rules should order by priority ASC (rule1 priority 50 comes before rule2 priority 100) let rules = store.list_firewall_rules().await.expect("list rules"); assert_eq!(rules.len(), 2); assert_eq!(rules[0].id, rule1_id); assert_eq!(rules[0].priority, 50); assert_eq!(rules[1].id, rule2_id); assert_eq!(rules[1].priority, 100); // List rules for interface let iface_rules = store .list_firewall_rules_for_interface(iface_id) .await .expect("list iface rules"); assert_eq!(iface_rules.len(), 2); // Enable/disable rule store .set_firewall_rule_enabled(rule1_id, false) .await .expect("disable"); let disabled = store .get_firewall_rule(rule1_id) .await .expect("get") .expect("rule"); assert!(!disabled.enabled); // Delete rule store.delete_firewall_rule(rule1_id).await.expect("delete"); assert!( store .get_firewall_rule(rule1_id) .await .expect("get") .is_none() ); // Peer-specific rule with port range let peer_id = Uuid::new_v4(); let peer = nx9_wg_core::types::wireguard::Peer { id: peer_id, interface_id: iface_id, name: "test-peer-fw".to_string(), peer_type: nx9_wg_core::types::wireguard::PeerType::RoadWarrior, state: nx9_wg_core::types::wireguard::PeerState::Active, public_key: nx9_wg_core::types::wireguard::WireGuardPublicKey::new( "testpubkey12345678901234567890123456789012=".to_string(), ), private_key: None, preshared_key: None, endpoint: None, allowed_ips: "10.0.0.2/32".to_string(), server_allowed_ips: None, address_v4: Some("10.0.0.2/32".parse().unwrap()), address_v6: None, dns: None, mtu: None, persistent_keepalive: None, profile: nx9_wg_core::types::wireguard::PeerProfile::FullTunnel, expires_at: None, last_handshake_at: None, created_at: now, updated_at: now, }; store.create_peer(&peer).await.expect("create peer"); let peer_rule_id = Uuid::new_v4(); let peer_rule = FirewallRule { id: peer_rule_id, name: "Peer Port Range Rule".to_string(), interface_id: Some(iface_id), peer_id: Some(peer_id), direction: FirewallDirection::In, action: FirewallAction::Accept, protocol: FirewallProtocol::TcpUdp, source: None, destination: None, source_port: None, destination_port: None, port_range: Some("8000-8100".to_string()), priority: 25, enabled: true, description: Some("Custom peer range".to_string()), created_at: now, updated_at: now, }; store .create_firewall_rule(&peer_rule) .await .expect("create peer rule"); let peer_rules = store .list_firewall_rules_for_peer(peer_id) .await .expect("list peer rules"); assert_eq!(peer_rules.len(), 1); assert_eq!(peer_rules[0].port_range.as_deref(), Some("8000-8100")); assert_eq!(peer_rules[0].protocol, FirewallProtocol::TcpUdp); }