Files
RenewCMS/features/mysql_database_driver.qa.md

4.5 KiB

QA: MySQL/MariaDB database driver

Backend only. Go tests in test/, black-box, no live server by default.

Decisions: separate DB_* vars, new DATA_DIR (DB_FILE ignored under mysql), pure-Go logical dump backup, task test:mysql against mysql:8.4 and mariadb:11, SQLite-to-MySQL data migration documented as manual export/import only.

Automated, no server (task test)

  1. Registry: Connect with Driver "sqlite" works against a temp file; Driver "oracle" returns unsupported database driver "oracle"; the registry contains sqlite and mysql.
  2. Env validation: table-driven over the Driver selection and Missing MySQL variable scenarios. SQLite needs no DB_* vars. DB_DRIVER defaults to sqlite. An invalid DB_TLS or a non-numeric pool var returns an error naming the variable.
  3. DSN builder: table-driven over the DSN scenario. Assert host:port, database, parseTime, loc=UTC, charset=utf8mb4, clientFoundRows, tls. Assert that the redacted form used in errors never contains the password.
  4. Settings.DataDir: table-driven (DB_FILE only, DATA_DIR overrides).
  5. Fail fast without a server: point mysql at a closed local port with a 1s timeout. Assert the error mentions the host and port, and not the password.
  6. Pool settings: assert sql.DB stats MaxOpenConnections via a stub opener, or via the real mysql test below.
  7. SQLite regression: build a database file using the pre-change schema (a fixture created with the old model definitions), run Migrate, and assert that the schema (sqlite_master) and the rows are unchanged. The existing suite must pass unchanged.
  8. Backup (SQLite): existing tests unchanged.
  9. Dump writer: unit-test the SQL dump formatter with a fake row source (quoting, NULL, time, bool, binary-safe strings, 3-file retention, existing-file handling, failure leaves no partial file).

Automated, real server (task test:mysql)

Setup: the task runs docker run for mysql:8.4 and, in a second pass, mariadb:11, waits for readiness, exports RENEWCMS_TEST_MYSQL_DSN, runs go test -tags nofrontend ./test/... -run MySQL -count=1, then removes the container. Tests call t.Skip when the var is unset, so task test skips them. The repository tests are parameterised over both drivers (SQLite temp file and the MySQL DSN, each with a fresh database), and run the "Repository behaviour is the same on both drivers" Examples. 10. Migrate on an empty database; table and index existence via information_schema; the backfill role rules. 11. User create/read, case-sensitive username and email, duplicate email, ErrUserNotFound. 12. Invite accept twice, accept with a taken username (invite stays unaccepted), renew with unchanged values. 13. Image duplicates grouping and counts, search with % and _, filename backfill listing. 14. Article body 1 MB, title lookup, online filter. 15. API key unique hashed_key, revoke, last_used. 16. Zero-time round trip, UTC times. 17. Purge settings upsert. 18. Backup: run on a populated MySQL database, assert the .sql file, the 3-file retention, a replay into a second empty database giving identical rows, and that the file contains no password. 19. Wrong password and missing database fail with the "Unable to open the database" prefix and no password in the message.

Build and lint

  1. task lint:go and task test pass. task test:mysql passes on MySQL 8.4 and MariaDB 11 (record both; NOT RUN if Docker is unavailable).
  2. CGO_ENABLED=0 go build -tags nofrontend ./... passes. Cross-compile the 6 task release targets (e.g. GOOS=linux GOARCH=arm64) with CGO_ENABLED=0.
  3. task mutate impact: none, because it only targets useCases. No change.
  4. No comment lines in the added Go files (the hook enforces this).

Manual (needs Compose and real SMTP)

  1. docker-compose.mysql.yml example: renewcms plus a mysql service with a named volume and a healthcheck, depends_on: condition: service_healthy, DB_DRIVER=mysql, DB_HOST=mysql, DATA_DIR=/data. docker compose up: /healthz returns 200, register, log in, create an article, restart the stack, and the data persists.
  2. Stop the mysql container and start renewcms: it exits with a clear error and no password in the logs. docker.env.example holds placeholders only.
  3. In the Docker image (non-root uid 10001), trigger the backup path and confirm the .sql file lands under /data/backups.
  4. Docs: configuration.md, docker.md, deployment.md, .env.example, docker.env.example and the architecture.md "Storage and database seams" section list the new vars and the manual SQLite to MySQL export/import note.
  5. Verify the SQLite default end to end with no new vars set: same behaviour as before.