Skip to content

fix(test): isolate usage-log fixtures from the real OpenCodex home - #997

Closed
Yuxin-Qiao wants to merge 2 commits into
lidge-jun:devfrom
Yuxin-Qiao:codex/fix-test-usage-log-isolation
Closed

fix(test): isolate usage-log fixtures from the real OpenCodex home#997
Yuxin-Qiao wants to merge 2 commits into
lidge-jun:devfrom
Yuxin-Qiao:codex/fix-test-usage-log-isolation

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • tests/management-api-logs-metrics.test.ts exercises GET /api/logs display metrics through addRequestLog, which persists every row to usage.jsonl. Unlike its sibling suites that also persist (usage-failure-persistence.test.ts, api-key-attribution.test.ts), it did not redirect OPENCODEX_HOME itself and relied entirely on the bunfig.toml preload (tests/preload.ts).
  • The preload only loads when Bun discovers bunfig.toml from the current directory. Running bun test <absolute path> from outside the repo (agent/IDE pattern) skips it, and the fixture rows (ok-a/ok-b/fail, no-such-model, unpriced-model, combo/my-combo, ...) are appended to the developer's real ~/.opencodex/usage.jsonl. The dashboard Usage page then renders never-used models with real token counts.
  • This change adds a per-test scratch OPENCODEX_HOME (mkdtemp, restored and cleaned in afterEach), matching the established pattern in the two sibling suites.

Reproduction before the fix

$ cd ~ && bun test /path/to/repo/tests/management-api-logs-metrics.test.ts
$ rg '"requestId":"ok-a"' ~/.opencodex/usage.jsonl   # fixture row now in the real log

Verification

  • Reproduced against a scratch OPENCODEX_HOME: 11 fixture rows were written before the fix.
  • After the fix, the same foreign-cwd invocation leaves the real usage.jsonl untouched.
  • bun run typecheck and bun run privacy:scan pass.
  • Focused suites (usage-summary, usage-log, request-log, usage-cost, management-api-logs-metrics) pass: 147 tests; this file is 9/9 in the full suite.
  • Full suite on this branch is green except 10 management-provider-validation.test.ts cases that fail only in this dev environment because chatgpt.com resolves to a non-global address (write-time DNS/SSRF gate returns 400); upstream CI for dev is green.

Summary by CodeRabbit

  • Tests
    • Improved test isolation by using temporary configuration directories for each test.
    • Added cleanup and environment restoration after tests complete.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I fixed all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@github-actions github-actions Bot added the bug Something isn't working label Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 173497a8-60b5-49a9-8d71-e6a2f4cf7a43

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The test setup creates a temporary OPENCODEX_HOME directory before each test. Teardown restores the previous environment value, clears request logs, and removes the temporary directory.

Changes

Management API test isolation

Layer / File(s) Summary
Temporary OPENCODEX_HOME lifecycle
tests/management-api-logs-metrics.test.ts
The test imports filesystem, OS, and path utilities. Each test receives a temporary OPENCODEX_HOME; teardown restores the prior environment, clears request logs, and removes the directory.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • lidge-jun/opencodex#1004: Related request-history and index test setup uses environment and data cleanup for isolated tests.

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: isolating usage-log test fixtures from the real OpenCodex home.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@lidge-jun

Copy link
Copy Markdown
Owner

Still wanted — the fixture isolation is correct and the setup/teardown restores the environment properly. It has drifted far behind dev (~142 commits); please rebase so CI can rerun on current code. Low conflict risk expected; after a green run it is ready for maintainer review.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the codex/fix-test-usage-log-isolation branch from 4b6532b to cdebe0e Compare August 6, 2026 02:52
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review readiness checklist

This PR is kept in draft until every requirement below is fulfilled. The tickable checklist has been added to your PR description — tick all four boxes there.

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I fixed all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This PR stays in draft until every box above is ticked.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR quality gates passed

This pull request now targets dev with acceptable ancestry, description, and UI screenshot coverage. It stays in draft until the review readiness checklist is complete.

The title was left unchanged. The draft is owned by the checklist message below.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@github-actions
github-actions Bot marked this pull request as draft August 6, 2026 02:52
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deterministic PR hygiene checks passed.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Rebased onto current dev (now 0 commits behind; new head cdebe0e1). enforce-target, hygiene, label, and CodeRabbit all pass. The Cross-platform CI and React Doctor runs are queued awaiting approval on the new head.

@lidge-jun

Copy link
Copy Markdown
Owner

This fixes a genuine hazard and the fix itself is right. Worth being explicit about why it matters, because the diff looks small: addRequestLog persists to usage.jsonl, and getConfigDir() resolves to the real ~/.opencodex whenever OPENCODEX_HOME is unset (src/config.ts:545-552). So running this one test file without the repo's preload writes fixture rows into a developer's actual usage history and shows up on their Usage page. That is a bad surprise, and your comment describes the mechanism accurately.

The implementation is complete for this file — per-test scratch home, prior value restored (including the delete when it was previously unset, which is the case people usually miss), request logs cleared, temp dir removed.

