From c9c829a37954a4698afc8699588aef1940ce5be9 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 23 Sep 2026 15:17:38 +0700 Subject: [PATCH] test(cli): scope probe journal assertions to their own log lines captureDebugMainLog swaps the process-wide mainLog, so every mainLog.Load().Warn()/.Error() site in the package writes into the buffer while it is installed, including goroutines left running by other tests. retainedProbeLines returned every retained line and the tests asserted field values over all of them, so one unrelated warn failed the run. Test_checkDnsLoopKeepsACustomKeyOutOfTheJournal hit this on Windows, where the loop probe held the buffer open for two seconds: upstream_probe_journal_test.go: field "upstream": got , want upstream.custom The sibling tests were exposed the same way; they index retained[0] after a strict length check, so a stray line fails them as got 2, want 1. Let retainedProbeLines take the messages a caller owns and return only those lines. wantNoProbeSecret keeps scanning every retained line, which is what that leak check is for. --- cmd/cli/upstream_probe_journal_test.go | 28 ++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/cmd/cli/upstream_probe_journal_test.go b/cmd/cli/upstream_probe_journal_test.go index d829d48..c1bbfe8 100644 --- a/cmd/cli/upstream_probe_journal_test.go +++ b/cmd/cli/upstream_probe_journal_test.go @@ -23,14 +23,30 @@ func customProbeUpstream() (string, *ctrld.UpstreamConfig) { } } -// retainedProbeLines returns the captured lines that the journal keeps. -func retainedProbeLines(t *testing.T, logs *syncBuffer) []map[string]any { +// retainedProbeLines returns the captured lines that the journal keeps. When +// messages are given, only the lines that start with one of them come back. +// captureDebugMainLog swaps the process-wide mainLog, so any goroutine of +// another test can add a warn or error line to the same buffer; a caller that +// asserts on field values names the lines its own probe wrote. +func retainedProbeLines(t *testing.T, logs *syncBuffer, messages ...string) []map[string]any { t.Helper() var retained []map[string]any for _, event := range jsonLogEvents(t, logs, "") { switch event["level"] { case "error", "warn": + default: + continue + } + if len(messages) == 0 { retained = append(retained, event) + continue + } + message, _ := event["message"].(string) + for _, want := range messages { + if strings.HasPrefix(message, want) { + retained = append(retained, event) + break + } } } return retained @@ -58,7 +74,7 @@ func Test_upstreamFailureLogKeepsACustomKeyOutOfTheJournal(t *testing.T) { (&upstreamFailureLog{}).report(probeTestProg(), upstream, uc, errors.New("probe failed"), time.Second) - retained := retainedProbeLines(t, logs) + retained := retainedProbeLines(t, logs, "Upstream check failed after ") if len(retained) != 1 { t.Fatalf("retained lines: got %d, want 1", len(retained)) } @@ -84,7 +100,7 @@ func Test_checkUpstreamOnceKeepsACustomKeyOutOfTheJournal(t *testing.T) { t.Fatal("expected the resolver creation to fail") } - retained := retainedProbeLines(t, logs) + retained := retainedProbeLines(t, logs, "Failed to create resolver") if len(retained) != 1 { t.Fatalf("retained lines: got %d, want 1", len(retained)) } @@ -110,7 +126,7 @@ func Test_checkDnsLoopKeepsACustomKeyOutOfTheJournal(t *testing.T) { p.checkDnsLoop() - retained := retainedProbeLines(t, logs) + retained := retainedProbeLines(t, logs, "Could not perform loop check", "Could not send DNS loop check query") if len(retained) == 0 { t.Fatal("the loop check failure must stay visible above debug") } @@ -132,7 +148,7 @@ func Test_logUpstreamPingFailuresKeepsACustomKeyOutOfTheJournal(t *testing.T) { logUpstreamPingFailures(context.Background(), map[string]*ctrld.UpstreamConfig{probeSecret: uc}) - retained := retainedProbeLines(t, logs) + retained := retainedProbeLines(t, logs, "Failed to connect to the upstream") if len(retained) != 1 { t.Fatalf("retained lines: got %d, want 1", len(retained)) }