Skip to content

ci(auth): run the Better Auth integration suite in CI - #8381

Merged
BhagyaAmarasinghe merged 10 commits into
epic/better-auth-migrationfrom
ci/wire-better-auth-integration-tests
Jul 1, 2026
Merged

ci(auth): run the Better Auth integration suite in CI#8381
BhagyaAmarasinghe merged 10 commits into
epic/better-auth-migrationfrom
ci/wire-better-auth-integration-tests

Conversation

@xernobyl

Copy link
Copy Markdown
Contributor

What does this PR do?

Wires the Better Auth integration suite (apps/web/integration/* + the *.integration.test.ts files) into CI. That harness is the only runtime validation of the Better Auth ↔ Formbricks boundary — it drives the real Better Auth handler against a real Postgres + Redis (no DB mocks), covering sign-up/in/out, email verification + password reset, 2FA, SSO provisioning, account deletion, the forward-auth proxy session read, and the recent security guards (password-less delete-user + inactive-user sign-in). Until now no workflow ran it: pnpm test mocks @formbricks/database and the e2e job is Playwright, so these ~35 tests only ran when someone ran them locally.

Changes:

  • New .github/workflows/integration-tests.yml — stands up Postgres (pgvector) + Valkey services, provisions a fresh formbricks_ba_test (migrate + the two Better-Auth-shape ALTERs: email_verified → boolean, Account.type nullable), and runs pnpm test:integration. Services / Node / pnpm / .env setup mirror e2e.yml.
  • global-setup.ts — a TEST_DB_PROVISIONED escape hatch. The harness normally provisions the test DB via docker exec <container> pg_dump|psql, which can't run against GitHub Actions service containers (reached over localhost, not docker exec-able by name). When TEST_DB_PROVISIONED=1, global-setup trusts the workflow's out-of-band provisioning and skips the docker clone (per-test isolation still comes from resetDb). Local dev behavior is unchanged when the flag is unset.
  • fix(test): the 2FA integration test was already broken on the epic — it queried the old plural twoFactors relation and asserted .toHaveLength(1), but 51a770cdb made TwoFactor one-per-user (User.twoFactor TwoFactor?, @@unique(userId)). It went unnoticed precisely because the suite wasn't in CI. Fixed to the singular relation. (This is the first thing the new CI gate caught.)

Base: epic/better-auth-migration (the harness lives on the epic, not main, so the workflow has to land where it can run).

How should this be tested?

Verified locally against the dev Postgres + Valkey, both provisioning paths:

  • Escape-hatch / CI path (exact): fresh formbricks_ba_test → migrated from scratch (confirmed TwoFactor_userId_key unique index) → the two ALTERs (email_verified confirmed boolean) → TEST_DB_PROVISIONED=1 pnpm test:integration35/35 pass.
  • Local docker path (flag unset): pnpm test:integration → global-setup's docker clone still runs → 35/35 pass (no regression to local dev).
  • Authoritative check: the new Integration Tests job on this PR.

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read How we Code at Formbricks
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand bits (the escape hatch + the workflow's provisioning steps)
  • Ran pnpm build (the @formbricks/database package, for the migration runner)
  • Checked for warnings, there are none
  • Removed all console.logs (none added)
  • Merged the latest changes from main — N/A: this targets epic/better-auth-migration and is branched off its current head
  • My changes don't cause any responsiveness issues (no UI changes)
  • First PR at Formbricks? — no

Appreciated

  • If a UI change was made — N/A
  • Updated the Formbricks Docs — N/A

xernobyl added 2 commits June 29, 2026 11:44
…elation

Commit 51a770c made TwoFactor one-per-user (User.twoFactor TwoFactor?,
@@unique(userId)), but the 2FA integration test still queried the old plural
`twoFactors` relation and asserted .toHaveLength(1). It has been broken since that
change, unnoticed because the integration suite isn't run in CI. Use the singular
relation and assert the row is present.
The integration harness (apps/web/integration + the *.integration.test.ts files) is the
only runtime validation of the BA<->Formbricks boundary against a real Postgres + Redis
(no DB mocks), but no workflow ran it — `pnpm test` mocks @formbricks/database and e2e is
Playwright. Add an integration-tests workflow that stands up Postgres + Valkey services,
provisions a fresh formbricks_ba_test (migrate + the two BA-shape ALTERs), and runs
`pnpm test:integration`.

GitHub Actions service containers aren't reachable via `docker exec`, so global-setup's
schema clone can't run in CI. Add a TEST_DB_PROVISIONED escape hatch: when set, global-setup
trusts the out-of-band provisioning and skips the docker clone (+ redis flush; a fresh CI
Valkey starts empty). Local dev behavior is unchanged when the flag is unset.
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

A new GitHub Actions workflow .github/workflows/integration-tests.yml is added to run Better Auth integration tests in CI using real Postgres (with pgvector) and Valkey services. The workflow provisions services, configures the environment, creates a test database, runs migrations, applies SQL adjustments to User and Account tables, and executes tests with TEST_DB_PROVISIONED="1". The test harness global-setup.ts is updated to skip docker-based DB provisioning and Redis flush when that flag is set. A Prisma relation name in the two-factor integration test is corrected from twoFactors to twoFactor, with the assertion updated accordingly.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately summarizes the main change: adding Better Auth integration tests to CI.
Description check ✅ Passed The PR description is detailed and includes purpose, changes, testing, and checklist sections required by the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/integration-tests.yml:
- Around line 83-95: The Better Auth schema shaping is duplicated between the
workflow step and the integration setup, which can cause CI and local
provisioning to drift. Move the ALTER TABLE patch logic into one shared script
or SQL entry point and call it from both the workflow and the setup code, using
the existing integration setup path (global-setup.ts) and the workflow migration
step as the two callers.
- Around line 72-77: The ENTERPRISE_LICENSE_KEY replacement in the
integration-tests workflow is interpolating a secret directly into a
double-quoted sed command, which can break on special characters or trigger
shell expansion. Update the .env rewrite step in the workflow job that edits
REDIS_URL, DATABASE_URL, and ENTERPRISE_LICENSE_KEY to pass the secret via env:
and use a safer data-only replacement approach in that same step rather than
embedding the secret in the sed expression.
- Around line 53-54: The workflow’s initial checkout step still persists the
GITHUB_TOKEN in local git config even though no later step needs git auth.
Update the actions/checkout usage in this integration test workflow to disable
persisted credentials by setting persist-credentials to false, while keeping the
existing dangerous-git-checkout step unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 25f3f351-d87c-4963-ba92-46263ca78b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 9a85417 and 8617d52.

📒 Files selected for processing (3)
  • .github/workflows/integration-tests.yml
  • apps/web/integration/global-setup.ts
  • apps/web/modules/auth/lib/auth-two-factor.integration.test.ts

Comment thread .github/workflows/integration-tests.yml
Comment thread .github/workflows/integration-tests.yml
Comment thread .github/workflows/integration-tests.yml Outdated
xernobyl added 4 commits June 29, 2026 11:53
The first CI run failed: all 11 suites errored with "Failed to resolve entry for
package @formbricks/logger". After a fresh install the workspace packages aren't built,
so their package.json `exports` point at missing dist/. Build web's workspace deps
(`--filter=@formbricks/web^...`, deps only — not the Next app) before running the suite,
mirroring how e2e.yml builds before its package tests.
- Centralize the Better Auth schema-shape ALTERs into apps/web/integration/ba-test-schema-shape.sql,
  applied by both global-setup.ts (local) and the workflow (CI, via psql -f) — removes the duplication
  that could drift CI vs local provisioning.
- Drop the ENTERPRISE_LICENSE_KEY sed: the harness calls enterprise-gated flows directly and the suite
  is green with it empty, so the secret-into-sed interpolation (template-injection / special-char
  breakage) is gone by removing the need entirely.
- Set persist-credentials: false on the initial checkout — no later step needs git auth
  (dangerous-git-checkout re-authenticates its own checkout).
…e positive

The doc comment line ending in "... Account.type as NOT NULL;" tripped SonarQube's
commented-out-code rule (S125) — it's prose, not disabled SQL. Reword to plain English
(no trailing semicolons / SQL keywords). The ALTER statements are unchanged.
- paths: packages/** (was just database+cache) — the harness transitively imports
  @formbricks/logger, types, email, …; a change to any of those can affect the BA
  boundary, so they should trigger the suite too.
- add a concurrency group (matches pr.yml) so a new push cancels superseded runs.
xernobyl added 3 commits June 29, 2026 15:03
CodeQL flagged the prisma-generate invocation: it built a shell command string by interpolating
import.meta.url-derived absolute paths (dbDir, testSchema), so a checkout path with shell
metacharacters could break or inject. Switch execSync(template-string) → execFileSync('pnpm', [args])
which runs without a shell and passes the paths as literal argv. Build-script only; no behavior change.
global-setup applied the shared ba-test-schema-shape.sql via
`psql -c "<multi-statement>"`, which runs the good ALTERs but exits 0 even
when a later statement errors — so a bad/renamed ALTER would silently leave a
half-shaped formbricks_ba_test that still "passes". That defeats the point of
centralizing the SQL (CI vs local can't drift) since CI applies the same file
with `psql -v ON_ERROR_STOP=1 -f`, which does abort on error.

Apply the file the same way CI does locally: pipe it to `psql -f -` over stdin
with ON_ERROR_STOP, so a failed statement exits non-zero and execSync throws,
aborting the harness loudly. Verified: a deliberately-failing ALTER now stops
globalSetup (exit 1) instead of reaching a green 35/35.

Also surface the otherwise-silent Redis-flush catch with a console.warn — the
skip stays non-fatal, but a genuinely failing flush is no longer invisible.
…ar S4036)

SonarCloud flagged `execFileSync("pnpm", ...)` (S4036): the command was resolved
via $PATH, so a planted `pnpm`/`prisma` on a writable PATH entry could run. Invoke
the Prisma CLI through `process.execPath` (the running Node binary's fixed, absolute
path) + the CLI path resolved from node_modules, so the command is never PATH-looked-up.
Still shell-free (keeps the earlier CodeQL fix). Prisma spawns the schema's custom
generators by bare name, so the child keeps node_modules/.bin on PATH — the fixed,
project-owned .bin dir alongside the resolved prisma package (what `pnpm exec` did).
Comment thread .github/workflows/integration-tests.yml
Comment thread .github/workflows/integration-tests.yml
Address review on the Better Auth integration workflow:
- Add a merge_group trigger (matches pr.yml / docker-build-validation.yml) so the
  suite runs in the merge queue and can be promoted to a required check that gates
  merges. merge_group events ignore paths, so it always runs in the queue.
- Widen the pull_request path filter to pnpm-lock.yaml, root package.json,
  pnpm-workspace.yaml, and the shared dangerous-git-checkout action — a lockfile-only
  Better Auth/Prisma transitive bump or a checkout-action change can alter this job's
  behavior without touching apps/web or packages/**, which would otherwise skip it.
@sonarqubecloud

Copy link
Copy Markdown

@BhagyaAmarasinghe
BhagyaAmarasinghe self-requested a review July 1, 2026 05:39
@BhagyaAmarasinghe
BhagyaAmarasinghe merged commit e733fd3 into epic/better-auth-migration Jul 1, 2026
14 checks passed
@BhagyaAmarasinghe
BhagyaAmarasinghe deleted the ci/wire-better-auth-integration-tests branch July 1, 2026 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants