Files
rbac/permission_test.go
Rene Nochebuena 18fcd2bee3 feat(rbac)!: promote to v1.0.0 — MaxPermission constant, audit logging policy
Add MaxPermission constant (62) to make the valid bit range explicit in the API.
Document in PermissionProvider that audit logging belongs in the application layer.
API committed as stable: Identity, PermissionMask, context helpers, and
PermissionProvider interface are unchanged from v0.9.0.
2026-05-07 22:46:44 -06:00

112 lines
2.9 KiB
Go

package rbac
import "testing"
// App-level permission constants used only within this test file.
const (
read Permission = 0
write Permission = 1
del Permission = 2
admin Permission = 62 // highest valid bit
)
func TestPermissionMask_Has(t *testing.T) {
tests := []struct {
name string
mask PermissionMask
perm Permission
want bool
}{
{"bit 0 set", 1, read, true},
{"bit 4 set", 16, Permission(4), true},
{"multi-bit, check bit 0", 17, read, true},
{"multi-bit, check bit 4", 17, Permission(4), true},
{"bit not set", 16, read, false},
{"out of range low", 1, Permission(-1), false},
{"out of range high", 1, Permission(63), false},
{"max valid bit (62)", PermissionMask(1 << 62), admin, true},
{"zero mask", 0, read, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.mask.Has(tt.perm); got != tt.want {
t.Errorf("PermissionMask(%d).Has(%d) = %v, want %v", tt.mask, tt.perm, got, tt.want)
}
})
}
}
func TestPermissionMask_Grant(t *testing.T) {
mask := PermissionMask(0)
mask = mask.Grant(read)
if !mask.Has(read) {
t.Error("Grant(read): read not set")
}
if mask.Has(write) {
t.Error("Grant(read): write must not be set")
}
mask = mask.Grant(write)
if !mask.Has(read) {
t.Error("Grant(write): read must still be set")
}
if !mask.Has(write) {
t.Error("Grant(write): write not set")
}
}
func TestPermissionMask_Grant_Chain(t *testing.T) {
mask := PermissionMask(0).Grant(read).Grant(write).Grant(del)
if !mask.Has(read) {
t.Error("chained Grant: read not set")
}
if !mask.Has(write) {
t.Error("chained Grant: write not set")
}
if !mask.Has(del) {
t.Error("chained Grant: del not set")
}
if mask.Has(admin) {
t.Error("chained Grant: admin must not be set")
}
}
func TestPermissionMask_Grant_OutOfRange(t *testing.T) {
mask := PermissionMask(0)
result := mask.Grant(Permission(-1))
if result != mask {
t.Error("Grant with out-of-range permission must return the original mask unchanged")
}
result = mask.Grant(Permission(63))
if result != mask {
t.Error("Grant with out-of-range permission (63) must return the original mask unchanged")
}
}
func TestPermissionMask_Grant_DoesNotMutateReceiver(t *testing.T) {
original := PermissionMask(0)
_ = original.Grant(read)
if original.Has(read) {
t.Error("Grant mutated the receiver; PermissionMask must be a value type")
}
}
func TestMaxPermission(t *testing.T) {
if MaxPermission != 62 {
t.Errorf("MaxPermission = %d, want 62", MaxPermission)
}
// MaxPermission must be usable as a valid bit position.
mask := PermissionMask(0).Grant(MaxPermission)
if !mask.Has(MaxPermission) {
t.Error("MaxPermission bit not set after Grant")
}
// One past the limit must be rejected.
mask2 := PermissionMask(0).Grant(MaxPermission + 1)
if mask2 != 0 {
t.Error("Grant(MaxPermission+1) must return the original mask unchanged")
}
}