Files
sm-user-plugin/frontend_permission_catalog_test.go
Jakub Zych 72de822a84 test: backend link, frontend permission catalog and MCP guard matrices (phase 19.7 plan 05)
- TestBackendLink: the fresh sign-in rule at the second, 1:1 on the
  frontend side, the show route's three keys, SetLink and RemoveLink events
  (none after a rollback), deactivated or deleted administrators, and
  deactivating the frontend user
- TestFrontendPermissionCatalog: a second plugin's duplicate and an empty
  code dropped; a group saved with a declared code grants it
- TestUserMCPGuard: parser refusals, a soft-deleted user, an expired token,
  and the dispatch credential against the route's own user.scope:read
2026-10-23 14:09:36 +02:00

96 lines
3.8 KiB
Go

package user
import (
"context"
"net/http"
"strings"
"testing"
"git.golem15.com/golem15/sm-user-plugin/classes"
"git.golem15.com/golem15/sm-user-plugin/models"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/party"
)
const acmeShopOrders = "acme.shop.orders"
// acmeShopPermissions declares a code of its own and acme.blog's manage code
// again, under another label: activated after acme.blog, its duplicate is
// dropped.
type acmeShopPermissions struct{}
func (acmeShopPermissions) ID() string { return "acme.shop" }
func (acmeShopPermissions) Requires() []string { return nil }
func (acmeShopPermissions) Register(*backpack.App) error { return nil }
func (acmeShopPermissions) Boot(*backpack.App) error { return nil }
func (acmeShopPermissions) FrontendPermissions() []pact.FrontendPermission {
return []pact.FrontendPermission{
{Code: acmeBlogManage, Label: "Shop's copy", Tab: "Shop"},
{Code: acmeShopOrders, Label: "Manage orders", Tab: "Shop"},
{Code: " ", Label: "Empty code", Tab: "Shop"},
}
}
func init() { party.Register(acmeShopPermissions{}) }
// TestFrontendPermissionCatalog (D-09) extends the smoke: with two declaring
// plugins the editors offer the seeded rows, then the first plugin's codes,
// then the second's, a code the first already declared and an empty code
// left out; a group saved with a declared code keeps it, and the member's
// grants (what user.permission checks) include it. Skips under -short.
func TestFrontendPermissionCatalog(t *testing.T) {
env := newAdminEnvWith(t, []string{"acme.blog", "acme.shop"})
if err := env.gdb.Exec(`INSERT INTO golem15_user_frontend_permissions (code, label, tab) VALUES (?, 'Seeded label', 'Blog') ON CONFLICT (code) DO NOTHING`, acmeBlogSeeded).Error; err != nil {
t.Fatal(err)
}
var seeded int64
if err := env.gdb.Raw(`SELECT COUNT(*) FROM golem15_user_frontend_permissions`).Scan(&seeded).Error; err != nil {
t.Fatal(err)
}
token := env.admin(t, permAccessUsers, permAccessGroups)
t.Run("second plugin's duplicate dropped", func(t *testing.T) {
for _, path := range []string{adminUsersPath, adminGroupsPath} {
options := env.permissionOptions(t, token, path)
declared := options[seeded:]
var codes []string
for _, o := range declared {
codes = append(codes, o.Code)
}
if len(declared) != 2 || declared[len(declared)-2].Code != acmeBlogManage || declared[len(declared)-2].Label != "Manage blog posts" ||
declared[len(declared)-1] != (offeredPermission{Code: acmeShopOrders, Label: "Manage orders", Tab: "Shop"}) {
t.Fatalf("%s declared options = %+v", path, declared)
}
seen := map[string]int{}
for _, o := range options {
seen[o.Code]++
if o.Code == "" || o.Code == " " {
t.Fatalf("%s offers an empty code", path)
}
}
if seen[acmeBlogManage] != 1 || seen[acmeShopOrders] != 1 {
t.Fatalf("%s codes offered %v (declared %v)", path, seen, codes)
}
}
})
t.Run("group saved with a declared code grants it", func(t *testing.T) {
stamp := adminStamp()
group := env.seedGroup(t, "Orders "+stamp, "orders-"+stamp)
member := env.seedUser(t, "orders", nil)
env.join(t, member, group)
env.expect(t, http.StatusOK, http.MethodPut, groupPath(group.ID), token, map[string]any{
"name": group.Name, "code": *group.Code, "permissions": map[string]int{acmeShopOrders: 1},
})
var saved models.UserGroup
if err := env.gdb.First(&saved, group.ID).Error; err != nil || saved.Permissions == nil || !strings.Contains(*saved.Permissions, `"`+acmeShopOrders+`"`) {
t.Fatalf("saved permissions = %v, %v", saved.Permissions, err)
}
grants, err := classes.UserPermissionGrants(context.Background(), env.gdb, member.ID)
if err != nil || !grants[acmeShopOrders] {
t.Fatalf("member grants = %v, %v", grants, err)
}
})
}