One thing missing: nothing tests the promise. All nine assertions in this file are about metrics; none asserts that the real home was left untouched. So the safety property this PR exists to establish is the one property the suite would not notice losing. If someone later reorders the beforeEach, or a new test in this file constructs its own logger before the hook runs, we are silently back to writing into ~/.opencodex with a green suite.

Please add one test that pins it — run with OPENCODEX_HOME pointed at a scratch dir, exercise a path that logs, then assert no file appeared at the default location. Asserting the resolved write target is enough; it does not need to spawn anything.

Worth a look while you are in there: if other test files call addRequestLog without isolation, they have the same hazard and a shared helper would be better than repeating the hook. Not a blocker for this PR — just likely to be a wider problem than one file.

Small ask on an otherwise clean change; I would like it to land.

lidge-jun added a commit that referenced this pull request Aug 6, 2026
Second loop of the bug campaign, scoped to author corrected replacements for
four contributor PRs and close theirs as absorbed. Three adversarial audit
rounds refuted the premise, and the plan now records that instead of the
outcome it was written to produce.

The decisive finding: I judged "has the author responded to our review" by
`updatedAt`, which moves when WE comment and therefore can never show author
activity. Comparing last-commit time against review time gives the real
picture:

    #1092  commits 09:38:19Z  review 09:09:51Z  -> acted, in under 30 minutes
    #1068  commits 08:52:23Z  review 09:13:15Z  -> predates the review
    #1036  commits 08-05      review 09:12:51Z  -> no response yet
    #997   commits 02:51Z     review 09:16:02Z  -> no response yet

Under the wrong reading I was about to close #1092 as absorbed — taking
credit for work its author did in direct response to my own request — and to
credit #1068 with agreeing to a review it had not yet read. Neither is a
process nit; both would have been visible to the contributor.

So the plan changes shape. #1092 and #1068 become re-reviews. #1036 and #997
get a stated 72-hour response window with a mandatory head re-check before
anything is authored or closed; "has not replied within an hour" is not
abandonment, and #997's author was active at 02:51Z. This loop therefore
authors no absorbed layer at all, and says so.

#1068's re-review carries a finding neither side has: its new test asserts
all three DeepSeek ids are in `noVisionModels`, but routing merges the
registry list, which holds only the `-free` one. Reproduced with the test's
own routeModel config — Pro=false, Flash=false, Flash-free=true — so two of
three cases fail. Latent because no check currently runs that suite.

Also corrected: the layers were called dependency-ordered when they share no
files, so s3/s4 are independent heads off `origin/dev` and the
`--update-refs` cascade is reserved for genuine chains; and #978's exclusion
no longer claims it is "already correct" when it needs an author-side docs
change.

Terminal outcome is deferred, not DONE. Shrinking the criteria to match what
finished would have hidden exactly the thing worth recording.
lidge-jun added a commit that referenced this pull request Aug 6, 2026
Records what the stack-and-absorb campaign actually produced once live data
replaced its assumptions.

#1068 merged at 10:45:57Z while I was writing its review, and the merged code
is not what I reviewed: `noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS,
...DEEPSEEK_THINKING_MODELS]` — the union the review asked for. Running the
merged suite against `origin/dev` gives 9 pass / 0 fail, including a new test
pinning the six probed text-only models. My 10:53Z comment claiming a failing
test was true of the head I had fetched and false of what landed, so it got a
public correction eight minutes later.

That is the second process error in this unit, and both are now written down
rather than quietly fixed. First: judging author activity by `updatedAt`,
which moves when WE comment. Second: commenting against a stale fetched head.
Each produced a wrong public statement to a contributor. The rules are
re-fetch immediately before commenting, and compare last-commit time against
review time.

#1092's author restored the fail-closed guard within 30 minutes of the
review, with a comment keeping `unknown` distinct in debug. Credited
explicitly; asked only to split the unrelated `imageInput` scope.

#1036 and #997 are deferred, not absorbed. Their last commits (08-05 07:18Z,
08-06 02:51Z) predate our reviews and neither carries the requested change,
but roughly two hours have passed and #997's author was active that morning.
050 states a 72-hour response window with a mandatory head re-check before
anything is authored or closed. The window exists because this unit
demonstrated the alternative: two of four absorb targets were being actively
fixed by their authors while we drafted replacements.

#994 updated with the dev-only landing (merge 7d0c02d, ancestor of
`origin/dev`) and deliberately left open pending the reporter's provider
confirmation and a release.

Zero contributor PRs closed by this loop.
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Added the pin test in 3304d5c8: fixture usage rows land in the scratch home, never the default location asserts the resolved write target equals join(testDir, "usage.jsonl"), that the fixture requestId is present there, and that the default-resolved target (no OPENCODEX_HOME) is neither the write target nor contains the fixture row.

I also verified the pin actually bites: with the beforeEach hook neutered and writes redirected to a scratch OPENCODEX_HOME, the new test fails. Nothing touched the real home during that check.

