Skip to content

test(proxy): cover pre-aborted abort listener review#1114

Closed
ding113 wants to merge 2 commits into
devfrom
fix/heap-retainer-review-followup-20260426
Closed

test(proxy): cover pre-aborted abort listener review#1114
ding113 wants to merge 2 commits into
devfrom
fix/heap-retainer-review-followup-20260426

Conversation

@ding113
Copy link
Copy Markdown
Owner

@ding113 ding113 commented Apr 26, 2026

Summary

Documentation and test coverage improvements for pre-aborted abort listener handling in the proxy pipeline. This is a follow-up PR addressing review feedback from #1113.

Related Issues & PRs

Changes

Documentation

  • Added inline documentation to bindClientAbortListener() explaining synchronous onAbort invocation for pre-aborted signals
  • Documented the requirement for callers to initialize resources before calling the function

Test Improvements

  • Fixed test fixture: Aligned Provider.providerType values with the real union type ("openai-compatible" instead of "openai")
  • Added new test case: invokes stream cancel once when client signal is already aborted
    • Verifies that when a client signal is already aborted, no event listeners are registered
    • Ensures cancelTask is called exactly once and localAbort fires once
    • Covers the edge case where cleanup is a no-op because onAbort was invoked synchronously

Files Changed

File Changes
src/app/v1/_lib/proxy/client-abort-listener.ts Added JSDoc comments documenting pre-aborted behavior
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts Fixed providerType values; added pre-aborted signal test case

Validation

# Run the specific new tests
bunx vitest run tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts tests/unit/lib/async-task-manager-edge-runtime.test.ts --reporter=dot

# Full check
bun run build
bun run lint
bun run lint:fix
bun run typecheck
bun run test

Checklist

  • Documentation added for pre-aborted signal behavior
  • Test coverage added for pre-aborted cleanup path
  • Test fixtures aligned with actual type definitions
  • All validation commands pass

This PR addresses review feedback from #1113 to ensure complete test coverage of edge cases in the abort listener lifecycle.

Greptile Summary

This follow-up PR adds a JSDoc comment to bindClientAbortListener documenting its synchronous-invocation behavior for pre-aborted signals, fixes providerType fixture values ("openai""openai-compatible"), and adds a new test case covering the streaming + pre-aborted signal edge case.

Confidence Score: 5/5

Safe to merge — documentation and test-only changes with no production logic modifications.

No production logic was changed; changes are limited to a JSDoc comment and test improvements. The only finding is a P2 style inconsistency between two assertion strengths in the test file.

No files require special attention.

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/client-abort-listener.ts Added JSDoc comment (in Chinese, consistent with existing inline comments) documenting synchronous onAbort invocation for pre-aborted signals and caller initialization requirement; no logic changes.
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts Fixed providerType fixture values from "openai" to "openai-compatible"; added new test covering streaming + pre-aborted signal path. Minor inconsistency: old pre-aborted test uses toHaveBeenCalled() while new test uses toHaveBeenCalledTimes(1) for the same behavioral contract.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[bindClientAbortListener called] --> B{signal null/undefined?}
    B -- yes --> C[return no-op cleanup]
    B -- no --> D{signal.aborted?}
    D -- yes --> E[invoke onAbort synchronously]
    E --> F[return no-op cleanup]
    D -- no --> G[addEventListener abort, once:true]
    G --> H[return idempotent cleanup fn]
    H --> I{cleanup called?}
    I -- yes --> J[removeEventListener abort]
    I -- already cleaned --> K[no-op]
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
Line: 281

Comment:
**Weaker assertion in the analogous non-stream pre-aborted test**

The existing non-stream pre-aborted test uses `toHaveBeenCalled()` (≥ 1 time) while the new stream test uses `toHaveBeenCalledTimes(1)` (exactly once). Both paths exercise the same `bindClientAbortListener` synchronous-invoke branch, so `cancelTask` should fire exactly once in both cases. Consider tightening the older assertion for consistency and to catch any future double-invocation regression on the non-stream path.

```suggestion
    expect(testState.cancelTask).toHaveBeenCalledTimes(1);
```

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

Reviews (2): Last reviewed commit: "test(proxy): avoid global abort prototyp..." | Re-trigger Greptile

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

📝 Walkthrough

Walkthrough

为客户端中止监听器的绑定函数补充 JSDoc,说明幂等清理语义和当 signal.aborted 已为 true 时 onAbort 可能同步执行;同时扩展单元测试,覆盖流式场景下信号已中止的不注册/不注销监听器路径并校验取消调用计数。

Changes

Cohort / File(s) Summary
文档注释
src/app/v1/_lib/proxy/client-abort-listener.ts
bindClientAbortListener 添加 JSDoc,说明清理为幂等操作、onAbortsignal.aborted 已为 true 时可同步执行,以及调用者需保证 onAbort 捕获的资源已初始化。
测试
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
将测试夹具默认 providerType"openai" 改为 "openai-compatible";新增流式场景单元测试,验证当客户端信号已中止时不注册/不注销 abort 监听器且流取消仅触发一次(cancelTask 调用计数)。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 分钟

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰地描述了主要变更:为预中止(pre-aborted)的中止监听器添加测试覆盖。标题简洁、相关,准确反映了PR的核心目标。
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 PR描述清晰详细地说明了所有改动内容,与文件变更完全吻合,包括文档改进、测试fixture修复和新增测试用例。

