package auth import ( "testing" ) func TestScopeIsValid(t *testing.T) { tests := []struct { name string scope Scope want bool }{ {"projects:read", ScopeProjectsRead, true}, {"projects:execute", ScopeProjectsExecute, true}, {"keys:read", ScopeKeysRead, true}, {"keys:write", ScopeKeysWrite, true}, {"admin", ScopeAdmin, true}, {"invalid", Scope("invalid"), false}, {"empty", Scope(""), false}, {"similar", Scope("projects:write"), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.scope.IsValid(); got != tt.want { t.Errorf("Scope(%q).IsValid() = %v, want %v", tt.scope, got, tt.want) } }) } } func TestScopesFromStrings(t *testing.T) { input := []string{"projects:read", "keys:write"} got := ScopesFromStrings(input) if len(got) != 2 { t.Fatalf("ScopesFromStrings() returned %d scopes, want 2", len(got)) } if got[0] != ScopeProjectsRead { t.Errorf("got[0] = %v, want %v", got[0], ScopeProjectsRead) } if got[1] != ScopeKeysWrite { t.Errorf("got[1] = %v, want %v", got[1], ScopeKeysWrite) } } func TestScopesToStrings(t *testing.T) { input := []Scope{ScopeProjectsRead, ScopeKeysWrite} got := ScopesToStrings(input) if len(got) != 2 { t.Fatalf("ScopesToStrings() returned %d strings, want 2", len(got)) } if got[0] != "projects:read" { t.Errorf("got[0] = %q, want %q", got[0], "projects:read") } if got[1] != "keys:write" { t.Errorf("got[1] = %q, want %q", got[1], "keys:write") } } func TestValidateScopes(t *testing.T) { tests := []struct { name string scopes []Scope want bool }{ {"all valid", []Scope{ScopeProjectsRead, ScopeKeysWrite}, true}, {"single valid", []Scope{ScopeAdmin}, true}, {"empty", []Scope{}, true}, {"one invalid", []Scope{ScopeProjectsRead, Scope("invalid")}, false}, {"all invalid", []Scope{Scope("foo"), Scope("bar")}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := ValidateScopes(tt.scopes); got != tt.want { t.Errorf("ValidateScopes() = %v, want %v", got, tt.want) } }) } } func TestHasScope(t *testing.T) { tests := []struct { name string scopes []Scope required Scope want bool }{ {"has exact scope", []Scope{ScopeProjectsRead, ScopeKeysRead}, ScopeProjectsRead, true}, {"admin grants all", []Scope{ScopeAdmin}, ScopeProjectsRead, true}, {"admin grants keys", []Scope{ScopeAdmin}, ScopeKeysWrite, true}, {"missing scope", []Scope{ScopeProjectsRead}, ScopeKeysWrite, false}, {"empty scopes", []Scope{}, ScopeProjectsRead, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := HasScope(tt.scopes, tt.required); got != tt.want { t.Errorf("HasScope() = %v, want %v", got, tt.want) } }) } } func TestHasAnyScope(t *testing.T) { tests := []struct { name string scopes []Scope required []Scope want bool }{ {"has first", []Scope{ScopeProjectsRead}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true}, {"has second", []Scope{ScopeKeysRead}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true}, {"has neither", []Scope{ScopeKeysWrite}, []Scope{ScopeProjectsRead, ScopeKeysRead}, false}, {"admin grants any", []Scope{ScopeAdmin}, []Scope{ScopeProjectsRead, ScopeKeysRead}, true}, {"empty required", []Scope{ScopeProjectsRead}, []Scope{}, false}, {"empty scopes", []Scope{}, []Scope{ScopeProjectsRead}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := HasAnyScope(tt.scopes, tt.required...); got != tt.want { t.Errorf("HasAnyScope() = %v, want %v", got, tt.want) } }) } } func TestHasProjectAccess(t *testing.T) { tests := []struct { name string allowed []string project string want bool }{ {"nil allows all", nil, "any-project", true}, {"in list", []string{"proj-a", "proj-b"}, "proj-a", true}, {"not in list", []string{"proj-a", "proj-b"}, "proj-c", false}, {"empty list denies", []string{}, "proj-a", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := HasProjectAccess(tt.allowed, tt.project); got != tt.want { t.Errorf("HasProjectAccess() = %v, want %v", got, tt.want) } }) } }