Files
summercms/modules/cabana/relation_test.go
Jakub Zych 5e50b166ef refactor(10.2-01): nest framework packages under modules
- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
2026-09-28 02:21:02 +02:00

190 lines
6.8 KiB
Go

package cabana
import (
"encoding/json"
"strings"
"testing"
"testing/fstest"
)
func TestRelationSchema(t *testing.T) {
ctl := relationTestController{}
fsys := relationTestFS(validRelationYAML)
form, err := CompileForm("acme.demo", ctl, fsys)
if err != nil {
t.Fatal(err)
}
relations, err := compileRelations("acme.demo", ctl, fsys, form)
if err != nil {
t.Fatal(err)
}
compiled := relations["editors"]
if compiled == nil || compiled.Contract.ParentForeignKey != "owner_id" || compiled.Contract.RelatedForeignKey != "target_id" {
t.Fatalf("compiled=%#v", compiled)
}
raw, err := json.Marshal(compiled.Schema)
if err != nil {
t.Fatal(err)
}
got := string(raw)
for _, want := range []string{`"name":"editors"`, `"key":"username"`, `"key":"email"`, `"toolbarButtons":["link","unlink"]`, `"showSearch":true`} {
if !strings.Contains(got, want) {
t.Fatalf("schema missing %s: %s", want, got)
}
}
if strings.Index(got, `"key":"username"`) >= strings.Index(got, `"key":"email"`) {
t.Fatalf("column order drifted: %s", got)
}
for _, tc := range []struct {
name string
yaml string
provider relationTestController
want string
}{
{name: "unknown key", yaml: strings.Replace(validRelationYAML, "showSearch: true", "showSearch: true\n conditions: unsafe", 1), want: "conditions"},
{name: "unsupported action", yaml: strings.Replace(validRelationYAML, "link|unlink", "link|execute", 1), want: "unsupported relation action execute"},
{name: "missing relation", yaml: "other:\n label: other\n view:\n list:\n columns: {}\n manage:\n list:\n columns: {}\n", want: "has no relation-manager field"},
{name: "bad target column", yaml: validRelationYAML, provider: relationTestController{badColumn: true}, want: "target is missing column missing"},
} {
t.Run(tc.name, func(t *testing.T) {
probe := tc.provider
form, err := CompileForm("acme.demo", probe, relationTestFS(tc.yaml))
if err != nil {
t.Fatal(err)
}
_, err = compileRelations("acme.demo", probe, relationTestFS(tc.yaml), form)
if err == nil || !strings.Contains(err.Error(), tc.want) {
t.Fatalf("err=%v want=%q", err, tc.want)
}
})
}
}
func TestRelationEmpty(t *testing.T) {
raw, err := json.Marshal(RelationSchema{Name: "empty"})
if err != nil {
t.Fatal(err)
}
got := string(raw)
if strings.Count(got, `"columns":[]`) != 2 || strings.Count(got, `"toolbarButtons":[]`) != 2 || strings.Contains(got, "null") {
t.Fatalf("empty relation collections=%s", got)
}
}
func TestRelationSingle(t *testing.T) {
schema := RelationSchema{Name: "editors", View: RelationPanel{List: RelationList{Columns: []RelationColumn{{Key: "email", Label: "email", Searchable: true, Sortable: true}}}}}
raw, err := json.Marshal(schema)
if err != nil {
t.Fatal(err)
}
if strings.Count(string(raw), `"key":"email"`) != 1 {
t.Fatalf("single column=%s", raw)
}
}
func TestRelationStablePages(t *testing.T) {
schema := &RelationSchema{View: RelationPanel{List: RelationList{Columns: []RelationColumn{{Key: "username", Sortable: true}, {Key: "email", Sortable: true}}}}}
page, per, key, desc, err := normalizeRelationQuery(schema, false, RelationQuery{Page: "2", PerPage: "20", Sort: "username", Dir: "asc"})
if err != nil || page != 2 || per != 20 || key != "username" || desc {
t.Fatalf("normalized=%d/%d %q desc=%v err=%v", page, per, key, desc, err)
}
if _, _, _, _, err := normalizeRelationQuery(schema, false, RelationQuery{Sort: "Username"}); err == nil {
t.Fatal("case-changed sort survived")
}
}
func TestRelationCandidateExclusions(t *testing.T) {
contract := relationTestController{}.AdminRelationContracts()[0]
ids, err := contract.ExcludedRelatedIDs(&relationTestOwner{OwnerID: 41})
if err != nil || len(ids) != 1 || ids[0] != 41 {
t.Fatalf("excluded=%v err=%v", ids, err)
}
if _, err := contract.ExcludedRelatedIDs(&relationTestTarget{}); err == nil {
t.Fatal("wrong parent type survived")
}
}
const validRelationYAML = `editors:
label: demo.editors
view:
list:
columns:
username:
label: demo.username
email:
label: demo.email
toolbarButtons: link|unlink
showSearch: true
manage:
list:
columns:
username:
label: demo.username
email:
label: demo.email
showSearch: true
`
func relationTestFS(relation string) fstest.MapFS {
return fstest.MapFS{
"controllers/owners/config_form.yaml": {Data: []byte("name: owners\nform: ~/plugins/acme/demo/models/owner/fields.yaml\nmodelClass: Owner\n")},
"controllers/owners/config_relation.yaml": {Data: []byte(relation)},
"models/owner/fields.yaml": {Data: []byte("fields:\n editors:\n type: relation-manager\n relation: editors\n")},
}
}
type relationTestController struct{ badColumn bool }
func (relationTestController) ID() string { return "acme.demo.owners" }
func (relationTestController) ModelName() string { return "Owner" }
func (relationTestController) ConfigDir() string { return "controllers/owners" }
func (relationTestController) NewRecord() any { return &relationTestOwner{} }
func (c relationTestController) AdminRelationContracts() []RelationContract {
columns := map[string]string{"username": "username", "email": "email"}
if c.badColumn {
columns["email"] = "missing"
}
return []RelationContract{{
Name: "editors", NewRelated: func() any { return &relationTestTarget{} }, NewPivot: func() any { return &relationTestPivot{} },
ParentForeignKey: "owner_id", RelatedForeignKey: "target_id", Columns: columns,
HookPivotColumns: []string{"role"},
ExcludedRelatedIDs: func(parent any) ([]uint, error) {
owner, ok := parent.(*relationTestOwner)
if !ok {
return nil, &relationTestError{}
}
return []uint{owner.OwnerID}, nil
},
}}
}
type relationTestOwner struct {
ID uint `gorm:"column:id;primaryKey"`
OwnerID uint `gorm:"column:owner_id"`
Editors []relationTestTarget `gorm:"many2many:relation_test_pivots"`
}
func (*relationTestOwner) TableName() string { return "relation_test_owners" }
type relationTestTarget struct {
ID uint `gorm:"column:id;primaryKey"`
Username string `gorm:"column:username"`
Email string `gorm:"column:email"`
}
func (*relationTestTarget) TableName() string { return "relation_test_targets" }
type relationTestPivot struct {
ID uint `gorm:"column:id;primaryKey"`
OwnerID uint `gorm:"column:owner_id"`
TargetID uint `gorm:"column:target_id"`
Role string `gorm:"column:role"`
}
func (*relationTestPivot) TableName() string { return "relation_test_pivots" }
type relationTestError struct{}
func (*relationTestError) Error() string { return "wrong relation parent" }