Thanks to visit codestin.com
Credit goes to github.com

Skip to content

test: harden codex manager cli isolation - #321

Merged
ndycode merged 3 commits into
mainfrom
fix/main-cli-test-regressions
Mar 23, 2026
Merged

ndycode merged 3 commits into
mainfrom
fix/main-cli-test-regressions

Conversation

@ndycode

@ndycode ndycode commented Mar 23, 2026

Copy link
Copy Markdown
Owner

What Changed

  • hardened test/codex-manager-cli.test.ts isolation by resetting shared mocks and reapplying auth/browser/server stubs after vi.resetModules()
  • pinned non-interactive stdin defaults and added a regression for the closed-stdin manual callback guard so worker reuse cannot persist login state incorrectly
  • fixed test/wait-utils.test.ts fake-timer sleep mocking so virtual time advances instead of spinning into worker OOM

Risk Level

  • low
  • test-only changes; no runtime or product code changed

Rollback Plan

  • revert PR #321

Validation

  • node_modules\\.bin\\vitest.cmd run --pool=threads --maxWorkers=1 test/codex-manager-auth-commands.test.ts test/codex-manager-cli.test.ts
  • node_modules\\.bin\\vitest.cmd run --pool=threads --maxWorkers=1 test/wait-utils.test.ts
  • node_modules\\.bin\\vitest.cmd run --pool=threads --maxWorkers=1 test/codex-manager-cli.test.ts
  • npm run lint
  • npm run typecheck
  • npm run build
  • split-suite fallback passed all 221/221 files and 3165/3165 tests
  • monolithic npm test -- --pool=threads --maxWorkers=1 still ends in a Vitest worker OOM near the end, but no functional test failures remained before the runner died

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

this pr hardens test isolation in the codex manager cli suite and fixes an oom in the wait-utils test. all three problems it fixes are real and the solutions are sound.

  • mock leak fix (codex-manager-cli.test.ts): six mocks (promptQuestionMock, planOcChatgptSyncMock, applyOcChatgptSyncMock, runNamedBackupExportMock, exportNamedBackupMock, detectOcChatgptMultiAuthTargetMock, normalizeAccountStorageMock) were not reset between tests; queued mockResolvedValueOnce values could bleed across cases. all are now reset in beforeEach.
  • stdin descriptor fix (codex-manager-cli.test.ts): readableEnded and destroyed are now captured at module load, restored in afterEach, and pinned to false in beforeEach via setOpenStdinState(). this prevents a previous worker's closed-stdin state from short-circuiting manual-callback tests.
  • oom fix (wait-utils.test.ts): the old sleep mock returned immediately without advancing fake time, so sleepWithCountdown's while (Date.now() < endTime) guard never moved forward. the fix injects await vi.advanceTimersByTimeAsync(ms) inside the mock, which advances fake Date.now() and lets the loop terminate correctly.
  • new test: "skips manual callback prompting when stdin is already closed in non-tty mode" covers the readableEnded: true early-exit path through lib/codex-manager.ts:1252; the sibling destroyed: true branch (same || condition) has no coverage yet.

Confidence Score: 5/5

  • safe to merge — all changes are test-only, fix documented leaks/oom, and add a new targeted assertion
  • both changed files are test-only; the fixes address concrete, reproducible problems (leaking mocks, closed-stdin short-circuit, fake-timer oom). the only remaining gap is the destroyed: true branch, which is a follow-up p2 — not a blocker
  • no files require special attention

Important Files Changed

Filename Overview
test/codex-manager-cli.test.ts adds full mock reset for previously-leaking mocks (promptQuestionMock, planOcChatgptSyncMock, etc.), pins stdin open state in beforeEach, and adds a new test for the closed-stdin early-exit path; only minor gap is missing coverage for the destroyed: true branch
test/wait-utils.test.ts replaces the no-op sleep mock with one that calls vi.advanceTimersByTimeAsync(ms), correctly advancing fake Date.now() inside sleepWithCountdown's while-loop guard, eliminating the infinite-spin OOM

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[beforeEach] --> B[vi.resetModules + all mockReset]
    B --> C[restoreTTYDescriptors]
    C --> D[setOpenStdinState\nreadableEnded=false, destroyed=false]
    D --> E[import auth/browser/server modules\nreset + default implementations]
    E --> F[test body runs]
    F --> G{stdin state?}
    G -- readableEnded=true --> H[skip promptQuestion\nskip browser open\nskip server start\nexit 0]
    G -- open --> I[normal manual OAuth flow]
    F --> J[afterEach: restoreTTYDescriptors]
Loading

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: test/codex-manager-cli.test.ts
Line: 5420-5466

Comment:
**missing `destroyed: true` branch coverage**

