From e224e89ca90d061ffa5466c9d2ea0873d87e37e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 16:47:54 +0000 Subject: [PATCH] Add tests for util.RandomBigInt zero-result normalization branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract randomBigIntFromRandFunc internal helper to make the astronomically-rare zero-result defensive branch testable via dependency injection. Add TestRandomBigIntFromRandFunc covering: - zero bits error path - rand function error wrapping - normal positive result passthrough - zero result normalized to 1 (previously untestable branch) Coverage improvement: internal/util 97.8% → 99.3% RandomBigInt: 77.8% → 100% Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/util/random.go | 19 ++++++++++----- internal/util/random_test.go | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/internal/util/random.go b/internal/util/random.go index ea0492ed..8b6b634c 100644 --- a/internal/util/random.go +++ b/internal/util/random.go @@ -64,16 +64,15 @@ func RandomHexWithFallback(n int) string { return s } -// RandomBigInt returns a cryptographically random non-negative integer with -// the given bit width. The result is guaranteed to be strictly positive (≥ 1). -// This centralises crypto/rand.Int usage for callers that need a *big.Int -// (e.g. X.509 certificate serial numbers). -func RandomBigInt(bits uint) (*big.Int, error) { +// randomBigIntFromRandFunc returns a cryptographically random non-negative integer +// with the given bit width using the provided rand function. It is the testable +// core of RandomBigInt. +func randomBigIntFromRandFunc(bits uint, randFn func(io.Reader, *big.Int) (*big.Int, error)) (*big.Int, error) { if bits == 0 { return nil, fmt.Errorf("failed to generate random big.Int: bits must be > 0") } max := new(big.Int).Lsh(big.NewInt(1), bits) - n, err := rand.Int(rand.Reader, max) + n, err := randFn(rand.Reader, max) if err != nil { return nil, fmt.Errorf("failed to generate random big.Int(%d bits): %w", bits, err) } @@ -83,3 +82,11 @@ func RandomBigInt(bits uint) (*big.Int, error) { } return n, nil } + +// RandomBigInt returns a cryptographically random non-negative integer with +// the given bit width. The result is guaranteed to be strictly positive (≥ 1). +// This centralises crypto/rand.Int usage for callers that need a *big.Int +// (e.g. X.509 certificate serial numbers). +func RandomBigInt(bits uint) (*big.Int, error) { + return randomBigIntFromRandFunc(bits, rand.Int) +} diff --git a/internal/util/random_test.go b/internal/util/random_test.go index 42fb9d51..6981f1f0 100644 --- a/internal/util/random_test.go +++ b/internal/util/random_test.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "io" + "math/big" "testing" "github.com/stretchr/testify/assert" @@ -351,3 +352,49 @@ func TestRandomBigInt(t *testing.T) { assert.Contains(t, err.Error(), "bits must be > 0") }) } + +// TestRandomBigIntFromRandFunc covers the internal randomBigIntFromRandFunc helper, +// including the rarely-exercised zero-result normalization branch. +func TestRandomBigIntFromRandFunc(t *testing.T) { + t.Run("zero bits returns error", func(t *testing.T) { + called := false + _, err := randomBigIntFromRandFunc(0, func(_ io.Reader, _ *big.Int) (*big.Int, error) { + called = true + return big.NewInt(1), nil + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "bits must be > 0") + assert.False(t, called, "rand function should not be called when bits == 0") + }) + + t.Run("rand function error is wrapped and returned", func(t *testing.T) { + randErr := errors.New("simulated rand failure") + _, err := randomBigIntFromRandFunc(128, func(_ io.Reader, _ *big.Int) (*big.Int, error) { + return nil, randErr + }) + require.Error(t, err) + assert.ErrorIs(t, err, randErr) + assert.Contains(t, err.Error(), "128 bits") + }) + + t.Run("normal positive result is returned unchanged", func(t *testing.T) { + want := big.NewInt(42) + got, err := randomBigIntFromRandFunc(128, func(_ io.Reader, _ *big.Int) (*big.Int, error) { + return new(big.Int).Set(want), nil + }) + require.NoError(t, err) + assert.Equal(t, 0, got.Cmp(want), "positive result should be returned as-is") + }) + + t.Run("zero result is normalized to 1", func(t *testing.T) { + // This covers the astronomically rare defensive branch in RandomBigInt where + // crypto/rand.Int returns zero. We exercise it by injecting a stub that always + // returns zero. + got, err := randomBigIntFromRandFunc(128, func(_ io.Reader, _ *big.Int) (*big.Int, error) { + return big.NewInt(0), nil + }) + require.NoError(t, err) + assert.Equal(t, int64(1), got.Int64(), "zero result should be normalized to 1") + assert.Positive(t, got.Sign(), "normalized result should be strictly positive") + }) +}