package cli import ( "errors" "strings" "testing" "alterminal/internal/auth" ) func TestAccountInputValidate(t *testing.T) { tests := []struct { name string in accountInput wantErr string // 空字串表示應通過 }{ {"最小欄位", accountInput{Username: "alice", Email: "alice@example.com"}, ""}, {"含顯示名稱", accountInput{Username: "alice", Email: "alice@example.com", Name: "Alice"}, ""}, {"role 為 admin", accountInput{Username: "alice", Email: "alice@example.com", Role: auth.RoleAdmin}, ""}, {"role 為 user", accountInput{Username: "alice", Email: "alice@example.com", Role: auth.RoleUser}, ""}, {"role 為空", accountInput{Username: "alice", Email: "alice@example.com"}, ""}, {"role 不允許的值", accountInput{Username: "alice", Email: "alice@example.com", Role: "superuser"}, "role"}, {"role 為 Admin(大寫)", accountInput{Username: "alice", Email: "alice@example.com", Role: "Admin"}, "role"}, {"username 允許的符號", accountInput{Username: "a_li-ce.01", Email: "alice@example.com"}, ""}, {"缺 username", accountInput{Email: "alice@example.com"}, "username"}, {"username 過長", accountInput{Username: strings.Repeat("a", 65), Email: "alice@example.com"}, "64"}, {"username 含空白", accountInput{Username: "alice wang", Email: "alice@example.com"}, "username"}, {"username 含 @", accountInput{Username: "alice@example.com", Email: "alice@example.com"}, "username"}, {"缺 email", accountInput{Username: "alice"}, "email"}, {"email 過長", accountInput{Username: "alice", Email: strings.Repeat("a", 250) + "@example.com"}, "255"}, {"email 格式無效", accountInput{Username: "alice", Email: "example.com"}, "email"}, {"email 帶顯示名稱", accountInput{Username: "alice", Email: "Alice "}, "email"}, {"name 過長", accountInput{Username: "alice", Email: "alice@example.com", Name: strings.Repeat("名", 256)}, "255"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.in.validate() if tt.wantErr == "" { if err != nil { t.Fatalf("validate() = %v, want nil", err) } return } if err == nil || !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("validate() = %v, want error containing %q", err, tt.wantErr) } }) } } func TestResolvePasswordFromFlag(t *testing.T) { got, err := resolvePassword("sup3r-secret") if err != nil { t.Fatalf("resolvePassword() = %v, want nil", err) } if got != "sup3r-secret" { t.Fatalf("resolvePassword() = %q, want %q", got, "sup3r-secret") } } func TestResolvePasswordTooShort(t *testing.T) { _, err := resolvePassword("1234567") if err == nil || !strings.Contains(err.Error(), "8") { t.Fatalf("resolvePassword(\"1234567\") = %v, want 長度錯誤", err) } } // go test 執行時 stdin 不是終端機,省略 -password 應直接報錯而非等待輸入。 func TestResolvePasswordRequiresFlagWithoutTerminal(t *testing.T) { _, err := resolvePassword("") if err == nil || !strings.Contains(err.Error(), "-password") { t.Fatalf("resolvePassword(\"\") = %v, want 提示改用 -password 的錯誤", err) } } func TestPromptPasswordTwice(t *testing.T) { t.Run("兩次一致", func(t *testing.T) { got, err := promptPasswordTwice(func(string) ([]byte, error) { return []byte("sup3r-secret"), nil }) if err != nil { t.Fatalf("promptPasswordTwice() = %v, want nil", err) } if got != "sup3r-secret" { t.Fatalf("promptPasswordTwice() = %q, want %q", got, "sup3r-secret") } }) t.Run("兩次不一致", func(t *testing.T) { inputs := []string{"sup3r-secret", "sup3r-secret2"} calls := 0 _, err := promptPasswordTwice(func(string) ([]byte, error) { b := []byte(inputs[calls]) calls++ return b, nil }) if err == nil || !strings.Contains(err.Error(), "不一致") { t.Fatalf("promptPasswordTwice() = %v, want 不一致錯誤", err) } }) t.Run("讀取失敗", func(t *testing.T) { _, err := promptPasswordTwice(func(string) ([]byte, error) { return nil, errors.New("boom") }) if err == nil { t.Fatal("promptPasswordTwice() = nil, want error") } }) }