infra(test): add mock_grpc_server_peer for gRPC unary loopback (Phase 2B of #1074)#1102
Merged
Merged
Conversation
… 2B of #1074) Phase 2B of issue #1074 (protocol-aware loopback peers + error injection). Adds a server-side gRPC framing peer composed on top of the existing mock_h2_server_peer's HTTP/2 SETTINGS exchange. What this ships: - tests/support/mock_grpc_server_peer.{h,cpp}: same SETTINGS handshake as the h2 peer, then with grpc_reply_mode::echo_unary the worker reads one client request stream (HEADERS + DATA up to END_STREAM) and replies on the same stream with: 1. response HEADERS (":status: 200", "content-type: application/grpc", END_HEADERS, no END_STREAM) 2. one length-prefixed gRPC DATA frame (5-byte gRPC header + body) 3. trailing HEADERS ("grpc-status: 0", END_HEADERS + END_STREAM) HPACK uses static index 8 for :status: 200 and literal-without-indexing for content-type/grpc-status to keep the encoder dependency-free. - tests/support/CMakeLists.txt: add the new translation unit to the network_test_support static library. - tests/support/README.md: document the new peer in the file table and add a copy-paste composition example covering the unary success path. - tests/unit/grpc_client_branch_test.cpp: one demo TEST_F (CallRawSucceedsWithMockGrpcPeerEchoUnary) that drives grpc_client::call_raw past the http2_client::post wait, the trailer scan that extracts grpc-status, and grpc_message::parse on the response body. Covers branches that the Phase 2A peer could not reach because it never replied with HEADERS+DATA+trailers. Hermetic: bound to 127.0.0.1:0 via the underlying tls_loopback_listener; RSA-2048 cert regenerated per construction; no DNS, no external network, no on-disk secrets. Issue scope: this PR delivers Phase 2B only. Phases 2C (mock_quic_peer_loop), 2D (network_test_friends.h injection), and 2E (frame_injector) remain. Per the #1074 acceptance criteria the issue stays open until all five phases land plus per-protocol demo tests and the README entries. Part of #1074 Part of #953
Contributor
Coverage Report
Coverage DetailsFull HTML report is available as a build artifact. |
1 task
kcenon
added a commit
that referenced
this pull request
May 5, 2026
…2C of #1074) (#1103) Add server-side QUIC Initial echo peer to tests/support/. The peer receives one client Initial datagram, derives QUIC v1 initial keys from the original DCID (RFC 9001 Section 5.2), and replies with a header-protected server Initial carrying a stub crypto_frame (type 0x06). This makes quic_socket::process_crypto_frame reachable from a hermetic test for the first time, unblocking coverage expansion under #1062 / #1065. Phase 2A (#1075) and 2B (#1102) shipped HTTP/2 and gRPC peers; this follows the same RAII shape: shared io_context, dedicated worker thread, atomic state flags polled via wait_for. Phase 2D (friend- test injection) and 2E (frame_injector) remain open under #1074. Files: - tests/support/mock_quic_peer_loop.{h,cpp} (new) - tests/support/CMakeLists.txt (+1) - tests/support/README.md (+1) - tests/unit/quic_socket_branch_test.cpp (+46) -- one demo TEST_F Part of #1074
This was referenced May 5, 2026
kcenon
added a commit
that referenced
this pull request
May 6, 2026
…#1104) Add friend-test injection so unit tests can drive two private server-side methods previously unreachable from public APIs: - messaging_quic_server::handle_packet (src/internal/experimental/quic_server.h:432) - messaging_ws_server::handle_new_connection (src/internal/http/websocket_server.h:415) Mechanism: new compile definition NETWORK_ENABLE_TEST_INJECTION gated behind BUILD_TESTS in cmake/network_system_targets.cmake. The two production headers gain a forward declaration block and a single 'friend class' line, all wrapped in '#if defined(NETWORK_ENABLE_TEST_INJECTION)'. When BUILD_TESTS=OFF the production binary is byte-identical to the previous develop tip. Probe types live entirely in tests/support/: - network_test_friends.h forward-declares the probes - quic_server_probe.{h,cpp} forwards to handle_packet - ws_server_probe.{h,cpp} forwards to handle_new_connection Demo tests verify end-to-end wiring: - QuicServerProbeTest.HandlePacket{Empty,Garbage}BufferDoesNotCrash - WsServerProbeTest.HandleNewConnectionEarlyReturnNoSessionManager - WsServerProbeTest.HandleNewConnectionEmptySocketEarlyReturn Per #1074 scope: no behavioral changes to src/. Phases 2A (#1075), 2A.2 (#1082), 2B (#1102), 2C (#1103) merged; 2E remains. Part of #1074
This was referenced May 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Summary
Phase 2B of issue #1074: ships
mock_grpc_server_peer— a server-side gRPC framing peer composed on top of the existingmock_h2_server_peer's HTTP/2 SETTINGS exchange. Withgrpc_reply_mode::echo_unary, the peer reads one client request stream and replies with response HEADERS (:status: 200,content-type: application/grpc), one length-prefixed gRPC DATA frame, and trailing HEADERS (grpc-status: 0, END_STREAM).Change Type
Affected Components
tests/support/mock_grpc_server_peer.{h,cpp}— new server-side gRPC peertests/support/CMakeLists.txt— add new TU tonetwork_test_supporttests/support/README.md— document the new peer + composition exampletests/unit/grpc_client_branch_test.cpp— one demoTEST_Fcovering the unary success pathWhy
Problem Solved
grpc_client::call_rawpost-connect success branches (http2_client::post wait, trailer-scan extractinggrpc-status,grpc_message::parseon the response body) cannot be exercised by the Phase 2Amock_h2_server_peerbecause it never replies with HEADERS+DATA+trailers. This blocks branch-coverage expansion onprotocols/grpc/client.cpp(#1063 follow-ups) and was explicitly called out in #1074's scope as Phase 2B.Related Issues
Alternative Approaches Considered
mock_h2_server_peer::echo_one: rejected because gRPC needs the trailer frame (grpc-status: 0) andcontent-type: application/grpc, neither of which are part of the bare h2 echo path.Where
tests/support/mock_grpc_server_peer.h,mock_grpc_server_peer.cpptests/support/CMakeLists.txttests/support/README.mdtests/unit/grpc_client_branch_test.cppAPI Changes
None. New test-only infrastructure under
tests/support/. No changes tosrc/or public headers.How
Implementation Highlights
mock_h2_server_peer: the new peer reuses the wire sequence (preface read -> server SETTINGS -> client SETTINGS read -> SETTINGS-ACK) so behavior diverges only after step 5.:status: 200uses static index 8 (one byte:0x88);content-type: application/grpcandgrpc-status: 0use literal-without-indexing (0x00prefix) with plain non-Huffman length-prefixed strings. All values fit in <=127 bytes so the simple 7-bit length form applies.response_headers, sogrpc_clientsees:statusandgrpc-statusin the same vector.127.0.0.1:0via the underlyingtls_loopback_listener; RSA-2048 cert regenerated per construction; no DNS, no external network, no on-disk secrets.Testing Done
Local C++ toolchain unavailable in the working environment — relying on CI for build / test verification. Code reviewed for HPACK correctness against
src/protocols/http2/hpack.cpp(verified0x88indexed and0x00literal-without-indexing decode paths) and frame constructors againstsrc/internal/protocols/http2/frame.h.Test Plan for Reviewers
cmake --preset release && cmake --build buildcd build && ctest -R CallRawSucceedsWithMockGrpcPeerEchoUnary --output-on-failuregrpc_client::call_rawreturns a successfulResult<grpc_message>{'o', 'k'}bodypeer.request_received()andpeer.response_sent()both flip truepeer.request_body()contains the gRPC 5-byte length prefix the client serializedBreaking Changes
None.
Rollback Plan
Single commit — revert in one step. No migrations, no data, no public-API surface change.
Issue scope acknowledgement
This PR ships Phase 2B only. Phases 2C (
mock_quic_peer_loop), 2D (network_test_friends.hfriend-test injection), and 2E (frame_injector) remain. Per the #1074 acceptance criteria the issue stays open until all five phases land plus per-protocol demo tests and the README entries. This PR usesPart of, notCloses.Part of #1074
Part of #953