diff options
| author | Paul Buetow <paul@buetow.org> | 2026-07-22 23:51:28 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-07-22 23:51:28 +0300 |
| commit | bc2767c87c4090798c4c7d15e101ed066e947301 (patch) | |
| tree | 0f47a2e0b159a617eb56509bbca1c998196453c4 /integrationtests/commandutils.go | |
| parent | 849951be1d1a7ee9f9302006ccb187bf5b4e36f3 (diff) | |
test: DTail fork — integration test suite and fixtures
Squashed development of the integration test suite (integrationtests/) covering
DCat, DGrep, DMap (serverless + server mode), DTail follow, DServer, DTailHealth,
journal source reads, auth-key fast reconnect, interactive query reload,
client-deadline/timeout behaviour, and the single-mode read/output path.
Includes real test fixtures (dserver*.cfg, dmap_csv_multifile_*.csv.in,
test_server_*.json, *.expected golden files) and deterministic synchronization
helpers (waitContains-style barriers) replacing racy fixed-timing assertions.
Accidental debug/output dumps that earlier commits added here (captured client
output, strace logs, ad-hoc turbo_test_output/manual_output/test_output files,
throwaway debug scripts) are intentionally excluded and gitignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'integrationtests/commandutils.go')
| -rw-r--r-- | integrationtests/commandutils.go | 80 |
1 files changed, 78 insertions, 2 deletions
diff --git a/integrationtests/commandutils.go b/integrationtests/commandutils.go index 8d81955..1947de4 100644 --- a/integrationtests/commandutils.go +++ b/integrationtests/commandutils.go @@ -2,6 +2,7 @@ package integrationtests import ( "bufio" + "bytes" "context" "fmt" "io" @@ -15,23 +16,38 @@ import ( func runCommand(ctx context.Context, t *testing.T, stdoutFile, cmdStr string, args ...string) (int, error) { + return runCommandWithEnv(ctx, t, stdoutFile, cmdStr, nil, args...) +} +func runCommandWithEnv(ctx context.Context, t *testing.T, stdoutFile, cmdStr string, + env map[string]string, args ...string) (int, error) { if _, err := os.Stat(cmdStr); err != nil { return 0, fmt.Errorf("no such executable '%s', please compile first: %w", cmdStr, err) } + // Log command execution if logger is available + if logger := GetTestLogger(ctx); logger != nil { + logger.LogCommand(cmdStr, args) + } + t.Log("Creating stdout file", stdoutFile) fd, err := os.Create(stdoutFile) if err != nil { - return 0, nil + return 0, err } defer fd.Close() t.Log("Running command", cmdStr, strings.Join(args, " ")) cmd := exec.CommandContext(ctx, cmdStr, args...) + cmd.Env = os.Environ() + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value)) + } out, err := cmd.CombinedOutput() t.Log("Done running command!", err) - _, _ = fd.Write(out) + if _, copyErr := io.Copy(fd, bytes.NewReader(out)); copyErr != nil { + return exitCodeFromError(err), copyErr + } return exitCodeFromError(err), err } @@ -48,8 +64,54 @@ func runCommandRetry(ctx context.Context, t *testing.T, retries int, stdoutFile, return } +func runCommandUntilValid(ctx context.Context, t *testing.T, attempts int, delay time.Duration, + stdoutFile, cmd string, validate func() error, args ...string) error { + + t.Helper() + + if attempts < 1 { + attempts = 1 + } + + var lastErr error + for i := 0; i < attempts; i++ { + exitCode, err := runCommand(ctx, t, stdoutFile, cmd, args...) + if err == nil { + validateErr := validate() + if validateErr == nil { + return nil + } + lastErr = validateErr + } else { + lastErr = fmt.Errorf("command %s failed with exit code %d: %w", cmd, exitCode, err) + } + + if i == attempts-1 { + break + } + + timer := time.NewTimer(delay) + select { + case <-ctx.Done(): + timer.Stop() + if lastErr != nil { + return lastErr + } + return ctx.Err() + case <-timer.C: + } + } + + return lastErr +} + func startCommand(ctx context.Context, t *testing.T, inPipeFile, cmdStr string, args ...string) (<-chan string, <-chan string, <-chan error, error) { + return startCommandWithEnv(ctx, t, inPipeFile, cmdStr, nil, args...) +} + +func startCommandWithEnv(ctx context.Context, t *testing.T, inPipeFile, + cmdStr string, env map[string]string, args ...string) (<-chan string, <-chan string, <-chan error, error) { stdoutCh := make(chan string) stderrCh := make(chan string) @@ -59,9 +121,23 @@ func startCommand(ctx context.Context, t *testing.T, inPipeFile, fmt.Errorf("no such executable '%s', please compile first: %w", cmdStr, err) } + // Log command execution if logger is available + if logger := GetTestLogger(ctx); logger != nil { + logger.LogCommand(cmdStr, args) + } + t.Log(cmdStr, strings.Join(args, " ")) cmd := exec.CommandContext(ctx, cmdStr, args...) + // Always inherit environment variables + cmd.Env = os.Environ() + // Add any additional environment variables if provided + if env != nil { + for k, v := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) + } + } + var stdinPipe io.WriteCloser if inPipeFile != "" { var err error |
