From 487cbe97744015e38481d74feeeb8c16c7b4481d Mon Sep 17 00:00:00 2001 From: jferrl Date: Sat, 19 Sep 2026 13:19:54 +0200 Subject: [PATCH] test: benchmark the installation token cache hit Almost every Token() call returns a cached token, and nothing measured that path. A change that reintroduced work there -- an allocation per call, or a lost cache -- would not fail any test, since the existing ones all assert behaviour rather than cost. The benchmark asserts the server saw exactly one request after the loop. Without that check it would still report a number if caching broke; it would just be timing HTTP round trips and calling them cache hits. Current baseline on an 18-core M-series machine: BenchmarkInstallationTokenSource_CacheHit-18 31613367 34.90 ns/op 0 B/op 0 allocs/op No new concurrency tests: TestReuseTokenSourceWithSkew already pins the collapse to one upstream fetch, and Test_WithInstallationExpirySkew_Wiring pins that the constructor uses the wrapper. Mutation testing confirmed both catch a disabled cache, a lock dropped across the refresh, and an unwrapped installation source. --- benchmark_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 benchmark_test.go diff --git a/benchmark_test.go b/benchmark_test.go new file mode 100644 index 0000000..a75ceaf --- /dev/null +++ b/benchmark_test.go @@ -0,0 +1,67 @@ +package githubauth + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/json" + "encoding/pem" + "net/http" + "net/http/httptest" + "sync/atomic" + "testing" + "time" +) + +// BenchmarkInstallationTokenSource_CacheHit measures the path almost every +// Token() call takes: a cached token that is still valid. The call count is +// asserted afterwards so a regression that turned cache hits back into HTTP +// requests would fail the benchmark rather than quietly reporting a slower +// number. +func BenchmarkInstallationTokenSource_CacheHit(b *testing.B) { + var calls atomic.Int64 + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + calls.Add(1) + w.WriteHeader(http.StatusCreated) + _ = json.NewEncoder(w).Encode(InstallationToken{ + Token: "ghs_benchmark", + ExpiresAt: time.Now().Add(time.Hour), + }) + })) + defer srv.Close() + + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + b.Fatalf("generating key: %v", err) + } + keyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + + app, err := NewApplicationTokenSource("Iv1.testclientid", keyPEM) + if err != nil { + b.Fatalf("NewApplicationTokenSource() error = %v", err) + } + src := NewInstallationTokenSource(1, app, WithBaseURL(srv.URL)) + + // Prime the cache so the loop measures hits, not the initial mint. + if _, err := src.Token(); err != nil { + b.Fatalf("priming Token() error = %v", err) + } + + b.ReportAllocs() + + for b.Loop() { + if _, err := src.Token(); err != nil { + b.Fatalf("Token() error = %v", err) + } + } + + b.StopTimer() + + if got := calls.Load(); got != 1 { + b.Fatalf("benchmark made %d installation-token requests, want 1: the loop was not measuring cache hits", got) + } +}