Files
caddy/modules/caddyhttp/caddyauth/bcrypt_test.go
Mohammed Al Sahaf 8619b04834 add caddyauth tests
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2026-06-05 18:00:44 +03:00

156 lines
3.7 KiB
Go

package caddyauth
import (
"testing"
"golang.org/x/crypto/bcrypt"
)
func TestBcryptHashCaddyModule(t *testing.T) {
b := BcryptHash{}
info := b.CaddyModule()
if info.ID != "http.authentication.hashes.bcrypt" {
t.Errorf("CaddyModule().ID = %v, want 'http.authentication.hashes.bcrypt'", info.ID)
}
}
func TestBcryptHashCompare(t *testing.T) {
hasher := BcryptHash{}
plaintext := []byte("correct-password")
hashed, err := bcrypt.GenerateFromPassword(plaintext, bcrypt.MinCost)
if err != nil {
t.Fatalf("failed to generate bcrypt hash: %v", err)
}
tests := []struct {
name string
hashed []byte
plaintext []byte
wantMatch bool
wantErr bool
}{
{
name: "correct password matches",
hashed: hashed,
plaintext: plaintext,
wantMatch: true,
},
{
name: "incorrect password does not match",
hashed: hashed,
plaintext: []byte("wrong-password"),
wantMatch: false,
},
{
name: "empty password does not match",
hashed: hashed,
plaintext: []byte(""),
wantMatch: false,
},
{
name: "invalid hash returns error",
hashed: []byte("not-a-bcrypt-hash"),
plaintext: plaintext,
wantMatch: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
match, err := hasher.Compare(tt.hashed, tt.plaintext)
if tt.wantErr {
if err == nil {
t.Error("Compare() should return error")
}
return
}
if err != nil {
t.Fatalf("Compare() error: %v", err)
}
if match != tt.wantMatch {
t.Errorf("Compare() = %v, want %v", match, tt.wantMatch)
}
})
}
}
func TestBcryptHashHash(t *testing.T) {
hasher := BcryptHash{}
plaintext := []byte("test-password")
hashed, err := hasher.Hash(plaintext)
if err != nil {
t.Fatalf("Hash() error: %v", err)
}
if len(hashed) == 0 {
t.Fatal("Hash() returned empty result")
}
// Verify the hash is valid bcrypt
err = bcrypt.CompareHashAndPassword(hashed, plaintext)
if err != nil {
t.Errorf("Hash() produced invalid bcrypt hash: %v", err)
}
// Verify re-hashing produces different output (different salt)
hashed2, err := hasher.Hash(plaintext)
if err != nil {
t.Fatalf("Hash() second call error: %v", err)
}
if string(hashed) == string(hashed2) {
t.Error("Hash() should produce different output on each call (different salt)")
}
}
func TestBcryptHashRoundTrip(t *testing.T) {
hasher := BcryptHash{}
plaintext := []byte("round-trip-test")
hashed, err := hasher.Hash(plaintext)
if err != nil {
t.Fatalf("Hash() error: %v", err)
}
match, err := hasher.Compare(hashed, plaintext)
if err != nil {
t.Fatalf("Compare() error: %v", err)
}
if !match {
t.Error("Hash then Compare round-trip failed: password should match")
}
}
func TestBcryptFakeHash(t *testing.T) {
hasher := BcryptHash{}
fake := hasher.FakeHash()
if len(fake) == 0 {
t.Fatal("FakeHash() returned empty result")
}
// FakeHash should be a valid bcrypt hash matching "antitiming"
err := bcrypt.CompareHashAndPassword(fake, []byte("antitiming"))
if err != nil {
t.Errorf("FakeHash() is not a valid bcrypt hash of 'antitiming': %v", err)
}
// Calling FakeHash multiple times should return the same value
fake2 := hasher.FakeHash()
if string(fake) != string(fake2) {
t.Error("FakeHash() should return constant value")
}
}
func TestBcryptHashWithCustomCost(t *testing.T) {
hasher := BcryptHash{cost: bcrypt.MinCost}
plaintext := []byte("low-cost-test")
hashed, err := hasher.Hash(plaintext)
if err != nil {
t.Fatalf("Hash() with MinCost error: %v", err)
}
match, err := hasher.Compare(hashed, plaintext)
if err != nil {
t.Fatalf("Compare() error: %v", err)
}
if !match {
t.Error("Hash/Compare with MinCost should match")
}
}