- Add new test files for auth, service, and handler modules - Improve test organization and coverage - Refactor code for better maintainability - Add captcha, settings, stats, and theme handler tests - Add auth module tests (CAS, OAuth, password, SSO, state) - Add service layer tests for auth, export, permissions, roles - All Go tests pass (exit code 0) - All frontend tests pass (325 tests in 59 files)
235 lines
5.8 KiB
Go
235 lines
5.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBcryptHash(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
password string
|
|
wantErr bool
|
|
}{
|
|
{"valid password", "password123", false},
|
|
{"empty password", "", false}, // bcrypt allows empty
|
|
{"long password", strings.Repeat("a", 50), false},
|
|
{"too long password - bcrypt limit", strings.Repeat("a", 73), true}, // bcrypt returns error for >72 bytes
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hash, err := BcryptHash(tt.password)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("BcryptHash() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !tt.wantErr && hash == "" {
|
|
t.Error("BcryptHash() returned empty hash")
|
|
}
|
|
if !tt.wantErr && !strings.HasPrefix(hash, "$2") {
|
|
t.Errorf("BcryptHash() hash should start with $2, got %s", hash[:3])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBcryptVerify(t *testing.T) {
|
|
// First create a hash to test against
|
|
hash, err := BcryptHash("correct-password")
|
|
if err != nil {
|
|
t.Fatalf("BcryptHash() error = %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
hash string
|
|
password string
|
|
want bool
|
|
}{
|
|
{"correct password", hash, "correct-password", true},
|
|
{"wrong password", hash, "wrong-password", false},
|
|
{"empty password", hash, "", false},
|
|
{"invalid hash", "invalid-hash", "password", false},
|
|
{"empty hash", "", "password", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := BcryptVerify(tt.hash, tt.password); got != tt.want {
|
|
t.Errorf("BcryptVerify() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBcryptVerify_DifferentPasswords(t *testing.T) {
|
|
hash1, _ := BcryptHash("password1")
|
|
hash2, _ := BcryptHash("password2")
|
|
|
|
// Each hash should only verify its own password
|
|
if !BcryptVerify(hash1, "password1") {
|
|
t.Error("hash1 should verify password1")
|
|
}
|
|
if BcryptVerify(hash1, "password2") {
|
|
t.Error("hash1 should not verify password2")
|
|
}
|
|
if !BcryptVerify(hash2, "password2") {
|
|
t.Error("hash2 should verify password2")
|
|
}
|
|
if BcryptVerify(hash2, "password1") {
|
|
t.Error("hash2 should not verify password1")
|
|
}
|
|
}
|
|
|
|
func TestPassword_Verify_Argon2id(t *testing.T) {
|
|
p := NewPassword()
|
|
|
|
hash, err := p.Hash("test-password")
|
|
if err != nil {
|
|
t.Fatalf("Hash() error = %v", err)
|
|
}
|
|
|
|
// Verify correct password
|
|
if !p.Verify(hash, "test-password") {
|
|
t.Error("Verify() should return true for correct password")
|
|
}
|
|
|
|
// Verify wrong password
|
|
if p.Verify(hash, "wrong-password") {
|
|
t.Error("Verify() should return false for wrong password")
|
|
}
|
|
}
|
|
|
|
func TestPassword_Verify_Bcrypt(t *testing.T) {
|
|
p := NewPassword()
|
|
|
|
// Create bcrypt hash
|
|
bcryptHash, err := BcryptHash("bcrypt-password")
|
|
if err != nil {
|
|
t.Fatalf("BcryptHash() error = %v", err)
|
|
}
|
|
|
|
// Verify using Argon2id password manager (should support bcrypt)
|
|
if !p.Verify(bcryptHash, "bcrypt-password") {
|
|
t.Error("Verify() should support bcrypt hashes")
|
|
}
|
|
|
|
if p.Verify(bcryptHash, "wrong-password") {
|
|
t.Error("Verify() should return false for wrong bcrypt password")
|
|
}
|
|
}
|
|
|
|
func TestPassword_Verify_InvalidFormat(t *testing.T) {
|
|
p := NewPassword()
|
|
|
|
tests := []struct {
|
|
name string
|
|
hash string
|
|
want bool
|
|
}{
|
|
{"empty hash", "", false},
|
|
{"invalid format", "invalid", false},
|
|
{"wrong number of parts", "$argon2id$v=19$m=65536,t=3,p=4$abc", false},
|
|
{"wrong algorithm", "$scrypt$v=19$m=65536,t=3,p=4$salt$hash", false},
|
|
{"invalid params", "$argon2id$v=19$m=abc,t=3,p=4$salt$hash", false},
|
|
{"invalid salt hex", "$argon2id$v=19$m=65536,t=3,p=4$ZZZZZZZZ$hash", false},
|
|
{"invalid hash hex", "$argon2id$v=19$m=65536,t=3,p=4$salt$ZZZZZZZZ", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := p.Verify(tt.hash, "password"); got != tt.want {
|
|
t.Errorf("Verify() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPassword_Hash_DifferentSalts(t *testing.T) {
|
|
p := NewPassword()
|
|
|
|
hash1, err := p.Hash("same-password")
|
|
if err != nil {
|
|
t.Fatalf("Hash() error = %v", err)
|
|
}
|
|
|
|
hash2, err := p.Hash("same-password")
|
|
if err != nil {
|
|
t.Fatalf("Hash() error = %v", err)
|
|
}
|
|
|
|
// Two hashes of the same password should be different (different salts)
|
|
if hash1 == hash2 {
|
|
t.Error("Hash() should generate different hashes for same password (different salts)")
|
|
}
|
|
|
|
// But both should verify the same password
|
|
if !p.Verify(hash1, "same-password") {
|
|
t.Error("hash1 should verify same-password")
|
|
}
|
|
if !p.Verify(hash2, "same-password") {
|
|
t.Error("hash2 should verify same-password")
|
|
}
|
|
}
|
|
|
|
func TestPassword_HashAndVerify_SpecialCharacters(t *testing.T) {
|
|
p := NewPassword()
|
|
|
|
tests := []string{
|
|
"p@ssw0rd!",
|
|
"密码测试",
|
|
"パスワード",
|
|
" spaces ",
|
|
"tab\ttab",
|
|
"newline\nnewline",
|
|
strings.Repeat("a", 100),
|
|
}
|
|
|
|
for _, password := range tests {
|
|
t.Run("password_"+password, func(t *testing.T) {
|
|
hash, err := p.Hash(password)
|
|
if err != nil {
|
|
t.Fatalf("Hash() error = %v", err)
|
|
}
|
|
|
|
if !p.Verify(hash, password) {
|
|
t.Errorf("Verify() failed for password: %q", password)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVerifyPassword_Wrapper(t *testing.T) {
|
|
// Test Argon2id hash
|
|
argonHash, err := HashPassword("argon-password")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
|
|
if !VerifyPassword(argonHash, "argon-password") {
|
|
t.Error("VerifyPassword() should verify Argon2id hash")
|
|
}
|
|
|
|
// Test bcrypt hash
|
|
bcryptHash, err := BcryptHash("bcrypt-password")
|
|
if err != nil {
|
|
t.Fatalf("BcryptHash() error = %v", err)
|
|
}
|
|
|
|
if !VerifyPassword(bcryptHash, "bcrypt-password") {
|
|
t.Error("VerifyPassword() should verify bcrypt hash")
|
|
}
|
|
}
|
|
|
|
func TestHashPassword_Wrapper(t *testing.T) {
|
|
hash, err := HashPassword("test-password")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
|
|
if !strings.HasPrefix(hash, "$argon2id$") {
|
|
t.Errorf("HashPassword() should return argon2id hash, got: %s", hash)
|
|
}
|
|
}
|