- TestTaglistFieldValidate: both modes, trim, dedupe, empty, null, wrong shapes, customTags false, required; TestTaglistFieldHydrate: stored NULL and bad text as [] - TestReorderFlatNodes: parent 422, duplicate and zero id 422, missing or extra 409, no depth or cycle check; TestReorderFlatCompile checks the localized list schema - TestReorderFlatSmoke/partitioned: flat reorder within one partition on Postgres - vitest: script markup as text, Backspace, blur, paste, datalist, customTags, string mode; flat ArrowUp/ArrowDown and the before/after drop zones - export_test.go wrappers for the taglist save check, hydration and node check - check-phase18.1.sh requires the new names in the go, admin and postgres stages
105 lines
3.6 KiB
Go
105 lines
3.6 KiB
Go
package cabana
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
"git.golem15.com/golem15/summercms/modules/phrasebook"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// CompiledListSchema compiles the admin controllers of plugins without a
|
|
// database or config, as Activate does, and returns the list schema of the
|
|
// controller id (with its reorder object).
|
|
func CompiledListSchema(plugins []party.Plugin, id string) (*ListSchema, error) {
|
|
items, err := collectControllers(plugins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
types, err := compileFieldTypes(plugins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reg, err := compileRegistryWithTypes(items, types)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cc, ok := reg.Get(id)
|
|
if !ok || cc.List == nil {
|
|
return nil, fmt.Errorf("cabana: no list schema for %s", id)
|
|
}
|
|
return cc.List, nil
|
|
}
|
|
|
|
// AdminAPIRoutes exposes the admin API route inventory (method plus
|
|
// prefix-relative path) to the external conformance test.
|
|
func AdminAPIRoutes() []string {
|
|
out := make([]string, 0, len(phase09Routes))
|
|
for _, route := range phase09Routes {
|
|
if !route.spa {
|
|
out = append(out, route.key)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// IssueTestAPIToken mints a personal API token for ownerID with the given
|
|
// scopes and optional expiry. Used by external Postgres smoke tests before
|
|
// the HTTP create route exists.
|
|
func IssueTestAPIToken(db *gorm.DB, ownerID uint, scopes []string, expiresAt *time.Time) (plain string, err error) {
|
|
plain, _, err = insertAPIToken(db, ownerID, "test", scopes, expiresAt)
|
|
return plain, err
|
|
}
|
|
|
|
// ValidateMoneyValue exposes the money field's save check (phrase key and
|
|
// placeholders of the first failure, "" when accepted) to the external
|
|
// money field tests.
|
|
func ValidateMoneyValue(value any, scale int, max string) (string, map[string]string) {
|
|
return validateMoneyValue(value, scale, max)
|
|
}
|
|
|
|
// CheckMoneyType exposes the money field's model column gate to the
|
|
// external money field tests.
|
|
func CheckMoneyType(model any, field FormField) error { return checkMoneyType(model, field) }
|
|
|
|
// ProjectRow exposes one list row's projection to the external money
|
|
// column tests.
|
|
func ProjectRow(row any, controller pact.AdminController, cols []ListColumn) map[string]any {
|
|
return projectRow(row, controller, cols)
|
|
}
|
|
|
|
// CheckTaglistValues exposes the taglist save check (every field allowed)
|
|
// to the external taglist tests: it rewrites body in place and returns the
|
|
// *ValidationError of the refused values, or nil.
|
|
func CheckTaglistValues(ctx context.Context, tr *phrasebook.Translator, fields []FormField, body map[string]any, create bool) error {
|
|
return checkTaglistValues(ctx, tr, fields, body, nil, create)
|
|
}
|
|
|
|
// HydrateTaglistFields exposes the record GET's taglist hydration to the
|
|
// external taglist tests.
|
|
func HydrateTaglistFields(fields []FormField, data map[string]any) {
|
|
hydrateTaglistFields(fields, data)
|
|
}
|
|
|
|
// CheckReorderNodes exposes the reorder save's node check to the external
|
|
// reorder tests, over a reorder that is flat or a tree with maxDepth.
|
|
func CheckReorderNodes(flat bool, maxDepth int, locked []AdminReorderNode, nodes []AdminReorderNodeInput, flatParent string) error {
|
|
ro := &compiledReorder{flat: flat, maxDepth: maxDepth}
|
|
if !flat {
|
|
ro.parentColumn = "parent_id"
|
|
}
|
|
ro.sortColumn = "sort_order"
|
|
return checkReorderNodes(ro, locked, nodes, flatParent)
|
|
}
|
|
|
|
// IsReorderStale reports whether err is the reorder save's 409 (the
|
|
// submitted node set differs from the stored one).
|
|
func IsReorderStale(err error) bool {
|
|
var stale reorderStale
|
|
return errors.As(err, &stale)
|
|
}
|