package classes import ( "errors" "math/rand" "strings" "testing" ) // robotsCombinations enumerates every combination of the C5 grammar's // directives (with representative arguments). func robotsCombinations() []Robots { ptr := func(n int) *int { return &n } var out []Robots for _, index := range []string{"", "index", "noindex"} { for _, follow := range []string{"", "follow", "nofollow"} { for flags := 0; flags < 8; flags++ { for _, snippet := range []*int{nil, ptr(-1), ptr(0), ptr(160)} { for _, image := range []string{"", "none", "standard", "large"} { for _, video := range []*int{nil, ptr(-1), ptr(30)} { for _, after := range []string{"", "2027-01-31"} { out = append(out, Robots{ Index: index, Follow: follow, NoArchive: flags&1 != 0, NoSnippet: flags&2 != 0, NoImageIndex: flags&4 != 0, MaxSnippet: snippet, MaxImagePreview: image, MaxVideoPreview: video, UnavailableAfter: after, }) } } } } } } } return out } func TestRobotsValueRoundTrip(t *testing.T) { rng := rand.New(rand.NewSource(32)) combos := robotsCombinations() if len(combos) != 3*3*8*4*4*3*2 { t.Fatalf("%d combinations", len(combos)) } for _, want := range combos { value := want.String() got, err := ParseRobots(value) if err != nil { t.Fatalf("ParseRobots(%q): %v", value, err) } if got.String() != value { t.Fatalf("round trip %q -> %q", value, got.String()) } if got.Header() != strings.ReplaceAll(value, ",", ", ") { t.Fatalf("Header(%q) = %q", value, got.Header()) } if got.Empty() != (value == "") { t.Fatalf("Empty(%q) = %v", value, got.Empty()) } // Any order, spaces and case read the same value back in the // fixed order. parts := strings.Split(value, ",") rng.Shuffle(len(parts), func(i, j int) { parts[i], parts[j] = parts[j], parts[i] }) for i := range parts { if i%2 == 0 { parts[i] = " " + strings.ToUpper(parts[i]) + " " } } if value != "" { norm, err := NormalizeRobots(strings.Join(parts, ",")) if err != nil || norm != value { t.Fatalf("NormalizeRobots(%q) = %q, %v; want %q", strings.Join(parts, ","), norm, err, value) } } } } func TestRobotsValueAllInheritIsEmpty(t *testing.T) { for _, value := range []string{"", " ", "\t"} { r, err := ParseRobots(value) if err != nil || !r.Empty() || r.String() != "" { t.Fatalf("ParseRobots(%q) = %+v, %v", value, r, err) } } if s := (Robots{}).String(); s != "" { t.Fatalf("all-inherit serialises to %q", s) } } func TestRobotsValueMalformed(t *testing.T) { for _, value := range []string{ ",", "index,", ",noindex", "index,,follow", "index,noindex", "follow,nofollow", "noindex,noindex", "noarchive,noarchive", "max-snippet:1,max-snippet:2", "index:1", "nofollow:yes", "noarchive:1", "max-snippet", "max-snippet:", "max-snippet:abc", "max-snippet:-2", "max-snippet:1.5", "max-video-preview", "max-video-preview:-5", "max-image-preview", "max-image-preview:huge", "max-image-preview:", "unavailable_after", "unavailable_after:2027-13-01", "unavailable_after:2027-02-30", "unavailable_after:27-01-01", "unavailable_after:2027-1-1", "none", "all", "noodp", "indexx", } { if _, err := ParseRobots(value); !errors.Is(err, ErrRobotsValue) { t.Errorf("ParseRobots(%q) = %v, want ErrRobotsValue", value, err) } if _, err := NormalizeRobots(value); err == nil { t.Errorf("NormalizeRobots(%q) accepted", value) } } } func TestRobotsValueInherit(t *testing.T) { parse := func(v string) Robots { t.Helper() r, err := ParseRobots(v) if err != nil { t.Fatal(err) } return r } for _, tc := range []struct{ upper, lower, want string }{ {"", "", ""}, {"noindex", "index,nofollow", "noindex,nofollow"}, {"", "noindex,follow", "noindex,follow"}, {"index,follow", "noindex,nofollow,noarchive", "index,follow,noarchive"}, {"max-snippet:10", "max-snippet:50,max-image-preview:large", "max-snippet:10,max-image-preview:large"}, {"unavailable_after:2027-01-01", "unavailable_after:2028-01-01,max-video-preview:5", "max-video-preview:5,unavailable_after:2027-01-01"}, {"nosnippet", "noimageindex", "nosnippet,noimageindex"}, } { if got := parse(tc.upper).Inherit(parse(tc.lower)).String(); got != tc.want { t.Errorf("%q over %q = %q, want %q", tc.upper, tc.lower, got, tc.want) } } }