Skip to content
All skills

Testing and release

Claude skill atlas-testing-release · for Release, Frontend, Backend

How Atlas is tested and shipped - the test layers (unit, DB on PGlite, Playwright e2e, smoke, Storybook, Lighthouse), which command to run when, CI jobs and required checks, nightly full tests, PR previews, the migrate-dev and deploy flows, checking production after a deploy, commits, backups, uptime and incidents. Use when writing tests, opening or merging a PR, changing a workflow, deploying, or when production looks wrong.

Atlas ships small PRs through one pipeline: PR → required CI checks → squash merge to main → deploy.yml (migrations first, then Vercel). Nobody deploys production by hand and nobody force-pushes main. One task = one branch = one PR.

Before you start

  • GitHub Actions: all workflows. Production: https://atlas-of-todays-world.vercel.app.
  • Supabase: production and atlas-dev. Tests run against atlas-dev only.
  • Related skills: atlas-backend (DB tests, migrations), atlas-security (workflow hardening), atlas-performance (Lighthouse budgets), atlas-accessibility (axe).
  • Incident and restore steps: reference.

Test layers

LayerWhereCommandNotes
Staticwhole reponpm run typecheck, npm run lint, npm run format:checktypecheck runs next typegen first; lint fails on any warning
Unitsrc/**/*.test.ts next to the code, guards in tests/unit/npm testVitest, Node env, server-only stubbed. Only .test.ts files are picked up
Coveragesrc/lib/security/**npm run test:coverage100 % statements, branches, functions, lines
DBsupabase/tests/*.test.mjsnpm run test:dbPGlite, no Docker; list new files in the script (atlas-backend)
E2Etests/*.spec.tsnpm run build, then npx playwright test [file]Desktop Chrome + Pixel 7; starts npm run start; account specs need the atlas-dev service key and skip otherwise
Accessibilitytests/a11y.spec.tspart of e2eaxe, WCAG 2.1 A/AA, fails on serious or critical
Smokescripts/smoke-test.mjsBASE_URL=... npm run test:smokeNo browser; fetches key pages; safe against production
Components*.stories.tsxnpm run storybook, npm run build-storybookEvery shared component has a story
Performancelighthouserc.jsonnightly onlyBudgets in atlas-performance
Linksscripts/check-links.mjsnpm run check:linksWeekly crawl of the live sitemap

Guards in tests/unit/ protect the architecture: client-bundle (no zod or @supabase/ssr in client code), migration-versions, cache-purge (no cache refresh after a zero-row write), route-table, public-form-errors, sanitized-length.

What to run when

You changedRun before pushing
Anythingnpm run typecheck && npm run lint && npm test
A migration, RLS, RPC or seednpm run test:db
A page, layout, route or next.config.tsnpm run verify 2>&1 | tee build.log (builds into .next-build, so next dev keeps running), then node scripts/check-static-routes.mjs build.log: public routes must not turn ƒ (dynamic)
A user flow, admin screen or formbuild, then the matching npx playwright test tests/<area>.spec.ts
A shared componentits story in Storybook
Moved or deleted codenpm run check:dup && npm run check:unused (atlas-dedup)
src/lib/security/**npm run test:coverage
  • A bug fix starts with a test that reproduces it.
  • Server Action: unit test the validation and the result mapping (mock the Supabase client, as src/features/auth/actions.test.ts does) and a DB test for allowed / denied.
  • Prefer role-based locators (getByRole) and web-first assertions in Playwright; no fixed waits.
  • Never point e2e at production. tests/support/accounts.ts refuses the production ref.

CI (.github/workflows/ci.yml)

Runs on every PR and push to main. Its two job names are the required checks on main. Do not rename them (branch rules):

  • "Typy, lint, testy": typecheck → lint → format:check → test → check:audit.
  • "Build, smoke a e2e": build (log saved) → scripts/check-static-routes.mjs → start → test:smoke. Despite the name it runs no e2e. Uses atlas-dev public values.

Full tests (full-tests.yml) run nightly at 01:30 UTC and on demand: Storybook build, coverage, test:db, jscpd, knip, audit, e2e in 3 shards against atlas-dev, and Lighthouse. They never block a deploy. A red nightly run means "fix it before the next feature". Run it manually on your branch (Run workflow) when a PR touches DB, auth or e2e-covered flows.

PR rules

  • Branch from main; one task per branch. Fill in the PR template (what and why, how tested, Definition of Done, Security impact for auth / RLS / migrations / proxy / lib/security).
  • PR title = commit message (squash merge). Use English Conventional Commits: feat(scope): ..., fix(scope): ..., refactor: ..., chore(deps): ..., content: ..., test: ..., docs: .... Imperative, lower case after the colon. Breaking: feat!:.
  • Pre-commit (.husky/pre-commit) runs lint-staged: ESLint --fix + Prettier on staged ts/tsx/mjs, Prettier on json/css/yml. Do not bypass it with --no-verify.
  • Previews: a PR gets a Vercel preview only with the preview label (not from forks or Dependabot). Previews read the production database and run no migrations: never test writes there, and a preview cannot show a new migration.

Migrations to atlas-dev

  • Migrate atlas-dev (migrate-dev.yml) applies new migrations to atlas-dev on every push to main that touches supabase/migrations/**. The script refuses the production ref.
  • When e2e or a teammate needs your migration before merge, run that workflow on your branch (Run workflow → your branch). From then on, the file is applied and must not change.
  • Confirm in atlas-dev migrations.

Deploy (.github/workflows/deploy.yml)

  1. Squash merge to main → CI runs on main.
  2. CI green → Deploy to Vercel starts (workflow_run). Job migrate: supabase db push --include-seed to production.
  3. Job deploy (only if migrate succeeded): vercel deploy --prod; the build runs on Vercel (deployments).
  4. A failed migration stops the deploy and production keeps the old app. Check which migrations were applied in production migrations and fix forward with a new migration in a new PR.

Never run vercel deploy --prod or supabase db push against production yourself, and never push straight to main.

Check production after a deploy

Within a few minutes of the deploy run turning green:

  1. curl -s https://atlas-of-todays-world.vercel.app/api/health → "status":"ok", "db":"ok" and "version" equal to the first 7 characters of the merge commit SHA.
  2. curl -sI https://atlas-of-todays-world.vercel.app/topics | grep -i x-vercel-cache → HIT (or PRERENDER on the first request). A steady MISS or no header on a public page means it went dynamic: treat it as a bug.
  3. BASE_URL=https://atlas-of-todays-world.vercel.app npm run test:smoke → all checks pass.
  4. Open the page you changed and, for admin changes, do one real save in the admin.
  5. Watch Vercel logs and Supabase logs for new errors.

Owner rule: follow every deploy until the change is live, then tell the owner. After any push that ends in a production deploy, do not stop at "pushed": follow the CI run and the Deploy to Vercel run (gh run list --commit <sha>, gh run watch <id>), then check the live site https://atlas-of-todays-world.vercel.app (/api/health version = the commit, then the changed page itself) until the change actually shows there. (The domain www.atlasoftodaysworld.org still serves the old Webflow site; check it too once it points to this app.) As soon as it does, tell the owner it is live and where to see it. If CI, the migration or the deploy fails, or the change does not show, tell the owner straight away with what failed.

Operations

  • Uptime: Keep-alive calls /api/health daily at 06:17 UTC (it also keeps Supabase Free awake; a failure emails the owner). An external monitor (UptimeRobot, owner's account) watches /api/health and a public page.
  • Backups: Supabase Free has no platform backups. Záloha DB (backup.yml, 02:40 UTC) dumps schema, roles and data, encrypts with age to the owner's key (variable BACKUP_AGE_RECIPIENT) and keeps the artifact 14 days. Storage files are not in the dump.
  • Restore drill quarterly into a throwaway database, never production or atlas-dev: steps in reference.
  • Dead links: Kontrola odkazů every Monday 05:41 UTC.
  • Rollback: revert the PR through a new PR (the normal pipeline). DB changes are never rolled back with "down" migrations; fix forward. Vercel Instant Rollback is the owner's emergency call.

Checklist

  • [ ] Right tests for the change (table above) pass locally; a bug fix has a reproducing test.
  • [ ] New DB test file is listed in test:db.
  • [ ] PR title is a Conventional Commit; template filled in, Security impact where needed.
  • [ ] Both required checks green; migration applied to atlas-dev before e2e relies on it.
  • [ ] After merge: deploy run green, /api/health shows the new version, X-Vercel-Cache: HIT, smoke passes.
  • [ ] The change is visible on the live site and the owner has been told it is live.

Anti-patterns

  • Testing admin writes on a preview deployment: it writes to production data.
  • Renaming a CI job: the required check disappears and merges stall (or pass unchecked).
  • --no-verify, force-push to main, or a manual production deploy "just this once".
  • Editing a migration after migrate-dev applied it: atlas-dev and production drift.
  • Treating a red nightly run as noise: it is the only run of e2e, DB tests and Lighthouse.

Sources

  • Playwright: Best practices - https://playwright.dev/docs/best-practices
  • Kent C. Dodds: The testing trophy - https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications
  • Conventional Commits 1.0.0 - https://www.conventionalcommits.org/en/v1.0.0/
  • Supabase: Database backups - https://supabase.com/docs/guides/platform/backups
  • Google SRE: Postmortem culture - https://sre.google/sre-book/postmortem-culture/
  • Trunk-based development - https://trunkbaseddevelopment.com/

Files of this skill

reference.md

Testing and release reference

Incident basics, restore drill and workflow map for atlas-testing-release.

Workflow map

WorkflowFileWhenWhat
CIci.ymlevery PR, push to mainthe two required checks
Deploy to Verceldeploy.ymlafter green CI on main; PRs with preview labelmigrations to production, then Vercel
Full testsfull-tests.yml01:30 UTC nightly, manualStorybook, coverage, DB tests, jscpd, knip, audit, e2e (3 shards), Lighthouse
Migrate atlas-devmigrate-dev.ymlpush to main touching migrations, manualapplies missing migrations to atlas-dev
Záloha DBbackup.yml02:40 UTC nightly, manualencrypted production dump, 14-day artifact
Keep-alivekeepalive.yml06:17 UTC daily/api/health ping
Kontrola odkazůlinks.ymlMonday 05:41 UTCdead links from the sitemap
Importsimport-topics.yml, import-portraits.yml, backfill-resource-previews.ymlmanual, choose dev or prodcontent imports; run against atlas-dev first

Workflow and job display names partly stay Czech because required checks and history depend on them.

Incident basics

  1. Confirm. /api/health (status, db, version), the page itself, then Vercel status and Supabase status.
  2. Classify. Site down, wrong or stale content, broken feature, or security event.
  3. Stop the harm.
    • Bad deploy: open a revert PR of the merge commit and let the pipeline ship it. If the site is unusable, the owner may use Vercel Instant Rollback in deployments and then merge the revert so main matches production.
    • Content or data problem: put the public site in maintenance in Admin → Switches & maintenance (feature_flags.maintenance, rendered by MaintenanceGate). The admin stays reachable.
    • Bad migration: fix forward with a new migration. Never edit the applied file.
    • Database paused (Supabase Free after inactivity): restore it from the project page; check why keep-alive failed.
    • Leaked secret or suspected breach: follow the key-leak runbook in atlas-security (reference). The GDPR 72-hour clock may apply.
  4. Look at the evidence. Vercel logs, Supabase logs, the failing workflow run, audit_log (Admin → Roles → audit).
  5. Verify the fix with the post-deploy checks in SKILL.md.
  6. Write a blameless postmortem when users saw downtime or wrong data, when data was lost, or when a rollback was needed: impact, timeline, root causes, what went well, action items. Keep it in the private docs/ (gitignored); action items become tasks in PLAN-REALIZACE.md.

Restore drill (quarterly)

Goal: prove that a nightly backup can rebuild the database, and measure how long it takes. Use a throwaway Postgres 17 (local Docker or a temporary Supabase project). Never production, never atlas-dev.

  1. Download the latest atlas-db-backup artifact from a Záloha DB run.
  2. Decrypt with the owner's private key: age -d -i <key file> atlas-db-<stamp>.tar.gz.age | tar xz (gives roles.sql, schema.sql, data.sql). Keep the files on an encrypted disk and delete them afterwards.
  3. Restore in one transaction: psql --single-transaction --variable ON_ERROR_STOP=1 --file roles.sql --file schema.sql --command 'SET session_replication_role = replica' --file data.sql --dbname <throwaway URL>.
  4. Compare row counts of key tables (entries, regions, profiles, site_pages) with production (read-only query in the production SQL editor).
  5. Note the backup age (how much data you would lose) and the time taken. Record the result in the private notes. Storage files are not in the dump: check that the buckets still hold the files the restored content references.

Release hygiene

  • Merge only with both required checks green and the PR template complete.
  • Keep PRs small; a PR that needs a migration and a contract step is two PRs.
  • After merging, watch the deploy run to the end and do the production checks.
  • Dependabot PRs: read the changelog for minor bumps of next, @supabase/*, maplibre-gl; run the full tests on the branch when in doubt.

This site uses only the cookies it needs to work — no advertising or tracking cookies. Visits are counted without cookies. Privacy