package auth import ( "errors" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" ) func newTestAPIKey() *APIKey { return NewAPIKey(config.AuthConfig{}) } func TestAPIKey_GenerateVerify(t *testing.T) { t.Parallel() k := newTestAPIKey() key, err := k.Generate() if err != nil { t.Fatalf("Generate error: %v", err) } if key == "" { t.Fatal("Generate returned empty key") } if !k.Verify(key) { t.Errorf("Verify(%q) = false, want true", key) } // 裁决 15.4.3:任何有效 key 都是超管级,不按 role 分档。 role, ok := k.Lookup(key) if !ok || role != RoleOwner { t.Errorf("Lookup(%q) = (%q,%v), want (owner,true)", key, role, ok) } } // TestAPIKey_GenerateIgnoresLegacyRole 钉住「key 不做角色分级」这条裁决的 // 可观察后果:即使调用方沿旧签名传了 admin/viewer,签出来的 key 仍然是 // 超管级,且表里不留任何角色维度。 func TestAPIKey_GenerateIgnoresLegacyRole(t *testing.T) { t.Parallel() k := newTestAPIKey() for _, want := range []Role{RoleAdmin, RoleViewer} { key, err := k.Generate(want) if err != nil { t.Fatalf("Generate(%q) error: %v", want, err) } role, ok := k.Lookup(key) if !ok || role != RoleOwner { t.Fatalf("Generate(%q) → Lookup = (%q,%v), want (owner,true)", want, role, ok) } } // 列表也一律如实报 owner,面板据此没有分级可显示。 for _, s := range k.List() { if s.Role != RoleOwner { t.Fatalf("List() entry role = %q, want owner", s.Role) } } } func TestAPIKey_GenerateUnique(t *testing.T) { t.Parallel() k := newTestAPIKey() a, err := k.Generate() if err != nil { t.Fatalf("Generate error: %v", err) } b, err := k.Generate() if err != nil { t.Fatalf("Generate error: %v", err) } if a == b { t.Fatal("two keys are identical, random generation ineffective") } } func TestAPIKey_GenerateInvalidRole(t *testing.T) { t.Parallel() k := newTestAPIKey() if _, err := k.Generate(Role("god")); !errors.Is(err, ErrInvalidRole) { t.Fatalf("Generate invalid role err = %v, want ErrInvalidRole", err) } } func TestAPIKey_VerifyUnknown(t *testing.T) { t.Parallel() k := newTestAPIKey() if k.Verify("not-a-key") { t.Error("Verify('not-a-key') = true, want false") } if k.Verify("") { t.Error("Verify('') = true, want false") } } func TestAPIKey_Revoke(t *testing.T) { t.Parallel() k := newTestAPIKey() key, err := k.Generate() if err != nil { t.Fatalf("Generate error: %v", err) } if !k.Verify(key) { t.Fatalf("Verify before revoke = false, want true") } k.Revoke(key) if k.Verify(key) { t.Error("Verify after revoke = true, want false") } } func TestAPIKey_RevokeAll(t *testing.T) { t.Parallel() k := newTestAPIKey() keys := make([]string, 0, 3) for range 3 { key, err := k.Generate() if err != nil { t.Fatalf("Generate error: %v", err) } keys = append(keys, key) } if got := k.RevokeAll(); got != 3 { t.Fatalf("RevokeAll() = %d, want 3", got) } for _, key := range keys { if k.Verify(key) { t.Errorf("Verify(%q) after RevokeAll = true, want false", key) } } // An empty table reports zero and is a safe no-op. if got := k.RevokeAll(); got != 0 { t.Fatalf("RevokeAll() on empty table = %d, want 0", got) } }