package test import ( articlehandler "RenewCMS/api/handlers/article" "RenewCMS/internal/domain/article" "RenewCMS/internal/infrastructure/useCases" "math" "net/http" "reflect" "strconv" "strings" "testing" ) func TestSeoOutputListPagingNeverPanics(t *testing.T) { e := newSeoOutEnv(t) e.cleanArticles() for id := uint32(200); id < 207; id++ { e.seedListed(id, "Listed "+strconv.Itoa(int(id)), true, nil) } run := func(page int, size int) useCases.ArticlePage { t.Helper() result, err := e.container.ListArticlesUseCase.ListArticlesPage(useCases.ListArticlesCommand{ Verified: true, Page: page, Size: size, SEO: article.SEOStatusReady, Sort: article.Sort{Field: "title", Direction: "asc"}, }) if err != nil { t.Fatalf("page %d size %d: %v", page, size, err) } return result } total := run(1, 100).Total if total != 7 { t.Fatalf("ready total = %d, want 7", total) } cases := []struct { page, size, items int }{ {math.MaxInt, 25, 0}, {math.MaxInt, math.MaxInt, 0}, {1, -5, 0}, {0, -5, 0}, {1, 0, 0}, {2, math.MaxInt, 0}, {1, math.MaxInt, 7}, {0, 5, 5}, {-3, 5, 5}, {2, 5, 2}, {3, 5, 0}, {2, 4, 3}, {3, 4, 0}, {math.MinInt, 5, 5}, } for _, c := range cases { result := run(c.page, c.size) if len(result.Items) != c.items || result.Total != total { t.Errorf("page %d size %d: items %d total %d, want items %d total %d", c.page, c.size, len(result.Items), result.Total, c.items, total) } } } func TestSeoOutputContractParity(t *testing.T) { oneof := "oneof=" + strings.Join(article.DismissCodes(), " ") max := "max=" + strconv.Itoa(article.MaxCanonicalURLRunes) for _, subject := range []reflect.Type{reflect.TypeOf(articlehandler.CheckArticleSEOBody{}), reflect.TypeOf(articlehandler.UpdateArticleBody{})} { dismissed, _ := subject.FieldByName("SEODismissed") if !strings.HasSuffix(dismissed.Tag.Get("validate"), oneof) { t.Errorf("%s seo_dismissed tag %q lacks %q", subject.Name(), dismissed.Tag.Get("validate"), oneof) } canonical, _ := subject.FieldByName("CanonicalURL") if !strings.Contains(canonical.Tag.Get("validate"), max) { t.Errorf("%s canonical_url tag %q lacks %q", subject.Name(), canonical.Tag.Get("validate"), max) } } e := newSeoOutEnv(t) for _, status := range []string{article.SEOStatusReady, article.SEOStatusSuggestions, article.SEOStatusHidden} { rec := e.call(t, "owner", http.MethodGet, "/v1/article/?seo="+status, "") if rec.Code != http.StatusOK { t.Errorf("seo=%s: %d", status, rec.Code) } } if rec := e.call(t, "owner", http.MethodGet, "/v1/article/?seo=bogus", ""); rec.Code != http.StatusBadRequest { t.Errorf("seo=bogus: %d", rec.Code) } }