Files
RenewCMS/features/article_localization_storage.qa.md
T

29 lines
3.9 KiB
Markdown

# QA: Article localization storage (backend)
Backend only. Go tests in test/, black-box. No design states. No new endpoints.
## Automated, no server (task test)
1. Language domain: table-driven over "Language tag normalization" and "Suggestion for a site's default language". Assert the sentinel errors with errors.Is.
2. Use case: with fake repositories, run "Creating an article before the default is confirmed" and "after the default is confirmed". Assert that CreateArticle runs inside the Transactor, that it locks the site row (site.Repository.LockSite) and then reads the starting language through the localization port (localization.Repository.Rows, localization.ConfirmedDefaultTag) and passes it to Create as Lang, and that the story is created by the article repository inside the same transaction. The former language.Repository, its SiteLanguageRepository adapter and the unwired ConfirmDefaultLanguageUseCase are retired; the confirmation tests drive ApplyLanguageSetupUseCase. Existing create tests stay green.
3. HTTP: NewHandler over use cases with in-memory fakes, a fresh chi.NewRouter mirroring api/router.go, validator.InitValidator(), httptest. Cover "Article reads expose lang and story_id" for GET /v1/article/{id} with a jwt cookie signed by JWT_SECRET (the test secret), the list GET /v1/article/, and the public read of an online article. Assert that old keys are unchanged, that lang and story_id are the added keys, and that "lang" or "story_id" in a PATCH body is ignored. POST /v1/article/ honours lang, story_id, source_article_id and copy only as defined in features/article_translation_create.feature, and a POST without them behaves as before. Errors stay plain text.
4. Regression: run the full existing article, pagination, publishing and SEO tests untouched.
## Automated, SQLite file (task test)
5. Fixture: a pre-change database built with the old models, holding a site with Locale "fr-CA", 3 articles (1 online, 1 soft-deleted) and article authors. Run Migrate and check the "Migration of an existing site" scenario. Check ids, bodies and slugs are unchanged, distinct story_ids, content_revision 1, the confirmed=false suggestion row, and the marker row.
6. Migrate twice: nothing changes. Compare sqlite_master and a hash of all rows.
7. Atomicity: inject a failing write (a wrapped *gorm.DB callback that errors on the second update). Assert no story_id is set and the marker is absent, then re-run successfully.
8. Unique indexes: (story_id, lang) and (site_id, lang, slug) reject duplicates, and lang "" rows with different stories coexist. The old (site_id, slug) index definition is replaced by (site_id, lang, slug) under the same name idx_articles_site_slug, kept so existing tests keep passing.
9. Concurrency: N goroutines create articles while the confirm-and-assign transaction runs. Assert the final state of "Create racing a confirmation". On SQLite the repository sets busy_timeout only (no WAL), so the test retries SQLITE_BUSY, counts the retries and asserts a bound.
## Automated, real databases (task test:mysql, task test:postgres)
10. Repeat 5-9 on MySQL 8.4, MariaDB 11 and PostgreSQL. Compare the unique indexes through information_schema or pg_indexes. Record NOT RUN if the daemon is down, or use the podman shim (see memory).
11. On MySQL and MariaDB, check the index key lengths (utf8mb4): lang size is at most 35 and the slug index is within limits.
## Build and lint
12. task test, task lint:go, and CGO_ENABLED=0 go build -tags nofrontend ./... pass.
13. docs/ gains the new article fields in the HTTP contract (lang, story_id) for the SPA slices.
14. task mutate scoped to the new use case code with MUTATE_FILES. No comments in the added Go files.
## Manual
15. Back up a real database copy with article rows, start the new binary on it, check the articles list and one public read in a browser (no shape change), and restart to check the migration does not run twice.