package publish import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "git.coffeylabs.org/coffey-labs/hotdog-cms/internal/build" ) func TestIndexNow(t *testing.T) { t.Setenv("XDG_CACHE_HOME", t.TempDir()) t.Setenv("HOME", t.TempDir()) var pings []map[string]any status := http.StatusOK srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body map[string]any json.NewDecoder(r.Body).Decode(&body) pings = append(pings, body) w.WriteHeader(status) })) defer srv.Close() old := indexNowEndpoint indexNowEndpoint = srv.URL defer func() { indexNowEndpoint = old }() site := t.TempDir() starterSite(t, site) cfg, _ := os.ReadFile(filepath.Join(site, "site.yaml")) os.WriteFile(filepath.Join(site, "site.yaml"), append(cfg, []byte("indexnow: { key: abcdef0123456789 }\nverify: { google: g00gleCode, bing: B1NG }\n")...), 0o644) www := filepath.Join(t.TempDir(), "www") publishOnce := func() string { res, err := build.Run(build.Options{SiteDir: site}) if err != nil { t.Fatal(err) } var log bytes.Buffer if err := Run("live", Target{Type: "dir", Path: www, IndexNow: true}, res.Out, &log); err != nil { t.Fatal(err) } return log.String() } log := publishOnce() if len(pings) != 1 || !strings.Contains(log, "told search engines about") { t.Fatalf("first publish: %d pings, log %s", len(pings), log) } p := pings[0] urls, _ := p["urlList"].([]any) if p["host"] != "example.org" || p["key"] != "abcdef0123456789" || p["keyLocation"] != "https://example.org/abcdef0123456789.txt" || len(urls) < 5 { t.Fatalf("ping: %v", p) } // The key file and the verification tags are in the build. res, _ := build.Run(build.Options{SiteDir: site}) if b, _ := os.ReadFile(filepath.Join(res.Out, "abcdef0123456789.txt")); string(b) != "abcdef0123456789" { t.Errorf("key file: %q", b) } home, _ := os.ReadFile(filepath.Join(res.Out, "index.html")) if !strings.Contains(string(home), ``) { t.Error("verification tags missing") } // Nothing changed: nothing sent. if log := publishOnce(); len(pings) != 1 || !strings.Contains(log, "no pages changed") { t.Errorf("unchanged: %d pings, %s", len(pings), log) } // One page changed: only that page. about := filepath.Join(site, "content", "about.md") src, _ := os.ReadFile(about) os.WriteFile(about, append(src, []byte("\nOne more line.\n")...), 0o644) publishOnce() if len(pings) != 2 { t.Fatalf("after an edit: %d pings", len(pings)) } urls, _ = pings[1]["urlList"].([]any) if len(urls) != 1 || urls[0] != "https://example.org/about/" { t.Errorf("changed: %v", urls) } // A refused ping is reported; the publish still succeeds. status = http.StatusForbidden os.WriteFile(about, append(src, []byte("\nAnother line.\n")...), 0o644) if log := publishOnce(); !strings.Contains(log, "403") || !strings.Contains(log, "publish itself went fine") { t.Errorf("refused: %s", log) } // A target that didn't ask for it never pings. res, _ = build.Run(build.Options{SiteDir: site}) n := len(pings) var quiet bytes.Buffer Run("staging", Target{Type: "dir", Path: filepath.Join(t.TempDir(), "s")}, res.Out, &quiet) if len(pings) != n || strings.Contains(quiet.String(), "indexnow") { t.Error("a target without indexnow: true pinged") } }