Skip to content

test(env): resolve two static process.env bracket shapes in the docs guard - #649

Merged
cevheri merged 1 commit into
libredb:mainfrom
saad-works:fix/env-doc-static-reads-609
Sep 8, 2026
Merged

test(env): resolve two static process.env bracket shapes in the docs guard#649
cevheri merged 1 commit into
libredb:mainfrom
saad-works:fix/env-doc-static-reads-609

Conversation

@saad-works

Copy link
Copy Markdown
Contributor

Extend the #566 env-documentation drift guard (tests/unit/env-documentation.test.ts) to resolve two static process.env[...] bracket shapes it was blind to, taking the extractor from 40 to 57 of the statically-resolvable names. Its header comment framed that gap as an edge case when it was most of the gap.

Type of Change

  • Test addition or update

Related Issue

Closes #609

Changes Made

  • Extend readNames() to resolve two same-file bracket shapes, in addition to literal process.env.NAME:
    1. const X = "NAME" (exported or not), then process.env[X] — the per-name aliases in src/lib/agent/config.ts.
    2. an object field whose value is a bare uppercase string literal, then process.env[<expr>.field] — the rate-limit bucket table in src/lib/api/rate-limit.ts (process.env[spec.maxVar]).
      Both resolve within a single file only; an alias defined elsewhere would need a module graph the guard does not have.
  • Recovers the 6 agent aliases (LIBREDB_AGENT_ENABLED, LIBREDB_AGENT_THREAD_CONTEXT, AGENT_MODEL_TUNING_PATH, AGENT_MODEL_TURN_TIMEOUT_MS, WORKFLOW_LOCAL_DATA_DIR, WORKFLOW_TARGET_WORLD) and the 10 RATE_LIMIT_* names the issue lists.
  • Adds VERCEL_DEPLOYMENT_ID (a seventh alias, platform-injected, not operator-facing) to ALLOWLIST with its reason; the guard's own "still read / still carries a reason" and "not also documented" tests keep that entry honest.
  • Rewrites the header comment to state the real coverage boundary: names reaching process.env[...] as a function argument stay out of scope — getEnvVar("LLM_PROVIDER") in src/lib/llm/utils/config.ts and process.env[envVar] in src/lib/seed/credential-resolver.ts need a call graph — leaving HOSTNAME, MY_DB_PASSWORD and the four LLM_* names undiscovered.
  • Adds 3 tests, each pinning specific variable names rather than a count: one per shape, and one that pins the out-of-scope boundary (asserts LLM_* / MY_DB_PASSWORD are not found) so a future widening of the extractor is a deliberate edit rather than a silent gain.
  • One test file, no product code.

Testing

  • I have tested this locally
  • I have added/updated tests
  • All existing tests pass — the ~255 failures in a local bun test tests/unit are pre-existing and environmental (no helm on PATH, native addons built for a different Node ABI, missing libstdc++); they are identical with and without this change, and this PR adds +3 passing tests.

Ran locally: bun run format, bun run lint (0 errors), bun run typecheck, bun run knip — all clean. bun test tests/unit/env-documentation.test.ts — 8 pass / 0 fail.

Could not run in full: bun run test and bun run build — the chart tests need helm (see #570) and several native-addon tests need a rebuild for this machine's Node ABI. This change touches one test file and no src/ lines, so the 100% line-coverage gate is unaffected; a maintainer approving the workflow run gets the full gate.

Test Environment

  • LibreDB Studio Version: main @ db40452
  • Browser: n/a (test-only change)
  • OS: Windows 11
  • Node.js/Bun Version: Bun 1.4.2
  • Database Type: n/a

Screenshots (if applicable)

n/a

Checklist

  • My code follows the project's code style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly (the guard's header comment, updated in-file; no docs/ change)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published (none)

…guard

The libredb#566 drift guard extracted env vars by matching literal `process.env.NAME`,
which is the minority shape here, so it saw 40 of the 57 statically resolvable
names and its header comment framed the gap as an edge case.

Extend the extractor to the two same-file bracket shapes that make up the gap:

1. `const X = "NAME"` (exported or not) read as `process.env[X]` -- the
   per-name aliases in `src/lib/agent/config.ts`;
2. an object field whose value is a bare uppercase string literal, read as
   `process.env[<expr>.field]` -- the rate-limit bucket table in
   `src/lib/api/rate-limit.ts` (`process.env[spec.maxVar]`).

This recovers the six agent aliases and the ten `RATE_LIMIT_*` names the issue
lists, plus `VERCEL_DEPLOYMENT_ID` (a seventh alias, platform-injected) which is
added to the allowlist with its reason. Names reaching `process.env[...]` as a
function argument stay out of scope -- `getEnvVar("LLM_PROVIDER")` and
`process.env[envVar]` need a call graph -- and the header comment now states
that boundary and the six names it leaves undiscovered instead of naming
dynamic reads as an aside.

New tests pin each shape by naming specific variables rather than asserting a
count, and one pins the out-of-scope boundary so a future widening is a
deliberate edit rather than a silent gain.

Closes libredb#609

Claude-Session: https://claude.ai/code/session_01TfCvCagTXgpe6bHmtQZ1ry
@cevheri

cevheri commented Sep 8, 2026

Copy link
Copy Markdown
Member

Good first PR here: you measured before you wrote, you pinned names instead of a count, and you wrote down what the guard still cannot see. That last part is the one most people skip.

@cevheri
cevheri merged commit 1b60ec1 into libredb:main Sep 8, 2026
22 checks passed
@cevheri

cevheri commented Sep 8, 2026

Copy link
Copy Markdown
Member

Merged.
Two optional follow-ups, both from probing the guard rather than from anything wrong in the diff. Pick them up in a second PR if you want, or leave them and I will file them.

  1. The header comment says process.env[<expr>.field], but fieldReadPattern only accepts a bare identifier between the brackets, so process.env[a.inner.field] and process.env[arr[0].field] are missed silently. I probed both. Nothing in src/ is in that shape today, so this is a wording fix and not a code one. The <expr> came from my issue text, but the comment is the guard's contract, so it should say what the regex actually accepts.

  2. The new boundary test still passes when readNames() returns []. I mutated it to check. The first test in the file exists for exactly that reason, so the file as a whole is not vacuous, but the negative test does not carry its own control. One line inside it, expect(names.has("JWT_SECRET")).toBe(true), makes it stand on its own in the file's own idiom.

cevheri pushed a commit that referenced this pull request Sep 8, 2026
…ary test a control (#652)

Two follow-ups from #649 review, both from probing the guard rather than a
defect in it.

- The header and inline comments described shape 2 as `process.env[<expr>.field]`,
  but `fieldReadPattern` accepts only a single bare identifier before `.field`:
  `process.env[a.b.field]` and `process.env[arr[0].field]` are not matched. The
  comment is the guard's contract, so it now says what the regex accepts and
  names the shapes it skips. Nothing in `src/` is in the deeper shape today, so
  this is wording only.

- `#609 boundary: ...` asserts only absences, so it passed on an empty
  `readNames()`. The file as a whole is not vacuous -- the first test guards
  that -- but the negative test now carries its own control:
  `expect(names.has("JWT_SECRET")).toBe(true)` before the absence checks, in the
  file's own idiom.

One test file, comments and one assertion, no product code.
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.

The env-documentation guard sees 40 of 62 variables, and 16 of the missing 22 are statically resolvable

2 participants