Skip to content

fix(rerun): emit partial stdout on TimeoutError in single-FE rerun --wait#219

Open
Awad-de wants to merge 28 commits into
TestSprite:mainfrom
Awad-de:fix/rerun-wait-timeout-stdout-pr
Open

fix(rerun): emit partial stdout on TimeoutError in single-FE rerun --wait#219
Awad-de wants to merge 28 commits into
TestSprite:mainfrom
Awad-de:fix/rerun-wait-timeout-stdout-pr

Conversation

@Awad-de

@Awad-de Awad-de commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes an asymmetry in testsprite test rerun --wait (single FE rerun path):
when the overall --timeout polling deadline is exceeded, the CLI now emits a
partial { runId, status: "running" } object to stdout before throwing
exit 7 — matching the behavior already present for RequestTimeoutError and
the recently fixed test run --wait / test wait paths.

Before: TimeoutError → throw ApiError immediately → stdout empty in
--output json mode → AI agents must scrape stderr to recover the runId.

After: TimeoutErrorout.print({ runId, status: "running" }) → throw
→ caller can programmatically chain into testsprite test wait <runId>.

Related issue

Closes #157
Hackathon CLI Improvement — agent recovery on polling timeout (complements
the test run --wait / test wait TimeoutError fix; this covers the
remaining single-FE test rerun --wait path).

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing behavior)
  • Documentation only
  • Build / CI / chore

Checklist

  • PR targets the main branch.
  • Commits follow Conventional Commits (feat(...), fix(...), docs(...), …).
  • npm run lint and npm run format:check pass.
  • npm run typecheck passes.
  • npm test passes and coverage stays at or above the 80% gate.
  • New behavior is covered by unit tests (mock-based; no network or credentials required).
  • No secrets, API keys, internal endpoints, or personal data are included.
  • User-facing changes are reflected in README.md / DOCUMENTATION.md where relevant. (N/A — internal error-handling parity fix, no CLI surface change.)

Notes for reviewers

Root cause

In runTestRerun (single FE rerun, no BE closure), the TimeoutError catch
block at ~line 6132 called ticker.finalize() then threw ApiError without
any out.print(). The adjacent RequestTimeoutError branch already emitted
a partial run to stdout — this was simply a missed mirror.

Scope

  • Changed: single-FE test rerun --wait TimeoutError path only (~12 lines).
  • Not changed: BE closure fan-out paths — those already aggregate runIds
    into the batch result object on timeout; test run --wait / test wait
    were fixed in a prior PR.

Test added

[finding-4] in test.rerun.spec.ts:

  • triggers TimeoutError via timeoutSeconds: 0 (immediate deadline)
  • asserts exit code 7
  • asserts stdout contains parseable JSON { runId, status: "running" }

Verification

npm test → 1571 passed
npm run typecheck → clean
npm run lint:fix → clean

Summary by CodeRabbit

  • Bug Fixes
    • Improved test rerun --wait timeout handling to emit a parseable partial JSON progress payload to stdout before exiting with the expected timeout code.
    • Batch reruns now include member runIds in the partial output with timeout/running status.
    • Added a clearer hint to resume a timed-out rerun.
  • Tests
    • Expanded regression coverage for single and batch rerun polling timeouts, validating exit behavior and the exact partial stdout contents.

…wait

Apply the fix in src/commands/test.ts. When the overall --timeout polling
deadline is exceeded on a single FE rerun, emit {runId, status:"running"}
to stdout before exit 7.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Updates test rerun --wait timeout handling for single reruns and adds regression coverage for single and batch timeout outputs, including exit code 7 and parseable JSON results.

Changes

Rerun wait timeout handling

Layer / File(s) Summary
Single-rerun timeout output
src/commands/test.ts
Prints { runId, status: 'running' } and a resume hint before re-throwing the timeout error.
Timeout regression coverage
src/commands/test.rerun.spec.ts
Verifies single rerun partial output and batch accepted entries marked status: "timeout" when polling times out.

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

Possibly related issues

Possibly related PRs

Suggested reviewers: zeshi-du

🚥 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 matches the main change: emitting partial stdout on timeout in the single-FE rerun --wait path.
✨ Finishing Touches
🧪 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/commands/test.ts (1)

6102-6113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate partial-timeout-print logic between TimeoutError and RequestTimeoutError branches.

