Skip to content

Add since-rejection tests for /a2a/stream (master validates it correctly but nothing covers it) - #278

Merged
jaylfc merged 1 commit into
masterfrom
exec/tsk-q6fjux
Aug 14, 2026
Merged

Add since-rejection tests for /a2a/stream (master validates it correctly but nothing covers it)#278
jaylfc merged 1 commit into
masterfrom
exec/tsk-q6fjux

Conversation

@jaylfc

@jaylfc jaylfc commented Aug 14, 2026

Copy link
Copy Markdown
Owner

CARD TITLE (intent, not commit subject): Add since-rejection tests for /a2a/stream (master validates it correctly but nothing covers it)

Autonomous build of board card tsk-q6fjux.

Master already rejects a bad since on /a2a/stream via _parse_since, but no
test covered it. Lift the stream rejection tests from PR #269 and extend
to nan, inf, a message id (1444) and an unparseable value (abc), each
asserting HTTP 400. Add a valid-epoch accept case (1786000000) that reads
only the SSE status line over a raw socket, so the never-closing accept
stream cannot hang the test. Covers the PR #269 regression where
/a2a/stream?since=abc returned 200.

Files:
tests/test_a2a.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation for invalid SSE since parameter values.
    • Invalid timestamps and message IDs now return clear HTTP 400 errors.
    • Valid epoch timestamps are accepted with an HTTP 200 response without hanging during connection setup.
  • Tests

    • Added coverage for numeric, non-numeric, infinite, and message ID inputs.

Master already rejects a bad since on /a2a/stream via _parse_since, but no
test covered it. Lift the stream rejection tests from PR #269 and extend
to nan, inf, a message id (1444) and an unparseable value (abc), each
asserting HTTP 400. Add a valid-epoch accept case (1786000000) that reads
only the SSE status line over a raw socket, so the never-closing accept
stream cannot hang the test. Covers the PR #269 regression where
/a2a/stream?since=abc returned 200.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 002352f5-0f05-4028-af6e-4e2b6436e46c

📥 Commits

Reviewing files that changed from the base of the PR and between 031b401 and 387a607.

📒 Files selected for processing (1)
  • tests/test_a2a.py

📝 Walkthrough

Walkthrough

The test suite adds bounded socket status-line reads for /a2a/stream. It verifies HTTP 400 responses for invalid since values and HTTP 200 for a valid epoch timestamp without hanging on the open SSE stream.

Changes

SSE stream validation

Layer / File(s) Summary
Status reader and invalid input tests
tests/test_a2a.py
Adds _stream_status_code and tests invalid since values, including non-finite, message-ID-like, and non-numeric values.
Valid epoch acceptance test
tests/test_a2a.py
Confirms that a valid epoch since value returns HTTP 200 without reading the persistent SSE connection.

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

Merge Risk: ⚪ Minimal · up to 387a6

This change adds focused coverage for valid and invalid /a2a/stream since values without changing production behavior; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the added rejection tests for the /a2a/stream since parameter and matches the pull request changes.
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.
✨ 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 exec/tsk-q6fjux

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.

@gitar-bot

gitar-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

Comment thread tests/test_a2a.py
break
line += chunk
status_line = line.decode("utf-8", "replace").splitlines()[0]
return int(status_line.split()[1])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[WARNING]: _stream_status_code raises IndexError on empty or malformed responses

If the server closes the connection without sending a complete HTTP status
line, status_line.split()[1] raises IndexError because the response
is empty or truncated. The function should validate that the response
is non-empty and parseable before indexing into it.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread tests/test_a2a.py
while b"\r\n" not in line:
try:
chunk = sock.recv(128)
except (socket.timeout, TimeoutError) as exc:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[WARNING]: _stream_status_code only catches timeout errors

The except clause only handles socket.timeout and TimeoutError.
Other socket errors (e.g., ConnectionResetError, ConnectionAbortedError,
OSError) will propagate as unhandled exceptions, making test failures
less diagnosable. Catching the broader OSError base class would cover
all socket-level failures with a single clear assertion.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
WARNING 2
Issue Details (click to expand)

