From df3906bc068eac1634ca689c94c10b67ed6219a0 Mon Sep 17 00:00:00 2001 From: "stefan.kunz" Date: Sat, 13 Jun 2026 09:30:56 +0200 Subject: [PATCH] test: add a real auth e2e flow, run against the dev database MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable a database-backed end-to-end test (register -> sign-out -> sign-in) that runs against the local dev database from .env, as the project already foresaw — no separate test database. - e2e/auth.spec.ts: real register -> sign-out -> sign-in flow (unique email per run so it is repeatable). - Rename docker-compose.yml -> compose.yml (auto-detected by both podman and docker) and upgrade PostgreSQL pg16 -> pg17 (compose + CI service). - playwright.config.ts: document that the dev database must be running and migrated before `pnpm test:e2e`. - Docs (README, AGENTS): podman-first compose commands and the e2e workflow (compose up -> db:migrate -> test:e2e). Verified against a podman pg17 container: pnpm test:e2e (3 passed), typecheck, and lint all pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 2 +- AGENTS.md | 4 ++-- README.md | 22 ++++++++++++------ docker-compose.yml => compose.yml | 2 +- e2e/auth.spec.ts | 38 +++++++++++++++++++++++++++++++ playwright.config.ts | 7 ++++-- 6 files changed, 62 insertions(+), 13 deletions(-) rename docker-compose.yml => compose.yml (88%) create mode 100644 e2e/auth.spec.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 718d6a6..3690004 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,7 +91,7 @@ jobs: needs: [build] services: postgres: - image: pgvector/pgvector:pg16 + image: pgvector/pgvector:pg17 env: POSTGRES_DB: postgres_dev POSTGRES_USER: dev_user diff --git a/AGENTS.md b/AGENTS.md index 0d7c26a..c19e457 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ - Use any testing tools, libraries available to the project for testing your changes - Never assume your changes simply work, always test! -- This project uses **Playwright** for end-to-end tests in `e2e/`. Run them with `pnpm test:e2e`. Add a test for new user-facing flows. +- This project uses **Playwright** for end-to-end tests in `e2e/`. They run against your local database (`POSTGRES_URL` from `.env`) — start it with `podman compose up -d` and run `pnpm db:migrate` first, then `pnpm test:e2e`. Add a test for new user-facing flows. - If the project does not have any testing tools, scripts, MCP tools, skills, etc. available for testing, ask the user whether testing should be skipped. ## STACK @@ -45,7 +45,7 @@ ## DEPLOYMENT -- The app builds to a standalone server (`output: "standalone"`) with a `Dockerfile`; it targets any Docker- or Podman-compatible host (Coolify, Hetzner, VPS). The same `Dockerfile` and `docker-compose.yml` work unchanged with `podman build` / `podman compose`. +- The app builds to a standalone server (`output: "standalone"`) with a `Dockerfile`; it targets any Docker- or Podman-compatible host (Coolify, Hetzner, VPS). The same `Dockerfile` and `compose.yml` work unchanged with `podman build` / `podman compose`. - Do **not** run database migrations during the build. `pnpm build` is `next build` only. Run `pnpm db:migrate` as a separate release/pre-deploy step. ## UI DESIGN diff --git a/README.md b/README.md index 2bc20c6..1ca07d3 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Then configure and run the app: ```bash cp env.example .env -docker compose up -d # or: podman compose up -d +podman compose up -d # or: docker compose up -d pnpm db:migrate pnpm dev ``` @@ -113,7 +113,7 @@ OPENROUTER_MODEL="openai/gpt-5-mini" OPENAI_EMBEDDING_MODEL="text-embedding-3-large" ``` -For local development, the default database URL works with the included `docker-compose.yml`. For production, use the database URL from your hosting provider. +For local development, the default database URL works with the included `compose.yml`. For production, use the database URL from your hosting provider. Generate a strong `BETTER_AUTH_SECRET` before deploying. The starter ships with a development value only so you can get moving quickly. @@ -263,7 +263,7 @@ Important root files: - `CLAUDE.md`: Claude entrypoint for the same guidance - `DESIGN.md`: UI design system and component guidance - `drizzle.config.ts`: Drizzle migration configuration -- `docker-compose.yml`: local PostgreSQL service +- `compose.yml`: local PostgreSQL service - `env.example`: environment variable template - `components.json`: shadcn/ui configuration @@ -300,7 +300,7 @@ Do not use schema push as a replacement for migrations in real project work. For local development: ```bash -docker compose up -d # or: podman compose up -d +podman compose up -d # or: docker compose up -d pnpm db:migrate ``` @@ -374,14 +374,22 @@ On Coolify, configure this as a pre-deploy command; in CI, run it behind a manua ## Testing -End-to-end tests live in `e2e/` and run with Playwright: +End-to-end tests live in `e2e/` and run with Playwright against your local +database (the `POSTGRES_URL` from `.env`). Make sure the database is running and +migrated first: ```bash pnpm exec playwright install # one-time: download browsers +podman compose up -d # or: docker compose up -d +pnpm db:migrate pnpm test:e2e ``` -The Playwright config starts the dev server automatically for local runs. In CI, a PostgreSQL service is provisioned, migrations are applied, and the smoke tests run after the build (see `.github/workflows/ci.yml`). +Playwright starts the dev server automatically for local runs. The suite +includes a real register → sign-out → sign-in flow (using a unique email per +run) alongside the static smoke tests. In CI, a PostgreSQL service is +provisioned, migrations are applied, and the suite runs after the build (see +`.github/workflows/ci.yml`). ## Troubleshooting @@ -390,7 +398,7 @@ The Playwright config starts the dev server automatically for local runs. In CI, Confirm Docker (or Podman) is running and start the database: ```bash -docker compose up -d # or: podman compose up -d +podman compose up -d # or: docker compose up -d ``` Then check that `POSTGRES_URL` in `.env` matches the database connection string. diff --git a/docker-compose.yml b/compose.yml similarity index 88% rename from docker-compose.yml rename to compose.yml index b56a353..eba85ce 100644 --- a/docker-compose.yml +++ b/compose.yml @@ -1,7 +1,7 @@ services: postgres: # Pin the major version to match your production database (e.g. Coolify/Hetzner). - image: pgvector/pgvector:pg16 + image: pgvector/pgvector:pg17 environment: POSTGRES_DB: postgres_dev POSTGRES_USER: dev_user diff --git a/e2e/auth.spec.ts b/e2e/auth.spec.ts new file mode 100644 index 0000000..384102d --- /dev/null +++ b/e2e/auth.spec.ts @@ -0,0 +1,38 @@ +import { expect, test } from "@playwright/test"; + +/** + * Database-backed flow: register a fresh account (which auto-signs-in and lands + * on the dashboard), sign out by clearing the session cookie, then sign back in + * with the same credentials. Exercises Better Auth + Postgres end to end. + */ +test("register, sign out, and sign back in", async ({ page }) => { + // Unique email per run so the test is repeatable against a persistent DB. + const email = `e2e-${Date.now()}@example.com`; + const password = "test-password-123"; + + // --- Register ----------------------------------------------------------- + await page.goto("/register"); + await page.getByLabel("Name").fill("E2E Test User"); + await page.getByLabel("Email").fill(email); + await page.getByLabel("Password", { exact: true }).fill(password); + await page.getByLabel("Confirm Password").fill(password); + await page.getByRole("button", { name: "Create account" }).click(); + + // Successful sign-up redirects to the protected dashboard. + await expect(page).toHaveURL(/\/dashboard$/); + await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible(); + + // --- Sign out (drop the session cookie) --------------------------------- + await page.context().clearCookies(); + + // --- Sign back in ------------------------------------------------------- + // Scope to the page form: the header also has a "Sign in" button when logged out. + const signInForm = page.locator("#main-content"); + await page.goto("/login"); + await signInForm.getByLabel("Email").fill(email); + await signInForm.getByLabel("Password", { exact: true }).fill(password); + await signInForm.getByRole("button", { name: "Sign in" }).click(); + + await expect(page).toHaveURL(/\/dashboard$/); + await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible(); +}); diff --git a/playwright.config.ts b/playwright.config.ts index 9a3a745..2b42129 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -19,8 +19,11 @@ export default defineConfig({ use: { ...devices["Desktop Chrome"] }, }, ], - // Starts the app for local runs. In CI, start the server in a separate step - // and point PLAYWRIGHT_BASE_URL at it, or let this reuse it. + // Starts the app for local runs against the database from your .env + // (POSTGRES_URL). Make sure the dev database is running and migrated first: + // podman compose up -d # or: docker compose up -d + // pnpm db:migrate + // In CI, start the server in a separate step and point PLAYWRIGHT_BASE_URL at it. webServer: { command: "pnpm dev", url: baseURL,