The new TimeoutError block (Lines 6102-6113) and the existing RequestTimeoutError block (Lines 6126-6142) build near-identical { runId, status: 'running' } objects and near-identical text formatters, differing only in the wording of the timeout reason and hint line. Consider extracting a small shared helper (e.g. printTimeoutPartial(out, runId, reasonText)) to keep both branches — and any future timeout branches — in sync.

♻️ Proposed refactor sketch
+function printRerunTimeoutPartial(
+  out: ReturnType<typeof makeOutput>,
+  runId: string,
+  reasonText: string,
+): void {
+  const partial = { runId, status: 'running' as const };
+  out.print(partial, data => {
+    const p = data as typeof partial;
+    return [
+      `runId       ${p.runId}`,
+      `status      ${p.status} (${reasonText})`,
+      `hint        Re-attach with: testsprite test wait ${p.runId}`,
+    ].join('\n');
+  });
+}

Then call it from both catch branches with the appropriate reasonText.

Also applies to: 6126-6142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/test.ts` around lines 6102 - 6113, The TimeoutError and
RequestTimeoutError branches in test.ts duplicate the same partial-run printing
logic. Extract the shared `{ runId, status: 'running' }` construction and
`out.print` formatter into a small helper (for example, a timeout-partial
printer) and call it from both catch paths, passing only the branch-specific
timeout reason/hint text so the two cases stay consistent.
src/commands/test.rerun.spec.ts (1)

4699-4703: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider also asserting nextAction on the thrown error.

Per DOCUMENTATION.md, --wait --timeout exceedance is expected to include a nextAction pointing callers to test wait <run-id>. Asserting err.details?.nextAction (or equivalent) here would guard that documented contract, not just the exit code and stdout partial.

As per path instructions: "Exit-code mapping: error paths must map to the documented exit code (see DOCUMENTATION.md / the exit-code table)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/test.rerun.spec.ts` around lines 4699 - 4703, The rerun timeout
assertion currently checks only the exit code and stdout, but it should also
verify the documented `nextAction` on the thrown error. Update the
`test.rerun.spec.ts` timeout case to assert `err.details?.nextAction` (or the
equivalent error field) in the same block where `err` is matched, using the
rerun/wait flow around the `rerunResp.runId` scenario, so the contract that
callers are directed to `test wait <run-id>` is covered.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/commands/test.rerun.spec.ts`:
- Around line 4699-4703: The rerun timeout assertion currently checks only the
exit code and stdout, but it should also verify the documented `nextAction` on
the thrown error. Update the `test.rerun.spec.ts` timeout case to assert
`err.details?.nextAction` (or the equivalent error field) in the same block
where `err` is matched, using the rerun/wait flow around the `rerunResp.runId`
scenario, so the contract that callers are directed to `test wait <run-id>` is
covered.

In `@src/commands/test.ts`:
- Around line 6102-6113: The TimeoutError and RequestTimeoutError branches in
test.ts duplicate the same partial-run printing logic. Extract the shared `{
runId, status: 'running' }` construction and `out.print` formatter into a small
helper (for example, a timeout-partial printer) and call it from both catch
paths, passing only the branch-specific timeout reason/hint text so the two
cases stay consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a45e95fc-68a8-4092-b342-b64f27518f03

📥 Commits

Reviewing files that changed from the base of the PR and between 3305dfa and cff19ae.

📒 Files selected for processing (2)
  • src/commands/test.rerun.spec.ts
  • src/commands/test.ts

@Awad-de

Awad-de commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Discord Username / User ID
muathawad_37013 — Muath Awad

@Awad-de

Awad-de commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@jangjos-128 @zeshi-du Hello. Is everything okay? This is the previous PR #155. Is there a problem?

@jangjos-128

Copy link
Copy Markdown
Contributor

Hi @Awad-de — yes, everything's okay!

There's no problem with your work at all.

We'll review it through the normal process from here. You don't need to do anything else.

Apologies for the confusion, and thanks again for contributing to TestSprite!

@Awad-de

Awad-de commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Hi @jangjos-128 thanks but I'm participating in the hackathon-s3 competition. Do I have to win every PR that's merged?

@jangjos-128

Copy link
Copy Markdown
Contributor

Not at all @Awad-de!

In our community, it's definitely quality over quantity. It really comes down to how impactful and useful your PR is for the folks managing the CLI.

Even a single, solid merge that brings real value can absolutely get rewarded! 🙌

@zeshi-du zeshi-du 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.

The production fix is correct — the TimeoutError branch now mirrors its RequestTimeoutError sibling (partial {runId, status} to stdout, hint to stderr, exit 7 preserved), and that's exactly the right shape.

One blocking issue in the test diff: it replaces the existing [finding-5] regression test (batch rerun --wait: RequestTimeoutError during fan-out poll writes JSON stdout + exit 7) in place with the new [finding-4] single-FE test. The batch fan-out classification path it guarded is still live production code, so merging as-is silently drops its only regression coverage — CI stays green because the new test fills the slot.

Ask: restore the [finding-5] batch test and add your new [finding-4] test alongside it, so both code paths keep independent coverage. No changes needed to the production code. Thanks @Awad-de!

@Awad-de
Awad-de requested a review from zeshi-du July 18, 2026 02:05
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

✅ This PR is linked to an issue assigned to @Awad-de — thanks! The needs-issue label has been removed.

@github-actions github-actions Bot added the needs-issue PR not linked to an issue yet — please open one first and claim it (see CONTRIBUTING) label Jul 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/commands/test.rerun.spec.ts (1)

4855-4857: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove duplicated properties in the type assertion.

The accepted property is duplicated four times in the type definition.

♻️ Proposed fix
     const parsed = JSON.parse(stdoutLines.join('\n')) as {
       accepted: Array<{ testId: string; runId: string; status: string }>;
-      accepted: Array<{ testId: string; runId: string; status: string }>;
-      accepted: Array<{ testId: string; runId: string; status: string }>;
-      accepted: Array<{ testId: string; runId: string; status: string }>;
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/test.rerun.spec.ts` around lines 4855 - 4857, Update the
JSON.parse type assertion in the test rerun parsing code so the accepted
property is declared only once, preserving its existing array element shape and
all surrounding parsing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/test.rerun.spec.ts`:
- Around line 4847-4852: Update the runTestRerun invocation to pass only the
options object and a deps object containing fetchImpl and the output capture
callback. Wrap the call in error handling for RequestTimeoutError, then assert
the thrown error’s expected properties instead of treating the call result as a
returned error.
- Around line 4847-4853: Update the runTestRerun invocation in the affected test
to include output: 'json' in its options object, ensuring stdoutLines contains
JSON for the existing parsing logic.

---

Nitpick comments:
In `@src/commands/test.rerun.spec.ts`:
- Around line 4855-4857: Update the JSON.parse type assertion in the test rerun
parsing code so the accepted property is declared only once, preserving its
existing array element shape and all surrounding parsing behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8641cf0c-d1be-4228-92df-ce18f962213d

📥 Commits

Reviewing files that changed from the base of the PR and between 95e3978 and 7975f6f.

📒 Files selected for processing (1)
  • src/commands/test.rerun.spec.ts

Comment thread src/commands/test.rerun.spec.ts Outdated
Comment thread src/commands/test.rerun.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/test.rerun.spec.ts`:
- Line 4827: Remove the unused creds binding in the test setup, or pass creds
into the relevant setup call if credentials are required there; ensure no
unreferenced assignment remains.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ebd16c3-168c-4fd0-abe7-6a5317fd8aee

