//! Integration test for reverse DNS pattern-based PTR generation use std::net::{IpAddr, Ipv4Addr}; use flashdns_types::ReverseZone; use std::sync::Arc; use tokio; #[tokio::test] #[ignore] // Requires running servers async fn test_reverse_dns_lifecycle() { // Test comprehensive reverse DNS lifecycle: // 1. Create ReverseZone via metadata store // 2. Query PTR via DNS handler pattern matching // 3. Verify response with pattern substitution // 4. Delete zone // 5. Verify PTR query fails after deletion // Setup: Create metadata store let metadata = Arc::new( flashdns_server::metadata::DnsMetadataStore::new_in_memory() ); // Step 1: Create reverse zone for 10.0.0.0/8 let zone = ReverseZone { id: uuid::Uuid::new_v4().to_string(), org_id: "test-org".to_string(), project_id: Some("test-project".to_string()), cidr: "10.0.0.0/8".to_string(), arpa_zone: "10.in-addr.arpa.".to_string(), // Will be auto-generated ptr_pattern: "{4}-{3}-{2}-{1}.hosts.cloud.local.".to_string(), ttl: 3600, created_at: chrono::Utc::now().timestamp() as u64, updated_at: chrono::Utc::now().timestamp() as u64, }; metadata.create_reverse_zone(zone.clone()).await.unwrap(); // Step 2: Simulate PTR query for 10.1.2.3 // Note: This requires DNS handler integration, which we'll test via pattern utilities use flashdns_server::dns::ptr_patterns::{parse_ptr_query_to_ip, apply_pattern}; let ptr_query = "3.2.1.10.in-addr.arpa."; let ip = parse_ptr_query_to_ip(ptr_query).unwrap(); assert_eq!(ip, IpAddr::V4(Ipv4Addr::new(10, 1, 2, 3))); // Step 3: Apply pattern substitution let result = apply_pattern(&zone.ptr_pattern, ip); assert_eq!(result, "3-2-1-10.hosts.cloud.local."); // Step 4: Verify zone can be retrieved let retrieved = metadata.get_reverse_zone(&zone.id).await.unwrap(); assert!(retrieved.is_some()); let retrieved_zone = retrieved.unwrap(); assert_eq!(retrieved_zone.cidr, "10.0.0.0/8"); assert_eq!(retrieved_zone.ptr_pattern, "{4}-{3}-{2}-{1}.hosts.cloud.local."); // Step 5: Delete zone metadata.delete_reverse_zone(&zone).await.unwrap(); // Step 6: Verify zone no longer exists let deleted_check = metadata.get_reverse_zone(&zone.id).await.unwrap(); assert!(deleted_check.is_none()); println!("✓ Reverse DNS lifecycle test passed"); } #[tokio::test] #[ignore] async fn test_reverse_dns_ipv6() { // Test IPv6 reverse DNS pattern use std::net::Ipv6Addr; use flashdns_server::dns::ptr_patterns::apply_pattern; let pattern = "v6-{short}.example.com."; let ip = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); let result = apply_pattern(pattern, ip); assert_eq!(result, "v6-2001-db8--1.example.com."); println!("✓ IPv6 reverse DNS pattern test passed"); } #[tokio::test] #[ignore] async fn test_multiple_reverse_zones_longest_prefix() { // Test longest prefix matching let metadata = Arc::new( flashdns_server::metadata::DnsMetadataStore::new_in_memory() ); // Create /8 zone let zone_8 = ReverseZone { id: uuid::Uuid::new_v4().to_string(), org_id: "test-org".to_string(), project_id: Some("test-project".to_string()), cidr: "192.0.0.0/8".to_string(), arpa_zone: "192.in-addr.arpa.".to_string(), ptr_pattern: "host-{ip}-slash8.example.com.".to_string(), ttl: 3600, created_at: chrono::Utc::now().timestamp() as u64, updated_at: chrono::Utc::now().timestamp() as u64, }; // Create /16 zone (more specific) let zone_16 = ReverseZone { id: uuid::Uuid::new_v4().to_string(), org_id: "test-org".to_string(), project_id: Some("test-project".to_string()), cidr: "192.168.0.0/16".to_string(), arpa_zone: "168.192.in-addr.arpa.".to_string(), ptr_pattern: "host-{ip}-slash16.example.com.".to_string(), ttl: 3600, created_at: chrono::Utc::now().timestamp() as u64, updated_at: chrono::Utc::now().timestamp() as u64, }; // Create /24 zone (most specific) let zone_24 = ReverseZone { id: uuid::Uuid::new_v4().to_string(), org_id: "test-org".to_string(), project_id: Some("test-project".to_string()), cidr: "192.168.1.0/24".to_string(), arpa_zone: "1.168.192.in-addr.arpa.".to_string(), ptr_pattern: "host-{ip}-slash24.example.com.".to_string(), ttl: 3600, created_at: chrono::Utc::now().timestamp() as u64, updated_at: chrono::Utc::now().timestamp() as u64, }; metadata.create_reverse_zone(zone_8.clone()).await.unwrap(); metadata.create_reverse_zone(zone_16.clone()).await.unwrap(); metadata.create_reverse_zone(zone_24.clone()).await.unwrap(); // Query IP that matches all three zones // Longest prefix (most specific) should win: /24 > /16 > /8 let _ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 5)); // Note: Actual longest-prefix matching is in DNS handler // Here we verify all zones are stored correctly let all_zones = metadata.list_reverse_zones("test-org", Some("test-project")).await.unwrap(); assert_eq!(all_zones.len(), 3); println!("✓ Multiple reverse zones test passed"); } #[tokio::test] async fn test_pattern_substitution_variations() { // Test various pattern substitution formats use flashdns_server::dns::ptr_patterns::apply_pattern; let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 5)); // Test individual octets assert_eq!(apply_pattern("{1}.{2}.{3}.{4}", ip), "192.168.1.5"); // Test reversed octets assert_eq!(apply_pattern("{4}.{3}.{2}.{1}", ip), "5.1.168.192"); // Test dashed IP assert_eq!(apply_pattern("{ip}", ip), "192-168-1-5"); // Test combined pattern assert_eq!(apply_pattern("server-{4}-subnet-{3}.dc.example.com.", ip), "server-5-subnet-1.dc.example.com."); println!("✓ Pattern substitution variations test passed"); }