Refactor and expand random string tests

Refactored the test for `randomStringSecure` to better organize test cases using subtests. Added new test cases to check failures with a broken rand.Reader, improving test coverage and robustness.
This commit is contained in:
Winni Neessen 2024-11-01 15:24:47 +01:00
parent d7e0b48567
commit 27a3985240
Signed by: wneessen
GPG key ID: 385AC9889632126E

View file

@ -5,45 +5,81 @@
package mail package mail
import ( import (
"crypto/rand"
"errors"
"strings" "strings"
"testing" "testing"
) )
// TestRandomStringSecure tests the randomStringSecure method // TestRandomStringSecure tests the randomStringSecure method
func TestRandomStringSecure(t *testing.T) { func TestRandomStringSecure(t *testing.T) {
tt := []struct { t.Run("randomStringSecure with varying length", func(t *testing.T) {
testName string tt := []struct {
length int testName string
mustNotMatch string length int
}{ mustNotMatch string
{"20 chars", 20, "'"}, }{
{"100 chars", 100, "'"}, {"20 chars", 20, "'"},
{"1000 chars", 1000, "'"}, {"100 chars", 100, "'"},
} {"1000 chars", 1000, "'"},
}
for _, tc := range tt { for _, tc := range tt {
t.Run(tc.testName, func(t *testing.T) { t.Run(tc.testName, func(t *testing.T) {
rs, err := randomStringSecure(tc.length) rs, err := randomStringSecure(tc.length)
if err != nil { if err != nil {
t.Errorf("random string generation failed: %s", err) t.Errorf("random string generation failed: %s", err)
} }
if strings.Contains(rs, tc.mustNotMatch) { if strings.Contains(rs, tc.mustNotMatch) {
t.Errorf("random string contains unexpected character. got: %s, not-expected: %s", t.Errorf("random string contains unexpected character. got: %s, not-expected: %s",
rs, tc.mustNotMatch) rs, tc.mustNotMatch)
} }
if len(rs) != tc.length { if len(rs) != tc.length {
t.Errorf("random string length does not match. expected: %d, got: %d", tc.length, len(rs)) t.Errorf("random string length does not match. expected: %d, got: %d", tc.length, len(rs))
} }
}) })
} }
})
t.Run("randomStringSecure fails on broken rand Reader (first read)", func(t *testing.T) {
defaultRandReader := rand.Reader
t.Cleanup(func() { rand.Reader = defaultRandReader })
rand.Reader = &randReader{failon: 1}
if _, err := randomStringSecure(22); err == nil {
t.Fatalf("expected failure on broken rand Reader")
}
})
t.Run("randomStringSecure fails on broken rand Reader (second read)", func(t *testing.T) {
defaultRandReader := rand.Reader
t.Cleanup(func() { rand.Reader = defaultRandReader })
rand.Reader = &randReader{failon: 0}
if _, err := randomStringSecure(22); err == nil {
t.Fatalf("expected failure on broken rand Reader")
}
})
} }
func BenchmarkGenerator_RandomStringSecure(b *testing.B) { func BenchmarkGenerator_RandomStringSecure(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := randomStringSecure(22) _, err := randomStringSecure(10)
if err != nil { if err != nil {
b.Errorf("RandomStringFromCharRange() failed: %s", err) b.Errorf("RandomStringFromCharRange() failed: %s", err)
} }
} }
} }
// randReader is type that satisfies the io.Reader interface. It can fail on a specific read
// operations and is therefore useful to test consecutive reads with errors
type randReader struct {
failon uint8
call uint8
}
// Read implements the io.Reader interface for the randReader type
func (r *randReader) Read(p []byte) (int, error) {
if r.call == r.failon {
r.call++
return len(p), nil
}
return 0, errors.New("broken reader")
}