Files
RenewCMS/test/article_list_seo_scale_test.go
T

168 lines
5.7 KiB
Go

package test
import (
domainArticle "RenewCMS/internal/domain/article"
entity "RenewCMS/internal/infrastructure/persistence/models"
persistence "RenewCMS/internal/infrastructure/persistence/repositories"
"RenewCMS/internal/infrastructure/useCases"
"fmt"
"testing"
"gorm.io/gorm"
)
const (
seoScaleVersions = 40_001
seoScaleBatch = 500
seoScaleHiddenEvery = 1000
seoScalePageSize = 25
seoScaleStoryOffset = 500_000
seoScaleTitledEvery = 7
seoScaleTitleFragment = "Lighthouse"
)
type countingStoryRepository struct {
*persistence.ArticleRepository
titleSearches int
pageLoads int
}
func (r *countingStoryRepository) FindTitleMatches(q string, readable domainArticle.StoryFilters) ([]uint32, error) {
r.titleSearches++
return r.ArticleRepository.FindTitleMatches(q, readable)
}
func (r *countingStoryRepository) FindPage(filters domainArticle.Filters) (domainArticle.PageResult, error) {
r.pageLoads++
return r.ArticleRepository.FindPage(filters)
}
func seoScaleTitle(index int) string {
if index%seoScaleTitledEvery == 0 {
return fmt.Sprintf("%s %d", seoScaleTitleFragment, index)
}
return fmt.Sprintf("Harbour %d", index)
}
func seedSEOScale(t *testing.T, db *gorm.DB, siteID uint32) {
t.Helper()
rows := make([]entity.Article, 0, seoScaleVersions)
for index := 0; index < seoScaleVersions; index++ {
rows = append(rows, entity.Article{
Title: seoScaleTitle(index),
Body: "<p>Short body.</p>",
SiteID: siteID,
Slug: fmt.Sprintf("scale-%d", index),
StoryID: uint32(seoScaleStoryOffset + index),
IsOnline: true,
Noindex: index%seoScaleHiddenEvery == 0,
})
}
if err := db.CreateInBatches(rows, seoScaleBatch).Error; err != nil {
t.Fatalf("bulk insert: %v", err)
}
}
func expectedSEOScale(hidden bool, titled bool) int {
total := 0
for index := 0; index < seoScaleVersions; index++ {
if (index%seoScaleHiddenEvery == 0) == hidden && (!titled || index%seoScaleTitledEvery == 0) {
total++
}
}
return total
}
func TestArticleStoryListSEOFilterAtScaleSQLite(t *testing.T) {
db := openContractSQLite(t)
site := storageDefaultSite(t, db)
seedSEOScale(t, db, site.ID)
repository := &countingStoryRepository{ArticleRepository: persistence.NewArticleRepository(db)}
list := useCases.NewListArticlesUseCase(repository, &fakeBodyRenderer{}, persistence.NewSiteRepository(db), persistence.NewLocalizationRepository(db))
cases := []struct {
name string
seo string
q string
hidden bool
titled bool
}{
{"More versions with suggestions than the database accepts bind parameters", domainArticle.SEOStatusSuggestions, "", false, false},
{"The hidden versions among them", domainArticle.SEOStatusHidden, "", true, false},
{"The SEO filter combines with a title search run once", domainArticle.SEOStatusSuggestions, seoScaleTitleFragment, false, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
repository.titleSearches, repository.pageLoads = 0, 0
want := expectedSEOScale(c.hidden, c.titled)
page, err := list.ListStoriesPage(useCases.ListStoriesCommand{Caller: ownerCaller(1), Page: 2, Size: seoScalePageSize, SEO: c.seo, Q: c.q})
if err != nil {
t.Fatalf("list: %v", err)
}
if page.Total != want || page.Counts.All != want {
t.Errorf("total %d, counts %+v, want %d", page.Total, page.Counts, want)
}
if len(page.Rows) != min(seoScalePageSize, max(want-seoScalePageSize, 0)) {
t.Errorf("rows = %d", len(page.Rows))
}
for _, row := range page.Rows {
if row.SEO.Status != c.seo || row.Article.Body == "" {
t.Errorf("row %d: status %q body %q", row.Article.ID, row.SEO.Status, row.Article.Body)
}
}
wantSearches := 0
if c.q != "" {
wantSearches = 1
}
if repository.titleSearches != wantSearches || repository.pageLoads != 1 {
t.Errorf("title searches %d, page loads %d per request", repository.titleSearches, repository.pageLoads)
}
})
}
}
func TestArticleRepositoryListByStoriesSkipsBodiesAndUnreadableDrafts(t *testing.T) {
db := openContractSQLite(t)
site := storageDefaultSite(t, db)
story := fixtureStory()
versions := []entity.Article{
{Title: "Published", Body: "<p>Published body</p>", SiteID: site.ID, Lang: "en", StoryID: story, Slug: uniqueTestSlug(), IsOnline: true, CreatedBy: localizationOtherUserID},
{Title: "Own draft", Body: "<p>Own body</p>", SiteID: site.ID, Lang: "fr", StoryID: story, Slug: uniqueTestSlug(), CreatedBy: localizationWriterID},
{Title: "Foreign draft", Body: "<p>Foreign body</p>", SiteID: site.ID, Lang: "de", StoryID: story, Slug: uniqueTestSlug(), CreatedBy: localizationOtherUserID},
}
if err := db.Create(&versions).Error; err != nil {
t.Fatal(err)
}
repository := persistence.NewArticleRepository(db)
writer := localizationWriterID
cases := []struct {
name string
readable domainArticle.StoryFilters
titles []string
}{
{"An editor reads every version", domainArticle.StoryFilters{}, []string{"Published", "Own draft", "Foreign draft"}},
{"An own-scope caller reads the published versions and their drafts", domainArticle.StoryFilters{VisibleTo: &writer}, []string{"Published", "Own draft"}},
{"A reader reads the published versions only", domainArticle.StoryFilters{OnlyOnline: true}, []string{"Published"}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
loaded, err := repository.ListByStories([]uint32{story}, c.readable)
if err != nil {
t.Fatal(err)
}
titles := []string{}
for _, version := range loaded {
titles = append(titles, version.Title)
if version.Body != "" {
t.Errorf("version %q carries its body", version.Title)
}
}
if fmt.Sprint(titles) != fmt.Sprint(c.titles) {
t.Errorf("titles = %v, want %v", titles, c.titles)
}
})
}
}