feat: site languages, setup and site URL settings
This commit is contained in:
1 parent
0806733d5c
commit
d9158025d4
106 files changed
+10184
-56
No files matched your search
@@ -0,0 +1,145 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
languagehandler "RenewCMS/api/handlers/language"
|
||||
"RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/site"
|
||||
entity "RenewCMS/internal/infrastructure/persistence/models"
|
||||
persistence "RenewCMS/internal/infrastructure/persistence/repositories"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func newLanguageRouter(db *gorm.DB) http.Handler {
|
||||
validator.InitValidator()
|
||||
sites := persistence.NewSiteRepository(db)
|
||||
reads := persistence.NewLocalizationRepository(db)
|
||||
transactor := persistence.NewTransactor(db)
|
||||
siteLanguages := languagehandler.NewSiteLanguagesHandler(useCases.NewGetSiteLanguagesUseCase(sites, reads), useCases.NewUpdateSiteLanguagesUseCase(transactor, discardRecorder))
|
||||
setup := languagehandler.NewSetupHandler(useCases.NewGetLanguageSetupUseCase(sites, reads), useCases.NewApplyLanguageSetupUseCase(transactor, discardRecorder))
|
||||
router := chi.NewRouter()
|
||||
router.Put("/site/{key}/languages", siteLanguages.Update)
|
||||
router.Post("/language/setup", setup.Apply)
|
||||
return router
|
||||
}
|
||||
|
||||
func sendTo(router http.Handler, method string, path string, body string) int {
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, httptest.NewRequest(method, path, strings.NewReader(body)))
|
||||
return rec.Code
|
||||
}
|
||||
|
||||
func runTogether(jobs ...func()) {
|
||||
var group sync.WaitGroup
|
||||
start := make(chan struct{})
|
||||
for _, job := range jobs {
|
||||
group.Add(1)
|
||||
go func() {
|
||||
defer group.Done()
|
||||
<-start
|
||||
job()
|
||||
}()
|
||||
}
|
||||
close(start)
|
||||
group.Wait()
|
||||
}
|
||||
|
||||
func TestLanguageConcurrencyOnSQLite(t *testing.T) {
|
||||
t.Run("PUT languages racing a site creation answers 200 or 409, never 500, and keeps one default", func(t *testing.T) {
|
||||
db := openContractSQLite(t)
|
||||
journal := seedLocalizedSite(t, db, "journal", []entity.SiteLanguage{{Lang: "en", IsDefault: true, Enabled: true, Confirmed: true}}, map[string][]entity.Article{"en": {{Title: "a", IsOnline: true}}})
|
||||
router := newLanguageRouter(db)
|
||||
read := useCases.NewGetSiteLanguagesUseCase(persistence.NewSiteRepository(db), persistence.NewLocalizationRepository(db))
|
||||
sites := persistence.NewSiteRepository(db)
|
||||
extra := []string{"fr", "de", "es", "ar", "pt-BR", "en-GB"}
|
||||
for round, tag := range extra {
|
||||
view, err := read.Get("journal")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body := languagesBody(view.Revision, "en", entryJSON("en", true, "null"), entryJSON(tag, true, "null"))
|
||||
status := 0
|
||||
var createErr error
|
||||
runTogether(
|
||||
func() { status = sendTo(router, http.MethodPut, "/site/journal/languages", body) },
|
||||
func() {
|
||||
key := fmt.Sprintf("race-%d", round)
|
||||
_, createErr = sites.Create(site.Site{Name: key, Key: key, BaseURL: "https://" + key + ".example.com", DefaultRobots: "index"})
|
||||
},
|
||||
)
|
||||
if status != http.StatusOK && status != http.StatusConflict {
|
||||
t.Errorf("round %d: PUT status %d", round, status)
|
||||
}
|
||||
if createErr != nil {
|
||||
t.Logf("round %d: create refused: %v", round, createErr)
|
||||
}
|
||||
defaults := 0
|
||||
for _, row := range storedLanguageRows(t, db, journal.ID) {
|
||||
if row.IsDefault {
|
||||
defaults++
|
||||
}
|
||||
}
|
||||
if defaults != 1 {
|
||||
t.Errorf("round %d: %d defaults", round, defaults)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Two simultaneous setups answer 200 or 409, at least one 200, and assign every article exactly once", func(t *testing.T) {
|
||||
for round := 0; round < 5; round++ {
|
||||
db := openContractSQLite(t)
|
||||
first := seedLocalizedSite(t, db, "journal", nil, map[string][]entity.Article{"": {{Title: "a"}, {Title: "deleted"}}})
|
||||
second := seedLocalizedSite(t, db, "notes", nil, map[string][]entity.Article{"": {{Title: "b"}, {Title: "c"}}})
|
||||
router := newLanguageRouter(db)
|
||||
body := `{"sites":[{"key":"default","lang":"en"},{"key":"journal","lang":"en"},{"key":"notes","lang":"fr"}]}`
|
||||
statuses := make([]int, 2)
|
||||
runTogether(
|
||||
func() { statuses[0] = sendTo(router, http.MethodPost, "/language/setup", body) },
|
||||
func() { statuses[1] = sendTo(router, http.MethodPost, "/language/setup", body) },
|
||||
)
|
||||
accepted := 0
|
||||
for _, status := range statuses {
|
||||
switch status {
|
||||
case http.StatusOK:
|
||||
accepted++
|
||||
case http.StatusConflict:
|
||||
default:
|
||||
t.Errorf("round %d: status %d", round, status)
|
||||
}
|
||||
}
|
||||
if accepted == 0 {
|
||||
t.Errorf("round %d: no setup succeeded: %v", round, statuses)
|
||||
}
|
||||
if accepted == 2 {
|
||||
t.Logf("round %d: the setups were serialized", round)
|
||||
}
|
||||
if sendTo(router, http.MethodPost, "/language/setup", body) != http.StatusOK {
|
||||
t.Errorf("round %d: a later retry did not succeed", round)
|
||||
}
|
||||
var unassigned int64
|
||||
db.Unscoped().Model(&entity.Article{}).Where("lang = ?", "").Count(&unassigned)
|
||||
if unassigned != 0 {
|
||||
t.Errorf("round %d: %d articles unassigned", round, unassigned)
|
||||
}
|
||||
for siteID, tag := range map[uint32]string{first.ID: "en", second.ID: "fr"} {
|
||||
rows := storedLanguageRows(t, db, siteID)
|
||||
if len(rows) != 1 || rows[0].Lang != tag || !rows[0].IsDefault || !rows[0].Confirmed {
|
||||
t.Errorf("round %d: rows of site %d = %+v", round, siteID, rows)
|
||||
}
|
||||
}
|
||||
var mislabeled int64
|
||||
db.Unscoped().Model(&entity.Article{}).Where("site_id = ? AND lang <> ?", second.ID, "fr").Count(&mislabeled)
|
||||
if mislabeled != 0 {
|
||||
t.Errorf("round %d: %d articles with the wrong language", round, mislabeled)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user