From 59923120e81de7170a6b7d8581b82e5033f13539 Mon Sep 17 00:00:00 2001 From: Deluan Date: Thu, 20 Aug 2026 00:52:55 -0400 Subject: [PATCH] test: tighten the tests added with the Go 1.27 bugfixes Drop the testItem stub in favour of the package's own cacheKey, register the pprof test profile once at package scope, and replace the hand-rolled goroutine settle loop with Eventually. Also corrects a comment that credited a TestMain the scanner suite does not have. --- cmd/root_test.go | 7 ++--- plugins/host_cache_test.go | 48 ++++++++++++++---------------- scanner/scanner_benchmark_test.go | 2 +- server/backgrounds/handler_test.go | 8 ++--- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/cmd/root_test.go b/cmd/root_test.go index eba07cdf8..aa58aeba5 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -13,6 +13,8 @@ import ( . "github.com/onsi/gomega" ) +var _ = pprof.NewProfile("nd-profiler-test") + var _ = Describe("profilerHandler", func() { // Mirrors how server.MountRouter mounts the handler. mount := func() http.Handler { @@ -23,9 +25,6 @@ var _ = Describe("profilerHandler", func() { BeforeEach(func() { DeferCleanup(configtest.SetupConfig()) - if pprof.Lookup("nd-profiler-test") == nil { - pprof.NewProfile("nd-profiler-test") - } }) DescribeTable("serves a named profile", @@ -33,7 +32,7 @@ var _ = Describe("profilerHandler", func() { conf.Server.BasePath = basePath w := httptest.NewRecorder() - target := path.Join(basePath, "/debug/pprof/nd-profiler-test") + "?debug=1" + target := basePath + "/debug/pprof/nd-profiler-test?debug=1" mount().ServeHTTP(w, httptest.NewRequest(http.MethodGet, target, nil)) Expect(w.Code).To(Equal(http.StatusOK)) diff --git a/plugins/host_cache_test.go b/plugins/host_cache_test.go index e3dbf6946..5e91b04ec 100644 --- a/plugins/host_cache_test.go +++ b/plugins/host_cache_test.go @@ -603,37 +603,33 @@ var _ = Describe("CacheService Integration", Ordered, func() { }) var _ = Describe("newCacheService", func() { - // Background goroutines wind down late, so poll for two consecutive equal samples. - settleGoroutines := func() int { - prev := -1 - for range 100 { + // The suite above leaves goroutines winding down, so settle before sampling. + settledBaseline := func() int { + var n int + Eventually(func() int { runtime.GC() - n := runtime.NumGoroutine() - if n == prev { - return n - } - prev = n - time.Sleep(10 * time.Millisecond) - } - return prev + prev := n + n = runtime.NumGoroutine() + return n - prev + }).WithTimeout(10 * time.Second).WithPolling(20 * time.Millisecond).Should(BeZero()) + return n } It("stops the janitor goroutine once the service is unreachable", func() { - const numServices = 20 + const numServices = 5 + baseline := settledBaseline() - baseline := settleGoroutines() + func() { + services := make([]*cacheServiceImpl, 0, numServices) + for i := range numServices { + services = append(services, newCacheService(fmt.Sprintf("plugin_%d", i))) + } + Expect(runtime.NumGoroutine()).To(BeNumerically(">=", baseline+numServices), + "expected one janitor goroutine per cache service") + }() - services := make([]*cacheServiceImpl, 0, numServices) - for i := range numServices { - services = append(services, newCacheService(fmt.Sprintf("plugin_%d", i))) - } - Expect(runtime.NumGoroutine()).To(BeNumerically(">=", baseline+numServices), - "expected one janitor goroutine per cache service") - - services = nil - Expect(services).To(BeNil()) - - Expect(settleGoroutines()).To(BeNumerically("<", baseline+numServices), - "janitor goroutines leaked after the services became unreachable") + Eventually(func() int { runtime.GC(); return runtime.NumGoroutine() }). + WithTimeout(10*time.Second).WithPolling(20*time.Millisecond). + Should(BeNumerically("<=", baseline), "janitor goroutines leaked") }) }) diff --git a/scanner/scanner_benchmark_test.go b/scanner/scanner_benchmark_test.go index 97ade09a7..91381aa0a 100644 --- a/scanner/scanner_benchmark_test.go +++ b/scanner/scanner_benchmark_test.go @@ -35,7 +35,7 @@ func BenchmarkScan(b *testing.B) { goleak.IgnoreTopFunction("github.com/rjeczalik/notify.(*recursiveTree).dispatch"), ) - // Benchmarks run without the suite's TestMain, so the config defaults must be loaded here + // go test -bench runs with -run=XXX, so TestScanner never loads the test config tests.Init(b, false) tmpDir := b.TempDir() diff --git a/server/backgrounds/handler_test.go b/server/backgrounds/handler_test.go index 31197f895..e8c77380f 100644 --- a/server/backgrounds/handler_test.go +++ b/server/backgrounds/handler_test.go @@ -10,10 +10,6 @@ import ( . "github.com/onsi/gomega" ) -type testItem string - -func (i testItem) Key() string { return string(i) } - type recordingBody struct { io.Reader closed *bool @@ -53,7 +49,7 @@ var _ = Describe("serveImage", func() { It("closes the response body when the hosting service returns an error", func() { stubStatus(http.StatusNotFound) - _, err := (&Handler{}).serveImage(context.Background(), testItem("some-image.webp")) + _, err := (&Handler{}).serveImage(context.Background(), cacheKey("some-image.webp")) Expect(err).To(MatchError(ContainSubstring("unexpected status code"))) Expect(closed).To(BeTrue(), "response body was left open") @@ -62,7 +58,7 @@ var _ = Describe("serveImage", func() { It("hands the still-open body to the caller on success", func() { stubStatus(http.StatusOK) - reader, err := (&Handler{}).serveImage(context.Background(), testItem("some-image.webp")) + reader, err := (&Handler{}).serveImage(context.Background(), cacheKey("some-image.webp")) Expect(err).ToNot(HaveOccurred()) Expect(closed).To(BeFalse(), "response body must stay open for the CachedStream wrapper")