package challenge import ( "testing" "time" ) func TestNewGenerator(t *testing.T) { config := TestConfig() generator := NewGenerator(config) if generator == nil { t.Fatal("expected generator to be created") } if generator.config != config { t.Error("expected generator to store config reference") } } func TestGenerator_GenerateChallenge(t *testing.T) { config := TestConfig() generator := NewGenerator(config) challenge, err := generator.GenerateChallenge() if err != nil { t.Fatalf("expected no error, got: %v", err) } // Validate basic properties if challenge.Difficulty != config.DefaultDifficulty { t.Errorf("expected difficulty %d, got %d", config.DefaultDifficulty, challenge.Difficulty) } if challenge.Resource != config.Resource { t.Errorf("expected resource %s, got %s", config.Resource, challenge.Resource) } if len(challenge.Random) != config.RandomBytes { t.Errorf("expected %d random bytes, got %d", config.RandomBytes, len(challenge.Random)) } if len(challenge.HMAC) == 0 { t.Error("expected HMAC to be set") } if challenge.Timestamp <= 0 { t.Error("expected positive timestamp") } // Verify timestamp is recent (within last minute) now := time.Now().Unix() if challenge.Timestamp < now-60 || challenge.Timestamp > now { t.Errorf("expected timestamp to be recent, got %d (now: %d)", challenge.Timestamp, now) } } func TestGenerator_GenerateChallengeWithOptions(t *testing.T) { config := TestConfig() generator := NewGenerator(config) customDifficulty := 8 challenge, err := generator.GenerateChallenge(WithDifficulty(customDifficulty)) if err != nil { t.Fatalf("expected no error, got: %v", err) } if challenge.Difficulty != customDifficulty { t.Errorf("expected custom difficulty %d, got %d", customDifficulty, challenge.Difficulty) } } func TestGenerator_GenerateChallengeValidation(t *testing.T) { config := TestConfig() generator := NewGenerator(config) tests := []struct { name string difficulty int expectErr bool }{ { name: "valid difficulty within range", difficulty: 5, expectErr: false, }, { name: "difficulty too low", difficulty: config.MinDifficulty - 1, expectErr: true, }, { name: "difficulty too high", difficulty: config.MaxDifficulty + 1, expectErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := generator.GenerateChallenge(WithDifficulty(tt.difficulty)) if tt.expectErr && err == nil { t.Error("expected error but got none") } if !tt.expectErr && err != nil { t.Errorf("expected no error but got: %v", err) } if err != nil && err != ErrInvalidDifficulty { t.Errorf("expected ErrInvalidDifficulty, got: %v", err) } }) } } func TestGenerator_HMACIntegrity(t *testing.T) { config := TestConfig() generator := NewGenerator(config) challenge, err := generator.GenerateChallenge() if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify HMAC with original secret err = challenge.VerifyHMAC(config.HMACSecret) if err != nil { t.Fatalf("expected HMAC verification to pass, got: %v", err) } // Verify HMAC fails with different secret differentConfig := TestConfig() err = challenge.VerifyHMAC(differentConfig.HMACSecret) if err != ErrInvalidHMAC { t.Fatalf("expected ErrInvalidHMAC with different secret, got: %v", err) } } func TestGenerator_RandomnessUniqueness(t *testing.T) { config := TestConfig() generator := NewGenerator(config) // Generate multiple challenges and ensure they have different random values challenges := make([]*Challenge, 10) for i := range challenges { challenge, err := generator.GenerateChallenge() if err != nil { t.Fatalf("unexpected error generating challenge %d: %v", i, err) } challenges[i] = challenge } // Check that all random values are different for i := 0; i < len(challenges); i++ { for j := i + 1; j < len(challenges); j++ { if string(challenges[i].Random) == string(challenges[j].Random) { t.Errorf("challenges %d and %d have identical random values: %x", i, j, challenges[i].Random) } } } } func TestWithDifficulty(t *testing.T) { challenge := &Challenge{Difficulty: 4} option := WithDifficulty(8) option(challenge) if challenge.Difficulty != 8 { t.Errorf("expected difficulty 8, got %d", challenge.Difficulty) } }