- TestURLTo (every Url::to golden case, BackendURL, URLPath), TestXMLEscaping (PHP XmlEscapingTest as behaviour), TestTiers (each default rule, first match, off, bad pattern fails Boot), TestRewrite, TestHooks, TestURLCap, TestManifest (fail-safe reads, key normalization, dates), TestFillable (PHP MassAssignmentTest), TestDefinitionItemJSON, TestBackendURL (trusted forwarded proto) - TestImportWinter on Postgres: maps and drops with report, --theme, --dry-run, --force, settings upsert, refusals
146 lines
6.2 KiB
Go
146 lines
6.2 KiB
Go
package models_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/lagoon"
|
|
)
|
|
|
|
// TestFillable ports the PHP MassAssignmentTest (SITE-001) as behaviour:
|
|
// Definition.Fillable is exactly theme and data, and a fill with extra keys
|
|
// (id, timestamps, unknown keys) writes only those two.
|
|
func TestFillable(t *testing.T) {
|
|
if got := (models.Definition{}).Fillable(); !slices.Equal(got, []string{"theme", "data"}) {
|
|
t.Fatalf("Fillable = %v, want [theme data]", got)
|
|
}
|
|
def := models.Definition{ID: 4}
|
|
requested := map[string]any{
|
|
"id": 99,
|
|
"theme": "site",
|
|
"data": `[{"type":"url","url":"/a"}]`,
|
|
"created_at": "2020-01-01 00:00:00",
|
|
"updated_at": "2020-01-01 00:00:00",
|
|
"is_admin": true,
|
|
}
|
|
for _, production := range []bool{true, false} {
|
|
d := def
|
|
if err := lagoon.Fill(&d, d.Fillable(), requested, production); err != nil {
|
|
t.Fatalf("Fill: %v", err)
|
|
}
|
|
if d.ID != 4 || d.CreatedAt != nil || d.UpdatedAt != nil || models.Text(d.Theme) != "site" {
|
|
t.Fatalf("filled definition = %+v", d)
|
|
}
|
|
if items := d.Items(); len(items) != 1 || models.Text(items[0].URL) != "/a" {
|
|
t.Fatalf("filled data = %+v", items)
|
|
}
|
|
}
|
|
if (models.Definition{}).TableName() != "golem15_sitemap_definitions" || (models.Definition{}).MorphName() != `Golem15\Sitemap\Models\Definition` {
|
|
t.Fatal("table or morph name changed")
|
|
}
|
|
if rules := (models.Definition{}).Rules(); len(rules) != 0 {
|
|
t.Fatalf("Rules = %v", rules)
|
|
}
|
|
if got := (models.Settings{}).Fillable(); slices.Contains(got, "id") || len(got) != 9 {
|
|
t.Fatalf("Settings.Fillable = %v", got)
|
|
}
|
|
}
|
|
|
|
// TestDefinitionItemJSON pins the tolerant decoding of PHP-stored items
|
|
// (strings, numbers, booleans and null for text fields, PHP truthiness for
|
|
// nesting, unknown keys ignored, a missing type is url, a null item a
|
|
// default url item, an object-keyed list in key order) and the PHP toArray
|
|
// encoding (key set and order, cmsPage always null, null text kept).
|
|
func TestDefinitionItemJSON(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
raw string
|
|
want string
|
|
}{
|
|
{`{"type":"url","url":"/a","priority":"0.5","changefreq":"daily","nesting":"0","reference":null,"urlPattern":null,"cmsPage":"x","code":"c","exists":false,"fillable":["a"]}`,
|
|
`type=url url=/a ref=<nil> pattern=<nil> nesting=false priority=0.5 changefreq=daily`},
|
|
{`{"url":"/b"}`, `type=url url=/b ref=<nil> pattern=<nil> nesting=false priority=<nil> changefreq=<nil>`},
|
|
{`{"type":null}`, `type= url=<nil> ref=<nil> pattern=<nil> nesting=false priority=<nil> changefreq=<nil>`},
|
|
{`{"type":"journal-post","reference":7,"priority":0.5,"changefreq":true,"nesting":1}`,
|
|
`type=journal-post url=<nil> ref=7 pattern=<nil> nesting=true priority=0.5 changefreq=1`},
|
|
{`{"type":"x","priority":1.0,"reference":12.50,"changefreq":false,"nesting":"1"}`,
|
|
`type=x url=<nil> ref=12.5 pattern=<nil> nesting=true priority=1 changefreq=`},
|
|
{`{"type":"x","nesting":"false"}`, `type=x url=<nil> ref=<nil> pattern=<nil> nesting=true priority=<nil> changefreq=<nil>`},
|
|
{`{"type":"x","nesting":""}`, `type=x url=<nil> ref=<nil> pattern=<nil> nesting=false priority=<nil> changefreq=<nil>`},
|
|
{`{"type":"x","nesting":null,"urlPattern":"/p/:slug"}`, `type=x url=<nil> ref=<nil> pattern=/p/:slug nesting=false priority=<nil> changefreq=<nil>`},
|
|
{`{"type":"x","reference":12345678901234}`, `type=x url=<nil> ref=12345678901234 pattern=<nil> nesting=false priority=<nil> changefreq=<nil>`},
|
|
{`null`, `type=url url=<nil> ref=<nil> pattern=<nil> nesting=false priority=<nil> changefreq=<nil>`},
|
|
} {
|
|
var item models.DefinitionItem
|
|
if err := json.Unmarshal([]byte(tc.raw), &item); err != nil {
|
|
t.Fatalf("%s: %v", tc.raw, err)
|
|
}
|
|
if got := describe(item); got != tc.want {
|
|
t.Errorf("%s\n got %s\nwant %s", tc.raw, got, tc.want)
|
|
}
|
|
}
|
|
for _, raw := range []string{`{"type":["x"]}`, `{"url":{"a":1}}`, `{"nesting":[1]}`, `[1]`, `"x"`} {
|
|
var item models.DefinitionItem
|
|
if err := json.Unmarshal([]byte(raw), &item); err == nil {
|
|
t.Errorf("%s decoded without an error", raw)
|
|
}
|
|
}
|
|
|
|
// A list, an object keyed like a PHP array with gaps, null and empty.
|
|
var list models.DefinitionItems
|
|
if err := json.Unmarshal([]byte(`{"3":{"url":"/c"},"1":{"url":"/a"},"x":{"url":"/x"}}`), &list); err != nil || len(list) != 3 ||
|
|
models.Text(list[0].URL) != "/c" || models.Text(list[1].URL) != "/a" || models.Text(list[2].URL) != "/x" {
|
|
t.Fatalf("object list = %+v %v", list, err)
|
|
}
|
|
for raw, n := range map[string]int{`[]`: 0, `null`: 0, `[{"url":"/a"},null]`: 2} {
|
|
var l models.DefinitionItems
|
|
if err := json.Unmarshal([]byte(raw), &l); err != nil || len(l) != n {
|
|
t.Fatalf("%s = %+v %v", raw, l, err)
|
|
}
|
|
}
|
|
var bad models.DefinitionItems
|
|
if err := json.Unmarshal([]byte(`"items"`), &bad); err == nil {
|
|
t.Fatal("a string decoded as an item list")
|
|
}
|
|
|
|
// Encoding: the PHP toArray key set and order.
|
|
pattern := "/p/:slug"
|
|
item := models.DefinitionItem{Type: "journal-post", Reference: str("7"), URLPattern: &pattern, Nesting: true, Priority: str("0.5")}
|
|
raw, err := json.Marshal(item)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if want := `{"changefreq":null,"priority":"0.5","nesting":true,"type":"journal-post","url":null,"reference":"7","cmsPage":null,"urlPattern":"/p/:slug"}`; string(raw) != want {
|
|
t.Fatalf("Marshal = %s\nwant %s", raw, want)
|
|
}
|
|
if raw, _ := json.Marshal(models.DefinitionItems(nil)); string(raw) != "[]" {
|
|
t.Fatalf("nil list = %s", raw)
|
|
}
|
|
var back models.DefinitionItem
|
|
if err := json.Unmarshal(raw, &back); err != nil || describe(back) != describe(item) {
|
|
t.Fatalf("round trip = %s, %v", describe(back), err)
|
|
}
|
|
if models.Text(nil) != "" || models.Text(str("x")) != "x" {
|
|
t.Fatal("Text")
|
|
}
|
|
if s, err := models.DecodeText(json.RawMessage(`1e3`)); err != nil || *s != "1000" {
|
|
t.Fatalf("DecodeText(1e3) = %v %v", s, err)
|
|
}
|
|
}
|
|
|
|
func str(s string) *string { return &s }
|
|
|
|
func describe(d models.DefinitionItem) string {
|
|
p := func(s *string) string {
|
|
if s == nil {
|
|
return "<nil>"
|
|
}
|
|
return *s
|
|
}
|
|
return strings.TrimSpace(fmt.Sprintf("type=%s url=%s ref=%s pattern=%s nesting=%v priority=%s changefreq=%s",
|
|
d.Type, p(d.URL), p(d.Reference), p(d.URLPattern), d.Nesting, p(d.Priority), p(d.Changefreq)))
|
|
}
|