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