fixup
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccountRole_Valid(t *testing.T) {
|
||||
valid := []AccountRole{RoleMember, RoleOwner, RoleAgent, RoleAdmin, ""}
|
||||
invalid := []AccountRole{"superuser", "MEMBER", "member ", "0"}
|
||||
|
||||
for _, role := range valid {
|
||||
if got := role.Valid(); role != "" && !got {
|
||||
t.Errorf("AccountRole(%q).Valid() = false, want true", role)
|
||||
}
|
||||
}
|
||||
for _, role := range invalid {
|
||||
if role.Valid() {
|
||||
t.Errorf("AccountRole(%q).Valid() = true, want false", role)
|
||||
}
|
||||
}
|
||||
if !RoleMember.Valid() {
|
||||
t.Error("RoleMember.Valid() = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_SetPassword(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
wantErr error
|
||||
}{
|
||||
{name: "合法密碼", password: "s3cret!pass"},
|
||||
{name: "剛好 8 字元", password: "12345678"},
|
||||
{name: "太短", password: "1234567", wantErr: ErrPasswordTooShort},
|
||||
{name: "空白", password: "", wantErr: ErrPasswordTooShort},
|
||||
{name: "太長", password: strings.Repeat("a", MaxPasswordLength+1), wantErr: ErrPasswordTooLong},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
acct := &Account{}
|
||||
err := acct.SetPassword(tt.password)
|
||||
if !errors.Is(err, tt.wantErr) {
|
||||
t.Fatalf("SetPassword() error = %v, want %v", err, tt.wantErr)
|
||||
}
|
||||
if tt.wantErr == nil {
|
||||
if acct.PasswordHash == "" {
|
||||
t.Fatal("SetPassword() 後 PasswordHash 為空")
|
||||
}
|
||||
if acct.PasswordHash == tt.password {
|
||||
t.Fatal("PasswordHash 不應儲存明文密碼")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_VerifyPassword(t *testing.T) {
|
||||
acct := &Account{}
|
||||
if err := acct.SetPassword("s3cret!pass"); err != nil {
|
||||
t.Fatalf("SetPassword() error = %v", err)
|
||||
}
|
||||
|
||||
if !acct.VerifyPassword("s3cret!pass") {
|
||||
t.Error("VerifyPassword(正確密碼) = false, want true")
|
||||
}
|
||||
if acct.VerifyPassword("wrong-pass") {
|
||||
t.Error("VerifyPassword(錯誤密碼) = true, want false")
|
||||
}
|
||||
if acct.VerifyPassword("") {
|
||||
t.Error("VerifyPassword(空字串) = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_VerifyPassword_每次雜湊不同(t *testing.T) {
|
||||
a1, a2 := &Account{}, &Account{}
|
||||
if err := a1.SetPassword("same-password"); err != nil {
|
||||
t.Fatalf("a1.SetPassword() error = %v", err)
|
||||
}
|
||||
if err := a2.SetPassword("same-password"); err != nil {
|
||||
t.Fatalf("a2.SetPassword() error = %v", err)
|
||||
}
|
||||
if a1.PasswordHash == a2.PasswordHash {
|
||||
t.Error("相同密碼的兩次雜湊應不同(bcrypt 應加鹽)")
|
||||
}
|
||||
if !a1.VerifyPassword("same-password") || !a2.VerifyPassword("same-password") {
|
||||
t.Error("兩個帳號都應能以原始密碼通過驗證")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_Validate(t *testing.T) {
|
||||
valid := func() *Account {
|
||||
acct := &Account{Email: "dan@example.com", Name: "Dan"}
|
||||
if err := acct.SetPassword("s3cret!pass"); err != nil {
|
||||
t.Fatalf("SetPassword() error = %v", err)
|
||||
}
|
||||
return acct
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(*Account)
|
||||
wantErr error
|
||||
}{
|
||||
{name: "合法帳號", mutate: func(*Account) {}},
|
||||
{
|
||||
name: "缺 email",
|
||||
mutate: func(a *Account) { a.Email = "" },
|
||||
wantErr: ErrEmailRequired,
|
||||
},
|
||||
{
|
||||
name: "email 格式錯誤",
|
||||
mutate: func(a *Account) { a.Email = "not-an-email" },
|
||||
wantErr: ErrEmailInvalid,
|
||||
},
|
||||
{
|
||||
name: "email 過長",
|
||||
mutate: func(a *Account) { a.Email = strings.Repeat("a", 250) + "@example.com" },
|
||||
wantErr: ErrEmailInvalid,
|
||||
},
|
||||
{
|
||||
name: "缺 name",
|
||||
mutate: func(a *Account) { a.Name = "" },
|
||||
wantErr: ErrNameRequired,
|
||||
},
|
||||
{
|
||||
name: "name 過長",
|
||||
mutate: func(a *Account) { a.Name = strings.Repeat("名", 101) },
|
||||
wantErr: ErrNameTooLong,
|
||||
},
|
||||
{
|
||||
name: "role 不合法",
|
||||
mutate: func(a *Account) { a.Role = "hacker" },
|
||||
wantErr: ErrRoleInvalid,
|
||||
},
|
||||
{
|
||||
name: "phone 過長",
|
||||
mutate: func(a *Account) { a.Phone = strings.Repeat("0", 31) },
|
||||
wantErr: ErrPhoneTooLong,
|
||||
},
|
||||
{
|
||||
name: "avatar_url 過長",
|
||||
mutate: func(a *Account) { a.AvatarURL = strings.Repeat("x", 513) },
|
||||
wantErr: ErrAvatarURLTooLong,
|
||||
},
|
||||
{
|
||||
name: "未設定密碼",
|
||||
mutate: func(a *Account) { a.PasswordHash = "" },
|
||||
wantErr: ErrPasswordRequired,
|
||||
},
|
||||
{
|
||||
name: "所有合法角色可通過",
|
||||
mutate: func(a *Account) {
|
||||
a.Role = RoleAdmin
|
||||
a.Phone = "0912345678"
|
||||
a.AvatarURL = "https://example.com/a.png"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
acct := valid()
|
||||
tt.mutate(acct)
|
||||
err := acct.Validate()
|
||||
if !errors.Is(err, tt.wantErr) {
|
||||
t.Fatalf("Validate() error = %v, want %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_Normalize(t *testing.T) {
|
||||
acct := &Account{
|
||||
Email: " Dan@Example.COM ",
|
||||
Name: " 陳大同 ",
|
||||
Phone: " 0912345678 ",
|
||||
AvatarURL: " https://example.com/a.png ",
|
||||
}
|
||||
acct.Normalize()
|
||||
|
||||
if acct.Email != "dan@example.com" {
|
||||
t.Errorf("Email = %q, want %q", acct.Email, "dan@example.com")
|
||||
}
|
||||
if acct.Name != "陳大同" {
|
||||
t.Errorf("Name = %q, want %q", acct.Name, "陳大同")
|
||||
}
|
||||
if acct.Phone != "0912345678" {
|
||||
t.Errorf("Phone = %q, want %q", acct.Phone, "0912345678")
|
||||
}
|
||||
if acct.AvatarURL != "https://example.com/a.png" {
|
||||
t.Errorf("AvatarURL = %q, want %q", acct.AvatarURL, "https://example.com/a.png")
|
||||
}
|
||||
if acct.Role != RoleMember {
|
||||
t.Errorf("空角色應預設為 RoleMember, got %q", acct.Role)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_Normalize_不覆寫已設角色(t *testing.T) {
|
||||
acct := &Account{Role: RoleAgent}
|
||||
acct.Normalize()
|
||||
if acct.Role != RoleAgent {
|
||||
t.Errorf("Role = %q, want %q", acct.Role, RoleAgent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccount_IsAdmin(t *testing.T) {
|
||||
admin := &Account{Role: RoleAdmin}
|
||||
member := &Account{Role: RoleMember}
|
||||
if !admin.IsAdmin() {
|
||||
t.Error("admin.IsAdmin() = false, want true")
|
||||
}
|
||||
if member.IsAdmin() {
|
||||
t.Error("member.IsAdmin() = true, want false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user