| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 | package nginximport (	"path/filepath"	"regexp"	"testing")// Mock nginx -T output for testing purposesconst mockNginxTOutput = `# configuration file /etc/nginx/nginx.conf:user  nginx;worker_processes  auto;error_log  /var/log/nginx/error.log notice;error_log  /var/log/nginx/error.local.log notice;pid        /var/run/nginx.pid;events {    worker_connections  1024;}http {    include       /etc/nginx/mime.types;    default_type  application/octet-stream;    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                      '$status $body_bytes_sent "$http_referer" '                      '"$http_user_agent" "$http_x_forwarded_for"';    access_log  /var/log/nginx/access.log  main;    access_log  /var/log/nginx/access.local.log  main;    sendfile        on;    keepalive_timeout  65;    gzip  on;    server {        listen       80;        server_name  localhost;                access_log   /var/log/nginx/server.access.log;        error_log    /var/log/nginx/server.error.log warn;        location / {            root   /usr/share/nginx/html;            index  index.html index.htm;        }    }}stream {    error_log /var/log/nginx/stream.error.log info;        server {        listen 3306;        proxy_pass backend;    }}`// Mock nginx -T output with relative pathsconst mockNginxTOutputRelative = `# configuration file /etc/nginx/nginx.conf:user  nginx;worker_processes  auto;error_log  logs/error.log notice;pid        /var/run/nginx.pid;http {    access_log  logs/access.log  main;        server {        listen       80;        server_name  localhost;                access_log   logs/server.access.log;        error_log    logs/server.error.log warn;    }}`// Mock nginx -T output with access_log offconst mockNginxTOutputOff = `# configuration file /etc/nginx/nginx.conf:user  nginx;worker_processes  auto;error_log  /var/log/nginx/error.log notice;http {    access_log  off;        server {        listen       80;        server_name  localhost;                access_log   /var/log/nginx/server.access.log;        error_log    /var/log/nginx/server.error.log warn;    }}`// Mock nginx -T output with commented log directivesconst mockNginxTOutputCommented = `# configuration file /etc/nginx/nginx.conf:user  nginx;worker_processes  auto;# error_log  /var/log/nginx/commented.error.log notice;error_log  /var/log/nginx/error.log notice;http {    # access_log  /var/log/nginx/commented.access.log  main;    access_log  /var/log/nginx/access.log  main;        server {        listen       80;        server_name  localhost;                # access_log   /var/log/nginx/commented.server.access.log;        access_log   /var/log/nginx/server.access.log;        # error_log    /var/log/nginx/commented.server.error.log warn;        error_log    /var/log/nginx/server.error.log warn;    }}`func TestAccessLogRegexParsing(t *testing.T) {	testCases := []struct {		name          string		nginxTOutput  string		expectedPath  string		shouldHaveLog bool	}{		{			name:          "standard access log",			nginxTOutput:  "access_log  /var/log/nginx/access.log  main;",			expectedPath:  "/var/log/nginx/access.log",			shouldHaveLog: true,		},		{			name:          "access log turned off",			nginxTOutput:  "access_log  off;",			expectedPath:  "",			shouldHaveLog: false,		},		{			name:          "no access log directive",			nginxTOutput:  "server_name  localhost;",			expectedPath:  "",			shouldHaveLog: false,		},		{			name:          "indented access log",			nginxTOutput:  "    access_log  /var/log/nginx/server.log;",			expectedPath:  "/var/log/nginx/server.log",			shouldHaveLog: true,		},		{			name:          "multiple access logs - should get first",			nginxTOutput:  "access_log  /var/log/nginx/access1.log  main;\naccess_log  /var/log/nginx/access2.log  combined;",			expectedPath:  "/var/log/nginx/access1.log",			shouldHaveLog: true,		},		{			name:          "commented access log should be ignored",			nginxTOutput:  "# access_log  /var/log/nginx/commented.access.log  main;\naccess_log  /var/log/nginx/access.log  main;",			expectedPath:  "/var/log/nginx/access.log",			shouldHaveLog: true,		},		{			name:          "only commented access log",			nginxTOutput:  "# access_log  /var/log/nginx/commented.access.log  main;",			expectedPath:  "",			shouldHaveLog: false,		},	}	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)	for _, tc := range testCases {		t.Run(tc.name, func(t *testing.T) {			matches := accessLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)			if !tc.shouldHaveLog {				if len(matches) > 0 {					// Check if it's the "off" directive					if len(matches[0]) >= 2 {						logPath := matches[0][1]						if logPath != "off" {							t.Errorf("Expected no valid access log, but found: %s", logPath)						}					}				}				return			}			if len(matches) == 0 {				t.Errorf("Expected to find access log directive, but found none")				return			}			if len(matches[0]) < 2 {				t.Errorf("Expected regex match to have at least 2 groups, got %d", len(matches[0]))				return			}			logPath := matches[0][1]			if logPath != tc.expectedPath {				t.Errorf("Expected access log path %s, got %s", tc.expectedPath, logPath)			}		})	}}func TestErrorLogRegexParsing(t *testing.T) {	testCases := []struct {		name          string		nginxTOutput  string		expectedPath  string		shouldHaveLog bool	}{		{			name:          "standard error log",			nginxTOutput:  "error_log  /var/log/nginx/error.log notice;",			expectedPath:  "/var/log/nginx/error.log",			shouldHaveLog: true,		},		{			name:          "error log without level",			nginxTOutput:  "error_log  /var/log/nginx/error.log;",			expectedPath:  "/var/log/nginx/error.log",			shouldHaveLog: true,		},		{			name:          "no error log directive",			nginxTOutput:  "server_name  localhost;",			expectedPath:  "",			shouldHaveLog: false,		},		{			name:          "indented error log",			nginxTOutput:  "        error_log  /var/log/nginx/server.error.log warn;",			expectedPath:  "/var/log/nginx/server.error.log",			shouldHaveLog: true,		},		{			name:          "multiple error logs - should get first",			nginxTOutput:  "error_log  /var/log/nginx/error1.log  notice;\nerror_log  /var/log/nginx/error2.log  warn;",			expectedPath:  "/var/log/nginx/error1.log",			shouldHaveLog: true,		},		{			name:          "commented error log should be ignored",			nginxTOutput:  "# error_log  /var/log/nginx/commented.error.log  notice;\nerror_log  /var/log/nginx/error.log  notice;",			expectedPath:  "/var/log/nginx/error.log",			shouldHaveLog: true,		},		{			name:          "only commented error log",			nginxTOutput:  "# error_log  /var/log/nginx/commented.error.log  notice;",			expectedPath:  "",			shouldHaveLog: false,		},	}	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)	for _, tc := range testCases {		t.Run(tc.name, func(t *testing.T) {			matches := errorLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)			if !tc.shouldHaveLog {				if len(matches) > 0 {					t.Errorf("Expected no error log directive, but found: %v", matches)				}				return			}			if len(matches) == 0 {				t.Errorf("Expected to find error log directive, but found none")				return			}			if len(matches[0]) < 2 {				t.Errorf("Expected regex match to have at least 2 groups, got %d", len(matches[0]))				return			}			logPath := matches[0][1]			if logPath != tc.expectedPath {				t.Errorf("Expected error log path %s, got %s", tc.expectedPath, logPath)			}		})	}}func TestLogPathParsing(t *testing.T) {	testCases := []struct {		name               string		nginxTOutput       string		expectedAccessPath string		expectedErrorPath  string		shouldHaveAccess   bool		shouldHaveError    bool	}{		{			name:               "complete configuration",			nginxTOutput:       mockNginxTOutput,			expectedAccessPath: "/var/log/nginx/access.log",			expectedErrorPath:  "/var/log/nginx/error.log",			shouldHaveAccess:   true,			shouldHaveError:    true,		},		{			name:               "configuration with commented directives",			nginxTOutput:       mockNginxTOutputCommented,			expectedAccessPath: "/var/log/nginx/access.log",			expectedErrorPath:  "/var/log/nginx/error.log",			shouldHaveAccess:   true,			shouldHaveError:    true,		},		{			name:               "access log turned off",			nginxTOutput:       mockNginxTOutputOff,			expectedAccessPath: "/var/log/nginx/server.access.log", // Should get the server-level access log			expectedErrorPath:  "/var/log/nginx/error.log",			shouldHaveAccess:   true,			shouldHaveError:    true,		},		{			name:               "empty configuration",			nginxTOutput:       "",			expectedAccessPath: "",			expectedErrorPath:  "",			shouldHaveAccess:   false,			shouldHaveError:    false,		},	}	for _, tc := range testCases {		t.Run(tc.name, func(t *testing.T) {			// Test access log parsing			accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)			accessMatches := accessLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)			var foundAccessPath string			for _, match := range accessMatches {				if len(match) >= 2 {					logPath := match[1]					if logPath != "off" {						foundAccessPath = logPath						break					}				}			}			if tc.shouldHaveAccess {				if foundAccessPath == "" {					t.Errorf("Expected access log path %s, but found none", tc.expectedAccessPath)				} else if foundAccessPath != tc.expectedAccessPath {					t.Errorf("Expected access log path %s, got %s", tc.expectedAccessPath, foundAccessPath)				}			} else {				if foundAccessPath != "" {					t.Errorf("Expected no access log path, but found %s", foundAccessPath)				}			}			// Test error log parsing			errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)			errorMatches := errorLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)			var foundErrorPath string			if len(errorMatches) > 0 && len(errorMatches[0]) >= 2 {				foundErrorPath = errorMatches[0][1]			}			if tc.shouldHaveError {				if foundErrorPath == "" {					t.Errorf("Expected error log path %s, but found none", tc.expectedErrorPath)				} else if foundErrorPath != tc.expectedErrorPath {					t.Errorf("Expected error log path %s, got %s", tc.expectedErrorPath, foundErrorPath)				}			} else {				if foundErrorPath != "" {					t.Errorf("Expected no error log path, but found %s", foundErrorPath)				}			}		})	}}func TestRelativePathHandling(t *testing.T) {	// Mock GetPrefix function for testing	originalGetPrefix := GetPrefix	defer func() {		// Restore original function (if needed for other tests)		_ = originalGetPrefix	}()	testPrefix := "/usr/local/nginx"	testCases := []struct {		name         string		inputPath    string		expectedPath string		isRelative   bool	}{		{			name:         "absolute path",			inputPath:    "/var/log/nginx/access.log",			expectedPath: "/var/log/nginx/access.log",			isRelative:   false,		},		{			name:         "relative path",			inputPath:    "logs/access.log",			expectedPath: filepath.Join(testPrefix, "logs/access.log"),			isRelative:   true,		},		{			name:         "relative path with ./",			inputPath:    "./logs/access.log",			expectedPath: filepath.Join(testPrefix, "./logs/access.log"),			isRelative:   true,		},	}	for _, tc := range testCases {		t.Run(tc.name, func(t *testing.T) {			var result string			if tc.isRelative {				result = filepath.Join(testPrefix, tc.inputPath)			} else {				result = tc.inputPath			}			if result != tc.expectedPath {				t.Errorf("Expected path %s, got %s", tc.expectedPath, result)			}		})	}}func TestComplexNginxConfiguration(t *testing.T) {	complexConfig := `# Main configurationuser nginx;worker_processes auto;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events {    worker_connections 1024;}http {    include /etc/nginx/mime.types;    default_type application/octet-stream;        log_format main '$remote_addr - $remote_user [$time_local] "$request" '                   '$status $body_bytes_sent "$http_referer" '                   '"$http_user_agent" "$http_x_forwarded_for"';        access_log /var/log/nginx/access.log main;        sendfile on;    tcp_nopush on;    tcp_nodelay on;    keepalive_timeout 65;    types_hash_max_size 2048;        # Virtual Host Configs    include /etc/nginx/conf.d/*.conf;    include /etc/nginx/sites-enabled/*;        server {        listen 80 default_server;        listen [::]:80 default_server;        server_name _;        root /var/www/html;        index index.html index.htm index.nginx-debian.html;                access_log /var/log/nginx/default.access.log;        error_log /var/log/nginx/default.error.log;                location / {            try_files $uri $uri/ =404;        }                location ~ /\.ht {            deny all;        }    }        server {        listen 443 ssl http2;        server_name example.com;        root /var/www/example.com;                access_log /var/log/nginx/example.access.log combined;        error_log /var/log/nginx/example.error.log info;                ssl_certificate /etc/ssl/certs/example.com.pem;        ssl_certificate_key /etc/ssl/private/example.com.key;    }}stream {    error_log /var/log/nginx/stream.error.log info;        upstream backend {        server 192.168.1.100:3306;        server 192.168.1.101:3306;    }        server {        listen 3306;        proxy_pass backend;        proxy_timeout 1s;        proxy_responses 1;    }}`	// Test that we can extract the main access log and error log from complex config	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)	// Find all access logs	accessMatches := accessLogRegex.FindAllStringSubmatch(complexConfig, -1)	if len(accessMatches) == 0 {		t.Error("Expected to find access log directives in complex config")	} else {		firstAccessLog := accessMatches[0][1]		expectedFirstAccess := "/var/log/nginx/access.log"		if firstAccessLog != expectedFirstAccess {			t.Errorf("Expected first access log to be %s, got %s", expectedFirstAccess, firstAccessLog)		}		t.Logf("Found %d access log directives, first: %s", len(accessMatches), firstAccessLog)	}	// Find all error logs	errorMatches := errorLogRegex.FindAllStringSubmatch(complexConfig, -1)	if len(errorMatches) == 0 {		t.Error("Expected to find error log directives in complex config")	} else {		firstErrorLog := errorMatches[0][1]		expectedFirstError := "/var/log/nginx/error.log"		if firstErrorLog != expectedFirstError {			t.Errorf("Expected first error log to be %s, got %s", expectedFirstError, firstErrorLog)		}		t.Logf("Found %d error log directives, first: %s", len(errorMatches), firstErrorLog)	}}func TestCommentedDirectivesIgnored(t *testing.T) {	testConfig := `# Main configurationuser nginx;worker_processes auto;# These should be ignored# error_log  /var/log/nginx/commented.error.log notice;# access_log  /var/log/nginx/commented.access.log  main;# Real directiveserror_log /var/log/nginx/error.log warn;http {    # This should be ignored too    # access_log /var/log/nginx/commented.http.access.log combined;        # Real directive    access_log /var/log/nginx/access.log main;        server {        listen 80;        server_name example.com;                # Commented server-level logs should be ignored        # access_log /var/log/nginx/commented.server.access.log;        # error_log /var/log/nginx/commented.server.error.log warn;                # Real server-level logs        access_log /var/log/nginx/server.access.log;        error_log /var/log/nginx/server.error.log info;    }}`	// Test access log parsing ignores comments	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)	accessMatches := accessLogRegex.FindAllStringSubmatch(testConfig, -1)	expectedAccessLogs := []string{		"/var/log/nginx/access.log",		"/var/log/nginx/server.access.log",	}	if len(accessMatches) != len(expectedAccessLogs) {		t.Errorf("Expected %d access log matches, got %d", len(expectedAccessLogs), len(accessMatches))	}	for i, match := range accessMatches {		if i < len(expectedAccessLogs) {			if match[1] != expectedAccessLogs[i] {				t.Errorf("Expected access log %d to be %s, got %s", i, expectedAccessLogs[i], match[1])			}		}	}	// Test error log parsing ignores comments	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)	errorMatches := errorLogRegex.FindAllStringSubmatch(testConfig, -1)	expectedErrorLogs := []string{		"/var/log/nginx/error.log",		"/var/log/nginx/server.error.log",	}	if len(errorMatches) != len(expectedErrorLogs) {		t.Errorf("Expected %d error log matches, got %d", len(expectedErrorLogs), len(errorMatches))	}	for i, match := range errorMatches {		if i < len(expectedErrorLogs) {			if match[1] != expectedErrorLogs[i] {				t.Errorf("Expected error log %d to be %s, got %s", i, expectedErrorLogs[i], match[1])			}		}	}}
 |