From 80771cd9565f2f0e60b9f08543f6dc8e531323c6 Mon Sep 17 00:00:00 2001 From: justrach <54503978+justrach@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:09:52 +0800 Subject: [PATCH 1/3] test(semantic): give retry mock the normal request deadline The retry fixture's two-second localhost deadline can expire while a busy test runner schedules its server thread, obscuring the intended 429/503-to-success assertion. Use the normal request deadline for this fixture only; the separate stalled-provider test continues to cover deadline cancellation. Co-Authored-By: Codegraff --- src/test_semantic.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test_semantic.zig b/src/test_semantic.zig index 041195b2..4a346df6 100644 --- a/src/test_semantic.zig +++ b/src/test_semantic.zig @@ -542,7 +542,9 @@ test "semantic: explicit index batches retry bounded 429 and 5xx responses" { const addr = try std.Io.net.IpAddress.parse("127.0.0.1", 0); var server = try addr.listen(io, .{ .reuse_address = true, .mode = .stream, .protocol = .tcp }); defer server.deinit(io); - try configureMockEndpoint(&server, "mock-model", 2_000); + // This case tests retry classes, not request deadlines (covered above). + // Allow the local mock the normal deadline on busy test runners. + try configureMockEndpoint(&server, "mock-model", semantic.default_timeout_ms); var mock = RetryEmbeddingServer{ .server = &server, .response = response }; const thread = try std.Thread.spawn(.{}, RetryEmbeddingServer.run, .{&mock}); From 8acaae4fdb1a54740200a425bbfa44b63fcec242 Mon Sep 17 00:00:00 2001 From: justrach <54503978+justrach@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:27:55 +0800 Subject: [PATCH 2/3] test(semantic): make the retry fixture deadline explicit Keep the test-only timeout self-contained at fifteen seconds, so its intent is visible without resolving a production constant. The retry assertion and production behavior are unchanged. Co-Authored-By: Codegraff --- src/test_semantic.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test_semantic.zig b/src/test_semantic.zig index 4a346df6..e655a188 100644 --- a/src/test_semantic.zig +++ b/src/test_semantic.zig @@ -542,9 +542,9 @@ test "semantic: explicit index batches retry bounded 429 and 5xx responses" { const addr = try std.Io.net.IpAddress.parse("127.0.0.1", 0); var server = try addr.listen(io, .{ .reuse_address = true, .mode = .stream, .protocol = .tcp }); defer server.deinit(io); - // This case tests retry classes, not request deadlines (covered above). - // Allow the local mock the normal deadline on busy test runners. - try configureMockEndpoint(&server, "mock-model", semantic.default_timeout_ms); + // This case tests retry status handling, not a short deadline. Give the + // local mock time to run on a busy test runner. + try configureMockEndpoint(&server, "mock-model", 15_000); var mock = RetryEmbeddingServer{ .server = &server, .response = response }; const thread = try std.Thread.spawn(.{}, RetryEmbeddingServer.run, .{&mock}); From d2cc4b5ac98e8bac2c060c328667bc225465cca3 Mon Sep 17 00:00:00 2001 From: justrach <54503978+justrach@users.noreply.github.com> Date: Sat, 26 Sep 2026 18:33:46 +0800 Subject: [PATCH 3/3] test(semantic): ignore unrelated HEAD probes in retry mock A loopback HEAD / probe can consume one of the three scripted mock connections, leaving the real embedding POST unanswered until its deadline. Ignore only that bodyless probe without advancing the 429/503/200 sequence, inject it in the test, and restore the original fixture deadline. Production retry behavior is unchanged. Co-Authored-By: Codegraff --- src/test_semantic.zig | 45 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/test_semantic.zig b/src/test_semantic.zig index e655a188..659b695c 100644 --- a/src/test_semantic.zig +++ b/src/test_semantic.zig @@ -53,6 +53,8 @@ const MockEmbeddingServer = struct { response: []const u8, status: []const u8 = "200 OK", delay_ms: u32 = 0, + allow_head_probe: bool = false, + ignored_head_probe: bool = false, request: [65536]u8 = undefined, request_len: usize = 0, failure: ?anyerror = null, @@ -84,6 +86,10 @@ const MockEmbeddingServer = struct { if (n == 0) break; self.request_len += n; const header_end = std.mem.indexOf(u8, self.request[0..self.request_len], "\r\n\r\n") orelse continue; + if (self.allow_head_probe and std.mem.startsWith(u8, self.request[0..header_end], "HEAD / HTTP/1.1\r\n")) { + self.ignored_head_probe = true; + return; + } const content_len = contentLength(self.request[0..header_end]) catch |err| { self.failure = err; return; @@ -106,14 +112,26 @@ const RetryEmbeddingServer = struct { server: *std.Io.net.Server, response: []const u8, failure: ?anyerror = null, + ignored_head_probes: usize = 0, fn run(self: *RetryEmbeddingServer) void { for ([_][]const u8{ "429 Too Many Requests", "503 Service Unavailable", "200 OK" }) |status| { - var one = MockEmbeddingServer{ .server = self.server, .response = self.response, .status = status }; - one.run(); - if (one.failure) |err| { - self.failure = err; - return; + while (true) { + var one = MockEmbeddingServer{ .server = self.server, .response = self.response, .status = status, .allow_head_probe = true }; + one.run(); + if (one.ignored_head_probe) { + self.ignored_head_probes += 1; + if (self.ignored_head_probes > 8) { + self.failure = error.TooManyHeadProbes; + return; + } + continue; + } + if (one.failure) |err| { + self.failure = err; + return; + } + break; } } } @@ -542,9 +560,19 @@ test "semantic: explicit index batches retry bounded 429 and 5xx responses" { const addr = try std.Io.net.IpAddress.parse("127.0.0.1", 0); var server = try addr.listen(io, .{ .reuse_address = true, .mode = .stream, .protocol = .tcp }); defer server.deinit(io); - // This case tests retry status handling, not a short deadline. Give the - // local mock time to run on a busy test runner. - try configureMockEndpoint(&server, "mock-model", 15_000); + try configureMockEndpoint(&server, "mock-model", 2_000); + + // An unrelated loopback HEAD probe must not consume one of the three + // scripted embedding responses. + { + const probe_addr = try std.Io.net.IpAddress.parse("127.0.0.1", server.socket.address.getPort()); + const probe = try probe_addr.connect(io, .{ .mode = .stream }); + defer probe.close(io); + var probe_buffer: [128]u8 = undefined; + var probe_writer = probe.writer(io, &probe_buffer); + try probe_writer.interface.writeAll("HEAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"); + try probe_writer.interface.flush(); + } var mock = RetryEmbeddingServer{ .server = &server, .response = response }; const thread = try std.Thread.spawn(.{}, RetryEmbeddingServer.run, .{&mock}); @@ -555,6 +583,7 @@ test "semantic: explicit index batches retry bounded 429 and 5xx responses" { defer testing.allocator.free(result.vectors); thread.join(); if (mock.failure) |err| return err; + try testing.expect(mock.ignored_head_probes >= 1); try testing.expectEqual(@as(usize, 1), result.count); }