163 lines
3.9 KiB
Go
163 lines
3.9 KiB
Go
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) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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 {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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 {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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 {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
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
|
|
}
|