Skip to content

fix(test): isolate pay tests from eager env.ts to remove parallel-load race#119

Closed
AquiGorka wants to merge 2 commits into
mainfrom
fix/isolate-pay-tests-from-env
Closed

fix(test): isolate pay tests from eager env.ts to remove parallel-load race#119
AquiGorka wants to merge 2 commits into
mainfrom
fix/isolate-pay-tests-from-env

Conversation

@AquiGorka

Copy link
Copy Markdown
Contributor

Summary

Fixes the flaky test CI job (the pay unit tests intermittently failing 34 passed / 11 failed with Error: DATABASE_URL is not loaded). The failure is a parallel-module-load race, not a logic bug, and it is independent of any feature PR — re-running known-green code (feat/pg-notify-event-bus) in the current CI environment reproduces it identically.

Root cause

The pay tests are unit tests that mock their infra deps in src/http/v1/pay/tests/deno.json (config.ts→PGlite, logger, jwt, channel-service, channel-resolver). But the real src/config/env.ts still leaked into their module graph via the auth-middleware chain:

pay handler → @/http/middleware/auth → core/service/auth/service/service-auth-secret.ts
            → import { MODE, SERVICE_AUTH_SECRET } from "@/config/env.ts"

env.ts evaluates every requireEnv at module load:

export const DATABASE_URL = requireEnv("DATABASE_URL"); // line 6
export const PORT         = requireEnv("PORT");         // …+9 more eager reads

That eager read is a deliberate production fail-fast — the service must refuse to start half-configured — and is kept as-is. But in the pay tests there is no real env, so under deno test --parallel on a cold CI cache the eager evaluation became a load-order race (the first missing var, DATABASE_URL, throws; which tests it takes down is timing-dependent → intermittent 11 failed).

Fix

Complete the existing test isolation: mock service-auth-secret.ts in the pay test import map (same mechanism as the other mocks), so the pay tests never evaluate the real env.ts.

"@/core/service/auth/service/service-auth-secret.ts": "./mock_service_auth_secret.ts"

The mock mirrors the real module's exports + crypto-key shape with a fixed test secret. Production env.ts and service-auth-secret.ts are untouched (fail-fast preserved); not lazified.

Verification

  • deno info confirms env.ts is no longer in any of the 11 previously-failing pay tests' module graphs (env.ts = 0 across all).
  • Pay suite passes deterministically on a cold-cache-equivalent run (env -u DATABASE_URL DENO_DIR=$(mktemp -d) deno test --parallel --config src/http/v1/pay/tests/deno.json116 passed | 0 failed, repeated).
  • Control experiment (before → after) reported on the PR.
  • deno fmt --check + deno lint green.

…d race

The pay unit tests mock their infra deps (config.ts→PGlite, logger, jwt,
channel-service, channel-resolver) but the real src/config/env.ts still leaked
into the graph via the auth-middleware chain (service-auth-secret.ts imports
MODE + SERVICE_AUTH_SECRET from env.ts).

env.ts evaluates every requireEnv at module load — a deliberate production
fail-fast (the service refuses to start half-configured), kept as-is. But in the
pay tests that eager read has no real env, so under `deno test --parallel` on a
cold CI cache it became a load-order race surfacing as "DATABASE_URL is not
loaded" in the DB-touching pay tests (34 passed / 11 failed, intermittently).

Complete the existing isolation: mock service-auth-secret.ts in the pay test
import map (same mechanism as the other mocks). env.ts is no longer in any pay
test's module graph (verified via `deno info`), so the eager reads never run and
the race is gone. Production env.ts + service-auth-secret.ts are untouched.
@AquiGorka

Copy link
Copy Markdown
Contributor Author

Closing — superseded by #120 (pin deno to 2.8.2).

The CI test failure was not a parallel-load race. Root cause: deno 2.8.3 (which setup-deno@v2's v2.x floated to) regressed import-map override resolution, so the pay tests' mocks (config.ts→PGlite, jwt, channel deps) stopped applying and the real src/config/env.ts eager-evaluated requireEnv("DATABASE_URL") at module load. Verified by bisection in Linux containers: green on 2.8.2 (deno info shows env.ts absent from the pay graph), red on 2.8.3 (real modules + env.ts in the graph; env-satisfied runs fail with assertion errors from real deps).

The existing mocks already isolate the pay tests correctly on 2.8.2, so the extra service-auth-secret mock here fixes a race that doesn't exist. #120 pins the toolchain instead.

@AquiGorka AquiGorka closed this Jun 11, 2026
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.

1 participant