- classes.SitemapFilter implements the sitemap plugin's contract.Filter on the production values: redirected, noindex and canonicalised addresses leave the sitemaps with their reason; staging lists the same set - the plugin requires golem15.sitemap and publishes the filter in Register - go.mod requires sm-sitemap-plugin, go.work mounts the sibling checkout
91 lines
3.3 KiB
Go
91 lines
3.3 KiB
Go
package seo_test
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
sitemapclasses "git.golem15.com/golem15/sm-sitemap-plugin/classes"
|
|
)
|
|
|
|
// rebuildSitemap runs the sitemap plugin's rebuild on the test app, as its
|
|
// job and command do.
|
|
func (e *testEnv) rebuildSitemap(t *testing.T) {
|
|
t.Helper()
|
|
if _, err := sitemapclasses.Rebuild(t.Context(), e.app); err != nil {
|
|
t.Fatalf("sitemap rebuild: %v", err)
|
|
}
|
|
}
|
|
|
|
// sitemapFile fetches one sitemap file by name; a missing file is "".
|
|
func (e *testEnv) sitemapFile(t *testing.T, name string) string {
|
|
t.Helper()
|
|
rec := e.call(t, http.MethodGet, "/_sitemap/files/"+name, "", nil)
|
|
if rec.Code == http.StatusNotFound {
|
|
return ""
|
|
}
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("GET %s = %d: %s", name, rec.Code, rec.Body.String())
|
|
}
|
|
return rec.Body.String()
|
|
}
|
|
|
|
// sitemapReason is the excluded reason the sitemap stored for a record's
|
|
// address in a locale ("" while it is listed).
|
|
func (e *testEnv) sitemapReason(t *testing.T, typeKey, id, locale string) string {
|
|
t.Helper()
|
|
var rows []string
|
|
if err := e.db.Raw(`SELECT excluded_reason FROM golem15_sitemap_urls WHERE type_key = ? AND record_id = ? AND locale = ?`, typeKey, id, locale).Scan(&rows).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) == 0 {
|
|
t.Fatalf("no sitemap row for %s %s %s", typeKey, id, locale)
|
|
}
|
|
return rows[0]
|
|
}
|
|
|
|
// TestSitemapFilterTracer is the plan's tracer: an editor sets a page to
|
|
// noindex in the SEO tab, the sitemap rebuild asks the SEO plugin's
|
|
// published filter, and the page leaves the sitemap file with the reason
|
|
// noindex. Staging lists the same set as production (the non-production
|
|
// lock never excludes an address).
|
|
func TestSitemapFilterTracer(t *testing.T) {
|
|
for _, envName := range []string{"production", "staging"} {
|
|
t.Run(envName, func(t *testing.T) {
|
|
env := bootEnv(t, "SUMMER_ENV="+envName)
|
|
token := env.admin(t, true)
|
|
first := env.createPage(t, token, map[string]any{"title": "First", "slug": "first", "published": true})
|
|
second := env.createPage(t, token, map[string]any{"title": "Second", "slug": "second", "published": true})
|
|
|
|
env.rebuildSitemap(t)
|
|
file := env.sitemapFile(t, "sitemap-pages-1.xml")
|
|
if !strings.Contains(file, ".test/pages/first</loc>") || !strings.Contains(file, ".test/pages/second</loc>") {
|
|
t.Fatalf("before noindex the file lists both pages:\n%s", file)
|
|
}
|
|
|
|
env.expect(t, http.StatusOK, http.MethodPut, adminAPI("/acme/pages/pages/"+first), token, map[string]any{
|
|
"title": "First", "slug": "first", "published": true,
|
|
"seo_robots": map[string]any{"en": "noindex"},
|
|
})
|
|
env.rebuildSitemap(t)
|
|
file = env.sitemapFile(t, "sitemap-pages-1.xml")
|
|
if strings.Contains(file, ".test/pages/first</loc>") {
|
|
t.Fatalf("the noindex page is still listed:\n%s", file)
|
|
}
|
|
if !strings.Contains(file, ".test/pages/second</loc>") {
|
|
t.Fatalf("the indexable page left the file:\n%s", file)
|
|
}
|
|
if got := env.sitemapReason(t, "acme-page", first, "en"); got != "noindex" {
|
|
t.Fatalf("excluded reason = %q, want noindex", got)
|
|
}
|
|
if got := env.sitemapReason(t, "acme-page", second, "en"); got != "" {
|
|
t.Fatalf("second page reason = %q, want listed", got)
|
|
}
|
|
// The pl address has no noindex of its own and stays listed.
|
|
if got := env.sitemapReason(t, "acme-page", first, "pl"); got != "" {
|
|
t.Fatalf("pl address reason = %q, want listed", got)
|
|
}
|
|
})
|
|
}
|
|
}
|