test: unit tests for the schema builder, crawler and page helpers

- schema: types per kind, @id links, every node kind, the publisher and
  local business, every validator rule, custom JSON-LD caps and merge
- crawl: HTML parse, each check's positive and negative case, fix routes
  with the fitting tool, the rendered comparison, allowRequest,
  resolverRules and NewRenderer
- pagination, alternates and x-default, primary parents, the filter's
  addresses, registry paths and fields, auto-link addresses
This commit is contained in:
Jakub Zych
2026-11-04 19:43:31 +01:00
parent dfc85bc805
commit 82b0dacff9
14 changed files with 1543 additions and 0 deletions

108
classes/pagination_test.go Normal file
View File

@@ -0,0 +1,108 @@
package classes
import (
"net/url"
"testing"
"git.golem15.com/golem15/sm-seo-plugin/models"
)
var pathForm = PageConfig{Form: models.PageFormPath, Segments: map[string]string{"pl": "strona", "en": "page"}}
var queryForm = PageConfig{Form: models.PageFormQuery, Segments: map[string]string{"pl": "strona", "en": "page"}}
// TestPageNumber pins the page value: digits only, at least 1, at most
// nine digits.
func TestPageNumber(t *testing.T) {
for v, want := range map[string]int{"1": 1, "2": 2, "007": 7, "999999999": 999999999} {
if n, ok := pageNumber(v); !ok || n != want {
t.Errorf("pageNumber(%q) = %d %v", v, n, ok)
}
}
for _, v := range []string{"", "0", "-1", "1.5", "a", "1e3", "1234567890", " 2"} {
if _, ok := pageNumber(v); ok {
t.Errorf("pageNumber(%q) accepted", v)
}
}
}
// TestParsePage pins both forms: ?page=N (other parameters kept, a
// non-canonical number not exact, an invalid value), and a trailing
// /<segment>/N with any locale's segment, an unknown segment ignored, and
// a non-number suffix invalid only in the path form.
func TestParsePage(t *testing.T) {
q := url.Values{"page": {"3"}, "sort": {"new"}}
pr := parsePage("/blog", q, queryForm)
if !pr.carried || pr.invalid || pr.page != 3 || pr.form != models.PageFormQuery || !pr.exact || pr.rest.Get("sort") != "new" || pr.rest.Has("page") || pr.base != "/blog" {
t.Fatalf("query form = %+v", pr)
}
if pr := parsePage("/blog", url.Values{"page": {"03"}}, queryForm); pr.exact || pr.page != 3 {
t.Fatalf("leading zero = %+v, want not exact", pr)
}
if pr := parsePage("/blog", url.Values{"page": {"2", "3"}}, queryForm); pr.exact {
t.Fatal("two values are not exact")
}
if pr := parsePage("/blog", url.Values{"page": {"x"}}, queryForm); !pr.carried || !pr.invalid {
t.Fatalf("invalid query value = %+v", pr)
}
if pr := parsePage("/blog", url.Values{"page": {""}}, queryForm); !pr.invalid {
t.Fatal("an empty page value is invalid")
}
pr = parsePage("/blog/strona/2", nil, pathForm)
if !pr.carried || pr.invalid || pr.page != 2 || pr.form != models.PageFormPath || pr.segment != "strona" || pr.base != "/blog" || !pr.exact {
t.Fatalf("path form = %+v", pr)
}
if pr := parsePage("/page/4/", nil, pathForm); pr.page != 4 || pr.base != "/" || pr.segment != "page" {
t.Fatalf("root path form = %+v", pr)
}
if pr := parsePage("/blog/seite/2", nil, pathForm); pr.carried {
t.Fatalf("an unknown segment = %+v", pr)
}
if pr := parsePage("/blog/strona/x", nil, pathForm); !pr.carried || !pr.invalid {
t.Fatalf("a non-number suffix in the path form = %+v", pr)
}
if pr := parsePage("/blog/strona/x", nil, queryForm); pr.carried {
t.Fatalf("a non-number suffix in the query form = %+v", pr)
}
if pr := parsePage("/blog/strona/02", nil, pathForm); pr.exact || pr.page != 2 {
t.Fatalf("a leading zero in the path = %+v", pr)
}
if pr := parsePage("/blog", nil, pathForm); pr.carried {
t.Fatal("no page")
}
}
// TestPageLocation pins the 301 targets: page 1 is the address without a
// number, page 2 and later in the site's form with the other parameters.
func TestPageLocation(t *testing.T) {
rest := url.Values{"sort": {"new"}}
if got := pageLocation("/blog", rest, 1, queryForm, "pl", NormalizeOptions{}); got != "/blog?sort=new" {
t.Fatalf("page 1 query = %q", got)
}
if got := pageLocation("/blog", rest, 3, queryForm, "pl", NormalizeOptions{}); got != "/blog?page=3&sort=new" {
t.Fatalf("page 3 query = %q", got)
}
if got := pageLocation("/blog/", nil, 2, pathForm, "pl", NormalizeOptions{}); got != "/blog/strona/2" {
t.Fatalf("page 2 path pl = %q", got)
}
if got := pageLocation("/blog", nil, 2, pathForm, "de", NormalizeOptions{}); got != "/blog/seite/2" {
t.Fatalf("page 2 path de = %q", got)
}
if got := pagedPath("/", "page", 5); got != "/page/5" {
t.Fatalf("pagedPath = %q", got)
}
}
// TestPageSegments pins the segment per locale and the placeholders.
func TestPageSegments(t *testing.T) {
cfg := PageConfig{Segments: map[string]string{"pl": "str"}}
if cfg.Segment("pl") != "str" || cfg.Segment("de") != "seite" || cfg.Segment("en") != "page" || cfg.Segment("fr") != "page" {
t.Fatal("Segment is wrong")
}
if !ValidPageSegment("strona-2") || ValidPageSegment("Strona") || ValidPageSegment("") || ValidPageSegment("a/b") {
t.Fatal("ValidPageSegment is wrong")
}
if fillPage(" – strona %page% z %pages%", 2) != " – strona 2 z %pages%" {
t.Fatal("fillPage must fill the page number and keep the pages placeholder")
}
}