✏️ 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/heap-retainer-review-followup-20260426

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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds documentation to the bindClientAbortListener function to clarify its synchronous execution behavior when a signal is already aborted. Additionally, it updates test helper configurations to use the openai-compatible provider type and introduces a new unit test to ensure that stream cancellation is correctly handled and invoked exactly once when the client signal is pre-aborted. I have no feedback to provide.

controller.abort();
const addSpy = vi.spyOn(controller.signal, "addEventListener");
const removeSpy = vi.spyOn(controller.signal, "removeEventListener");
const localAbortSpy = vi.spyOn(AbortController.prototype, "abort");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Global prototype spy may produce a fragile assertion

vi.spyOn(AbortController.prototype, "abort") intercepts every .abort() call on every AbortController instance for the duration of this test — including any controllers that mock infrastructure (e.g., AsyncTaskManager.register's returned controller) might abort. toHaveBeenCalledTimes(1) is therefore sensitive to internal implementation details: adding a second internal controller that also calls .abort() will silently break this test.

Consider holding a reference to the specific internal controller (e.g., by spying on the AbortController constructor and capturing this) or documenting explicitly in a comment why exactly one call is expected.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
Line: 289

Comment:
**Global prototype spy may produce a fragile assertion**

`vi.spyOn(AbortController.prototype, "abort")` intercepts every `.abort()` call on every `AbortController` instance for the duration of this test — including any controllers that mock infrastructure (e.g., `AsyncTaskManager.register`'s returned controller) might abort. `toHaveBeenCalledTimes(1)` is therefore sensitive to internal implementation details: adding a second internal controller that also calls `.abort()` will silently break this test.

Consider holding a reference to the specific internal controller (e.g., by spying on the `AbortController` constructor and capturing `this`) or documenting explicitly in a comment why exactly one call is expected.

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

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@github-actions github-actions Bot added the size/XS Extra Small PR (< 50 lines) label Apr 26, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts (1)

284-306: 新增 stream 预中止用例覆盖到位,但 prototype spy 范围偏宽。

用例正确覆盖了 signal.aborted === truebindClientAbortListener 同步执行 onAbort、不注册/不移除监听器、cancelTask 仅触发一次的语义。

唯一可改进点:vi.spyOn(AbortController.prototype, "abort") 会捕获测试期间所有 AbortController 实例的 abort() 调用(包括第 23 行 AsyncTaskManager.register 返回的 controller,以及 dispatch 内部可能创建的其它 controller)。当前 toHaveBeenCalledTimes(1) 通过依赖于实现细节,未来若内部多创建一个被 abort 的 controller,本用例会出现误报。

如希望将断言锚定在“某个特定本地 controller”,可以考虑改为对调用结果 Response 关联的 reader/upstream controller 做更精确的断言,或仅断言 >= 1

♻️ 可选的稳健化改写
-    expect(testState.cancelTask).toHaveBeenCalledTimes(1);
-    expect(localAbortSpy).toHaveBeenCalledTimes(1);
+    expect(testState.cancelTask).toHaveBeenCalledTimes(1);
+    // 至少触发一次本地 AbortController.abort(),避免与其他内部 controller 的 abort 调用耦合
+    expect(localAbortSpy.mock.calls.length).toBeGreaterThanOrEqual(1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts` around
lines 284 - 306, The prototype-wide spy vi.spyOn(AbortController.prototype,
"abort") is too broad and may catch aborts from other controllers created during
dispatch; replace it with a targeted assertion: either spy on the specific
controller instance you care about (the local controller created/registered
during AsyncTaskManager.register or returned/accessible via testState/session)
instead of the prototype, or keep the prototype spy but relax the assertion to
expect at least one call (e.g., expect(localAbortSpy).toHaveBeenCalled(); or
toBeGreaterThanOrEqual(1)). Update the test around the symbols controller,
ProxyResponseHandler.dispatch, bindClientAbortListener, and testState.cancelTask
accordingly so the abort assertion is anchored to the intended controller.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts`:
- Around line 284-306: The prototype-wide spy
vi.spyOn(AbortController.prototype, "abort") is too broad and may catch aborts
from other controllers created during dispatch; replace it with a targeted
assertion: either spy on the specific controller instance you care about (the
local controller created/registered during AsyncTaskManager.register or
returned/accessible via testState/session) instead of the prototype, or keep the
prototype spy but relax the assertion to expect at least one call (e.g.,
expect(localAbortSpy).toHaveBeenCalled(); or toBeGreaterThanOrEqual(1)). Update
the test around the symbols controller, ProxyResponseHandler.dispatch,
bindClientAbortListener, and testState.cancelTask accordingly so the abort
assertion is anchored to the intended controller.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6a6d9374-6dbb-473b-9e95-e6e7052524cb

📥 Commits

Reviewing files that changed from the base of the PR and between 8968a42 and 11ea004.

📒 Files selected for processing (2)
  • src/app/v1/_lib/proxy/client-abort-listener.ts
  • tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts

@github-actions github-actions Bot added area:core javascript Pull requests that update javascript code labels Apr 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

No significant issues identified in this PR.

PR Size: XS

  • Lines changed: 34
  • Files changed: 2

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Automated review by Codex AI

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

  • Reviewed PR #1114 (32 additions / 2 deletions across 2 files) and applied the size/XS label.
  • Checked diff-only lines across logic/security/error handling/types/comments/tests/simplification and found no issues that met the reporting threshold (>= 80 confidence) after context validation, so no inline review comments were posted.
  • Submitted the required review summary via gh pr review --comment.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core javascript Pull requests that update javascript code size/XS Extra Small PR (< 50 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant