From 28dd88d02450fb0b9d71dba0a5b24dc2a82cda24 Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Sat, 31 Oct 2026 01:31:00 -0600 Subject: [PATCH] Test that a server which never answers costs one report, not the sender Each request is bounded by TIMEOUT_SECONDS; nothing asserted it. A server that accepts the connection and hangs must time out that report, log it at DEBUG, and leave the sender free to deliver the next one. Co-Authored-By: Claude Opus 5.5 --- tests/test_trace_client.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_trace_client.py b/tests/test_trace_client.py index a050666..03bcd97 100644 --- a/tests/test_trace_client.py +++ b/tests/test_trace_client.py @@ -198,6 +198,26 @@ def read(self, *args): self.assertTrue(any("answered 503" in r.getMessage() for r in self.log), [r.getMessage() for r in self.log]) self.assertTrue(all(r.levelno == logging.DEBUG for r in self.log)) + def test_a_server_that_never_answers_costs_one_report_not_the_sender(self): + # Each request is bounded by TIMEOUT_SECONDS (shortened here), so a + # server that accepts the connection and then hangs drops that report + # and the sender moves on to the next one. + self.capture.release.clear() # hold the first request unanswered + with mock.patch.object(TraceClient, "TIMEOUT_SECONDS", 0.5): + client = TraceClient(self.base_url, "MyGame", "1.2.3", key="k") + client.report("first") + deadline = time.monotonic() + 10 + while time.monotonic() < deadline and not any("could not deliver" in r.getMessage() for r in self.log): + time.sleep(0.05) + self.assertTrue(any("could not deliver" in r.getMessage() for r in self.log), + "a hung request must time out: %s" % [r.getMessage() for r in self.log]) + self.capture.release.set() + client.report("second") + client.close() + self.assertIn("second", [json.loads(r["body"])["name"] for r in self.capture.requests], + "the sender is still alive after a request times out") + self.assertTrue(all(r.levelno == logging.DEBUG for r in self.log)) + def test_disabled_client_sends_nothing(self): for client in (TraceClient(self.base_url, "MyGame", "1.2.3", key="k", enabled=False), TraceClient(self.base_url, "MyGame", "1.2.3"),