Files
sm-seo-plugin/classes/owner_test.go
Jakub Zych 9e45ddc848 test: unit tests for the SEO classes
Pure tests of the classes package (no database): the resolver's value
chains with every layer present and absent, robots per directive, the
staging lock and drafts; the redirect graph (chains, flattened and
back-and-forth graphs, loops by exact, pattern and regex probes, the
10-hop cap); URL normalisation across every address rule; the robots
grammar over every directive combination; Google's robots.txt matcher
examples; templates and unknown placeholders; CSV export and import;
owner resolution; and every kind of the issue report.

go.mod: gorm.io/driver/postgres becomes a direct requirement (the tests'
dry-run database).
2026-10-27 13:21:38 +01:00

75 lines
3.4 KiB
Go

package classes
import (
"errors"
"testing"
)
// TestOwnerResolution: a URL's SEO belongs to the record behind it in the
// matched locale when the URL is the record's exact address without a
// query; otherwise, and with force_rule, to an exact URL rule on the
// normalised address (D-11, P-25).
func TestOwnerResolution(t *testing.T) {
svc := testServiceWithPages(t, "https://example.test",
testPage{id: "1", slugs: map[string]string{"en": "about", "pl": "o-nas"}, published: true},
testPage{id: "2", slugs: map[string]string{"en": "draft"}, published: false},
)
db := dryDB(t)
for _, tc := range []struct {
name, url string
force bool
writtenTo string
id string
locale string
address string
published bool
}{
{"the record's own address", "/pages/about", false, WrittenToRecord, "1", "en", "/pages/about", true},
{"an absolute URL of the site", "https://example.test/pages/about", false, WrittenToRecord, "1", "en", "/pages/about", true},
{"the locale prefix names the locale", "/pl/pages/o-nas", false, WrittenToRecord, "1", "pl", "/pl/pages/o-nas", true},
{"a trailing slash still finds the record", "/pages/about/", false, WrittenToRecord, "1", "en", "/pages/about/", true},
{"double slashes are normalised first", "//pages//about", false, "", "", "", "", false},
{"force_rule writes a rule on the record's address", "/pages/about", true, WrittenToRule, "", "", "/pages/about", false},
{"a query makes it a rule", "/pages/about?page=2", false, WrittenToRule, "", "", "/pages/about?page=2", false},
{"another locale's slug is not the record's address", "/pages/o-nas", false, WrittenToRule, "", "", "/pages/o-nas", false},
{"a draft is not matched", "/pages/draft", false, WrittenToRule, "", "", "/pages/draft", false},
{"an address without a record", "/landing", false, WrittenToRule, "", "", "/landing", false},
{"the home page", "/", false, WrittenToRule, "", "", "/", false},
{"an index file is normalised away", "/pages/about/index.html", false, WrittenToRecord, "1", "en", "/pages/about", true},
} {
t.Run(tc.name, func(t *testing.T) {
owner, typ, err := svc.FindOwner(t.Context(), db, tc.url, tc.force)
if tc.writtenTo == "" {
if !errors.Is(err, ErrBadURL) {
t.Fatalf("FindOwner(%q) = %+v, %v; want ErrBadURL", tc.url, owner, err)
}
return
}
if err != nil {
t.Fatal(err)
}
if owner.WrittenTo != tc.writtenTo || owner.ID != tc.id || owner.Locale != tc.locale || owner.URL != tc.address || owner.Published != tc.published {
t.Fatalf("FindOwner(%q) = %+v", tc.url, owner)
}
if tc.writtenTo == WrittenToRecord && (owner.Type != "acme-page" || typ.Key != "acme-page") {
t.Fatalf("type = %q %q", owner.Type, typ.Key)
}
if tc.writtenTo == WrittenToRule && (owner.Type != "" || typ.Key != "" || owner.RuleID != 0) {
t.Fatalf("rule owner = %+v (%s)", owner, typ.Key)
}
})
}
for _, bad := range []string{"", "pages/about", "ftp://example.test/a", "javascript:alert(1)"} {
if _, _, err := svc.FindOwner(t.Context(), db, bad, false); !errors.Is(err, ErrBadURL) {
t.Errorf("FindOwner(%q) = %v, want ErrBadURL", bad, err)
}
}
t.Run("without a signpost registry every URL is a rule", func(t *testing.T) {
owner, _, err := testService(t, "production", "https://example.test").FindOwner(t.Context(), db, "/pages/about", false)
if err != nil || owner.WrittenTo != WrittenToRule || owner.URL != "/pages/about" {
t.Fatalf("owner = %+v, %v", owner, err)
}
})
}