Files
MujicaUI/account/api_key_manager_test.go
T

202 lines
5.6 KiB
Go

package account
import (
"fmt"
"strings"
"testing"
"time"
"github.com/egoist/mygo/ui"
)
var apiKeys = []APIKey{{ID: "k1", Name: "Deploy bot", Prefix: "mk_live_9f2c", Created: time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC)}}
func TestApiKeyManagerCreateRevealOnceAndRevoke(t *testing.T) {
logs := accountLogs(t)
const secret = "mk_live_9f2c_SECRET_DO_NOT_LOG"
var s ApiKeyManagerState
var created, revoked []string
tt := accountTester(700, 600, func(c *ui.Context) {
r := ApiKeyManager(c, &s, apiKeys, ApiKeyManagerOptions{})
if name, ok := r.Create(); ok {
created = append(created, name)
}
if id, ok := r.Revoked(); ok {
revoked = append(revoked, id)
}
})
if !tt.HasText("mk_live_9f2c••••••••••••") || !tt.HasText("Created 2026-09-01 · last used never") {
t.Fatalf("masked list: %q", tt.Texts())
}
_ = tt.Click("Create key")
tt.Type("CI")
tt.Key(0, ui.KeyEnter)
tt.Key(0, ui.KeyEnter)
if len(created) != 1 || created[0] != "CI" {
t.Fatalf("created %v", created)
}
s.Reveal(secret)
tt.Frame()
if s.secret != secret || !tt.HasText("Copy key") {
t.Fatal("new key not shown")
}
if err := tt.Click("Copy key"); err != nil {
t.Fatal(err)
}
if tt.Clipboard() != secret {
t.Fatal("copy did not take the key")
}
_ = tt.Click("I've saved it")
if tt.HasText("Copy key") || s.secret != "" {
t.Fatal("key still shown or kept after dismissal")
}
if strings.Contains(logs.String(), secret) {
t.Fatal("the key reached the log")
}
_ = tt.Click("Revoke Deploy bot")
if !tt.Focused("Cancel") {
t.Fatal("revoke confirmation not focused on Cancel")
}
tt.Key(0, ui.KeyTab)
tt.Key(0, ui.KeyEnter)
if len(revoked) != 1 || revoked[0] != "k1" {
t.Fatalf("revoked %v", revoked)
}
}
func TestApiKeyManagerFailAndDisabled(t *testing.T) {
var s ApiKeyManagerState
disabled, made := false, 0
tt := accountTester(700, 600, func(c *ui.Context) {
if _, ok := ApiKeyManager(c, &s, nil, ApiKeyManagerOptions{Disabled: disabled}).Create(); ok {
made++
}
})
if !tt.HasText("No API keys") {
t.Fatal("no empty state")
}
_ = tt.Click("Create key")
tt.Type("x")
tt.Key(0, ui.KeyEnter)
tt.Key(0, ui.KeyEscape)
if made != 1 || !s.pending {
t.Fatal("escape left a pending create")
}
s.Fail("Key limit reached.")
tt.Frame()
if !tt.HasText("Key limit reached.") {
t.Fatalf("failure: %q", tt.Texts())
}
tt.Key(0, ui.KeyEscape)
disabled = true
tt.Frame()
_ = tt.Click("Create key")
if tt.HasText("New API key") {
t.Fatal("disabled manager opened the dialog")
}
}
func TestApiKeyManagerSnapshot(t *testing.T) {
snapshot(t, "api_key_manager", 560, 220, func(c *ui.Context) {
s := ui.Local(c.Root(), "s", func() ApiKeyManagerState { return ApiKeyManagerState{} })
ui.Column(c).Padding(16).Children(func() { ApiKeyManager(c, s, apiKeys, ApiKeyManagerOptions{}) })
})
}
func TestApiKeyManagerDisablingClosesEveryDialog(t *testing.T) {
for _, phase := range []string{"name", "pending", "secret", "revoke"} {
t.Run(phase, func(t *testing.T) {
var s ApiKeyManagerState
disabled := false
tt := accountTester(700, 600, func(c *ui.Context) {
ui.Column(c).Disabled(disabled).Children(func() { ApiKeyManager(c, &s, apiKeys, ApiKeyManagerOptions{}) })
})
if phase == "revoke" {
if err := tt.Click("Revoke Deploy bot"); err != nil {
t.Fatal(err)
}
} else {
if err := tt.Click("Create key"); err != nil {
t.Fatal(err)
}
if phase != "name" {
tt.Type("CI")
tt.Key(0, ui.KeyEnter)
}
if phase == "secret" {
s.Reveal("one-time-test-secret")
tt.Frame()
}
}
disabled = true
tt.Frame()
if tt.HasText("New API key") || tt.HasText("Copy your new key") || tt.HasText("Revoke Deploy bot?") || s.secret != "" {
t.Fatal("disabled manager retains an active dialog or secret")
}
})
}
}
func TestApiKeyManagerRemovedKeyCancelsConfirmation(t *testing.T) {
var s ApiKeyManagerState
keys := apiKeys
tt := accountTester(700, 600, func(c *ui.Context) { ApiKeyManager(c, &s, keys, ApiKeyManagerOptions{}) })
if err := tt.Click("Revoke Deploy bot"); err != nil {
t.Fatal(err)
}
keys = nil
tt.Frame()
if s.revoking != "" || tt.HasText("Apps using this key stop working at once.") {
t.Fatal("removed key keeps confirmation open")
}
}
func TestApiKeyManagerLargeDataBuildsVisibleRows(t *testing.T) {
items := make([]APIKey, 100000)
for i := range items {
items[i] = APIKey{ID: fmt.Sprint(i), Name: fmt.Sprintf("Key %d", i), Prefix: "public"}
}
var s ApiKeyManagerState
tt := accountTester(600, 500, func(c *ui.Context) { ApiKeyManager(c, &s, items, ApiKeyManagerOptions{}) })
if n := len(tt.Texts()); n > 100 {
t.Fatalf("built %d text elements for a bounded viewport", n)
}
}
func TestApiKeyManagerFailureSurvivesDisabledRequest(t *testing.T) {
for _, whileDisabled := range []bool{false, true} {
t.Run(fmt.Sprint(whileDisabled), func(t *testing.T) {
var s ApiKeyManagerState
disabled, requests := false, 0
tt := accountTester(700, 600, func(c *ui.Context) {
if _, ok := ApiKeyManager(c, &s, nil, ApiKeyManagerOptions{Disabled: disabled}).Create(); ok {
requests++
}
})
if err := tt.Click("Create key"); err != nil {
t.Fatal(err)
}
tt.Type("CI")
tt.Key(0, ui.KeyEnter)
disabled = true
tt.Frame()
if whileDisabled {
s.Fail("Create failed")
tt.Frame()
}
disabled = false
if !whileDisabled {
s.Fail("Create failed")
}
tt.Frame()
if !tt.HasText("Create failed") || s.name != "CI" || s.pending {
t.Fatal("disabled request lost its failure or draft")
}
tt.Key(0, ui.KeyEnter)
if requests != 2 || !s.pending {
t.Fatal("failed request cannot be retried")
}
})
}
}