WARNING

File Line Issue
tests/test_a2a.py 351 _stream_status_code raises IndexError on empty or malformed responses
tests/test_a2a.py 345 _stream_status_code only catches timeout errors
Files Reviewed (1 file)
  • tests/test_a2a.py - 2 issues

Fix these issues in Kilo Cloud


Reviewed by step-3.7-flash · Input: 135.3K · Output: 22K · Cached: 369.2K

@jaylfc

jaylfc commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

The blocker from #276 is fixed, and fixed in the right place. One residual, and I am flagging it rather than asserting it — see the note at the end on what I did and did not run.

What this gets right

_stream_status_code reads only the HTTP status line over a raw socket with a bounded settimeout, and raises on timeout instead of treating it as an accept:

except (socket.timeout, TimeoutError) as exc:
    raise AssertionError(f"timed out reading stream status line: {exc}") from exc

That is exactly the shape #276 was missing. And test_http_a2a_stream_since_valid_epoch_accepted now asserts status == 200 on the accept path, so ACCEPT and REJECT are separated by status code rather than, in the docstring's own words, "by absence of a 400". That sentence is the thing I was asking for.

The two defects from #276 are gone by construction: the tests live in tests/test_a2a.py, which already exists on master (776 lines, 35 tests), the addition is 85 lines, there are zero test-name collisions with tests/test_http_server.py, and no name is defined twice. There is no misnamed accept test this time — the one that says it checks a valid epoch does check a valid epoch.

Residual: the reject half still uses the unbounded reader

The four rejection tests call _get(...), and _get at tests/test_a2a.py:103 is urllib.request.urlopen(req, timeout=10). That timeout is per socket read, and the accepted SSE stream writes a keepalive every second, so it never fires.

On today's server that is harmless: a bad since is rejected before the SSE headers are written, so the 400 comes back cleanly and the header comment correctly says so. The concern is the regression case, which is the one these tests exist for. If the validation is ever removed again, ?since=1444 becomes an accept, and those four tests would hang rather than fail — the same failure mode as #276, now confined to the reject half.

The fix is small because the primitive is already in this PR: point the reject tests at _stream_status_code too and assert == 400. Then every one of the five distinguishes accept from reject by status code, and none of them can block.

Not a blocker. The PR does what its card asked, and it does not make anything worse: today the reject path genuinely returns 400 without streaming. Whether to fix it here or card it is a judgement call, and I would take it here since it is a two-line change against a helper this PR already wrote.

What I ran, and what I did not

Everything above is read from the diff and from the file's source on the PR head — the helper's timeout value, the collision count, the file's existing size on master. I have not re-run the red control (removing _parse_since from _handle_a2a_stream and confirming the reject tests hang), which is the measurement that would turn the residual from a reading into a result. I am at 7d 91%, and under the band ruled at bus 2646/2650 a test run I have to re-derive myself is hands-on work rather than adjudication.

So: treat the residual as a reasoned concern, not a measured finding. If it is fixed here, the fix should carry its own red control, and that control is the thing worth running.

jaylfc added a commit that referenced this pull request Aug 14, 2026
…rge-gate card refiled

Closing a blocked PR frees its card and discards the diagnosis in the same
motion, because the executor builds the lane prompt from the card body only.
Measured: the merge-gate rebuild returned with the identical blocker forty
minutes later. Close and refile, never close alone.
@jaylfc
jaylfc merged commit 762982d into master Aug 14, 2026
6 checks passed
@jaylfc
jaylfc deleted the exec/tsk-q6fjux branch August 14, 2026 16:06
jaylfc added a commit that referenced this pull request Aug 14, 2026
…led as cards

The bounded-probe question is ruled (bus 2679): executing a pre-specified
acceptance bar is orchestration, inventing one is hands-on, and a probe run
under the usage band must print a positive control or a denominator. The
freshness predicate is now a checked-in script that prints its own controls
rather than something retyped each session.

Also records that the automated 5h usage notice still contradicts the ruled
band, that assignee "@any" is not a stored field (37 of 37 claimable cards
carry null), and that a heredoc cannot read a pipe.
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