Skip to content

test(harness): unset the DB env keys instead of assigning undefined - #1873

Merged
lilyshen0722 merged 5 commits into
mainfrom
kai/task128-setup-env-absence
Sep 25, 2026
Merged

lilyshen0722 merged 5 commits into
mainfrom
kai/task128-setup-env-absence

Conversation

@lilyshen0722

@lilyshen0722 lilyshen0722 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

process.env.PG_HOST = undefined stores the string 'undefined', which is truthy. In unit suites that satisfied every if (process.env.PG_HOST) guard in the backend and made config/db-pg.ts:75 build a real Pool for host 'undefined', so connectPG()'s not-configured branch (:106-108) was unreachable. Same for MONGO_URI.

Change. Delete the keys instead of assigning undefined, with the comment saying why.

Scope, searched rather than assumed. PG_HOST is also a feature flag: server.ts:68/:363, controllers/podController.ts:22/:426/:627, controllers/authController.ts:121, routes/agentsRuntime.ts:3103 all took the configured branch in every unit suite and now take the not-configured one — the shape a unit suite is supposed to see. Every test file that reads PG_HOST also sets it, and testUtils.js builds its Pool only under useRealServices().

MONGO_URI adds no new failure mode: server.ts:297 calls connectDB() unconditionally and config/db.ts:11-13 already answers a connect failure with console.error + process.exit(1), which is what the string URL 'undefined' produced.