📥 Commits

Reviewing files that changed from the base of the PR and between 7975f6f and 51af939.

📒 Files selected for processing (1)
  • src/commands/test.rerun.spec.ts

Comment thread src/commands/test.rerun.spec.ts
Awad-de added 5 commits July 18, 2026 06:05
Fix missing newline at end of file.
Fix missing newline at end of file in test.rerun.spec.ts
Fix missing newline at end of file in test.rerun.spec.ts
Fix missing newline at end of file in test.rerun.spec.ts
@github-actions github-actions Bot removed the needs-issue PR not linked to an issue yet — please open one first and claim it (see CONTRIBUTING) label Jul 18, 2026
@Awad-de

Awad-de commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi @zeshi-du

The production fix is correct — the TimeoutError branch now mirrors its RequestTimeoutError sibling (partial {runId, status} to stdout, hint to stderr, exit 7 preserved), and that's exactly the right shape.

One blocking issue in the test diff: it replaces the existing [finding-5] regression test (batch rerun --wait: RequestTimeoutError during fan-out poll writes JSON stdout + exit 7) in place with the new [finding-4] single-FE test. The batch fan-out classification path it guarded is still live production code, so merging as-is silently drops its only regression coverage — CI stays green because the new test fills the slot.

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.

[Hackathon] fix(rerun): partial stdout on TimeoutError in single-FE rerun --wait (#219)

3 participants