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
| Layer | Where | Command | Notes |
|---|---|---|---|
| Static | whole repo | npm run typecheck, npm run lint, npm run format:check | typecheck runs next typegen first; lint fails on any warning |
| Unit | src/**/*.test.ts next to the code, guards in tests/unit/ | npm test | Vitest, Node env, server-only stubbed. Only .test.ts files are picked up |
| Coverage | src/lib/security/** | npm run test:coverage | 100 % statements, branches, functions, lines |
| DB | supabase/tests/*.test.mjs | npm run test:db | PGlite, no Docker; list new files in the script (atlas-backend) |
| E2E | tests/*.spec.ts | npm 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 |
| Accessibility | tests/a11y.spec.ts | part of e2e | axe, WCAG 2.1 A/AA, fails on serious or critical |
| Smoke | scripts/smoke-test.mjs | BASE_URL=... npm run test:smoke | No browser; fetches key pages; safe against production |
| Components | *.stories.tsx | npm run storybook, npm run build-storybook | Every shared component has a story |
| Performance | lighthouserc.json | nightly only | Budgets in atlas-performance |
| Links | scripts/check-links.mjs | npm run check:links | Weekly 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 changed | Run before pushing |
|---|---|
| Anything | npm run typecheck && npm run lint && npm test |
| A migration, RLS, RPC or seed | npm run test:db |
A page, layout, route or next.config.ts | npm 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 form | build, then the matching npx playwright test tests/<area>.spec.ts |
| A shared component | its story in Storybook |
| Moved or deleted code | npm 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.tsdoes) 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.tsrefuses 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 stagedts/tsx/mjs, Prettier onjson/css/yml. Do not bypass it with--no-verify. - Previews: a PR gets a Vercel preview only with the
previewlabel (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 tomainthat touchessupabase/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)
- Squash merge to
main→ CI runs onmain. - CI green → Deploy to Vercel
starts (
workflow_run). Jobmigrate:supabase db push --include-seedto production. - Job
deploy(only ifmigratesucceeded):vercel deploy --prod; the build runs on Vercel (deployments). - 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:
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.curl -sI https://atlas-of-todays-world.vercel.app/topics | grep -i x-vercel-cache→HIT(orPRERENDERon the first request). A steadyMISSor no header on a public page means it went dynamic: treat it as a bug.BASE_URL=https://atlas-of-todays-world.vercel.app npm run test:smoke→ all checks pass.- Open the page you changed and, for admin changes, do one real save in the admin.
- 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/healthdaily at 06:17 UTC (it also keeps Supabase Free awake; a failure emails the owner). An external monitor (UptimeRobot, owner's account) watches/api/healthand a public page. - Backups: Supabase Free has no platform backups. Záloha DB
(
backup.yml, 02:40 UTC) dumps schema, roles and data, encrypts withageto the owner's key (variableBACKUP_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/healthshows 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
previewdeployment: it writes to production data. - Renaming a CI job: the required check disappears and merges stall (or pass unchecked).
--no-verify, force-push tomain, or a manual production deploy "just this once".- Editing a migration after
migrate-devapplied 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
| Workflow | File | When | What |
|---|---|---|---|
| CI | ci.yml | every PR, push to main | the two required checks |
| Deploy to Vercel | deploy.yml | after green CI on main; PRs with preview label | migrations to production, then Vercel |
| Full tests | full-tests.yml | 01:30 UTC nightly, manual | Storybook, coverage, DB tests, jscpd, knip, audit, e2e (3 shards), Lighthouse |
| Migrate atlas-dev | migrate-dev.yml | push to main touching migrations, manual | applies missing migrations to atlas-dev |
| Záloha DB | backup.yml | 02:40 UTC nightly, manual | encrypted production dump, 14-day artifact |
| Keep-alive | keepalive.yml | 06:17 UTC daily | /api/health ping |
| Kontrola odkazů | links.yml | Monday 05:41 UTC | dead links from the sitemap |
| Imports | import-topics.yml, import-portraits.yml, backfill-resource-previews.yml | manual, choose dev or prod | content imports; run against atlas-dev first |
Workflow and job display names partly stay Czech because required checks and history depend on them.
Incident basics
- Confirm.
/api/health(status, db, version), the page itself, then Vercel status and Supabase status. - Classify. Site down, wrong or stale content, broken feature, or security event.
- 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
mainmatches production. - Content or data problem: put the public site in maintenance in
Admin → Switches & maintenance
(
feature_flags.maintenance, rendered byMaintenanceGate). 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.
- 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
- Look at the evidence. Vercel logs,
Supabase logs,
the failing workflow run,
audit_log(Admin → Roles → audit). - Verify the fix with the post-deploy checks in SKILL.md.
- 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 inPLAN-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.
- Download the latest
atlas-db-backupartifact from a Záloha DB run. - Decrypt with the owner's private key:
age -d -i <key file> atlas-db-<stamp>.tar.gz.age | tar xz(givesroles.sql,schema.sql,data.sql). Keep the files on an encrypted disk and delete them afterwards. - 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>. - Compare row counts of key tables (
entries,regions,profiles,site_pages) with production (read-only query in the production SQL editor). - 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.