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) } }