diff options
Diffstat (limited to 'integrationtests/dgrep_literal_regex_test.go')
| -rw-r--r-- | integrationtests/dgrep_literal_regex_test.go | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/integrationtests/dgrep_literal_regex_test.go b/integrationtests/dgrep_literal_regex_test.go new file mode 100644 index 0000000..1f06267 --- /dev/null +++ b/integrationtests/dgrep_literal_regex_test.go @@ -0,0 +1,392 @@ +package integrationtests + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + "time" + + "github.com/mimecast/dtail/internal/config" +) + +// TestDGrepLiteralPatterns tests grep with literal string patterns (no regex metacharacters) +func TestDGrepLiteralPatterns(t *testing.T) { + if !config.Env("DTAIL_INTEGRATION_TEST_RUN_MODE") { + t.Log("Skipping") + return + } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDGrepLiteralPatterns") + defer testLogger.WriteLogFile() + + // Create test data file with various literal patterns + testData := `2025-07-02 10:00:00 ERROR Database connection failed +2025-07-02 10:00:01 WARNING High memory usage detected +2025-07-02 10:00:02 INFO Application started successfully +2025-07-02 10:00:03 ERROR File not found: config.yaml +2025-07-02 10:00:04 DEBUG Processing request ID 12345 +2025-07-02 10:00:05 ERROR Network timeout occurred +2025-07-02 10:00:06 WARNING Disk space running low +2025-07-02 10:00:07 INFO User logged in: john.doe@example.com +2025-07-02 10:00:08 ERROR Invalid input data +2025-07-02 10:00:09 CRITICAL System shutdown initiated +` + + testFile := "literal_test_data.log.tmp" + if err := os.WriteFile(testFile, []byte(testData), 0644); err != nil { + t.Fatal("Failed to create test file:", err) + } + defer os.Remove(testFile) + + tests := []struct { + name string + pattern string + expectedCount int + description string + }{ + { + name: "SimpleLiteral_ERROR", + pattern: "ERROR", + expectedCount: 4, + description: "Simple literal string 'ERROR'", + }, + { + name: "SimpleLiteral_WARNING", + pattern: "WARNING", + expectedCount: 2, + description: "Simple literal string 'WARNING'", + }, + { + name: "LiteralWithSpaces", + pattern: "File not found", + expectedCount: 1, + description: "Literal string with spaces", + }, + { + name: "LiteralWithNumbers", + pattern: "ID 12345", + expectedCount: 1, + description: "Literal string with numbers", + }, + { + name: "LiteralWithEmail", + pattern: "john.doe@example.com", + expectedCount: 1, + description: "Literal string with dots (not regex dots)", + }, + { + name: "LiteralNotFound", + pattern: "NOTFOUND", + expectedCount: 0, + description: "Literal string that doesn't exist", + }, + } + + // Test in both serverless and server modes + modes := []struct { + name string + serverMode bool + }{ + {"Serverless", false}, + {"ServerMode", true}, + } + + for _, mode := range modes { + t.Run(mode.name, func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + outFile := fmt.Sprintf("dgrep_literal_%s.stdout.tmp", test.name) + defer os.Remove(outFile) + + if mode.serverMode { + testLiteralPatternWithServer(t, testLogger, testFile, outFile, test.pattern, test.expectedCount) + } else { + testLiteralPatternServerless(t, testLogger, testFile, outFile, test.pattern, test.expectedCount) + } + }) + } + }) + } +} + +// TestDGrepRegexPatterns tests grep with actual regex patterns +func TestDGrepRegexPatterns(t *testing.T) { + if !config.Env("DTAIL_INTEGRATION_TEST_RUN_MODE") { + t.Log("Skipping") + return + } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDGrepRegexPatterns") + defer testLogger.WriteLogFile() + + // Create test data file + testData := `2025-07-02 10:00:00 ERROR Database connection failed on server db01 +2025-07-02 10:00:01 WARNING High memory usage: 85% +2025-07-02 10:00:02 INFO Application v1.2.3 started +2025-07-02 10:00:03 ERROR File not found: /var/log/app.log +2025-07-02 10:00:04 DEBUG Request from 192.168.1.100 +2025-07-02 10:00:05 ERROR Network timeout after 30s +2025-07-02 10:00:06 WARNING Disk usage: 90% +2025-07-02 10:00:07 INFO User 'admin' logged in from 10.0.0.1 +2025-07-02 10:00:08 ERROR Parse error in line 42 +2025-07-02 10:00:09 CRITICAL Emergency shutdown +2025-07-02 10:00:10 DEBUG Request from 192.168.1.101 +2025-07-02 10:00:11 INFO Processing batch job #12345 +2025-07-02 10:00:12 ERROR Connection reset by peer +` + + testFile := "regex_test_data.log.tmp" + if err := os.WriteFile(testFile, []byte(testData), 0644); err != nil { + t.Fatal("Failed to create test file:", err) + } + defer os.Remove(testFile) + + tests := []struct { + name string + pattern string + expectedCount int + description string + }{ + { + name: "CharacterClass", + pattern: "v1.2.[0-9]", + expectedCount: 1, + description: "Character class pattern", + }, + { + name: "AlternationPattern", + pattern: "(ERROR|CRITICAL)", + expectedCount: 6, // 5 ERROR + 1 CRITICAL + description: "Alternation with parentheses", + }, + { + name: "WildcardPattern", + pattern: "ERROR.*failed", + expectedCount: 1, + description: "Wildcard pattern", + }, + { + name: "IPAddressPattern", + pattern: "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+", + expectedCount: 3, + description: "IP address regex pattern", + }, + { + name: "PercentagePattern", + pattern: "[0-9]+%", + expectedCount: 2, + description: "Percentage pattern", + }, + { + name: "AnchoredPattern", + pattern: "^2025-07-02 10:00:0[0-5]", + expectedCount: 6, + description: "Anchored pattern with start of line", + }, + { + name: "OptionalPattern", + pattern: "errors?", + expectedCount: 1, // Matches 'error' in "Parse error in line 42" + description: "Optional character pattern", + }, + { + name: "PlusQuantifier", + pattern: "0+1", + expectedCount: 3, // matches '01' in db01, 10.0.0.1, and 192.168.1.101 + description: "Plus quantifier pattern", + }, + } + + // Test in both serverless and server modes + modes := []struct { + name string + serverMode bool + }{ + {"Serverless", false}, + {"ServerMode", true}, + } + + for _, mode := range modes { + t.Run(mode.name, func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + outFile := fmt.Sprintf("dgrep_regex_%s.stdout.tmp", test.name) + defer os.Remove(outFile) + + if mode.serverMode { + testRegexPatternWithServer(t, testLogger, testFile, outFile, test.pattern, test.expectedCount) + } else { + testRegexPatternServerless(t, testLogger, testFile, outFile, test.pattern, test.expectedCount) + } + }) + } + }) + } +} + +// TestDGrepMixedPatterns tests that both literal and regex patterns work in the same session +func TestDGrepMixedPatterns(t *testing.T) { + if !config.Env("DTAIL_INTEGRATION_TEST_RUN_MODE") { + t.Log("Skipping") + return + } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDGrepMixedPatterns") + defer testLogger.WriteLogFile() + + // Create test data + testData := `ERROR: System failure +WARNING: Low memory +ERROR: Disk full +INFO: Process started +ERROR: Network down +` + + testFile := "mixed_test_data.log.tmp" + if err := os.WriteFile(testFile, []byte(testData), 0644); err != nil { + t.Fatal("Failed to create test file:", err) + } + defer os.Remove(testFile) + + // Test a sequence of literal and regex patterns + patterns := []struct { + pattern string + isRegex bool + expectedCount int + }{ + {"ERROR", false, 3}, // Literal + {"ERROR.*full", true, 1}, // Regex + {"WARNING", false, 1}, // Literal + {"(ERROR|WARNING)", true, 4}, // Regex + {"Process started", false, 1}, // Literal with space + {"^INFO:", true, 1}, // Regex with anchor + } + + t.Run("ServerlessMode", func(t *testing.T) { + for i, p := range patterns { + outFile := fmt.Sprintf("mixed_%d.stdout.tmp", i) + defer os.Remove(outFile) + + testLiteralPatternServerless(t, testLogger, testFile, outFile, p.pattern, p.expectedCount) + } + }) +} + +// Helper functions + +func testLiteralPatternServerless(t *testing.T, logger *TestLogger, inFile, outFile, pattern string, expectedCount int) { + ctx := WithTestLogger(context.Background(), logger) + + _, err := runCommand(ctx, t, outFile, + "../dgrep", + "--plain", + "--cfg", "none", + "--grep", pattern, + inFile) + + if err != nil { + t.Errorf("Failed to run dgrep with pattern '%s': %v", pattern, err) + return + } + + // Count matching lines + content, err := os.ReadFile(outFile) + if err != nil { + t.Errorf("Failed to read output file: %v", err) + return + } + + lines := strings.Split(strings.TrimSpace(string(content)), "\n") + actualCount := 0 + for _, line := range lines { + if line != "" { + actualCount++ + } + } + + if actualCount != expectedCount { + t.Errorf("Pattern '%s': expected %d matches, got %d", pattern, expectedCount, actualCount) + if actualCount > 0 && actualCount <= 10 { + t.Errorf("Output:\n%s", string(content)) + } + } +} + +func testLiteralPatternWithServer(t *testing.T, logger *TestLogger, inFile, outFile, pattern string, expectedCount int) { + port := getUniquePortNumber() + bindAddress := "localhost" + + ctx, cancel := context.WithCancel(context.Background()) + ctx = WithTestLogger(ctx, logger) + defer cancel() + + // Start dserver + _, _, _, err := startCommand(ctx, t, + "", "../dserver", + "--cfg", "none", + "--logger", "stdout", + "--logLevel", "error", + "--bindAddress", bindAddress, + "--port", fmt.Sprintf("%d", port), + ) + if err != nil { + t.Error(err) + return + } + + if err := waitForServerReady(ctx, bindAddress, port); err != nil { + t.Error(err) + return + } + + err = runCommandUntilValid(ctx, t, 5, 200*time.Millisecond, outFile, "../dgrep", func() error { + content, readErr := os.ReadFile(outFile) + if readErr != nil { + return fmt.Errorf("failed to read output file: %w", readErr) + } + + lines := strings.Split(strings.TrimSpace(string(content)), "\n") + actualCount := 0 + for _, line := range lines { + if line != "" { + actualCount++ + } + } + + if actualCount != expectedCount { + if actualCount > 0 && actualCount <= 10 { + return fmt.Errorf("pattern %q: expected %d matches, got %d\noutput:\n%s", pattern, expectedCount, actualCount, string(content)) + } + return fmt.Errorf("pattern %q: expected %d matches, got %d", pattern, expectedCount, actualCount) + } + return nil + }, + "--plain", + "--cfg", "none", + "--grep", pattern, + "--servers", fmt.Sprintf("%s:%d", bindAddress, port), + "--trustAllHosts", + "--noColor", + "--files", inFile) + cancel() + if err != nil { + t.Error(err) + } +} + +func testRegexPatternServerless(t *testing.T, logger *TestLogger, inFile, outFile, pattern string, expectedCount int) { + // Same implementation as testLiteralPatternServerless + // The regex vs literal detection happens internally + testLiteralPatternServerless(t, logger, inFile, outFile, pattern, expectedCount) +} + +func testRegexPatternWithServer(t *testing.T, logger *TestLogger, inFile, outFile, pattern string, expectedCount int) { + // Same implementation as testLiteralPatternWithServer + // The regex vs literal detection happens internally + testLiteralPatternWithServer(t, logger, inFile, outFile, pattern, expectedCount) +} |