the production code at `lib/codex-manager.ts:1252` gates on `input.readableEnded || input.destroyed`. this new test covers `readableEnded: true` (with `destroyed` left `false`), but there is no test for the `destroyed: true` path (with `readableEnded` left `false`). consider adding a sibling test that calls:

```ts
Object.defineProperty(process.stdin, "destroyed", {
  value: true,
  configurable: true,
});
```

and re-asserts the same "no prompt / no exchange" expectations. without it, a regression that drops the `destroyed` check would go undetected.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "test: cover closed stdin manual callback..." | Re-trigger Greptile

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

test setup and mocks updated: process.stdin state capture/restoration added, beforeEach converted to async with dynamic auth module re-imports and expanded mock resets; setOpenStdinState() helper added; new test asserting manual auth skip when stdin is closed; sleep mock in countdown test now advances fake timers.

Changes

Cohort / File(s) Summary
codex manager cli tests
test/codex-manager-cli.test.ts:1
made beforeEach async, capture/restore process.stdin properties (isTTY, readableEnded, destroyed) with fallback deletion; added setOpenStdinState() to force open stdin; expanded mock resets (promptQuestionMock, planOcChatgptSync, applyOcChatgptSync, runNamedBackupExport, exportNamedBackup, detectOcChatgptMultiAuthTarget, normalizeAccountStorage); dynamic re-imports of ../lib/auth/* and selective mock resets; override parseAuthorizationInput to extract code/state via regex; stubbed isBrowserLaunchSuppressed and copyTextToClipboard; added test for auth login --manual behavior when stdin.readableEnded=true.
wait util tests
test/wait-utils.test.ts:1
changed sleep mock from a no-arg noop to an async function that accepts ms and calls vi.advanceTimersByTimeAsync(ms), ensuring countdown test advances timers in step with mocked sleep.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

test, flaky, platform

notes for review

  • concurrency risk: beforeEach is now async and manipulates global process.stdin state. verify parallel test execution doesn't cause races across workers. see test/codex-manager-cli.test.ts:1.
  • windows edge cases: manipulation and deletion of process.stdin.readableEnded/destroyed may behave differently on windows. add CI runs on windows or guard logic in test/codex-manager-cli.test.ts:1.
  • missing regression tests: new parsing override for authorization (code/state) and stdin-closed auth flow lack focused regression tests. add tests for malformed query strings, absent state, and manual-login behavior across tty/non-tty modes in test/codex-manager-cli.test.ts:1.
🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed title follows conventional commits format (test: ...) with lowercase imperative summary under 72 chars, clearly describing test isolation hardening.
Description check ✅ Passed PR description covers all essential sections: what changed, risk level, rollback plan, and validation steps. Structure aligns with template requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/main-cli-test-regressions
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/main-cli-test-regressions

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 and usage tips.

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/codex-manager-cli.test.ts`:
- Around line 301-310: The test suite currently forces stdin to be "open" via
setOpenStdinState but lacks a focused regression that exercises the
short-circuit guard in lib/codex-manager.ts (the input.readableEnded ||
input.destroyed check); add a deterministic vitest regression test in
test/codex-manager-cli.test.ts that explicitly sets either
process.stdin.readableEnded = true or process.stdin.destroyed = true (after
calling setOpenStdinState or directly) and then runs the manual callback/login
flow used around the existing test call at lines ~769-771, asserting that manual
callback handling does not persist login state (i.e., the login state remains
unauthenticated or unchanged); keep the test deterministic (no real secrets, no
network flakiness) and assert the expected state to guard against future
concurrency/worker-reuse regressions.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7b9fb00e-1209-43dd-a1c5-2f97d653b1c1

📥 Commits

Reviewing files that changed from the base of the PR and between e3f5dfc and a6dc1db.

📒 Files selected for processing (1)
  • test/codex-manager-cli.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/codex-manager-cli.test.ts
🔇 Additional comments (2)
test/codex-manager-cli.test.ts (2)

250-293: nice descriptor restore hardening for test isolation

the stdin/stdout descriptor capture+restore flow is solid and deterministic in test/codex-manager-cli.test.ts:250-293. this reduces cross-test leakage risk in pooled workers and stabilizes windows-adjacent tty behavior.


593-799: good reset discipline in async beforeeach

the async beforeEach reset/re-import flow in test/codex-manager-cli.test.ts:593-799 is a strong isolation improvement. reapplying default stubs for auth/browser/server after vi.resetModules() removes one-shot mock bleed and makes concurrent/racy auth tests deterministic.

as per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Comment thread test/codex-manager-cli.test.ts

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/codex-manager-cli.test.ts (1)

593-626: ⚠️ Potential issue | 🔴 Critical

add getPluginConfigExplainReportMock.mockReset() to the beforeEach hook.

the mock is defined at test/codex-manager-cli.test.ts:23 as a top-level vi.fn(). vi.clearAllMocks() clears call history but leaves queued one-shots alive. the queued calls at test/codex-manager-cli.test.ts:888, 925, and 955 will leak to the next test if the current test exits early. all other mocks in this block get explicit .mockReset() calls; this one is missing.

suggested fix
 		loadPluginConfigMock.mockReset();
 		savePluginConfigMock.mockReset();
+		getPluginConfigExplainReportMock.mockReset();
 		selectMock.mockReset();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/codex-manager-cli.test.ts` around lines 593 - 626, Add a .mockReset()
call for getPluginConfigExplainReportMock inside the beforeEach hook so its
queued one-shot behaviors are cleared between tests; locate the beforeEach block
that already calls mockReset() on many mocks (e.g.,
loadAccountsMock.mockReset(), confirmMock.mockReset()) and add
getPluginConfigExplainReportMock.mockReset() alongside them to prevent leaked
queued calls used later at the tests referencing that mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/codex-manager-cli.test.ts`:
- Around line 775-788: The test currently replaces parseAuthorizationInput with
a narrow regex that only handles query-style "code=..." inputs; instead either
stop mocking parseAuthorizationInput so the real implementation in
lib/auth/auth.ts (parseAuthorizationInput) is used, or extend the mocked
implementation to match the real parser’s accepted shapes (full URLs, `#code`=
fragments, code#state and bare codes); make sure the manual callback test cases
that exercise createAuthorizationFlow and exchangeAuthorizationCode also include
pasted-hash formats so regressions are caught, and add deterministic vitest
regression tests for pasted-hash inputs and relevant token-refresh/concurrency
edge cases rather than mocking real secrets.

---

Outside diff comments:
In `@test/codex-manager-cli.test.ts`:
- Around line 593-626: Add a .mockReset() call for
getPluginConfigExplainReportMock inside the beforeEach hook so its queued
one-shot behaviors are cleared between tests; locate the beforeEach block that
already calls mockReset() on many mocks (e.g., loadAccountsMock.mockReset(),
confirmMock.mockReset()) and add getPluginConfigExplainReportMock.mockReset()
alongside them to prevent leaked queued calls used later at the tests
referencing that mock.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f2220300-77fe-40d2-8476-601074cf9ba8

📥 Commits

Reviewing files that changed from the base of the PR and between a6dc1db and ebbaf46.

📒 Files selected for processing (2)
  • test/codex-manager-cli.test.ts
  • test/wait-utils.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/wait-utils.test.ts
  • test/codex-manager-cli.test.ts

Comment on lines +775 to +788
const authModule = await import("../lib/auth/auth.js");
vi.mocked(authModule.createAuthorizationFlow).mockReset();
vi.mocked(authModule.exchangeAuthorizationCode).mockReset();
vi.mocked(authModule.parseAuthorizationInput).mockReset();
vi.mocked(authModule.parseAuthorizationInput).mockImplementation(
(input: string) => {
const codeMatch = input.match(/code=([^&]+)/);
const stateMatch = input.match(/state=([^&#]+)/);
return {
code: codeMatch?.[1],
state: stateMatch?.[1],
};
},
);

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.

⚠️ Potential issue | 🟡 Minor

keep parseAuthorizationInput aligned with production.

test/codex-manager-cli.test.ts:779-788 still installs a query-only regex, but lib/auth/auth.ts:69-105 accepts full urls, #code=... fragments, code#state, and bare codes. the manual callback cases at test/codex-manager-cli.test.ts:4446-4448, 5236-5238, and 5339-5341 only cover that single shape, so a supported pasted-callback format can regress without this suite noticing. use the real parser here, or add one pasted-hash regression alongside these manual-login tests.

As per coding guidelines, test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/codex-manager-cli.test.ts` around lines 775 - 788, The test currently
replaces parseAuthorizationInput with a narrow regex that only handles
query-style "code=..." inputs; instead either stop mocking
parseAuthorizationInput so the real implementation in lib/auth/auth.ts
(parseAuthorizationInput) is used, or extend the mocked implementation to match
the real parser’s accepted shapes (full URLs, `#code`= fragments, code#state and
bare codes); make sure the manual callback test cases that exercise
createAuthorizationFlow and exchangeAuthorizationCode also include pasted-hash
formats so regressions are caught, and add deterministic vitest regression tests
for pasted-hash inputs and relevant token-refresh/concurrency edge cases rather
than mocking real secrets.

@ndycode
ndycode merged commit 428d3a6 into main Mar 23, 2026
2 checks passed
@ndycode
ndycode deleted the fix/main-cli-test-regressions branch March 24, 2026 18:32
ndycode added a commit that referenced this pull request Apr 6, 2026
test: harden codex manager cli isolation
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