analytics_service_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package nginx_log
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. )
  7. func TestAnalyticsService_ValidateLogPath(t *testing.T) {
  8. service := NewAnalyticsService()
  9. testCases := []struct {
  10. name string
  11. path string
  12. wantErr bool
  13. }{
  14. {
  15. name: "Empty path should be allowed",
  16. path: "",
  17. wantErr: false,
  18. },
  19. {
  20. name: "Invalid path should be rejected",
  21. path: "/var/log/nginx/access.log",
  22. wantErr: true, // This path is not in whitelist in test environment
  23. },
  24. }
  25. for _, tc := range testCases {
  26. t.Run(tc.name, func(t *testing.T) {
  27. err := service.ValidateLogPath(tc.path)
  28. if (err != nil) != tc.wantErr {
  29. t.Errorf("ValidateLogPath() error = %v, wantErr %v", err, tc.wantErr)
  30. }
  31. })
  32. }
  33. }
  34. func TestAnalyticsService_ValidateAndNormalizeSearchRequest(t *testing.T) {
  35. service := NewAnalyticsService()
  36. testCases := []struct {
  37. name string
  38. req *QueryRequest
  39. wantErr bool
  40. checkFn func(*QueryRequest) bool
  41. }{
  42. {
  43. name: "Default limit should be set",
  44. req: &QueryRequest{
  45. Limit: 0,
  46. },
  47. wantErr: false,
  48. checkFn: func(req *QueryRequest) bool {
  49. return req.Limit == 100
  50. },
  51. },
  52. {
  53. name: "Limit should be capped at 1000",
  54. req: &QueryRequest{
  55. Limit: 2000,
  56. },
  57. wantErr: false,
  58. checkFn: func(req *QueryRequest) bool {
  59. return req.Limit == 1000
  60. },
  61. },
  62. {
  63. name: "Negative offset should be normalized",
  64. req: &QueryRequest{
  65. Limit: 100,
  66. Offset: -10,
  67. },
  68. wantErr: false,
  69. checkFn: func(req *QueryRequest) bool {
  70. return req.Offset == 0
  71. },
  72. },
  73. {
  74. name: "Invalid time range should return error",
  75. req: &QueryRequest{
  76. StartTime: time.Now().Unix(),
  77. EndTime: time.Now().Add(-1 * time.Hour).Unix(),
  78. Limit: 100,
  79. },
  80. wantErr: true,
  81. checkFn: nil,
  82. },
  83. {
  84. name: "Invalid status code should return error",
  85. req: &QueryRequest{
  86. Status: []int{999},
  87. Limit: 100,
  88. },
  89. wantErr: true,
  90. checkFn: nil,
  91. },
  92. }
  93. for _, tc := range testCases {
  94. t.Run(tc.name, func(t *testing.T) {
  95. err := service.validateAndNormalizeSearchRequest(tc.req)
  96. if (err != nil) != tc.wantErr {
  97. t.Errorf("validateAndNormalizeSearchRequest() error = %v, wantErr %v", err, tc.wantErr)
  98. return
  99. }
  100. if !tc.wantErr && tc.checkFn != nil {
  101. if !tc.checkFn(tc.req) {
  102. t.Errorf("validateAndNormalizeSearchRequest() validation check failed")
  103. }
  104. }
  105. })
  106. }
  107. }
  108. func TestAnalyticsService_GetLogEntries(t *testing.T) {
  109. service := NewAnalyticsService()
  110. testCases := []struct {
  111. name string
  112. path string
  113. limit int
  114. tail bool
  115. wantErr bool
  116. }{
  117. {
  118. name: "Empty path should return error",
  119. path: "",
  120. limit: 100,
  121. tail: false,
  122. wantErr: true,
  123. },
  124. {
  125. name: "Invalid path should return error",
  126. path: "/var/log/nginx/access.log",
  127. limit: 100,
  128. tail: false,
  129. wantErr: true, // This path is not in whitelist in test environment
  130. },
  131. }
  132. for _, tc := range testCases {
  133. t.Run(tc.name, func(t *testing.T) {
  134. entries, err := service.GetLogEntries(tc.path, tc.limit, tc.tail)
  135. if (err != nil) != tc.wantErr {
  136. t.Errorf("GetLogEntries() error = %v, wantErr %v", err, tc.wantErr)
  137. return
  138. }
  139. if !tc.wantErr {
  140. // Should return a slice (even if empty)
  141. if entries == nil {
  142. t.Errorf("GetLogEntries() returned nil entries")
  143. }
  144. }
  145. })
  146. }
  147. }
  148. func BenchmarkAnalyticsService_ValidateAndNormalizeSearchRequest(b *testing.B) {
  149. service := NewAnalyticsService()
  150. req := &QueryRequest{
  151. StartTime: time.Now().Add(-1 * time.Hour).Unix(),
  152. EndTime: time.Now().Unix(),
  153. Query: "test query",
  154. IP: "192.168.1.1",
  155. Method: "GET",
  156. Status: []int{200, 404},
  157. Path: "/api/test",
  158. UserAgent: "Mozilla/5.0",
  159. Limit: 100,
  160. Offset: 0,
  161. }
  162. b.ResetTimer()
  163. for i := 0; i < b.N; i++ {
  164. // Create a copy for each iteration to avoid side effects
  165. reqCopy := *req
  166. _ = service.validateAndNormalizeSearchRequest(&reqCopy)
  167. }
  168. }
  169. func TestAnalyticsService_SearchLogs_WithoutIndexer(t *testing.T) {
  170. service := NewAnalyticsService()
  171. // Don't set indexer to test error handling
  172. ctx := context.Background()
  173. req := &QueryRequest{
  174. Limit: 100,
  175. }
  176. _, err := service.SearchLogs(ctx, req)
  177. if err == nil {
  178. t.Error("SearchLogs() should return error when indexer is not available")
  179. }
  180. if err != ErrIndexerNotAvailable {
  181. t.Errorf("SearchLogs() error = %v, want %v", err, ErrIndexerNotAvailable)
  182. }
  183. }
  184. func TestAnalyticsService_GetIndexStatus_WithoutIndexer(t *testing.T) {
  185. service := NewAnalyticsService()
  186. // Don't set indexer to test error handling
  187. _, err := service.GetIndexStatus()
  188. if err == nil {
  189. t.Error("GetIndexStatus() should return error when indexer is not available")
  190. }
  191. if err != ErrIndexerNotAvailable {
  192. t.Errorf("GetIndexStatus() error = %v, want %v", err, ErrIndexerNotAvailable)
  193. }
  194. }