package nginx_log import ( "testing" ) // TestScanForLogDirectivesRemoval tests that removed log directives are properly cleaned up func TestScanForLogDirectivesRemoval(t *testing.T) { // Clear cache before test ClearLogCache() configPath := "/etc/nginx/sites-available/test.conf" // First scan with two log directives content1 := []byte(` server { listen 80; server_name example.com; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; } `) err := scanForLogDirectives(configPath, content1) if err != nil { t.Fatalf("First scan failed: %v", err) } // Check that both logs are cached logs := GetAllLogPaths() if len(logs) != 2 { t.Fatalf("Expected 2 logs after first scan, got %d", len(logs)) } // Verify the config file is tracked accessFound := false errorFound := false for _, log := range logs { if log.ConfigFile != configPath { t.Errorf("Expected config file %s, got %s", configPath, log.ConfigFile) } if log.Type == "access" { accessFound = true } if log.Type == "error" { errorFound = true } } if !accessFound || !errorFound { t.Error("Expected both access and error logs to be found") } // Second scan with only one log directive (error_log removed) content2 := []byte(` server { listen 80; server_name example.com; access_log /var/log/nginx/access.log; } `) err = scanForLogDirectives(configPath, content2) if err != nil { t.Fatalf("Second scan failed: %v", err) } // Check that only access log remains logs = GetAllLogPaths() if len(logs) != 1 { t.Fatalf("Expected 1 log after second scan, got %d", len(logs)) } if logs[0].Type != "access" { t.Errorf("Expected remaining log to be access log, got %s", logs[0].Type) } // Third scan with no log directives content3 := []byte(` server { listen 80; server_name example.com; } `) err = scanForLogDirectives(configPath, content3) if err != nil { t.Fatalf("Third scan failed: %v", err) } // Check that no logs remain logs = GetAllLogPaths() if len(logs) != 0 { t.Fatalf("Expected 0 logs after third scan, got %d", len(logs)) } } // TestScanForLogDirectivesMultipleConfigs tests that logs from different config files are handled independently func TestScanForLogDirectivesMultipleConfigs(t *testing.T) { // Clear cache before test ClearLogCache() configPath1 := "/etc/nginx/sites-available/site1.conf" configPath2 := "/etc/nginx/sites-available/site2.conf" // Scan first config content1 := []byte(` server { listen 80; server_name site1.com; access_log /var/log/nginx/site1_access.log; } `) err := scanForLogDirectives(configPath1, content1) if err != nil { t.Fatalf("First config scan failed: %v", err) } // Scan second config content2 := []byte(` server { listen 80; server_name site2.com; access_log /var/log/nginx/site2_access.log; } `) err = scanForLogDirectives(configPath2, content2) if err != nil { t.Fatalf("Second config scan failed: %v", err) } // Should have 2 logs total logs := GetAllLogPaths() if len(logs) != 2 { t.Fatalf("Expected 2 logs from 2 configs, got %d", len(logs)) } // Remove log from first config, should only affect that config emptyContent := []byte(` server { listen 80; server_name site1.com; } `) err = scanForLogDirectives(configPath1, emptyContent) if err != nil { t.Fatalf("Empty config scan failed: %v", err) } // Should have 1 log remaining (from config2) logs = GetAllLogPaths() if len(logs) != 1 { t.Fatalf("Expected 1 log after removing from config1, got %d", len(logs)) } if logs[0].ConfigFile != configPath2 { t.Errorf("Expected remaining log to be from config2 (%s), got %s", configPath2, logs[0].ConfigFile) } } // TestScanForLogDirectivesIgnoreComments tests that commented log directives are ignored func TestScanForLogDirectivesIgnoreComments(t *testing.T) { // Clear cache before test ClearLogCache() configPath := "/etc/nginx/sites-available/test.conf" // Content with both active and commented log directives content := []byte(` server { listen 80; server_name example.com; # This is a commented access log - should be ignored # access_log /var/log/nginx/commented_access.log; # Multi-line comment block #error_log /var/log/nginx/commented_error.log; # Active log directives (not commented) access_log /var/log/nginx/active_access.log; error_log /var/log/nginx/active_error.log; # Another commented directive with indentation # access_log /var/log/nginx/indented_comment.log; # Inline comment after directive should still work access_log /var/log/nginx/inline_comment.log; # this is active with comment } `) err := scanForLogDirectives(configPath, content) if err != nil { t.Fatalf("Scan failed: %v", err) } // Should only find 3 active log directives (not the commented ones) logs := GetAllLogPaths() expectedCount := 3 if len(logs) != expectedCount { t.Fatalf("Expected %d logs, got %d. Logs found: %+v", expectedCount, len(logs), logs) } // Verify the correct paths were found expectedPaths := map[string]bool{ "/var/log/nginx/active_access.log": false, "/var/log/nginx/active_error.log": false, "/var/log/nginx/inline_comment.log": false, } for _, log := range logs { if _, exists := expectedPaths[log.Path]; !exists { t.Errorf("Unexpected log path found: %s", log.Path) } else { expectedPaths[log.Path] = true } } // Check that all expected paths were found for path, found := range expectedPaths { if !found { t.Errorf("Expected log path not found: %s", path) } } // Verify no commented paths were included commentedPaths := []string{ "/var/log/nginx/commented_access.log", "/var/log/nginx/commented_error.log", "/var/log/nginx/indented_comment.log", } for _, log := range logs { for _, commentedPath := range commentedPaths { if log.Path == commentedPath { t.Errorf("Commented log path should not be included: %s", commentedPath) } } } } // TestLogDirectiveRegex tests the regex pattern and comment filtering logic func TestLogDirectiveRegex(t *testing.T) { testCases := []struct { name string content string expectedActive int // number of active (non-commented) matches expected }{ { name: "Active directives", content: "access_log /var/log/nginx/access.log;\nerror_log /var/log/nginx/error.log;", expectedActive: 2, }, { name: "Commented directives", content: "# access_log /var/log/nginx/access.log;\n#error_log /var/log/nginx/error.log;", expectedActive: 0, }, { name: "Mixed active and commented", content: "access_log /var/log/nginx/access.log;\n# error_log /var/log/nginx/error.log;", expectedActive: 1, }, { name: "Indented comments", content: " # access_log /var/log/nginx/access.log;\n error_log /var/log/nginx/error.log;", expectedActive: 1, }, { name: "Inline comments after directive", content: "access_log /var/log/nginx/access.log; # this is a comment", expectedActive: 1, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Find all matches using the regex matches := logDirectiveRegex.FindAllSubmatch([]byte(tc.content), -1) // Count how many are not commented activeCount := 0 for _, match := range matches { if !isCommentedMatch([]byte(tc.content), match) { activeCount++ } } if activeCount != tc.expectedActive { t.Errorf("Test '%s': expected %d active matches, got %d. Content: %s", tc.name, tc.expectedActive, activeCount, tc.content) } }) } } // TestIsCommentedMatch tests the isCommentedMatch function directly func TestIsCommentedMatch(t *testing.T) { testCases := []struct { name string content string matchStr string isCommented bool }{ { name: "Not commented", content: "access_log /var/log/nginx/access.log;", matchStr: "access_log /var/log/nginx/access.log;", isCommented: false, }, { name: "Commented with #", content: "# access_log /var/log/nginx/access.log;", matchStr: "access_log /var/log/nginx/access.log;", isCommented: true, }, { name: "Commented with spaces and #", content: " # access_log /var/log/nginx/access.log;", matchStr: "access_log /var/log/nginx/access.log;", isCommented: true, }, { name: "Not commented with spaces", content: " access_log /var/log/nginx/access.log;", matchStr: "access_log /var/log/nginx/access.log;", isCommented: false, }, { name: "Inline comment after directive", content: "access_log /var/log/nginx/access.log; # comment", matchStr: "access_log /var/log/nginx/access.log;", isCommented: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Create a fake match to test with match := [][]byte{[]byte(tc.matchStr)} result := isCommentedMatch([]byte(tc.content), match) if result != tc.isCommented { t.Errorf("Test '%s': expected isCommented=%v, got %v. Content: %q, Match: %q", tc.name, tc.isCommented, result, tc.content, tc.matchStr) } }) } }