mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
Add explicit error discards (_ =) for Close() calls on network connections, SQL rows, and file handles across 28 files. Also add .golangci.yml with standard linter defaults to match CI configuration.
341 lines
7.5 KiB
Go
341 lines
7.5 KiB
Go
package token
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestGenerate_Length(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
length int
|
|
}{
|
|
{"zero length", 0},
|
|
{"short", 5},
|
|
{"medium", 32},
|
|
{"long", 100},
|
|
{"very long", 1000},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := Generate(tt.length)
|
|
if len(result) != tt.length {
|
|
t.Errorf("Generate(%d) length = %d, want %d", tt.length, len(result), tt.length)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerate_CharacterSet(t *testing.T) {
|
|
// Verify that generated tokens only contain alphanumeric characters
|
|
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
validCharMap := make(map[rune]bool)
|
|
for _, c := range validChars {
|
|
validCharMap[c] = true
|
|
}
|
|
|
|
token := Generate(1000) // Large sample
|
|
for _, c := range token {
|
|
if !validCharMap[c] {
|
|
t.Errorf("Generate() produced invalid character: %c", c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerate_Randomness(t *testing.T) {
|
|
// Generate multiple tokens and verify they're different
|
|
tokens := make(map[string]bool)
|
|
count := 100
|
|
length := 32
|
|
|
|
for i := 0; i < count; i++ {
|
|
token := Generate(length)
|
|
if tokens[token] {
|
|
t.Errorf("Generate() produced duplicate token: %s", token)
|
|
}
|
|
tokens[token] = true
|
|
}
|
|
|
|
if len(tokens) != count {
|
|
t.Errorf("Generated %d unique tokens, want %d", len(tokens), count)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ContainsUppercase(t *testing.T) {
|
|
// With enough characters, should contain at least one uppercase letter
|
|
token := Generate(1000)
|
|
hasUpper := false
|
|
for _, c := range token {
|
|
if c >= 'A' && c <= 'Z' {
|
|
hasUpper = true
|
|
break
|
|
}
|
|
}
|
|
if !hasUpper {
|
|
t.Error("Generate(1000) should contain at least one uppercase letter")
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ContainsLowercase(t *testing.T) {
|
|
// With enough characters, should contain at least one lowercase letter
|
|
token := Generate(1000)
|
|
hasLower := false
|
|
for _, c := range token {
|
|
if c >= 'a' && c <= 'z' {
|
|
hasLower = true
|
|
break
|
|
}
|
|
}
|
|
if !hasLower {
|
|
t.Error("Generate(1000) should contain at least one lowercase letter")
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ContainsDigit(t *testing.T) {
|
|
// With enough characters, should contain at least one digit
|
|
token := Generate(1000)
|
|
hasDigit := false
|
|
for _, c := range token {
|
|
if c >= '0' && c <= '9' {
|
|
hasDigit = true
|
|
break
|
|
}
|
|
}
|
|
if !hasDigit {
|
|
t.Error("Generate(1000) should contain at least one digit")
|
|
}
|
|
}
|
|
|
|
func TestGenerate_Distribution(t *testing.T) {
|
|
// Test that characters are reasonably distributed
|
|
token := Generate(6200) // 62 chars * 100 = good sample size
|
|
charCount := make(map[rune]int)
|
|
|
|
for _, c := range token {
|
|
charCount[c]++
|
|
}
|
|
|
|
// With 62 valid characters and 6200 samples, average should be 100 per char
|
|
// We'll accept a range to account for randomness
|
|
minExpected := 50 // Allow some variance
|
|
maxExpected := 150
|
|
|
|
for c, count := range charCount {
|
|
if count < minExpected || count > maxExpected {
|
|
t.Logf("Character %c appeared %d times (outside expected range %d-%d)", c, count, minExpected, maxExpected)
|
|
}
|
|
}
|
|
|
|
// Just verify we have a good spread of characters
|
|
if len(charCount) < 50 {
|
|
t.Errorf("Only %d different characters used, want at least 50", len(charCount))
|
|
}
|
|
}
|
|
|
|
func TestNewSafeRand(t *testing.T) {
|
|
rng := NewSafeRand()
|
|
if rng == nil {
|
|
t.Fatal("NewSafeRand() returned nil")
|
|
}
|
|
|
|
// Test that it produces different values on subsequent calls
|
|
val1 := rng.Intn(1000000)
|
|
val2 := rng.Intn(1000000)
|
|
|
|
if val1 == val2 {
|
|
// This is possible but unlikely, let's try a few more times
|
|
same := true
|
|
for i := 0; i < 10; i++ {
|
|
if rng.Intn(1000000) != val1 {
|
|
same = false
|
|
break
|
|
}
|
|
}
|
|
if same {
|
|
t.Error("NewSafeRand() produced same value 12 times in a row")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRNG_GlobalVariable(t *testing.T) {
|
|
// Test that the global RNG variable is initialized
|
|
if RNG == nil {
|
|
t.Fatal("Global RNG is nil")
|
|
}
|
|
|
|
// Test that it works
|
|
val := RNG.Intn(100)
|
|
if val < 0 || val >= 100 {
|
|
t.Errorf("RNG.Intn(100) = %d, out of range [0, 100)", val)
|
|
}
|
|
}
|
|
|
|
func TestRNG_Uint32(t *testing.T) {
|
|
// Test that RNG can generate uint32 values
|
|
val1 := RNG.Uint32()
|
|
val2 := RNG.Uint32()
|
|
|
|
// They should be different (with very high probability)
|
|
if val1 == val2 {
|
|
// Try a few more times
|
|
same := true
|
|
for i := 0; i < 10; i++ {
|
|
if RNG.Uint32() != val1 {
|
|
same = false
|
|
break
|
|
}
|
|
}
|
|
if same {
|
|
t.Error("RNG.Uint32() produced same value 12 times")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerate_Concurrency(t *testing.T) {
|
|
// Test that Generate works correctly when called concurrently
|
|
done := make(chan string, 100)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
go func() {
|
|
token := Generate(32)
|
|
done <- token
|
|
}()
|
|
}
|
|
|
|
tokens := make(map[string]bool)
|
|
for i := 0; i < 100; i++ {
|
|
token := <-done
|
|
if len(token) != 32 {
|
|
t.Errorf("Token length = %d, want 32", len(token))
|
|
}
|
|
tokens[token] = true
|
|
}
|
|
|
|
// Should have many unique tokens (allow some small chance of duplicates)
|
|
if len(tokens) < 95 {
|
|
t.Errorf("Only %d unique tokens from 100 concurrent calls", len(tokens))
|
|
}
|
|
}
|
|
|
|
func TestGenerate_EmptyString(t *testing.T) {
|
|
token := Generate(0)
|
|
if token != "" {
|
|
t.Errorf("Generate(0) = %q, want empty string", token)
|
|
}
|
|
}
|
|
|
|
func TestGenerate_OnlyAlphanumeric(t *testing.T) {
|
|
// Verify no special characters
|
|
token := Generate(1000)
|
|
for i, c := range token {
|
|
isValid := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
|
|
if !isValid {
|
|
t.Errorf("Token[%d] = %c (invalid character)", i, c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewSafeRand_DifferentSeeds(t *testing.T) {
|
|
// Create two RNGs at different times and verify they produce different sequences
|
|
rng1 := NewSafeRand()
|
|
time.Sleep(1 * time.Millisecond) // Ensure different seed
|
|
rng2 := NewSafeRand()
|
|
|
|
val1 := rng1.Intn(1000000)
|
|
val2 := rng2.Intn(1000000)
|
|
|
|
// They should be different with high probability
|
|
if val1 == val2 {
|
|
// Try again
|
|
val1 = rng1.Intn(1000000)
|
|
val2 = rng2.Intn(1000000)
|
|
if val1 == val2 {
|
|
t.Log("Two RNGs created at different times produced same first two values (possible but unlikely)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerate_Short(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = Generate(8)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerate_Medium(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = Generate(32)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerate_Long(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = Generate(128)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewSafeRand(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = NewSafeRand()
|
|
}
|
|
}
|
|
|
|
func BenchmarkRNG_Intn(b *testing.B) {
|
|
rng := NewSafeRand()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = rng.Intn(62)
|
|
}
|
|
}
|
|
|
|
func BenchmarkRNG_Uint32(b *testing.B) {
|
|
rng := NewSafeRand()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = rng.Uint32()
|
|
}
|
|
}
|
|
|
|
func TestGenerate_ConsistentCharacterSet(t *testing.T) {
|
|
// Verify the character set matches what's defined in the code
|
|
expectedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
if len(expectedChars) != 62 {
|
|
t.Errorf("Expected character set length = %d, want 62", len(expectedChars))
|
|
}
|
|
|
|
// Count each type
|
|
lowercase := 0
|
|
uppercase := 0
|
|
digits := 0
|
|
for _, c := range expectedChars {
|
|
if c >= 'a' && c <= 'z' {
|
|
lowercase++
|
|
} else if c >= 'A' && c <= 'Z' {
|
|
uppercase++
|
|
} else if c >= '0' && c <= '9' {
|
|
digits++
|
|
}
|
|
}
|
|
|
|
if lowercase != 26 {
|
|
t.Errorf("Lowercase count = %d, want 26", lowercase)
|
|
}
|
|
if uppercase != 26 {
|
|
t.Errorf("Uppercase count = %d, want 26", uppercase)
|
|
}
|
|
if digits != 10 {
|
|
t.Errorf("Digits count = %d, want 10", digits)
|
|
}
|
|
}
|
|
|
|
func TestRNG_Type(t *testing.T) {
|
|
// Verify RNG is of type *SafeRand
|
|
var _ = (*SafeRand)(nil)
|
|
_ = RNG
|
|
_ = NewSafeRand()
|
|
}
|