refac: missing mutation tests, spa specs, checks shown to pass, touch targets, catalogue entry, production code

This commit is contained in:
Florian Sylvain committed 2026-11-04 03:04:35 +01:00
1 parent 7c15206170
commit 3e02e14da4
48 files changed
+2173 -83

No files matched your search

+63 -5
View File
@@ -10,6 +10,7 @@ import (
"slices"
"sort"
"strings"
"sync"
"testing"
"unicode"
@@ -19,6 +20,7 @@ import (
)
type mockArticleRepo struct {
mu sync.Mutex
articles map[uint32]article.Article
failUpdateBodyFor map[uint32]error
nextID uint32
@@ -64,6 +66,8 @@ func (m *mockArticleRepo) withAuthors(a article.Article) article.Article {
}
func (m *mockArticleRepo) ListForAccount(creatorID uint32, authorID *uint32) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("ListForAccount"); err != nil {
return nil, err
}
@@ -91,6 +95,8 @@ func (m *mockArticleRepo) sortedIds() []uint32 {
}
func (m *mockArticleRepo) FindByFilters(filters article.Filters) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("FindByFilters"); err != nil {
return nil, err
}
@@ -106,6 +112,8 @@ func (m *mockArticleRepo) FindByFilters(filters article.Filters) ([]article.Arti
}
func (m *mockArticleRepo) FindPage(filters article.Filters) (article.PageResult, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
if err := m.fail.trigger("FindPage"); err != nil {
return article.PageResult{}, err
@@ -159,6 +167,8 @@ func (m *mockArticleRepo) FindPage(filters article.Filters) (article.PageResult,
}
func (m *mockArticleRepo) CountByVisibility(onlineOnly bool) (article.Counts, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("CountByVisibility"); err != nil {
return article.Counts{}, err
}
@@ -175,6 +185,8 @@ func (m *mockArticleRepo) CountByVisibility(onlineOnly bool) (article.Counts, er
}
func (m *mockArticleRepo) CountVersions(filters article.Filters) (article.Counts, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
if err := m.fail.trigger("CountVersions"); err != nil {
return article.Counts{}, err
@@ -195,26 +207,36 @@ func (m *mockArticleRepo) CountVersions(filters article.Filters) (article.Counts
}
func (m *mockArticleRepo) FindStoryPage(filters article.StoryFilters) (article.StoryPage, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
return article.StoryPage{Refs: []article.StoryRef{}}, m.fail.trigger("FindStoryPage")
}
func (m *mockArticleRepo) CountStories(filters article.StoryFilters) (article.StoryCounts, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
return article.StoryCounts{}, m.fail.trigger("CountStories")
}
func (m *mockArticleRepo) ListByStories(storyIDs []uint32, readable article.StoryFilters) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
return []article.Article{}, m.fail.trigger("ListByStories")
}
func (m *mockArticleRepo) FindTitleMatches(q string, readable article.StoryFilters) ([]uint32, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.listCalls++
return []uint32{}, m.fail.trigger("FindTitleMatches")
}
func (m *mockArticleRepo) CountVisibleTo(userID uint32) (article.Counts, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("CountVisibleTo"); err != nil {
return article.Counts{}, err
}
@@ -270,11 +292,13 @@ func foldArticleSearch(value string) string {
}
func (m *mockArticleRepo) Get(id uint32) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("Get"); err != nil {
return article.Article{}, err
}
if m.onGet != nil {
if err := m.onGet(id); err != nil {
if err := outsideLockErr(&m.mu, func() error { return m.onGet(id) }); err != nil {
return article.Article{}, err
}
}
@@ -286,6 +310,8 @@ func (m *mockArticleRepo) Get(id uint32) (article.Article, error) {
}
func (m *mockArticleRepo) GetByName(name string) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("GetByName"); err != nil {
return article.Article{}, err
}
@@ -299,6 +325,8 @@ func (m *mockArticleRepo) GetByName(name string) (article.Article, error) {
}
func (m *mockArticleRepo) GetAll() ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
result := make([]article.Article, 0)
for _, id := range m.sortedIds() {
result = append(result, m.articles[id])
@@ -319,6 +347,8 @@ func (m *mockArticleRepo) slugUsedOn(siteID uint32, lang string, slug string, ex
}
func (m *mockArticleRepo) SlugTaken(siteID uint32, lang string, slug string, exceptID uint32) (bool, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("SlugTaken"); err != nil {
return false, err
}
@@ -326,6 +356,8 @@ func (m *mockArticleRepo) SlugTaken(siteID uint32, lang string, slug string, exc
}
func (m *mockArticleRepo) FindByImage(imageID uint32) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("FindByImage"); err != nil {
return nil, err
}
@@ -339,6 +371,8 @@ func (m *mockArticleRepo) FindByImage(imageID uint32) ([]article.Article, error)
}
func (m *mockArticleRepo) ClearImage(imageID uint32) (article.ImageReferences, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("ClearImage"); err != nil {
return article.ImageReferences{}, err
}
@@ -359,6 +393,8 @@ func (m *mockArticleRepo) ClearImage(imageID uint32) (article.ImageReferences, e
}
func (m *mockArticleRepo) RestoreImage(references article.ImageReferences, imageID uint32) error {
m.mu.Lock()
defer m.mu.Unlock()
for _, id := range references.Cover {
a := m.articles[id]
a.CoverImageID = idPointer(imageID)
@@ -373,6 +409,8 @@ func (m *mockArticleRepo) RestoreImage(references article.ImageReferences, image
}
func (m *mockArticleRepo) RepointImage(fromImageIDs []uint32, toImageID uint32) ([]uint32, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("RepointImage"); err != nil {
return nil, err
}
@@ -397,10 +435,12 @@ func (m *mockArticleRepo) RepointImage(fromImageIDs []uint32, toImageID uint32)
}
func (m *mockArticleRepo) Create(a article.Article) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.createCalled = true
if hook := m.beforeCreate; hook != nil {
m.beforeCreate = nil
hook()
outsideLock(&m.mu, hook)
}
if err := m.fail.trigger("Create"); err != nil {
return article.Article{}, err
@@ -439,6 +479,8 @@ func (m *mockArticleRepo) Create(a article.Article) (article.Article, error) {
}
func (m *mockArticleRepo) UpdateBody(id uint32, body string) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
m.updateBodyIDs = append(m.updateBodyIDs, id)
if err := m.fail.trigger("UpdateBody"); err != nil {
return article.Article{}, err
@@ -474,6 +516,8 @@ func (m *mockArticleRepo) storyHasLanguage(storyID uint32, lang string) bool {
}
func (m *mockArticleRepo) GetByLangSlug(siteID uint32, lang string, slug string) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("GetByLangSlug"); err != nil {
return article.Article{}, err
}
@@ -486,6 +530,8 @@ func (m *mockArticleRepo) GetByLangSlug(siteID uint32, lang string, slug string)
}
func (m *mockArticleRepo) ListByStory(storyID uint32) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("ListByStory"); err != nil {
return nil, err
}
@@ -502,12 +548,14 @@ func (m *mockArticleRepo) ListByStory(storyID uint32) ([]article.Article, error)
}
func (m *mockArticleRepo) MoveToSite(ids []uint32, siteID uint32) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("MoveToSite"); err != nil {
return err
}
if hook := m.beforeMove; hook != nil {
m.beforeMove = nil
hook()
outsideLock(&m.mu, hook)
}
snapshot := maps.Clone(m.articles)
for position, id := range ids {
@@ -532,6 +580,8 @@ func (m *mockArticleRepo) MoveToSite(ids []uint32, siteID uint32) error {
}
func (m *mockArticleRepo) Update(id uint32, update article.Update) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("Update"); err != nil {
return article.Article{}, err
}
@@ -541,7 +591,7 @@ func (m *mockArticleRepo) Update(id uint32, update article.Update) (article.Arti
}
if hook := m.beforeUpdate; hook != nil {
m.beforeUpdate = nil
hook()
outsideLock(&m.mu, hook)
a = m.articles[id]
}
if update.IfRevision != nil && a.EditRevision != *update.IfRevision {
@@ -609,6 +659,8 @@ func applySeoUpdate(a *article.Article, update article.Update) {
}
func (m *mockArticleRepo) UpdateIsOnline(id uint32, isOnline bool) (article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("UpdateIsOnline"); err != nil {
return article.Article{}, err
}
@@ -622,6 +674,8 @@ func (m *mockArticleRepo) UpdateIsOnline(id uint32, isOnline bool) (article.Arti
}
func (m *mockArticleRepo) Delete(id uint32) error {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("Delete"); err != nil {
return err
}
@@ -634,6 +688,8 @@ func (m *mockArticleRepo) Delete(id uint32) error {
}
func (m *mockArticleRepo) FindByBodyContaining(filename string) ([]article.Article, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("FindByBodyContaining"); err != nil {
return nil, err
}
@@ -645,12 +701,14 @@ func (m *mockArticleRepo) FindByBodyContaining(filename string) ([]article.Artic
}
}
if m.afterFindByBody != nil {
defer m.afterFindByBody()
defer outsideLock(&m.mu, m.afterFindByBody)
}
return result, nil
}
func (m *mockArticleRepo) AssignUnassignedLanguage(siteID uint32, lang string) (int, error) {
m.mu.Lock()
defer m.mu.Unlock()
if err := m.fail.trigger("AssignUnassignedLanguage"); err != nil {
return 0, err
}