Files
RenewCMS/features/language_setup.qa.md
T

4.1 KiB

QA: First-time language setup

Accepted together with features/language_setup.feature.

Automated, no server (task test)

  1. Use case over in-memory fakes: "Apply for every pending site", "The owner may change a suggestion", "Retrying is safe", "A site with no articles still needs confirming", refused rows. Assert sentinels with errors.Is. Assert that the whole apply is one Transactor call and that site-row locks are taken in ascending site id order.
  2. HTTP: NewHandler over the use case, a fresh chi.NewRouter mirroring api/router.go under /v1/language, behind require(role.KeySettingsManageSites), validator.InitValidator(), httptest. Cover 200, 400, 401, 403, 404, 409 and 422 with exact plain-text bodies and the JSON key set. setup_in_progress is simulated by a fake whose apply returns the sentinel.
  3. Regression: slice 1 tests ("Create racing a confirmation", "Assigning a language is idempotent") untouched.

Automated, SQLite file (task test)

  1. Fixture: the four sites above with 8, 3, 5 and 0 articles, one soft-deleted. POST applies; assert ids, titles, bodies, slugs, is_online, story ids and timestamps unchanged; soft-deleted articles are assigned too; no pending site remains; a second POST changes nothing (hash of all rows). A site created after migration with no row is listed and can be confirmed.
  2. Atomicity: inject a failing write on the second site; assert no row is confirmed and no article is assigned; the next POST completes.
  3. Concurrency: goroutines creating articles during setup; every final lang equals the confirmed tag; bound the SQLITE_BUSY retries. Two concurrent POSTs: one 200, the other 200 or 409 setup_in_progress, never a deadlock.

Automated, real databases (task test:mysql, task test:postgres)

  1. One targeted run of steps 4 to 6 on MySQL 8.4, MariaDB 11 and PostgreSQL, sharing the run planned in the site_languages QA. Check lock ordering for deadlocks. Record NOT RUN if the daemon is down, or use the podman shim.

Automated (Vitest + Vue Test Utils, task test:web)

  1. One spec per row of "Setup states". Assert the copy, the disabled confirm button until every row has a language, that Journal and Archive are seeded and Notes is not, the single POST body, and the 409 / 500 transitions.
  2. Mock set: GET with Journal, Notes, Archive (8, 3, 5 articles); GET with only Notes; GET with only Gallery; GET with nothing pending; GET with a substituted suggestion; POST 200 { assigned: 16 }; POST 409; POST 500.
  3. Route guard: a reader is redirected; an owner reaches the page; the back link goes to /settings/sites/:key/languages, and to Settings, Sites for an unknown :key.
  4. The Languages page banner links to /settings/sites/:key/languages/setup when needs_setup is true.
  5. task test:web, the type check, bun lint, and a diff check for comments.

Visual (task visual:check -- localization-site-languages, hard gate at 10 %)

  1. Run once at the end of the slice (group L2, 7 states) with the board fixtures Journal, Notes, Archive and Gallery (SETUP in locale-fixtures.js: 8, 3 and 5 articles). Desktop 1440 and mobile 390, light and dark.
  2. Not checked visually (no frame): nothing-pending, unknown :key, substituted seed.

Assumptions recorded for gaps without a frame

  • Nothing pending: the Done alert without a count.
  • Substituted seed: the Languages "broader" wording.
  • Unknown or complete 🔑 the global list is shown and the back link falls back to Settings, Sites.
  • Setup route: one global page (Option 1), reached from a per-site banner.

Fix round additions

  • Skipped list and stable lock loop: TestLanguageSetupSkippedChoices, TestLanguageSetupLocksUntilStable (test/localization_fix_http_test.go).
  • Setup activity events: TestLanguageActivity.
  • Two simultaneous setups on SQLite: TestLanguageConcurrencyOnSQLite.

Manual

  • Keyboard order and focus return after "Try again" and "Check again".
  • Real two-owner run: start setup in two browsers and confirm the second sees "Setup is already running".
  • A copy of a real database with articles: run setup, check the articles list and one public read still match, then restart and confirm nothing changes.