On the wider hazard: I scanned every test file that calls addRequestLog/appendUsageEntry/usageLogPath. Only this file lacked self-isolation; usage-failure-persistence.test.ts and api-key-attribution.test.ts already isolate their own home, so there is no other live hole today. A shared helper remains a sensible follow-up, but it is not closing a gap right now.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Heads-up on the enforce-target failure on this head (parseGateState is not a function) — it is a repo-level infra mismatch, not this PR. Evidence:

  • The workflow checks out trusted scripts at base.sha (ref: ${{ github.event.pull_request.base.sha }}), while the inline script itself comes from the default-branch workflow. pull_request_target runs the default-branch YAML, which at main (v2.10.2) calls parseGateState (.github/workflows/enforce-pr-target.yml:145), importing it at line 63.
  • Current dev tip (b3a1d90a) ships .github/scripts/pr-quality-state.cjs that only exports parseState (line 184) — no parseGateState. Hence TypeError: parseGateState is not a function.
  • Older dev bases still carry the export: e.g. PR fix(pi): keep loopback models visible #1085 (base 8ed03e72) passed enforce-target at 11:49Z with the same gate. So every PR based on current dev will fail enforce-target until main's gate YAML is synced with the dev script API (or the script keeps a parseGateState alias).

Fix options: sync the parseGateState -> parseState rename into main's workflow (release-train), or keep a backward-compatible alias export in the dev script. No change on this PR side can affect the gate; the PR itself is ready for the test shards once approved.

@lidge-jun

Copy link
Copy Markdown
Owner

You beat me to it, and your version is better than the one I had staged.

I had a commit ready to push onto this branch adding the assertion — then found 3304d5c8 already there. Yours checks more than mine did: I was pinning the resolved config dir, you assert the actual usage.jsonl path receives the row and that the default location does not contain the request id. That is the stronger claim, because it survives a refactor that changes how the path is resolved.

Verified on your head rebased onto current dev:

bun test tests/management-api-logs-metrics.test.ts
10 pass, 0 fail

And I ablated it to make sure it is not vacuous — commenting out process.env.OPENCODEX_HOME = testDir in beforeEach:

(fail) fixture usage rows land in the scratch home, never the default location
 9 pass, 1 fail

Exactly one test goes red, and it is yours. The other nine keep passing, which is the whole point — they are metrics tests and cannot see this hazard. That is a genuine regression test.

No changes requested from me. Your branch is now 2 commits, both yours, and it needs a rebase onto current dev plus the readiness checklist ticked; after that it is ready for a maintainer.

Thanks for turning that around quickly, and sorry for the near-collision — I should have re-fetched your head before preparing a commit rather than working from the diff I had.

lidge-jun added a commit that referenced this pull request Aug 6, 2026
Two loops circled a problem the campaign had invented. The user named the
answer in one line: their PRs report maintainerCanModify=true, so push our
completion commit onto their branch. Their commits stay, blame stays
accurate, their PR merges, and attribution is the commit graph rather than a
paragraph. No replacement PR, no close, no "absorbed with credit to" prose.

#1036 landed that way. Four Agent59353 commits, then ours on top, pushed
with a lease pinned to their head after re-verifying the remote. The change
is bounded: live-transport records the bare names we actually advertised
this request, the event state carries them, and both translate call sites
convert only for names in that set. Their conversion logic is byte-
unchanged. The stateless fallback passes through, since it has no state to
consult and an unconverted call is recoverable while a wrongly converted one
edits a file — disclosed in the PR comment as a judgment call open to
disagreement. Ablation: restoring the name-only gate gives 21 pass / 1 fail,
red on exactly the new collision test.

#997 was not ours to push. The lease rejected it as stale: the author landed
their own fix mid-flight, and theirs is stronger than what we staged — we
pinned getConfigDir(), they assert the resolved usage.jsonl receives the row
and the default location does not. Verified instead of overwritten, 10 pass
with a 9/1 ablation on their own assertion.

That is the third time in this campaign that acting on a stale head produced
wasted or wrong work: updatedAt as an activity signal, a "failing test"
comment posted eight minutes after #1068 merged with the fix, and a commit
built against a head the author had already improved. The lease caught the
third; the first two reached a contributor as a wrong public statement. The
rule is now written down — re-fetch immediately before preparing a commit,
not before pushing.
@lidge-jun

Copy link
Copy Markdown
Owner

Thank you — this is landing as #1150, with both of your commits cherry-picked under your authorship.

The detail that made this adoptable rather than merely acceptable: you assert the target path and the content explicitly, not just that the real home is left alone. A test that only stopped writing would pass silently if the isolation regressed later — yours would not.

One conflict resolved during the pick, for the record: your two commits are stacked, so applying them required keeping the superset of imports from the later commit while applying the earlier one's scratch-home beforeEach/afterEach. The final file matches your tree exactly.

Your #999 landed in the same PR — see the comment there.

Verified: tests/management-api-logs-metrics.test.ts 10 pass / 0 fail; full suite 9,556 pass / 0 fail; typecheck and privacy scan clean.

Leaving this PR open for you.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Closing — the fix landed as #1150 with both commits cherry-picked under my authorship, verified 10/10 on this file plus the full suite. Thanks for the adoption and the ablation check.

@Yuxin-Qiao Yuxin-Qiao closed this Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants