diff options
Diffstat (limited to 'integrationtests/dtailhealth_test.go')
| -rw-r--r-- | integrationtests/dtailhealth_test.go | 215 |
1 files changed, 206 insertions, 9 deletions
diff --git a/integrationtests/dtailhealth_test.go b/integrationtests/dtailhealth_test.go index 7946824..bba6a9d 100644 --- a/integrationtests/dtailhealth_test.go +++ b/integrationtests/dtailhealth_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strings" "testing" "github.com/mimecast/dtail/internal/config" @@ -14,21 +15,84 @@ func TestDTailHealth1(t *testing.T) { t.Log("Skipping") return } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDTailHealth1") + defer testLogger.WriteLogFile() + + // Test in serverless mode + t.Run("Serverless", func(t *testing.T) { + testDTailHealth1Serverless(t, testLogger) + }) + + // Test in server mode - this test checks when no servers are specified + // so server mode behavior should be the same + t.Run("ServerMode", func(t *testing.T) { + testDTailHealth1WithServer(t, testLogger) + }) +} + +func testDTailHealth1Serverless(t *testing.T, logger *TestLogger) { outFile := "dtailhealth1.stdout.tmp" expectedOutFile := "dtailhealth1.expected" + ctx := WithTestLogger(context.Background(), logger) t.Log("Serverless check, is supposed to exit with warning state.") - exitCode, err := runCommand(context.TODO(), t, outFile, "../dtailhealth") + exitCode, err := runCommand(ctx, t, outFile, "../dtailhealth") + if exitCode != 1 { + t.Errorf("Expected exit code '1' but got '%d': %v", exitCode, err) + return + } + + if err := compareFilesWithContext(ctx, t, outFile, expectedOutFile); err != nil { + t.Error(err) + return + } +} + +func testDTailHealth1WithServer(t *testing.T, logger *TestLogger) { + outFile := "dtailhealth1.stdout.tmp" + expectedOutFile := "dtailhealth1.expected" + 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 + } + + t.Log("Server mode check without --server flag, is supposed to exit with warning state.") + // Run dtailhealth without specifying --server flag + exitCode, err := runCommand(ctx, t, outFile, "../dtailhealth") if exitCode != 1 { t.Errorf("Expected exit code '1' but got '%d': %v", exitCode, err) return } - if err := compareFiles(t, outFile, expectedOutFile); err != nil { + cancel() + + if err := compareFilesWithContext(ctx, t, outFile, expectedOutFile); err != nil { t.Error(err) return } - os.Remove(outFile) } func TestDTailHealth2(t *testing.T) { @@ -36,11 +100,29 @@ func TestDTailHealth2(t *testing.T) { t.Log("Skipping") return } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDTailHealth2") + defer testLogger.WriteLogFile() + + // Test in serverless mode + t.Run("Serverless", func(t *testing.T) { + testDTailHealth2Serverless(t, testLogger) + }) + + // Test in server mode - testing unreachable server + t.Run("ServerMode", func(t *testing.T) { + testDTailHealth2WithServer(t, testLogger) + }) +} + +func testDTailHealth2Serverless(t *testing.T, logger *TestLogger) { outFile := "dtailhealth2.stdout.tmp" expectedOutFile := "dtailhealth2.expected" + ctx := WithTestLogger(context.Background(), logger) t.Log("Negative test, is supposed to exit with a critical state.") - exitCode, err := runCommand(context.TODO(), t, outFile, + exitCode, err := runCommand(ctx, t, outFile, "../dtailhealth", "--server", "example:1") if exitCode != 2 { @@ -48,12 +130,57 @@ func TestDTailHealth2(t *testing.T) { return } - if err := compareFiles(t, outFile, expectedOutFile); err != nil { + if err := compareFilesWithContext(ctx, t, outFile, expectedOutFile); err != nil { + t.Error(err) + return + } +} + +func testDTailHealth2WithServer(t *testing.T, logger *TestLogger) { + outFile := "dtailhealth2.stdout.tmp" + expectedOutFile := "dtailhealth2.expected" + 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 } - os.Remove(outFile) + t.Log("Server mode negative test, checking unreachable server, is supposed to exit with a critical state.") + // Check an unreachable server (not the one we started) + exitCode, err := runCommand(ctx, t, outFile, + "../dtailhealth", "--server", "example:1") + + if exitCode != 2 { + t.Error(fmt.Sprintf("Expected exit code '2' but got '%d': %v", exitCode, err)) + return + } + + cancel() + + if err := compareFilesWithContext(ctx, t, outFile, expectedOutFile); err != nil { + t.Error(err) + return + } } func TestDTailHealthCheck3(t *testing.T) { @@ -61,12 +188,25 @@ func TestDTailHealthCheck3(t *testing.T) { t.Log("Skipping") return } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDTailHealthCheck3") + defer testLogger.WriteLogFile() + + // This test only makes sense with a server + t.Run("ServerMode", func(t *testing.T) { + testDTailHealthCheck3WithServer(t, testLogger) + }) +} + +func testDTailHealthCheck3WithServer(t *testing.T, logger *TestLogger) { outFile := "dtailhealth3.stdout.tmp" port := getUniquePortNumber() bindAddress := "localhost" expectedOut := fmt.Sprintf("OK: All fine at %s:%d :-)", bindAddress, port) ctx, cancel := context.WithCancel(context.Background()) + ctx = WithTestLogger(ctx, logger) defer cancel() _, _, _, err := startCommand(ctx, t, @@ -83,16 +223,73 @@ func TestDTailHealthCheck3(t *testing.T) { } _, err = runCommandRetry(ctx, t, 10, outFile, - "../dtailhealth", "--server", fmt.Sprintf("%s:%d", bindAddress, port)) + "../dtailhealth", "--server", fmt.Sprintf("%s:%d", bindAddress, port), "--no-auth-key") if err != nil { t.Error(err) return } - if err := fileContainsStr(t, outFile, expectedOut); err != nil { + if err := fileContainsStrWithContext(ctx, t, outFile, expectedOut); err != nil { t.Error(err) return } +} + +// TestDTailHealthNoPanicOnClientConstruction is a regression test for task a5. +// dtailhealth previously discarded the error returned by clients.NewHealthClient +// and then dereferenced the (potentially nil) client, which would panic. The +// fix checks the error and exits 2 (Nagios CRITICAL). This test ensures that +// no invocation of dtailhealth leaks a Go panic/runtime-error trace to stderr +// and that the exit code stays in the expected Nagios range (1 WARNING or +// 2 CRITICAL) rather than becoming 2 from a runtime panic accident. +func TestDTailHealthNoPanicOnClientConstruction(t *testing.T) { + if !config.Env("DTAIL_INTEGRATION_TEST_RUN_MODE") { + t.Log("Skipping") + return + } + + cleanupTmpFiles(t) + testLogger := NewTestLogger("TestDTailHealthNoPanicOnClientConstruction") + defer testLogger.WriteLogFile() + ctx := WithTestLogger(context.Background(), testLogger) + + cases := []struct { + name string + args []string + wantExitCode int + }{ + { + name: "Serverless", + args: nil, + wantExitCode: 1, + }, + { + name: "UnreachableServer", + args: []string{"--server", "example:1"}, + wantExitCode: 2, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + outFile := fmt.Sprintf("dtailhealth_nopanic_%s.stdout.tmp", tc.name) + exitCode, _ := runCommand(ctx, t, outFile, "../dtailhealth", tc.args...) + if exitCode != tc.wantExitCode { + t.Errorf("Expected exit code %d but got %d", tc.wantExitCode, exitCode) + } - os.Remove(outFile) + data, err := os.ReadFile(outFile) + if err != nil { + t.Fatalf("failed to read %s: %v", outFile, err) + } + combined := string(data) + for _, needle := range []string{"panic:", "runtime error:"} { + if strings.Contains(combined, needle) { + t.Errorf("combined output contained %q (regression, dtailhealth should not panic):\n%s", + needle, combined) + } + } + }) + } } |