Witness — __tests__/unit/config/setupEnvAbsence.test.js. Both assertions were red before the change and green after:

  1. neither var is the literal string 'undefined' (the defect's signature);
  2. no Pool is built for host 'undefined', read off the pool's own options.host rather than pool === null, so a developer with a real PG_HOST in the environment still passes. Assertion 2 also confirms the consequence at the level the defect actually has it, not only the env-var level.

The two dependent populations this exposes

Suites that depended on the defect. __tests__/unit/config/init-pg-db.test.js:17 reached for a REAL pool, which existed only because of the bogus host; once the harness stopped lying, both tests died on Cannot set properties of null (setting 'connect'). The unit under test is initializeDatabase and its pool is a dependency, so it now stubs config/db-pg and keeps the same two assertions. That file also gets the import/no-unresolved, import/extensions disable its siblings carry (lint 4 errors → 0).

Three files set PG_HOST = process.env.PG_HOST || 'localhost-test' (unit/config/db-pg.test.js:8, unit/routes/health.db.test.js:26, unit/routes/health.ready.test.js:20). That fallback has never fired, because the string was truthy; it does now. Both values are truthy, so no arm flips, and all three are green.

Runtime code that read the bogus host as configured. Five sites branch on a null pool without naming PG_HOST, and each is a degrade path: controllers/pgStatusController.ts:10 → {available:false}, models/ToolCall.ts:104 → throw tool broker requires PostgreSQL, services/activityService.ts:364 → unranked podIds, services/attachmentAccess.ts:205 → [], services/systemExchangeTriggers.ts:123 → null. Today they get a Pool, the query fails, and the catch degrades; after this change they return early. The result is the same and the arm is different, so a green suite cannot tell the two apart — stated here because it is the one place this change moves behaviour without moving an outcome. Found by a scan of the 28 modules importing db-pg for !pool / pool && / pool ? / pool?., which shares no term with the PG_HOST grep.

Dead code this makes removable

  • agentIdentityService.ts — syncUserToPostgreSQL's pgHost === 'undefined' clause (feat(v2): align Settings with artboard #1546) was there to compensate for exactly this harness. !process.env.PG_HOST covers the unconfigured case on its own; the clause and its stale comment go.
  • backend/jest.setup.js is orphaned. Verified, not assumed: jest.config.js:35 lists __tests__/setup.js as the only setupFilesAfterEnv entry, package.json has no jest key, no script passes --config, and a repo-wide grep finds the file named only in tsconfig.build.json's exclude — so the file and that exclude line both go. tsconfig.json:23 stays; it records a landing-time count, which is history rather than configuration.

Docs, same PR

backend/TESTING.md now says process.env is per test FILE (mechanism: jest-environment-node's detached createProcessObject copy per file), plus the corollary that a child process needs env passed explicitly. docs/development/review-checklist.md rule 31 had called the TMPDIR mechanism unexplained; it is that same per-file copy, so the rule keeps its conclusion and loses only the rider — reason rewritten, rule not deleted.

Suite

427 of 433 suites, 4001 tests, 0 failures (6 suites / 24 tests skipped). Two intermittent things, disclosed rather than buried, neither caused by this change:

  • __tests__/unit/server.test.js "returns available:false when PG init throws" failed once under full-suite load, passes 3/3 in isolation, and passed in two other full runs with this same change set. It is a race: the fallback /api/pg/status mount happens inside the connectPG().then(...) chain while the test awaits a single setImmediate, so under load the request can arrive before the mount. I have deliberately not touched it — I cannot demonstrate a fix for a flake I cannot reproduce in isolation.
  • __tests__/unit/models/AgentMemory.test.ts "enforces unique (agentName, instanceId)" — one of the three known flaky members.

`process.env.PG_HOST = undefined` stores the STRING 'undefined', which is truthy.
In unit suites that satisfied every `if (process.env.PG_HOST)` guard in the
backend (server.ts:68 and :363, controllers/podController.ts:22/:426/:627,
controllers/authController.ts:121, routes/agentsRuntime.ts:3103) and made
db-pg.ts:75 build a real Pool for host 'undefined', so connectPG()'s
not-configured branch (:106-108) was unreachable. Same for MONGO_URI.

Delete the keys instead. MONGO_URI introduces no new failure mode: server.ts:297
calls connectDB() unconditionally and config/db.ts:11-13 answers a connect
failure with console.error + process.exit(1) — which is already what the string
URL 'undefined' produced. The mongoose mock keeps that inert in tests.

Witness (both assertions red before the change, green after):
__tests__/unit/config/setupEnvAbsence.test.js asserts neither var is the literal
string, and that no Pool is built for host 'undefined'. The second is read off
the pool's own options rather than `pool === null`, so a developer with a real
PG_HOST in the environment still passes; it also confirms the row's claim at the
consequence level rather than only at the env-var level.

Dependents, searched rather than assumed: every test file that reads PG_HOST also
sets it, and testUtils.js builds its Pool only under useRealServices(). The
remaining unconditional readers are the backend guards above, which now take the
not-configured path — the shape a unit suite is supposed to see.
Found by the full-suite run the harness change required, and it was relying on
the defect: `__tests__/unit/config/init-pg-db.test.js:17` reaches for a REAL
`pool` object, which existed only because PG_HOST was the truthy string
'undefined'. Once the harness stopped lying about the host, `pool` was null and
both tests died on `Cannot set properties of null (setting 'connect')`.

The unit under test is `initializeDatabase`, and its pool is a dependency: stub
`config/db-pg` and keep asserting the schema execution and the missing-file path.
TESTING.md: `process.env` is per test FILE, not per test worker.
`jest-environment-node` gives each file its own detached copy (`jest-util`'s
`createProcessObject`), measured with two files in one `--runInBand` worker on a
single pid, neither observing the other's writes. Corollary for child processes:
pass env explicitly, because `child_process`'s default env reads the real
environment, not the test file's copy.

`docs/development/review-checklist.md` rule 31 called the TMPDIR mechanism
unexplained, which was honest; the mechanism is that same per-file copy, so the
rule keeps its conclusion and loses only that rider — the reason is rewritten
rather than the rule deleted (rule 15's second rider).
- `agentIdentityService.syncUserToPostgreSQL` no longer needs its
  `pgHost === 'undefined'` clause (#1546). It compensated for the harness storing
  the literal string 'undefined'; `!process.env.PG_HOST` covers the unconfigured
  case on its own, because `config/db-pg.ts:75` leaves `pool` null for it.
- `backend/jest.setup.js` is orphaned. Verified rather than assumed:
  `jest.config.js:35` lists `__tests__/setup.js` as the only `setupFilesAfterEnv`
  entry, `package.json` has no `jest` key, no script passes `--config`, and a
  repo-wide grep finds the file named only in `tsconfig.build.json`'s exclude —
  so the file and that exclude line both go. `tsconfig.json:23` stays: it records
  a landing-time count, which is history rather than configuration.
…73720)

Five of the seven sites named were TASK-126-branch offsets carried onto a comment
that merges to main and outlives the branch. Re-derived against `9a32fca5` before
writing them down: server.ts:68/:378, podController.ts:22/:462/:663,
authController.ts:171, agentsRuntime.ts:3188.

The list also read as the population when it was an enumeration, so it now states
the count it is a sample of: 25 `process.env.PG_HOST` reads across 11 runtime
modules (26/12 with the backend-root `testPgConnection.ts`), measured by grep
excluding `__tests__/` and `backend/scripts/`.
@lilyshen0722
lilyshen0722 added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit e83a858 Sep 25, 2026
14 checks passed
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