- golem15.sitemap plugin: golem15_sitemap_definitions migration, Definition model (Fillable theme and data) with tolerant PHP-shaped item decoding and PHP toArray item encoding - Url::to and buildUrl port, libxml-format serializer, generator for url and registry items - GET /sitemap.xml serves the definition with theme default (404 without one, empty body without items) - tests/php/gen-goldens.php and the tracer golden; TestSitemapGolden and the Postgres TestSitemapTracer
110 lines
3.0 KiB
Go
110 lines
3.0 KiB
Go
package classes_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/classes"
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// The goldens in testdata are written by tests/php/gen-goldens.php from the
|
|
// same JSON inputs with the PHP plugin's generation code and the vendored
|
|
// Winter URL generator; these tests feed the inputs through the Go port and
|
|
// compare bytes.
|
|
|
|
// goldenResolved is a registry item's resolve result in a golden input.
|
|
type goldenResolved struct {
|
|
URL string `json:"url"`
|
|
MTime *string `json:"mtime"`
|
|
Alternates [][2]string `json:"alternates"`
|
|
Items []goldenResolved `json:"items"`
|
|
}
|
|
|
|
func (g goldenResolved) toPact(t *testing.T) pact.ResolvedMenuItem {
|
|
t.Helper()
|
|
out := pact.ResolvedMenuItem{URL: g.URL}
|
|
if g.MTime != nil {
|
|
at, err := time.Parse(time.RFC3339, *g.MTime)
|
|
if err != nil {
|
|
t.Fatalf("mtime %q: %v", *g.MTime, err)
|
|
}
|
|
out.MTime = &at
|
|
}
|
|
for _, a := range g.Alternates {
|
|
out.Alternates = append(out.Alternates, pact.MenuItemAlternate{Locale: a[0], URL: a[1]})
|
|
}
|
|
for _, child := range g.Items {
|
|
out.Items = append(out.Items, child.toPact(t))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// goldenCase is one sitemap case of a golden input.
|
|
type goldenCase struct {
|
|
Name string `json:"name"`
|
|
Root string `json:"root"`
|
|
Items models.DefinitionItems `json:"items"`
|
|
Types map[string]map[string]goldenResolved `json:"types"`
|
|
}
|
|
|
|
// fakeResolver answers Resolve from a golden case's types.
|
|
type fakeResolver struct {
|
|
t *testing.T
|
|
types map[string]map[string]goldenResolved
|
|
}
|
|
|
|
func (f fakeResolver) Type(key string) (pact.MenuItemType, bool) {
|
|
_, ok := f.types[key]
|
|
return pact.MenuItemType{Key: key}, ok
|
|
}
|
|
|
|
func (f fakeResolver) Resolve(_ context.Context, key, reference, _ string, _ bool, _ string) (*pact.ResolvedMenuItem, error) {
|
|
r, ok := f.types[key][reference]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
out := r.toPact(f.t)
|
|
return &out, nil
|
|
}
|
|
|
|
func renderCase(t *testing.T, c goldenCase) string {
|
|
t.Helper()
|
|
gen := &classes.Generator{Root: c.Root, Resolver: fakeResolver{t: t, types: c.Types}}
|
|
xml, err := gen.Generate(context.Background(), c.Items)
|
|
if err != nil {
|
|
t.Fatalf("generate %s: %v", c.Name, err)
|
|
}
|
|
return xml
|
|
}
|
|
|
|
func readTestdata(t *testing.T, name string) []byte {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("testdata", name))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
// TestSitemapGolden compares the Go sitemap with the PHP goldens.
|
|
func TestSitemapGolden(t *testing.T) {
|
|
t.Run("tracer", func(t *testing.T) {
|
|
var in struct {
|
|
Case goldenCase `json:"case"`
|
|
}
|
|
if err := json.Unmarshal(readTestdata(t, "tracer.json"), &in); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := renderCase(t, in.Case)
|
|
if want := string(readTestdata(t, "tracer.golden")); got != want {
|
|
t.Fatalf("tracer sitemap differs from the PHP golden\n got: %q\nwant: %q", got, want)
|
|
}
|
|
})
|
|
}
|