xfy fc71cf4835 refactor(test): 统一测试文件错误处理风格
使用空白标识符忽略测试辅助函数中 Close、ReadFrom、Set 等返回值,
与主代码风格保持一致。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-03 17:37:05 +08:00

400 lines
8.5 KiB
Go

package security
import (
"testing"
"github.com/valyala/fasthttp"
"golang.org/x/crypto/bcrypt"
"rua.plus/lolly/internal/config"
)
func TestNewBasicAuth(t *testing.T) {
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)
tests := []struct {
name string
cfg *config.AuthConfig
wantErr bool
}{
{
name: "nil config",
cfg: nil,
wantErr: true,
},
{
name: "invalid type",
cfg: &config.AuthConfig{
Type: "digest",
},
wantErr: true,
},
{
name: "no users",
cfg: &config.AuthConfig{
Type: "basic",
},
wantErr: true,
},
{
name: "empty username",
cfg: &config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "", Password: string(hashedPassword)},
},
},
wantErr: true,
},
{
name: "empty password",
cfg: &config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: ""},
},
},
wantErr: true,
},
{
name: "valid config",
cfg: &config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
},
},
{
name: "valid with bcrypt",
cfg: &config.AuthConfig{
Type: "basic",
Algorithm: "bcrypt",
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
},
},
{
name: "valid with argon2id format",
cfg: &config.AuthConfig{
Type: "basic",
Algorithm: "argon2id",
Users: []config.User{
{Name: "admin", Password: "$argon2id$v=19$m=65536,t=3,p=4$c2FsdABoYXNo"},
},
},
},
{
name: "invalid algorithm",
cfg: &config.AuthConfig{
Type: "basic",
Algorithm: "md5",
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
auth, err := NewBasicAuth(tt.cfg)
if (err != nil) != tt.wantErr {
t.Errorf("NewBasicAuth() error = %v, wantErr %v", err, tt.wantErr)
}
if !tt.wantErr && auth == nil {
t.Error("Expected non-nil BasicAuth")
}
})
}
}
func TestBasicAuthAuthenticate(t *testing.T) {
password := "testpassword"
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
tests := []struct {
name string
username string
password string
expected bool
}{
{
name: "valid credentials",
username: "admin",
password: password,
expected: true,
},
{
name: "wrong password",
username: "admin",
password: "wrongpassword",
expected: false,
},
{
name: "unknown user",
username: "unknown",
password: password,
expected: false,
},
{
name: "empty username",
username: "",
password: password,
expected: false,
},
{
name: "empty password",
username: "admin",
password: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := auth.Authenticate(tt.username, tt.password)
if result != tt.expected {
t.Errorf("Authenticate(%s, ***) = %v, expected %v", tt.username, result, tt.expected)
}
})
}
}
func TestBasicAuthProcess(t *testing.T) {
password := "testpassword"
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
RequireTLS: false, // Disable TLS for testing
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
Realm: "Test Realm",
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
nextHandler := func(ctx *fasthttp.RequestCtx) {
_, _ = ctx.WriteString("OK")
}
handler := auth.Process(nextHandler)
if handler == nil {
t.Error("Process() returned nil handler")
}
}
func TestBasicAuthAddUser(t *testing.T) {
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: "$2b$12$existinghash"},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
// Test adding user
err = auth.AddUser("newuser", "$2b$12$newhash")
if err != nil {
t.Errorf("AddUser() error: %v", err)
}
if !auth.HasUser("newuser") {
t.Error("Expected newuser to exist")
}
// Test empty username
err = auth.AddUser("", "$2b$12$hash")
if err == nil {
t.Error("Expected error for empty username")
}
// Test invalid hash format
err = auth.AddUser("user2", "invalidhash")
if err == nil {
t.Error("Expected error for invalid hash")
}
}
func TestBasicAuthRemoveUser(t *testing.T) {
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: "$2b$12$hash"},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
// Remove existing user
auth.RemoveUser("admin")
if auth.HasUser("admin") {
t.Error("Expected admin to be removed")
}
// Remove non-existent user (should not error)
auth.RemoveUser("nonexistent")
}
func TestBasicAuthUserCount(t *testing.T) {
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "user1", Password: "$2b$12$hash1"},
{Name: "user2", Password: "$2b$12$hash2"},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
if count := auth.UserCount(); count != 2 {
t.Errorf("Expected UserCount 2, got %d", count)
}
_ = auth.AddUser("user3", "$2b$12$hash3")
if count := auth.UserCount(); count != 3 {
t.Errorf("Expected UserCount 3, got %d", count)
}
auth.RemoveUser("user1")
if count := auth.UserCount(); count != 2 {
t.Errorf("Expected UserCount 2, got %d", count)
}
}
func TestHashPasswordBcrypt(t *testing.T) {
password := "testpassword"
hash, err := HashPasswordBcrypt(password, bcrypt.DefaultCost)
if err != nil {
t.Fatalf("HashPasswordBcrypt() error: %v", err)
}
if hash == "" {
t.Error("Expected non-empty hash")
}
// Verify the hash works
err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
if err != nil {
t.Errorf("Hash verification failed: %v", err)
}
}
func TestValidatePasswordHash(t *testing.T) {
tests := []struct {
name string
hash string
algorithm HashAlgorithm
wantErr bool
}{
{
name: "valid bcrypt",
hash: "$2b$12$hash",
algorithm: HashBcrypt,
},
{
name: "invalid bcrypt format",
hash: "nothere",
algorithm: HashBcrypt,
wantErr: true,
},
{
name: "valid argon2id",
hash: "$argon2id$v=19$m=65536,t=3,p=4$salt$hash",
algorithm: HashArgon2id,
},
{
name: "invalid argon2id format",
hash: "$bcrypt$hash",
algorithm: HashArgon2id,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validatePasswordHash(tt.hash, tt.algorithm)
if (err != nil) != tt.wantErr {
t.Errorf("validatePasswordHash() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestExtractCredentials(t *testing.T) {
password := "testpassword"
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
RequireTLS: false,
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
// Create a mock request context
ctx := &fasthttp.RequestCtx{}
// Test without Authorization header
_, _, ok := auth.extractCredentials(ctx)
if ok {
t.Error("Expected no credentials without header")
}
// Test with valid Basic auth header
ctx.Request.Header.Set("Authorization", "Basic YWRtaW46dGVzdHBhc3N3b3Jk")
username, pwd, ok := auth.extractCredentials(ctx)
if !ok {
t.Error("Expected credentials to be extracted")
}
if username != "admin" {
t.Errorf("Expected username 'admin', got %s", username)
}
if pwd != "testpassword" {
t.Errorf("Expected password 'testpassword', got %s", pwd)
}
}
func TestName(t *testing.T) {
password := "test"
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
auth, err := NewBasicAuth(&config.AuthConfig{
Type: "basic",
Users: []config.User{
{Name: "admin", Password: string(hashedPassword)},
},
})
if err != nil {
t.Fatalf("NewBasicAuth() error: %v", err)
}
if auth.Name() != "basic_auth" {
t.Errorf("Expected name 'basic_auth', got %s", auth.Name())
}
}