Configs updated
This commit is contained in:
@@ -21,21 +21,54 @@ NC='\033[0m' # No Color
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
# Function to run a position test
|
||||
# Function to create stored IP for testing IP-based replacement
|
||||
create_stored_ip() {
|
||||
local hostname="$1"
|
||||
local ip="$2"
|
||||
|
||||
# Ensure test storage directory exists
|
||||
mkdir -p "./test_storage"
|
||||
|
||||
# Create JSON IP entry (matching the FileIpRepository format)
|
||||
cat > "./test_storage/${hostname}.json" << EOF
|
||||
{
|
||||
"ip": "${ip}",
|
||||
"hostname": "${hostname}",
|
||||
"comment": null,
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"updated_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Function to run a position test with IP-based replacement support
|
||||
run_position_test() {
|
||||
local test_name="$1"
|
||||
local config_file="$2"
|
||||
local hostname="$3"
|
||||
local expected_line_number="$4"
|
||||
local check_pattern="$5"
|
||||
local fallback_pattern="$6" # Optional: for IP-based replacement pattern
|
||||
local test_with_stored_ip="$7" # Optional: create stored IP for IP-based replacement
|
||||
|
||||
echo -e "\n${BLUE}Testing: $test_name${NC}"
|
||||
|
||||
# Store original content
|
||||
local original_content=$(cat "$config_file")
|
||||
|
||||
# Run the DDNS updater
|
||||
local command="cargo run --quiet -- --host $hostname --config $config_file --no-reload"
|
||||
# If testing IP-based replacement, create stored IP entry
|
||||
if [ "$test_with_stored_ip" = "true" ]; then
|
||||
# Extract the IP from the config file at the expected line for stored IP testing
|
||||
local current_line=$(sed -n "${expected_line_number}p" "$config_file")
|
||||
local stored_ip=$(echo "$current_line" | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1)
|
||||
if [ -n "$stored_ip" ]; then
|
||||
echo "Creating stored IP entry: $hostname -> $stored_ip"
|
||||
create_stored_ip "$hostname" "$stored_ip"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run the DDNS updater with test mode
|
||||
local command="DDNS_TEST_MODE=1 cargo run --quiet -- --host $hostname --config $config_file --no-reload"
|
||||
echo "Command: $command"
|
||||
|
||||
local output
|
||||
@@ -46,12 +79,30 @@ run_position_test() {
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
# Check if the entry is at the expected line and position
|
||||
local line_content=$(sed -n "${expected_line_number}p" "$config_file")
|
||||
local pattern_matched=false
|
||||
|
||||
# First check the primary pattern (legacy DDNS comment format)
|
||||
if echo "$line_content" | grep -q "$check_pattern"; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number"
|
||||
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (legacy format)"
|
||||
pattern_matched=true
|
||||
# If fallback pattern provided, check IP-based replacement
|
||||
elif [ -n "$fallback_pattern" ] && echo "$line_content" | grep -q "$fallback_pattern"; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (IP-based format)"
|
||||
pattern_matched=true
|
||||
# For backward compatibility, also check if line contains 'allow' and looks like an IP
|
||||
elif echo "$line_content" | grep -q "allow [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+"; then
|
||||
echo -e "${GREEN}✓ PASS${NC} - Entry found at expected line $expected_line_number (IP updated)"
|
||||
pattern_matched=true
|
||||
fi
|
||||
|
||||
if [ "$pattern_matched" = true ]; then
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC} - Entry not found at line $expected_line_number"
|
||||
echo "Expected pattern: $check_pattern"
|
||||
if [ -n "$fallback_pattern" ]; then
|
||||
echo "Fallback pattern: $fallback_pattern"
|
||||
fi
|
||||
echo "Actual line content: $line_content"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
@@ -61,6 +112,11 @@ run_position_test() {
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
|
||||
# Cleanup stored IP files if created
|
||||
if [ "$test_with_stored_ip" = "true" ]; then
|
||||
rm -f "./test_storage/${hostname}.json"
|
||||
fi
|
||||
|
||||
# Restore original content for clean testing
|
||||
echo "$original_content" > "$config_file"
|
||||
}
|
||||
@@ -77,8 +133,8 @@ run_non_addition_test() {
|
||||
local original_content=$(cat "$config_file")
|
||||
local original_hash=$(md5sum "$config_file" | cut -d' ' -f1)
|
||||
|
||||
# Run the DDNS updater
|
||||
local command="cargo run --quiet -- --host $hostname --config $config_file --no-reload"
|
||||
# Run the DDNS updater with test mode
|
||||
local command="DDNS_TEST_MODE=1 cargo run --quiet -- --host $hostname --config $config_file --no-reload"
|
||||
echo "Command: $command"
|
||||
|
||||
local output
|
||||
@@ -120,6 +176,27 @@ git checkout test_configs/valid/basic_server.conf test_configs/valid/full_nginx.
|
||||
echo -e "\n${YELLOW}=== Testing Non-Addition Behavior ===${NC}"
|
||||
run_non_addition_test "No IP added to config without DDNS entries" "test_configs/valid/no_ddns_entries.conf" "google.com"
|
||||
|
||||
# Test 1.5: IP-based replacement (new feature test)
|
||||
echo -e "\n${YELLOW}=== Testing IP-Based Replacement (New Feature) ===${NC}"
|
||||
|
||||
# Create a config with clean IP entries (no DDNS comments)
|
||||
cat > test_configs/valid/ip_based_test.conf << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com;
|
||||
|
||||
location / {
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
allow 192.168.1.1;
|
||||
allow 142.250.102.138;
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "IP-based replacement without DDNS comments" "test_configs/valid/ip_based_test.conf" "google.com" "9" "allow.*# DDNS: google.com" "allow [0-9]" "true"
|
||||
|
||||
# Test 2: Position preservation in basic_server.conf
|
||||
echo -e "\n${YELLOW}=== Testing Position Preservation ===${NC}"
|
||||
|
||||
@@ -139,7 +216,7 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "Position preserved in basic_server.conf location block" "test_configs/valid/basic_server.conf" "google.com" "9" "allow.*# DDNS: google.com"
|
||||
run_position_test "Position preserved in basic_server.conf location block" "test_configs/valid/basic_server.conf" "google.com" "9" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Test 3: Position preservation in complex_ssl.conf (location block)
|
||||
# Add a DDNS entry to complex_ssl.conf
|
||||
@@ -162,12 +239,12 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "Position preserved in complex_ssl.conf location block" "test_configs/valid/complex_ssl.conf" "google.com" "13" "allow.*# DDNS: google.com"
|
||||
run_position_test "Position preserved in complex_ssl.conf location block" "test_configs/valid/complex_ssl.conf" "google.com" "13" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Test 4: Position preservation in full_nginx.conf (location block)
|
||||
# The full_nginx.conf already has a DDNS entry, let's test it
|
||||
git checkout test_configs/valid/full_nginx.conf 2>/dev/null || true
|
||||
run_position_test "Position preserved in full_nginx.conf location block" "test_configs/valid/full_nginx.conf" "google.com" "25" "allow.*# DDNS: google.com"
|
||||
run_position_test "Position preserved in full_nginx.conf location block" "test_configs/valid/full_nginx.conf" "google.com" "25" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Test 5: Server block level position preservation
|
||||
cat > test_configs/valid/server_level_test.conf << 'EOF'
|
||||
@@ -184,7 +261,7 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "Position preserved at server block level" "test_configs/valid/server_level_test.conf" "google.com" "5" "allow.*# DDNS: google.com"
|
||||
run_position_test "Position preserved at server block level" "test_configs/valid/server_level_test.conf" "google.com" "5" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Test 6: Multiple DDNS entries - ensure only the first one is replaced
|
||||
cat > test_configs/valid/multiple_ddns_test.conf << 'EOF'
|
||||
@@ -205,7 +282,7 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "First DDNS entry replaced when multiple exist" "test_configs/valid/multiple_ddns_test.conf" "google.com" "6" "allow.*# DDNS: google.com"
|
||||
run_position_test "First DDNS entry replaced when multiple exist" "test_configs/valid/multiple_ddns_test.conf" "google.com" "6" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Test 7: Legacy format conversion and position preservation
|
||||
cat > test_configs/valid/legacy_format_test.conf << 'EOF'
|
||||
@@ -222,12 +299,14 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
run_position_test "Legacy format converted and position preserved" "test_configs/valid/legacy_format_test.conf" "google.com" "8" "allow.*# DDNS: google.com"
|
||||
run_position_test "Legacy format converted and position preserved" "test_configs/valid/legacy_format_test.conf" "google.com" "8" "allow.*# DDNS: google.com" "allow [0-9]"
|
||||
|
||||
# Cleanup temporary test files
|
||||
rm -f test_configs/valid/server_level_test.conf
|
||||
rm -f test_configs/valid/multiple_ddns_test.conf
|
||||
rm -f test_configs/valid/legacy_format_test.conf
|
||||
rm -f test_configs/valid/ip_based_test.conf
|
||||
rm -rf test_storage
|
||||
|
||||
# Restore original configs
|
||||
echo -e "\n${YELLOW}Restoring original configs...${NC}"
|
||||
@@ -241,9 +320,10 @@ echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "\n${GREEN}🎉 All position and non-addition tests passed!${NC}"
|
||||
echo -e "${GREEN}✅ IP addresses are only updated in existing DDNS entries${NC}"
|
||||
echo -e "${GREEN}✅ IP addresses are updated using both IP-based and legacy DDNS comment methods${NC}"
|
||||
echo -e "${GREEN}✅ Original positions are preserved during updates${NC}"
|
||||
echo -e "${GREEN}✅ No new IP addresses are added when none exist${NC}"
|
||||
echo -e "${GREEN}✅ IP-based replacement works without requiring DDNS comments${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}❌ Some position/non-addition tests failed!${NC}"
|
||||
|
||||
@@ -52,40 +52,73 @@ impl NginxHandler {
|
||||
&self,
|
||||
config_path: &std::path::Path,
|
||||
hostname: &str,
|
||||
_old_ip: Option<IpAddr>,
|
||||
old_ip: Option<IpAddr>,
|
||||
new_ip: IpAddr,
|
||||
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let content = fs::read_to_string(config_path).await?;
|
||||
let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
|
||||
let mut updated = false;
|
||||
|
||||
// Comment pattern for hostname identification
|
||||
let hostname_comment = format!("# DDNS: {}", hostname);
|
||||
// If we have an old IP, look for it and replace with new IP
|
||||
if let Some(old_ip_addr) = old_ip {
|
||||
// First pass: look for existing IP entries to replace
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let trimmed = line.trim();
|
||||
|
||||
// Look for existing DDNS-related entries for this hostname (both formats)
|
||||
// Only replace existing entries - do NOT add new ones if none exist
|
||||
// Check if this line contains the old IP in an allow directive
|
||||
if trimmed.starts_with("allow ") && trimmed.contains(&old_ip_addr.to_string()) {
|
||||
// Get the current indentation
|
||||
let indent = &line[..line.len() - line.trim_start().len()];
|
||||
|
||||
// First pass: look for existing entries to replace
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let trimmed = line.trim();
|
||||
// Preserve any existing comment
|
||||
let comment_part = if trimmed.contains('#') {
|
||||
let parts: Vec<&str> = trimmed.splitn(2, '#').collect();
|
||||
if parts.len() > 1 {
|
||||
format!(" # {}", parts[1].trim())
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
// Check if this is a DDNS-related allow entry for our hostname
|
||||
if trimmed.starts_with("allow ")
|
||||
&& (trimmed.contains(&format!("# DDNS: {}", hostname))
|
||||
|| trimmed.contains(&format!("# DDNS for {}", hostname)))
|
||||
{
|
||||
// Get the current indentation
|
||||
let indent = &line[..line.len() - line.trim_start().len()];
|
||||
// Replace this line with the new IP, preserving formatting and comments
|
||||
lines[i] = format!("{}allow {};{}", indent, new_ip, comment_part);
|
||||
updated = true;
|
||||
eprintln!(
|
||||
"DEBUG: Replaced old IP {} with new IP {} for hostname: {}",
|
||||
old_ip_addr, new_ip, hostname
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no old IP is stored, look for DDNS-managed entries (legacy support)
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let trimmed = line.trim();
|
||||
|
||||
// Replace this line with our new entry
|
||||
lines[i] = format!("{}allow {}; {}", indent, new_ip, hostname_comment);
|
||||
updated = true;
|
||||
break;
|
||||
// Check if this is a DDNS-related allow entry for our hostname
|
||||
if trimmed.starts_with("allow ")
|
||||
&& (trimmed.contains(&format!("# DDNS: {}", hostname))
|
||||
|| trimmed.contains(&format!("# DDNS for {}", hostname)))
|
||||
{
|
||||
// Get the current indentation
|
||||
let indent = &line[..line.len() - line.trim_start().len()];
|
||||
|
||||
// Replace this line with our new entry
|
||||
lines[i] = format!("{}allow {}; # DDNS: {}", indent, new_ip, hostname);
|
||||
updated = true;
|
||||
eprintln!(
|
||||
"DEBUG: Replaced DDNS-commented entry with new IP {} for hostname: {}",
|
||||
new_ip, hostname
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Important: Do NOT add new entries if none existed before
|
||||
// This ensures we only update existing DDNS-managed entries
|
||||
// This ensures we only update existing entries
|
||||
|
||||
if updated {
|
||||
let new_content = lines.join("\n");
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use ddns_updater::domain::models::WebServerConfig;
|
||||
use ddns_updater::infrastructure::webservers::WebServerHandler;
|
||||
use ddns_updater::infrastructure::webservers::nginx::NginxHandler;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Testing IP-based replacement in nginx config...");
|
||||
|
||||
// Create nginx handler
|
||||
let handler = NginxHandler::new();
|
||||
|
||||
// Create config
|
||||
let config = WebServerConfig {
|
||||
server_type: "nginx".to_string(),
|
||||
path: "test_configs/valid/basic_server.conf".to_string(),
|
||||
};
|
||||
|
||||
// Test replacing 142.250.102.138 with 8.8.8.8
|
||||
let old_ip = IpAddr::from_str("142.250.102.138")?;
|
||||
let new_ip = IpAddr::from_str("8.8.8.8")?;
|
||||
|
||||
println!(
|
||||
"Attempting to replace {} with {} for hostname 'example.com'",
|
||||
old_ip, new_ip
|
||||
);
|
||||
|
||||
let result = handler
|
||||
.update_allow_list(&config, "example.com", Some(old_ip), new_ip)
|
||||
.await?;
|
||||
|
||||
println!("Update result: {}", result);
|
||||
|
||||
// Read the file to see the result
|
||||
let content = std::fs::read_to_string("test_configs/valid/basic_server.conf")?;
|
||||
println!("Updated config content:\n{}", content);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user