- 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
111 lines
4.6 KiB
Go
111 lines
4.6 KiB
Go
package classes
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/sm-seo-plugin/models"
|
|
)
|
|
|
|
// TestValidPagePath pins the registry address rule: a path from one /,
|
|
// no domain, query, fragment, backslash or space, valid UTF-8 without
|
|
// control characters, at most MaxRouteBytes.
|
|
func TestValidPagePath(t *testing.T) {
|
|
for p, want := range map[string]bool{
|
|
"/": true, "/kontakt": true, "/trwają-prace": true, "/a/b/c": true, "/" + strings.Repeat("a", MaxRouteBytes-1): true,
|
|
"": false, "kontakt": false, "//evil.test": false, "/a?b": false, "/a#b": false, `/a\b`: false, "/a b": false,
|
|
"/a\x01": false, "/\xff": false, "https://example.test/a": false, "/" + strings.Repeat("a", MaxRouteBytes): false,
|
|
} {
|
|
if got := ValidPagePath(p); got != want {
|
|
t.Errorf("ValidPagePath(%q) = %v, want %v", p, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNormalPagePath pins the site's address rules on a registry path: the
|
|
// path trimmed, lower-cased and slashed as the settings say, an invalid
|
|
// path returned trimmed.
|
|
func TestNormalPagePath(t *testing.T) {
|
|
if got := normalPagePath(" /Kontakt/ ", NormalizeOptions{Lowercase: true, Slash: "remove"}); got != "/kontakt" {
|
|
t.Fatalf("lowercase, no slash = %q", got)
|
|
}
|
|
if got := normalPagePath("/Kontakt", NormalizeOptions{}); got != "/Kontakt" {
|
|
t.Fatalf("no rules = %q", got)
|
|
}
|
|
if got := normalPagePath(" kontakt ", NormalizeOptions{Lowercase: true}); got != "kontakt" {
|
|
t.Fatalf("invalid path = %q", got)
|
|
}
|
|
}
|
|
|
|
// TestApplyPageFields pins the scalar fields of a page write: kind and
|
|
// note trimmed, the parent set and cleared, the content date parsed,
|
|
// cleared by "" and refused when invalid; unset fields stay.
|
|
func TestApplyPageFields(t *testing.T) {
|
|
kind, note, date, parent := " offer ", " n ", "2026-11-04", uint(7)
|
|
p := &models.Page{Kind: models.PageKindPage}
|
|
if problems := ApplyPageFields(p, PageWrite{Kind: &kind, Note: ¬e, ContentUpdatedAt: &date, ParentSet: true, ParentID: &parent}); len(problems) != 0 {
|
|
t.Fatalf("problems = %v", problems)
|
|
}
|
|
if p.Kind != "offer" || p.Note != "n" || p.ContentUpdatedAt == nil || p.ContentUpdatedAt.String() != "2026-11-04" || p.ParentID == nil || uint(*p.ParentID) != 7 {
|
|
t.Fatalf("page = %+v", p)
|
|
}
|
|
empty, bad, zero := "", "04.11.2026", uint(0)
|
|
if problems := ApplyPageFields(p, PageWrite{ContentUpdatedAt: &empty, ParentSet: true, ParentID: &zero}); len(problems) != 0 || p.ContentUpdatedAt != nil || p.ParentID != nil || p.Kind != "offer" {
|
|
t.Fatalf("cleared = %+v %v", p, problems)
|
|
}
|
|
if problems := ApplyPageFields(p, PageWrite{ContentUpdatedAt: &bad}); len(problems["content_updated_at"]) != 1 {
|
|
t.Fatalf("an invalid date: %v", problems)
|
|
}
|
|
}
|
|
|
|
// TestPageGroupAndMissing pins the sitemap group per kind (unknown kinds
|
|
// with the pages) and the Not on the site flag: a reported page the
|
|
// newest report did not name; never a manual page or before any report.
|
|
func TestPageGroupAndMissing(t *testing.T) {
|
|
for kind, want := range map[string]string{models.PageKindPage: "pages", models.PageKindOffer: "offers", models.PageKindService: "services", models.PageKindLanding: "landing-pages", "other": "pages"} {
|
|
if got := PageGroup(kind); got != want {
|
|
t.Errorf("PageGroup(%q) = %q, want %q", kind, got, want)
|
|
}
|
|
}
|
|
now := time.Now().UTC()
|
|
older := now.Add(-time.Hour)
|
|
for _, tc := range []struct {
|
|
p models.Page
|
|
reported *time.Time
|
|
want bool
|
|
}{
|
|
{models.Page{Origin: models.PageOriginReported, ReportedAt: &older}, &now, true},
|
|
{models.Page{Origin: models.PageOriginReported}, &now, true},
|
|
{models.Page{Origin: models.PageOriginReported, ReportedAt: &now}, &now, false},
|
|
{models.Page{Origin: models.PageOriginManual, ReportedAt: &older}, &now, false},
|
|
{models.Page{Origin: models.PageOriginReported, ReportedAt: &older}, nil, false},
|
|
} {
|
|
if got := PageMissing(tc.p, tc.reported); got != tc.want {
|
|
t.Errorf("PageMissing(%+v) = %v, want %v", tc.p, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPageBeforeSaveUTC pins the registry page's timestamps in UTC
|
|
// whatever the process's zone: a new row gets both, a saved row keeps its
|
|
// created_at.
|
|
func TestPageBeforeSaveUTC(t *testing.T) {
|
|
local := time.Local
|
|
time.Local = time.FixedZone("UTC+3", 3*3600)
|
|
defer func() { time.Local = local }()
|
|
p := &models.Page{}
|
|
if err := p.BeforeSave(nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if p.CreatedAt == nil || p.UpdatedAt == nil || p.CreatedAt.Location() != time.UTC || p.UpdatedAt.Location() != time.UTC {
|
|
t.Fatalf("stamps = %v %v", p.CreatedAt, p.UpdatedAt)
|
|
}
|
|
created := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
p.CreatedAt = &created
|
|
_ = p.BeforeSave(nil)
|
|
if !p.CreatedAt.Equal(created) || !p.UpdatedAt.After(created) {
|
|
t.Fatalf("a saved row = %v %v", p.CreatedAt, p.UpdatedAt)
|
|
}
|
|
}
|