Files
pipu-music/services/api/internal/auth/policy_test.go
T
mrleo1nidandClaude Opus 5.5 978f03b8f8
ci / api / api (push) Successful in 40s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 55s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m10s
ci / db / db (push) Successful in 2m35s
ci / ts / ts (packages + web) (push) Successful in 1m23s
feat(admin): reset a user's recommendations from the users panel
- POST /admin/users/{id}/reset-recommendations clears the taste profile
  (onboarding, CF vector, bandit), play stats and wave impressions; likes,
  dislikes and history stay, wave sessions stay so a playing wave keeps going
- moderators may reset plain users, admins anyone including themselves;
  the reset is written to the audit log
- users panel gets the menu action with a confirmation; docs updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:52:31 +03:00

93 lines
3.4 KiB
Go

package auth
import (
"fmt"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestCan(t *testing.T) {
// Строки матрицы прав из docs/auth-and-roles.md: user, moderator, admin.
matrix := []struct {
perm Permission
name string
want [3]bool
}{
{PermAdminPanel, "admin panel", [3]bool{false, true, true}},
{PermListUsers, "list users", [3]bool{false, true, true}},
{PermManageLibrary, "manage library", [3]bool{false, false, true}},
{PermViewLibraryStatus, "library status", [3]bool{false, true, true}},
{PermSuggestTracks, "suggest tracks", [3]bool{false, true, true}},
{PermReviewUploads, "review uploads", [3]bool{false, false, true}},
{PermSystemStatus, "system status", [3]bool{false, false, true}},
}
roles := [3]Role{RoleUser, RoleModerator, RoleAdmin}
for _, row := range matrix {
for i, r := range roles {
t.Run(fmt.Sprintf("%s/%s", row.name, r), func(t *testing.T) {
assert.Equal(t, row.want[i], Can(r, row.perm))
})
}
}
assert.False(t, Can(Role("guest"), PermAdminPanel))
}
func TestCanManageUser(t *testing.T) {
actions := map[UserAction]string{
UserCreate: "create", UserEdit: "edit", UserResetPassword: "reset", UserBlock: "block",
UserChangeRole: "role", UserDelete: "delete", UserResetRecommendations: "recs",
}
// want[actor][target] для каждого действия; порядок ролей — user, moderator, admin.
cases := map[UserAction][3][3]bool{
UserCreate: {{false, false, false}, {true, false, false}, {true, true, true}},
UserEdit: {{false, false, false}, {true, false, false}, {true, true, true}},
UserResetPassword: {{false, false, false}, {true, false, false}, {true, true, true}},
UserBlock: {{false, false, false}, {true, false, false}, {true, true, true}},
UserChangeRole: {{false, false, false}, {false, false, false}, {true, true, true}},
UserDelete: {{false, false, false}, {false, false, false}, {true, true, true}},
UserResetRecommendations: {{false, false, false}, {true, false, false}, {true, true, true}},
}
roles := [3]Role{RoleUser, RoleModerator, RoleAdmin}
for action, want := range cases {
for ai, ar := range roles {
for ti, tr := range roles {
t.Run(fmt.Sprintf("%s/%s->%s", actions[action], ar, tr), func(t *testing.T) {
actor := Actor{ID: uuid.New(), Role: ar}
target := Actor{ID: uuid.New(), Role: tr}
assert.Equal(t, want[ai][ti], CanManageUser(actor, target, action))
})
}
}
}
}
func TestCanManageUserSelf(t *testing.T) {
admin := Actor{ID: uuid.New(), Role: RoleAdmin}
for _, a := range []UserAction{UserBlock, UserDelete, UserChangeRole} {
assert.False(t, CanManageUser(admin, admin, a), "action %d on self", a)
}
assert.True(t, CanManageUser(admin, admin, UserEdit))
assert.True(t, CanManageUser(admin, admin, UserResetPassword))
}
func TestCanAssignRole(t *testing.T) {
cases := []struct {
actor, role Role
want bool
}{
{RoleAdmin, RoleAdmin, true},
{RoleAdmin, RoleModerator, true},
{RoleAdmin, RoleUser, true},
{RoleAdmin, Role("root"), false},
{RoleModerator, RoleUser, true},
{RoleModerator, RoleModerator, false},
{RoleModerator, RoleAdmin, false},
{RoleUser, RoleUser, false},
}
for _, tc := range cases {
assert.Equal(t, tc.want, CanAssignRole(tc.actor, tc.role), "%s assigns %s", tc.actor, tc.role)
}
}