From 058b9b4e9e48210820a536d251e9122fc16f6dfd Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 25 Sep 2026 20:49:53 -0400 Subject: [PATCH] test(http): expect #811's negative-length ProtocolError, not #814's generic wrap - test_guess_version_reports_a_preface_with_no_frame_as_such's GOAWAY case asserted str(exception) == 'HTTP/2: invalid format', which #814 produced when the sixteen-octet GOAWAY's oversized declared length drove the ``debug`` field negative and struct.calcsize raised a bare struct.error. - #811 (fix(fields): raise ProtocolError, not struct.error, on a negative resolved field length) landed after #814's branch point and fixed that exact case at its root: FieldBase.length now raises ProtocolError itself, so http.py's ``except ProtocolError: raise`` passes it through unchanged instead of reaching the ``except (ValueError, struct.error)`` wrap that produced #814's message. The assertion was stale, not the behaviour. - Update the assertion to the field-level message ("Field debug resolved to a negative length; template='-1s'") while keeping every other check #814 cared about: still a catchable BaseError, still not a bare struct.error, still chained to the original struct.error via __cause__. Verified the failure on stock 3cbdf8999 (CPython 3.14) and the fix passing on both 3.14 and 3.10; tests/protocols/application/ and tests/corekit/ otherwise pass (338 passed, 16 skipped -- 5 unrelated pre-existing failures in test_http_runtime.py were just missing generated sample captures, fixed by running examples/generators/make_samples.py). No production code changed, so coverage of pcapkit/protocols/application/http.py (98%) and pcapkit/corekit/fields/field.py (75%) is unchanged. Closes #822 --- tests/protocols/application/test_http_unit.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/protocols/application/test_http_unit.py b/tests/protocols/application/test_http_unit.py index 7d099ea50d..3865e160e4 100644 --- a/tests/protocols/application/test_http_unit.py +++ b/tests/protocols/application/test_http_unit.py @@ -635,11 +635,21 @@ def test_guess_version_reports_a_preface_with_no_frame_as_such(self) -> None: # And a preface followed by a frame that trips the #805 residual -- a # sixteen-octet GOAWAY, whose fixed ``stream`` and ``error`` fields alone - # want eight octets after the header -- must come back catchable. The - # identified HTTP/2 route normalises that, because the trial-parse arm it - # replaces for this input used to suppress :exc:`struct.error` and answer - # ``unknown HTTP version``; without the conversion the bare stdlib error - # would now escape a dispatcher documented to raise ``ProtocolError``. + # want eight octets after the header -- must come back catchable. #805 + # is now fixed at its actual root by #811: ``FieldBase.length`` raises + # ``ProtocolError`` itself on a negative resolved length, instead of + # letting a bare :exc:`struct.error` escape from :func:`struct.calcsize`. + # So the ``debug`` field -- whose length is ``pkt['__length__']`` minus + # the fixed ``stream``/``error`` octets, negative once the declared frame + # length outruns this 16-octet buffer -- raises that ``ProtocolError`` + # directly, and ``http.py``'s dispatcher (``except ProtocolError: raise``) + # passes it through unchanged rather than wrapping it as + # ``'HTTP/2: invalid format'``, exactly as it does for the + # '9-octet frame header' case above. What #814 needed preserved survives + # this: it is still a catchable ``BaseError``, still not a bare + # :exc:`struct.error`, and still chained to the original one via + # ``__cause__`` -- only the message is now the more specific one #811 + # provides. import struct from pcapkit.utilities.exceptions import BaseError @@ -649,7 +659,8 @@ def test_guess_version_reports_a_preface_with_no_frame_as_such(self) -> None: raw = preface + goaway with self.assertRaises(ProtocolError) as ctx: HTTP(io.BytesIO(raw), len(raw)) - self.assertEqual(str(ctx.exception), 'HTTP/2: invalid format') + self.assertEqual(str(ctx.exception), + "Field debug resolved to a negative length; template='-1s'") self.assertIsInstance(ctx.exception, BaseError) self.assertNotIsInstance(ctx.exception, struct.error) self.assertIsInstance(ctx.exception.__cause__, struct.error)