go-loose/internal/role/role_test.go
2026-10-04 12:24:13 +02:00

135 lines
4.1 KiB
Go

package role
import (
"slices"
"strings"
"testing"
)
func TestParseRejectsUnknownRoles(t *testing.T) {
tests := []struct {
value string
want bool
}{
{value: "owner", want: true},
{value: "admin", want: true},
{value: "developer", want: true},
{value: "operator", want: true},
{value: "viewer", want: true},
{value: "user", want: true},
{value: "Owner", want: false},
{value: "", want: false},
{value: "superuser", want: false},
{value: "owner; DROP TABLE memberships", want: false},
}
for _, test := range tests {
t.Run(test.value, func(t *testing.T) {
_, ok := Parse(test.value)
if ok != test.want {
t.Fatalf("Parse(%q) ok = %v, want %v", test.value, ok, test.want)
}
})
}
}
func TestPermissionMatrix(t *testing.T) {
tests := []struct {
role Role
permission Permission
want bool
}{
{role: Owner, permission: ManageTenants, want: true},
{role: Owner, permission: ManageUsers, want: true},
{role: Admin, permission: ManageTenants, want: false},
{role: Admin, permission: ManageUsers, want: true},
{role: Admin, permission: ManageApplications, want: true},
{role: Developer, permission: ManageUsers, want: false},
{role: Developer, permission: ManageApplications, want: true},
{role: Developer, permission: ManageKeys, want: true},
{role: Developer, permission: ManageContracts, want: true},
{role: Operator, permission: ManageApplications, want: false},
{role: Operator, permission: ManageKeys, want: true},
{role: Operator, permission: ManageContracts, want: true},
{role: Viewer, permission: ManageKeys, want: false},
{role: Viewer, permission: ReadConsole, want: true},
{role: User, permission: ReadConsole, want: false},
{role: User, permission: ReadUsers, want: false},
{role: User, permission: EditProfile, want: true},
}
for _, test := range tests {
t.Run(string(test.role)+"/"+string(test.permission), func(t *testing.T) {
if got := test.role.Allows(test.permission); got != test.want {
t.Fatalf("Allows(%q) = %v, want %v", test.permission, got, test.want)
}
})
}
}
func TestEveryRoleCanEditItsOwnProfile(t *testing.T) {
for _, item := range All() {
if !item.Allows(EditProfile) {
t.Fatalf("role %q cannot edit its own profile", item)
}
}
}
func TestGrantsMatchAllows(t *testing.T) {
for _, permission := range ordered {
granted := Grants(permission)
for _, item := range All() {
if slices.Contains(granted, string(item)) != item.Allows(permission) {
t.Fatalf("Grants(%q) and Allows disagree for role %q", permission, item)
}
}
}
}
func TestGrantsListsRolesInPrivilegeOrder(t *testing.T) {
if got := Grants(ManageKeys); !slices.Equal(got, []string{"owner", "admin", "developer", "operator"}) {
t.Fatalf("Grants(ManageKeys) = %v", got)
}
if got := Grants(ManageUsers); !slices.Equal(got, []string{"owner", "admin"}) {
t.Fatalf("Grants(ManageUsers) = %v", got)
}
if got := Grants(ReadConsole); !slices.Contains(got, string(Owner)) || slices.Contains(got, string(User)) {
t.Fatalf("Grants(ReadConsole) = %v", got)
}
}
func TestCatalogCoversEveryRoleWithALabel(t *testing.T) {
entries := Catalog()
if len(entries) != len(catalog) {
t.Fatalf("Catalog() length = %d, want %d", len(entries), len(catalog))
}
for _, entry := range entries {
if entry.Label == "" {
t.Fatalf("role %q has no label", entry.Value)
}
parsed, ok := Parse(entry.Value)
if !ok {
t.Fatalf("catalog entry %q is not a known role", entry.Value)
}
if len(entry.Permissions) != len(parsed.Permissions()) {
t.Fatalf("role %q catalog permissions drifted from the matrix", entry.Value)
}
}
}
func TestGrantableExcludesOwner(t *testing.T) {
for _, item := range Grantable() {
if item == Owner {
t.Fatal("owner must not be grantable through an invitation")
}
}
if len(GrantableValues()) != len(catalog)-1 {
t.Fatalf("GrantableValues() length = %d", len(GrantableValues()))
}
}
func TestPermissionsUseDottedNames(t *testing.T) {
for _, permission := range All()[0].Permissions() {
if !strings.Contains(permission, ".") {
t.Fatalf("permission %q should be a dotted capability name", permission)
}
}
}