feat: editor write path

This commit is contained in:
Florian Sylvain committed 2026-11-02 03:24:30 +01:00
1 parent d9158025d4
commit a0b3c58c52
88 files changed
+5738 -263

No files matched your search

+25 -11
View File
@@ -234,11 +234,11 @@ func TestContentScopeArticleUseCases(t *testing.T) {
id uint32
allowed bool
}{
{"update own", updateCall, "content.update", callerWith(21, "content.update=own"), 10, true},
{"update foreign", updateCall, "content.update", callerWith(21, "content.update=own"), 11, false},
{"update unattributed", updateCall, "content.update", callerWith(21, "content.update=own"), 14, false},
{"update all", updateCall, "content.update", callerWith(9, "content.update=all"), 14, true},
{"update refused by a typed none row", updateCall, "content.update", callerWith(25, "content.update=all", "content.update:article=none"), 10, false},
{"update own", updateCall, "content.update", callerWith(21, "content.update=own", "content.read=all"), 10, true},
{"update foreign", updateCall, "content.update", callerWith(21, "content.update=own", "content.read=all"), 11, false},
{"update unattributed", updateCall, "content.update", callerWith(21, "content.update=own", "content.read=all"), 14, false},
{"update all", updateCall, "content.update", callerWith(9, "content.update=all", "content.read=all"), 14, true},
{"update refused by a typed none row", updateCall, "content.update", callerWith(25, "content.update=all", "content.read=all", "content.update:article=none"), 10, false},
{"publish own", publishCall, "content.publish", callerWith(24, "content.publish=own"), 17, true},
{"publish foreign", publishCall, "content.publish", callerWith(24, "content.publish=own"), 10, false},
{"publish is not implied by update", publishCall, "content.publish", callerWith(9, "content.update=all"), 10, false},
@@ -267,15 +267,29 @@ func TestContentScopeArticleUseCases(t *testing.T) {
}
})
t.Run("Update of an article the caller cannot read is not found, after the permission check", func(t *testing.T) {
repo, sites, authors, images := newRepos()
unreadable := repo.articles[10]
unreadable.IsOnline = false
repo.articles[10] = unreadable
snapshot := repo.articles[10]
err := updateCall(repo, sites, authors, images, callerWith(21, "content.update=own"), 10)
if !errors.Is(err, article.ErrArticleNotFound) || !sameArticle(repo.articles[10], snapshot) {
t.Errorf("expected not found and no change, got %v", err)
}
err = updateCall(repo, sites, authors, images, callerWith(21, "content.read=all"), 10)
requireMissingKey(t, err, "content.update")
})
t.Run("A refused update performs no site, author or image lookup and records nothing", func(t *testing.T) {
repo, sites, authors, images := newRepos()
sites.fail.failNext("Get")
authors.fail.failNext("Get")
images.fail.failNext("Get")
recorder := &contentScopeRecorder{}
update := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, recorder, sites, authors, images)
update := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, recorder, sites, authors, images, newFakeLocalizationStore(repo), plainStoryMover(repo))
missingSite := uint32(5)
_, err := update.Update(testCtx, 11, callerWith(21, "content.update=own"), useCases.UpdateArticleCommand{SiteID: &missingSite, AuthorIDs: &[]uint32{7}})
_, err := update.Update(testCtx, 11, callerWith(21, "content.update=own", "content.read=all"), useCases.UpdateArticleCommand{SiteID: &missingSite, AuthorIDs: &[]uint32{7}})
requireMissingKey(t, err, "content.update")
if sites.fail.method != "Get" || authors.fail.method != "Get" || len(recorder.events) != 0 {
t.Errorf("a refused update must not look anything up or record an event")
@@ -284,9 +298,9 @@ func TestContentScopeArticleUseCases(t *testing.T) {
t.Run("An unknown id is not found even under own scope", func(t *testing.T) {
repo, sites, authors, images := newRepos()
update := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images)
update := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images, newFakeLocalizationStore(repo), plainStoryMover(repo))
body := "x"
_, err := update.Update(testCtx, 999, callerWith(21, "content.update=own"), useCases.UpdateArticleCommand{Body: &body})
_, err := update.Update(testCtx, 999, callerWith(21, "content.update=own", "content.read=all"), useCases.UpdateArticleCommand{Body: &body})
if !errors.Is(err, article.ErrArticleNotFound) {
t.Errorf("expected not found, got %v", err)
}
@@ -344,12 +358,12 @@ func sortIDs(ids []uint32) {
func updateCall(repo *mockArticleRepo, sites *mockSiteRepo, authors *mockAuthorRepo, images *mockImageRepo, caller useCases.ContentCaller, id uint32) error {
body := "new"
_, err := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images).Update(testCtx, id, caller, useCases.UpdateArticleCommand{Body: &body})
_, err := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images, newFakeLocalizationStore(repo), plainStoryMover(repo)).Update(testCtx, id, caller, useCases.UpdateArticleCommand{Body: &body})
return err
}
func publishCall(repo *mockArticleRepo, sites *mockSiteRepo, authors *mockAuthorRepo, images *mockImageRepo, caller useCases.ContentCaller, id uint32) error {
_, err := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images).UpdateIsOnline(testCtx, id, caller, true, article.BodyFormatHTML)
_, err := useCases.NewUpdateArticleUseCase(repo, &fakeBodyRenderer{}, discardRecorder, sites, authors, images, newFakeLocalizationStore(repo), plainStoryMover(repo)).UpdateIsOnline(testCtx, id, caller, true, article.BodyFormatHTML)
return err
}