nginx_log_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. package nginx_log
  2. import (
  3. "testing"
  4. )
  5. // TestScanForLogDirectivesRemoval tests that removed log directives are properly cleaned up
  6. func TestScanForLogDirectivesRemoval(t *testing.T) {
  7. // Clear cache before test
  8. ClearLogCache()
  9. configPath := "/etc/nginx/sites-available/test.conf"
  10. // First scan with two log directives
  11. content1 := []byte(`
  12. server {
  13. listen 80;
  14. server_name example.com;
  15. access_log /var/log/nginx/access.log;
  16. error_log /var/log/nginx/error.log;
  17. }
  18. `)
  19. err := scanForLogDirectives(configPath, content1)
  20. if err != nil {
  21. t.Fatalf("First scan failed: %v", err)
  22. }
  23. // Check that both logs are cached
  24. logs := GetAllLogPaths()
  25. if len(logs) != 2 {
  26. t.Fatalf("Expected 2 logs after first scan, got %d", len(logs))
  27. }
  28. // Verify the config file is tracked
  29. accessFound := false
  30. errorFound := false
  31. for _, log := range logs {
  32. if log.ConfigFile != configPath {
  33. t.Errorf("Expected config file %s, got %s", configPath, log.ConfigFile)
  34. }
  35. if log.Type == "access" {
  36. accessFound = true
  37. }
  38. if log.Type == "error" {
  39. errorFound = true
  40. }
  41. }
  42. if !accessFound || !errorFound {
  43. t.Error("Expected both access and error logs to be found")
  44. }
  45. // Second scan with only one log directive (error_log removed)
  46. content2 := []byte(`
  47. server {
  48. listen 80;
  49. server_name example.com;
  50. access_log /var/log/nginx/access.log;
  51. }
  52. `)
  53. err = scanForLogDirectives(configPath, content2)
  54. if err != nil {
  55. t.Fatalf("Second scan failed: %v", err)
  56. }
  57. // Check that only access log remains
  58. logs = GetAllLogPaths()
  59. if len(logs) != 1 {
  60. t.Fatalf("Expected 1 log after second scan, got %d", len(logs))
  61. }
  62. if logs[0].Type != "access" {
  63. t.Errorf("Expected remaining log to be access log, got %s", logs[0].Type)
  64. }
  65. // Third scan with no log directives
  66. content3 := []byte(`
  67. server {
  68. listen 80;
  69. server_name example.com;
  70. }
  71. `)
  72. err = scanForLogDirectives(configPath, content3)
  73. if err != nil {
  74. t.Fatalf("Third scan failed: %v", err)
  75. }
  76. // Check that no logs remain
  77. logs = GetAllLogPaths()
  78. if len(logs) != 0 {
  79. t.Fatalf("Expected 0 logs after third scan, got %d", len(logs))
  80. }
  81. }
  82. // TestScanForLogDirectivesMultipleConfigs tests that logs from different config files are handled independently
  83. func TestScanForLogDirectivesMultipleConfigs(t *testing.T) {
  84. // Clear cache before test
  85. ClearLogCache()
  86. configPath1 := "/etc/nginx/sites-available/site1.conf"
  87. configPath2 := "/etc/nginx/sites-available/site2.conf"
  88. // Scan first config
  89. content1 := []byte(`
  90. server {
  91. listen 80;
  92. server_name site1.com;
  93. access_log /var/log/nginx/site1_access.log;
  94. }
  95. `)
  96. err := scanForLogDirectives(configPath1, content1)
  97. if err != nil {
  98. t.Fatalf("First config scan failed: %v", err)
  99. }
  100. // Scan second config
  101. content2 := []byte(`
  102. server {
  103. listen 80;
  104. server_name site2.com;
  105. access_log /var/log/nginx/site2_access.log;
  106. }
  107. `)
  108. err = scanForLogDirectives(configPath2, content2)
  109. if err != nil {
  110. t.Fatalf("Second config scan failed: %v", err)
  111. }
  112. // Should have 2 logs total
  113. logs := GetAllLogPaths()
  114. if len(logs) != 2 {
  115. t.Fatalf("Expected 2 logs from 2 configs, got %d", len(logs))
  116. }
  117. // Remove log from first config, should only affect that config
  118. emptyContent := []byte(`
  119. server {
  120. listen 80;
  121. server_name site1.com;
  122. }
  123. `)
  124. err = scanForLogDirectives(configPath1, emptyContent)
  125. if err != nil {
  126. t.Fatalf("Empty config scan failed: %v", err)
  127. }
  128. // Should have 1 log remaining (from config2)
  129. logs = GetAllLogPaths()
  130. if len(logs) != 1 {
  131. t.Fatalf("Expected 1 log after removing from config1, got %d", len(logs))
  132. }
  133. if logs[0].ConfigFile != configPath2 {
  134. t.Errorf("Expected remaining log to be from config2 (%s), got %s", configPath2, logs[0].ConfigFile)
  135. }
  136. }
  137. // TestScanForLogDirectivesIgnoreComments tests that commented log directives are ignored
  138. func TestScanForLogDirectivesIgnoreComments(t *testing.T) {
  139. // Clear cache before test
  140. ClearLogCache()
  141. configPath := "/etc/nginx/sites-available/test.conf"
  142. // Content with both active and commented log directives
  143. content := []byte(`
  144. server {
  145. listen 80;
  146. server_name example.com;
  147. # This is a commented access log - should be ignored
  148. # access_log /var/log/nginx/commented_access.log;
  149. # Multi-line comment block
  150. #error_log /var/log/nginx/commented_error.log;
  151. # Active log directives (not commented)
  152. access_log /var/log/nginx/active_access.log;
  153. error_log /var/log/nginx/active_error.log;
  154. # Another commented directive with indentation
  155. # access_log /var/log/nginx/indented_comment.log;
  156. # Inline comment after directive should still work
  157. access_log /var/log/nginx/inline_comment.log; # this is active with comment
  158. }
  159. `)
  160. err := scanForLogDirectives(configPath, content)
  161. if err != nil {
  162. t.Fatalf("Scan failed: %v", err)
  163. }
  164. // Should only find 3 active log directives (not the commented ones)
  165. logs := GetAllLogPaths()
  166. expectedCount := 3
  167. if len(logs) != expectedCount {
  168. t.Fatalf("Expected %d logs, got %d. Logs found: %+v", expectedCount, len(logs), logs)
  169. }
  170. // Verify the correct paths were found
  171. expectedPaths := map[string]bool{
  172. "/var/log/nginx/active_access.log": false,
  173. "/var/log/nginx/active_error.log": false,
  174. "/var/log/nginx/inline_comment.log": false,
  175. }
  176. for _, log := range logs {
  177. if _, exists := expectedPaths[log.Path]; !exists {
  178. t.Errorf("Unexpected log path found: %s", log.Path)
  179. } else {
  180. expectedPaths[log.Path] = true
  181. }
  182. }
  183. // Check that all expected paths were found
  184. for path, found := range expectedPaths {
  185. if !found {
  186. t.Errorf("Expected log path not found: %s", path)
  187. }
  188. }
  189. // Verify no commented paths were included
  190. commentedPaths := []string{
  191. "/var/log/nginx/commented_access.log",
  192. "/var/log/nginx/commented_error.log",
  193. "/var/log/nginx/indented_comment.log",
  194. }
  195. for _, log := range logs {
  196. for _, commentedPath := range commentedPaths {
  197. if log.Path == commentedPath {
  198. t.Errorf("Commented log path should not be included: %s", commentedPath)
  199. }
  200. }
  201. }
  202. }
  203. // TestLogDirectiveRegex tests the regex pattern and comment filtering logic
  204. func TestLogDirectiveRegex(t *testing.T) {
  205. testCases := []struct {
  206. name string
  207. content string
  208. expectedActive int // number of active (non-commented) matches expected
  209. }{
  210. {
  211. name: "Active directives",
  212. content: "access_log /var/log/nginx/access.log;\nerror_log /var/log/nginx/error.log;",
  213. expectedActive: 2,
  214. },
  215. {
  216. name: "Commented directives",
  217. content: "# access_log /var/log/nginx/access.log;\n#error_log /var/log/nginx/error.log;",
  218. expectedActive: 0,
  219. },
  220. {
  221. name: "Mixed active and commented",
  222. content: "access_log /var/log/nginx/access.log;\n# error_log /var/log/nginx/error.log;",
  223. expectedActive: 1,
  224. },
  225. {
  226. name: "Indented comments",
  227. content: " # access_log /var/log/nginx/access.log;\n error_log /var/log/nginx/error.log;",
  228. expectedActive: 1,
  229. },
  230. {
  231. name: "Inline comments after directive",
  232. content: "access_log /var/log/nginx/access.log; # this is a comment",
  233. expectedActive: 1,
  234. },
  235. }
  236. for _, tc := range testCases {
  237. t.Run(tc.name, func(t *testing.T) {
  238. // Find all matches using the regex
  239. matches := logDirectiveRegex.FindAllSubmatch([]byte(tc.content), -1)
  240. // Count how many are not commented
  241. activeCount := 0
  242. for _, match := range matches {
  243. if !isCommentedMatch([]byte(tc.content), match) {
  244. activeCount++
  245. }
  246. }
  247. if activeCount != tc.expectedActive {
  248. t.Errorf("Test '%s': expected %d active matches, got %d. Content: %s",
  249. tc.name, tc.expectedActive, activeCount, tc.content)
  250. }
  251. })
  252. }
  253. }
  254. // TestIsCommentedMatch tests the isCommentedMatch function directly
  255. func TestIsCommentedMatch(t *testing.T) {
  256. testCases := []struct {
  257. name string
  258. content string
  259. matchStr string
  260. isCommented bool
  261. }{
  262. {
  263. name: "Not commented",
  264. content: "access_log /var/log/nginx/access.log;",
  265. matchStr: "access_log /var/log/nginx/access.log;",
  266. isCommented: false,
  267. },
  268. {
  269. name: "Commented with #",
  270. content: "# access_log /var/log/nginx/access.log;",
  271. matchStr: "access_log /var/log/nginx/access.log;",
  272. isCommented: true,
  273. },
  274. {
  275. name: "Commented with spaces and #",
  276. content: " # access_log /var/log/nginx/access.log;",
  277. matchStr: "access_log /var/log/nginx/access.log;",
  278. isCommented: true,
  279. },
  280. {
  281. name: "Not commented with spaces",
  282. content: " access_log /var/log/nginx/access.log;",
  283. matchStr: "access_log /var/log/nginx/access.log;",
  284. isCommented: false,
  285. },
  286. {
  287. name: "Inline comment after directive",
  288. content: "access_log /var/log/nginx/access.log; # comment",
  289. matchStr: "access_log /var/log/nginx/access.log;",
  290. isCommented: false,
  291. },
  292. }
  293. for _, tc := range testCases {
  294. t.Run(tc.name, func(t *testing.T) {
  295. // Create a fake match to test with
  296. match := [][]byte{[]byte(tc.matchStr)}
  297. result := isCommentedMatch([]byte(tc.content), match)
  298. if result != tc.isCommented {
  299. t.Errorf("Test '%s': expected isCommented=%v, got %v. Content: %q, Match: %q",
  300. tc.name, tc.isCommented, result, tc.content, tc.matchStr)
  301. }
  302. })
  303. }
  304. }