package classes import ( "fmt" "net/http" "regexp" "strings" "testing" "git.golem15.com/golem15/sm-sitemap-plugin/models" ) // TestIndexNowBatches pins the requests of at most 10 000 addresses, in // order, with nothing for no addresses. func TestIndexNowBatches(t *testing.T) { if MaxIndexNowURLs != 10000 { t.Fatalf("MaxIndexNowURLs = %d", MaxIndexNowURLs) } urls := func(n int) []string { out := make([]string, n) for i := range out { out[i] = fmt.Sprintf("https://example.test/%d", i) } return out } for _, tc := range []struct { n int want []int }{{0, nil}, {1, []int{1}}, {10000, []int{10000}}, {10001, []int{10000, 1}}, {25000, []int{10000, 10000, 5000}}} { got := indexNowBatches(urls(tc.n)) var sizes []int next := 0 for _, b := range got { sizes = append(sizes, len(b)) if len(b) > 0 && b[0] != fmt.Sprintf("https://example.test/%d", next) { t.Fatalf("%d addresses: a batch starts at %s, want %d", tc.n, b[0], next) } next += len(b) } if fmt.Sprint(sizes) != fmt.Sprint(tc.want) { t.Errorf("%d addresses: batches %v, want %v", tc.n, sizes, tc.want) } } } // TestIndexNowOutcome pins the answer mapping: 200 and 202 sent, 429 and // 5xx retried, 400, 403 and 422 (and any other status) refused. func TestIndexNowOutcome(t *testing.T) { for status, want := range map[int]int{ http.StatusOK: indexNowSent, http.StatusAccepted: indexNowSent, http.StatusTooManyRequests: indexNowRetry, 500: indexNowRetry, 502: indexNowRetry, 503: indexNowRetry, http.StatusBadRequest: indexNowRefused, http.StatusForbidden: indexNowRefused, http.StatusUnprocessableEntity: indexNowRefused, http.StatusNoContent: indexNowRefused, http.StatusNotFound: indexNowRefused, } { if got := indexNowOutcome(status); got != want { t.Errorf("status %d: outcome %d, want %d", status, got, want) } } } // TestIndexNowKeys pins the key format (8 to 128 letters, digits and // hyphens), the generated key (32 lowercase hex characters, a new one each // time), the key file name and the active key only while IndexNow is on. func TestIndexNowKeys(t *testing.T) { for k, want := range map[string]bool{ "abcd1234": true, "ABCD-1234-efgh": true, strings.Repeat("a", 128): true, "abc1234": false, strings.Repeat("a", 129): false, "abcd_1234": false, "abcd 1234": false, "../abcd1234": false, "": false, } { if ValidKey(k) != want { t.Errorf("ValidKey(%q) = %v, want %v", k, !want, want) } } a, err := GenerateKey() if err != nil { t.Fatal(err) } b, _ := GenerateKey() if !regexp.MustCompile(`^[0-9a-f]{32}$`).MatchString(a) || a == b || !ValidKey(a) { t.Fatalf("GenerateKey = %q, %q", a, b) } if KeyFileName(a) != a+".txt" || KeyFileName("short") != "" || KeyFileName("x/../y12345") != "" { t.Fatal("KeyFileName is wrong") } key := " " + a + " " bad := "bad key" if ActiveKey(models.Settings{IndexNowEnabled: true, IndexNowKey: &key}) != a || ActiveKey(models.Settings{IndexNowEnabled: false, IndexNowKey: &key}) != "" || ActiveKey(models.Settings{IndexNowEnabled: true, IndexNowKey: &bad}) != "" || ActiveKey(models.Settings{IndexNowEnabled: true}) != "" { t.Fatal("ActiveKey is wrong") } } // TestSiteURLs pins the host filter: only http(s) addresses on the site's // host (case-insensitive, any port or scheme), each once, in order; // nothing without a site address. func TestSiteURLs(t *testing.T) { got := SiteURLs("https://Example.test", []string{ "https://example.test/a", "https://EXAMPLE.test/b", "http://example.test/c", "https://example.test:8443/d", "https://example.test/a", "https://www.example.test/e", "https://other.test/f", "/g", "ftp://example.test/h", "", }) want := "https://example.test/a https://EXAMPLE.test/b http://example.test/c https://example.test:8443/d" if strings.Join(got, " ") != want { t.Fatalf("SiteURLs = %v, want %s", got, want) } if SiteURLs("", []string{"https://example.test/a"}) != nil || SiteURLs("/relative", []string{"/a"}) != nil { t.Fatal("without a site address nothing is kept") } if got := SiteURLs("https://example.test", nil); got == nil || len(got) != 0 { t.Fatalf("no addresses: %v, want an empty list", got) } }