package test import ( "RenewCMS/internal/domain/article" "reflect" "slices" ) var _ article.ListingRepository = &mockArticleRepo{} type assessedVersion struct { inputs article.Article credits []uint32 } func seoInputsOf(version article.Article) article.Article { version.SEO = article.SEOSummary{} version.SEOVersion = 0 version.UpdatedAt = version.UpdatedAt.UTC() return version } func (m *mockArticleRepo) seoFresh(version article.Article) bool { assessed, found := m.assessed[version.ID] return found && !version.SEO.Stale() && reflect.DeepEqual(assessed.inputs, seoInputsOf(version)) && slices.Equal(assessed.credits, m.credits[version.ID]) } func (m *mockArticleRepo) StaleSEO(afterID uint32, limit int) ([]article.Article, error) { if err := m.fail.trigger("StaleSEO"); err != nil { return nil, err } stale := []article.Article{} for _, id := range m.sortedIds() { version := m.articles[id] if id <= afterID || m.seoFresh(version) { continue } version.SEO = article.SEOSummary{} stale = append(stale, m.withAuthors(version)) if len(stale) == limit { break } } return stale, nil } func (m *mockArticleRepo) StoreSEO(records []article.SEORecord) error { if err := m.fail.trigger("StoreSEO"); err != nil { return err } if m.assessed == nil { m.assessed = map[uint32]assessedVersion{} } for _, record := range records { version, found := m.articles[record.ArticleID] if !found { continue } version.SEO = record.Summary m.articles[record.ArticleID] = version m.assessed[record.ArticleID] = assessedVersion{inputs: seoInputsOf(version), credits: slices.Clone(m.credits[record.ArticleID])} } return nil } func (m *mockArticleRepo) MarkSiteSEOStale(siteID uint32) error { for id, version := range m.articles { if version.SiteID == siteID { delete(m.assessed, id) } } return nil } func (m *mockArticleRepo) CountSuggestions(filters article.Filters) (int, error) { if err := m.fail.trigger("CountSuggestions"); err != nil { return 0, err } total := 0 for _, version := range m.articles { if mockVisible(version, filters.IsOnline != nil && *filters.IsOnline, filters.VisibleTo) && version.SEO.Status == article.SEOStatusSuggestions { total++ } } return total, nil } func (m *mockArticleRepo) CountSuggestedStories(readable article.StoryFilters) (int, error) { m.listCalls++ if err := m.fail.trigger("CountSuggestedStories"); err != nil { return 0, err } stories := map[uint32]bool{} standalone := 0 for _, version := range m.articles { if !mockVisible(version, readable.OnlyOnline, readable.VisibleTo) || version.SEO.Status != article.SEOStatusSuggestions { continue } if version.StoryID == 0 { standalone++ continue } stories[version.StoryID] = true } return len(stories) + standalone, nil } func mockVisible(version article.Article, onlineOnly bool, visibleTo *uint32) bool { if version.IsOnline { return true } if onlineOnly { return false } return visibleTo == nil || version.CreatedBy == *visibleTo } func (m *mockArticleRepo) ListVersions(ids []uint32) ([]article.Article, error) { m.listCalls++ if err := m.fail.trigger("ListVersions"); err != nil { return nil, err } found := []article.Article{} for _, id := range ids { if version, ok := m.articles[id]; ok { found = append(found, m.withAuthors(version)) } } return found, nil } func (m *mockArticleRepo) DeleteStory(storyID uint32) error { if err := m.fail.trigger("DeleteStory"); err != nil { return err } deleted := 0 for id, version := range m.articles { if version.StoryID == storyID { delete(m.articles, id) delete(m.credits, id) deleted++ } } if deleted == 0 { return article.ErrArticleNotFound } return nil }