- 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
96 lines
4.1 KiB
Go
96 lines
4.1 KiB
Go
package classes_test
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/classes"
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
)
|
|
|
|
// TestXMLEscaping ports the PHP XmlEscapingTest (SITE-002) as behaviour:
|
|
// every value the sitemap writes is escaped exactly as libxml escapes it,
|
|
// text nodes (loc, lastmod, changefreq, priority) and attribute values
|
|
// (hreflang, href), and no raw value reaches the output.
|
|
func TestXMLEscaping(t *testing.T) {
|
|
const raw = "a&b<c>d\"e'f\rg\th\ni"
|
|
if got := classes.EscapeText(raw); got != "a&b<c>d\"e'f g\th\ni" {
|
|
t.Fatalf("EscapeText = %q", got)
|
|
}
|
|
if got := classes.EscapeAttr(raw); got != "a&b<c>d"e'f g	h i" {
|
|
t.Fatalf("EscapeAttr = %q", got)
|
|
}
|
|
if got := classes.EscapeText("zażółć — &"); got != "zażółć — &amp;" {
|
|
t.Fatalf("EscapeText keeps UTF-8 and escapes an existing entity again: %q", got)
|
|
}
|
|
|
|
xml := classes.Serialize("https://h/sitemap.xsl", []classes.URLEntry{{
|
|
Loc: "https://h/a?x=1&y=<2>",
|
|
Lastmod: "2026-01-02T03:04:05+00:00",
|
|
Changefreq: "weekly&<>",
|
|
Priority: "0.5\r",
|
|
Alternates: []classes.Alternate{
|
|
{Hreflang: "pl\"x", Href: "https://h/pl?a=1&b=\"2\"\t\n"},
|
|
{Hreflang: "en", Href: "https://h/en'x"},
|
|
},
|
|
}})
|
|
want := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
|
"<?xml-stylesheet type=\"text/xsl\" href=\"https://h/sitemap.xsl\"?>\n" +
|
|
`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">` +
|
|
"<url><loc>https://h/a?x=1&y=<2></loc><lastmod>2026-01-02T03:04:05+00:00</lastmod><changefreq>weekly&<></changefreq><priority>0.5 </priority>" +
|
|
`<xhtml:link rel="alternate" hreflang="pl"x" href="https://h/pl?a=1&b="2"	 "/>` +
|
|
`<xhtml:link rel="alternate" hreflang="en" href="https://h/en'x"/>` +
|
|
"</url></urlset>\n"
|
|
if xml != want {
|
|
t.Fatalf("serialized:\n%q\nwant:\n%q", xml, want)
|
|
}
|
|
|
|
// One alternate is not written; an empty urlset is self-closed.
|
|
single := classes.Serialize("s", []classes.URLEntry{{Loc: "l", Changefreq: "c", Priority: "p", Alternates: []classes.Alternate{{Hreflang: "pl", Href: "x"}}}})
|
|
if strings.Contains(single, "xhtml:link") || strings.Contains(single, "<lastmod>") {
|
|
t.Fatalf("single alternate or empty lastmod written: %q", single)
|
|
}
|
|
if empty := classes.Serialize("s", nil); !strings.HasSuffix(empty, `sitemap.xsd"/>`+"\n") {
|
|
t.Fatalf("empty urlset = %q", empty)
|
|
}
|
|
|
|
// Through the generator: stored changefreq and priority, a resolved
|
|
// alternate's locale and a url item with every special character.
|
|
url := "/q?a=1&b=<x>&c=\"y\""
|
|
changefreq := "daily&<weekly>"
|
|
priority := "0.9\"'"
|
|
gen := &classes.Generator{Root: "https://api.example.test", Settings: models.Settings{}, Resolver: fakeResolver{t: t, types: map[string]map[string]goldenResolved{
|
|
"acme-post": {"1": {URL: "/p?x=1&y=2", Alternates: [][2]string{{"pl&<", "/p?x=1&y=2"}, {"en\"", "/en/p?z=<1>"}}}},
|
|
}}}
|
|
out, err := gen.Generate(context.Background(), []models.DefinitionItem{
|
|
{Type: models.ItemTypeURL, URL: &url, Changefreq: &changefreq, Priority: &priority},
|
|
{Type: "acme-post", Reference: text("1"), Changefreq: &changefreq, Priority: &priority},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := out[strings.Index(out, "<url>"):]
|
|
for _, bad := range []string{"<x>", "&b", "&y", `"y"`, "<weekly>", "daily&<", `hreflang="pl&<"`, `en""`, "<1>"} {
|
|
if strings.Contains(body, bad) {
|
|
t.Fatalf("raw value %q reached the output:\n%s", bad, body)
|
|
}
|
|
}
|
|
for _, good := range []string{
|
|
"<changefreq>daily&<weekly></changefreq>",
|
|
"<priority>0.9\"'</priority>",
|
|
`hreflang="pl&<"`,
|
|
`hreflang="en""`,
|
|
"y=2",
|
|
} {
|
|
if !strings.Contains(body, good) {
|
|
t.Fatalf("output lacks %q:\n%s", good, body)
|
|
}
|
|
}
|
|
if strings.Count(body, "<url>") != 2 {
|
|
t.Fatalf("urls = %d:\n%s", strings.Count(body, "<url>"), body)
|
|
}
|
|
}
|
|
|
|
func text(s string) *string { return &s }
|