- user_groups (seeded with the guest and registered groups) and the users_groups pivot, folded from the PHP v1.1.1 and v2.8.0 migrations into migration 202610020001_create_user_groups - models.UserGroup and a User.Groups relation that is never serialized - classes.UserGroupCodes and classes.HasGroupCode for server-side checks such as the site-admin group code admin - additive only: no route, payload or existing column changes; the user payload still answers "groups":[]
148 lines
4.6 KiB
Go
148 lines
4.6 KiB
Go
package updates
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/sm-user-plugin/classes"
|
|
"git.golem15.com/golem15/sm-user-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/lagoon"
|
|
"github.com/go-gormigrate/gormigrate/v2"
|
|
)
|
|
|
|
func TestUserGroupsMigration(t *testing.T) {
|
|
sqlDB := dedicatedDB(t, "user_groups")
|
|
gdb, err := lagoon.Use(t.Context(), sqlDB)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := gormigrate.New(gdb, &gormigrate.Options{
|
|
TableName: "summer_migrations_golem15_user",
|
|
UseTransaction: true,
|
|
}, All())
|
|
if err := m.Migrate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, table := range []string{"user_groups", "users_groups"} {
|
|
if !gdb.Migrator().HasTable(table) {
|
|
t.Fatalf("%s missing after migrate", table)
|
|
}
|
|
}
|
|
for _, col := range []string{"id", "name", "code", "description", "permissions", "created_at", "updated_at"} {
|
|
if !gdb.Migrator().HasColumn("user_groups", col) {
|
|
t.Fatalf("user_groups.%s missing", col)
|
|
}
|
|
}
|
|
var seeded []models.UserGroup
|
|
if err := gdb.Order("id").Find(&seeded).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(seeded) != 2 || seeded[0].Name != "Guest" || *seeded[0].Code != models.GroupGuest ||
|
|
*seeded[0].Description != "Default group for guest users." ||
|
|
seeded[1].Name != "Registered" || *seeded[1].Code != models.GroupRegistered ||
|
|
*seeded[1].Description != "Default group for registered users." ||
|
|
seeded[0].CreatedAt == nil || seeded[0].Permissions != nil {
|
|
t.Fatalf("seed rows = %+v", seeded)
|
|
}
|
|
var pk string
|
|
if err := gdb.Raw(`SELECT conname FROM pg_constraint WHERE conrelid = 'users_groups'::regclass AND contype = 'p'`).Scan(&pk).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if pk != "user_group" {
|
|
t.Fatalf("users_groups primary key = %q", pk)
|
|
}
|
|
|
|
if err := m.RollbackLast(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if gdb.Migrator().HasTable("user_groups") || gdb.Migrator().HasTable("users_groups") {
|
|
t.Fatal("user groups tables survived rollback")
|
|
}
|
|
if !gdb.Migrator().HasTable("jwt_blacklist") || !gdb.Migrator().HasTable("users") {
|
|
t.Fatal("rollback dropped an earlier table")
|
|
}
|
|
if err := m.Migrate(); err != nil {
|
|
t.Fatalf("re-up: %v", err)
|
|
}
|
|
var n int64
|
|
if err := gdb.Table("user_groups").Count(&n).Error; err != nil || n != 2 {
|
|
t.Fatalf("re-up rows = %d err = %v", n, err)
|
|
}
|
|
}
|
|
|
|
func TestUserGroupsCodesAndRelation(t *testing.T) {
|
|
sqlDB := dedicatedDB(t, "user_groups_codes")
|
|
gdb, err := lagoon.Use(t.Context(), sqlDB)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := gormigrate.New(gdb, &gormigrate.Options{
|
|
TableName: "summer_migrations_golem15_user",
|
|
UseTransaction: true,
|
|
}, All())
|
|
if err := m.Migrate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
admin := "admin"
|
|
adminGroup := models.UserGroup{Name: "Admins", Code: &admin}
|
|
if err := gdb.Create(&adminGroup).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
noCode := models.UserGroup{Name: "Uncoded"}
|
|
if err := gdb.Create(&noCode).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
site := models.User{Email: "site-admin@example.test"}
|
|
plain := models.User{Email: "plain@example.test"}
|
|
for _, u := range []*models.User{&site, &plain} {
|
|
if err := gdb.Omit("Groups").Create(u).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, gid := range []uint{noCode.ID, adminGroup.ID, 2} {
|
|
if err := gdb.Exec(`INSERT INTO users_groups (user_id, user_group_id) VALUES (?, ?)`, site.ID, gid).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
codes, err := classes.UserGroupCodes(t.Context(), gdb, site.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if want := []string{"registered", "admin"}; !reflect.DeepEqual(codes, want) {
|
|
t.Fatalf("codes = %v, want %v", codes, want)
|
|
}
|
|
codes, err = classes.UserGroupCodes(t.Context(), gdb, plain.ID)
|
|
if err != nil || codes == nil || len(codes) != 0 {
|
|
t.Fatalf("plain codes = %#v err = %v", codes, err)
|
|
}
|
|
for _, c := range []struct {
|
|
user uint
|
|
code string
|
|
want bool
|
|
}{{site.ID, "admin", true}, {plain.ID, "admin", false}, {site.ID, "guest", false}} {
|
|
got, err := classes.HasGroupCode(t.Context(), gdb, c.user, c.code)
|
|
if err != nil || got != c.want {
|
|
t.Fatalf("HasGroupCode(%d, %s) = %v, %v", c.user, c.code, got, err)
|
|
}
|
|
}
|
|
|
|
// The relation loads through the pivot and is never serialized.
|
|
var loaded models.User
|
|
if err := gdb.Preload("Groups").Take(&loaded, site.ID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(loaded.Groups) != 3 {
|
|
t.Fatalf("groups = %+v", loaded.Groups)
|
|
}
|
|
raw, err := json.Marshal(loaded)
|
|
if err != nil || strings.Contains(string(raw), "Groups") || strings.Contains(string(raw), "Admins") {
|
|
t.Fatalf("user JSON = %s err = %v", raw, err)
|
|
}
|
|
if _, err := classes.UserGroupCodes(t.Context(), nil, site.ID); err == nil {
|
|
t.Fatal("nil handle must be an error")
|
|
}
|
|
}
|