@@ -363,3 +363,80 @@ SCENARIO("Discovery connection deferred until announce heard", "[nuclearnet][dis
363363 REQUIRE (join_called);
364364 REQUIRE (disc.is_connected (peer_addr));
365365}
366+
367+ SCENARIO (" Discovery retransmits SYN when announce received in SYN_SENT state" , " [nuclearnet][discovery]" ) {
368+ Discovery disc (std::chrono::seconds (5 ));
369+
370+ sock_t peer_addr = make_addr (0x0A000001 , 5000 );
371+
372+ // First announce — new peer
373+ auto announce = Discovery::build_announce_packet (" peer_a" , {});
374+ auto result = disc.process_announce (peer_addr, announce.data (), announce.size ());
375+ REQUIRE (result.is_new );
376+
377+ // We send SYN (externally) and mark state
378+ disc.mark_syn_sent (peer_addr);
379+ REQUIRE (disc.get_peer (peer_addr)->handshake == HandshakeState::SYN_SENT );
380+
381+ // SYN was dropped. Another announce arrives — should indicate SYN retransmit
382+ result = disc.process_announce (peer_addr, announce.data (), announce.size ());
383+ REQUIRE_FALSE (result.is_new );
384+ REQUIRE (result.response_flags == SYN );
385+ }
386+
387+ SCENARIO (" Discovery retransmits SYN+ACK when announce received in SYN_RECEIVED state" , " [nuclearnet][discovery]" ) {
388+ Discovery disc (std::chrono::seconds (5 ));
389+
390+ sock_t peer_addr = make_addr (0x0A000001 , 5000 );
391+
392+ // Add peer via announce
393+ auto announce = Discovery::build_announce_packet (" peer_a" , {});
394+ disc.process_announce (peer_addr, announce.data (), announce.size ());
395+
396+ // Peer sends SYN — we go to SYN_RECEIVED
397+ auto connect_result = disc.process_connect (peer_addr, SYN );
398+ REQUIRE (connect_result.response_flags == (SYN | CON_ACK ));
399+ REQUIRE (disc.get_peer (peer_addr)->handshake == HandshakeState::SYN_RECEIVED );
400+
401+ // Our SYN+ACK was dropped. Another announce arrives — should indicate SYN+ACK retransmit
402+ auto result = disc.process_announce (peer_addr, announce.data (), announce.size ());
403+ REQUIRE_FALSE (result.is_new );
404+ REQUIRE (result.response_flags == (SYN | CON_ACK ));
405+ }
406+
407+ SCENARIO (" Discovery retransmits ACK when announce received in CONFIRMED but peer not connected" , " [nuclearnet][discovery]" ) {
408+ Discovery disc (std::chrono::seconds (5 ));
409+
410+ sock_t peer_addr = make_addr (0x0A000001 , 5000 );
411+
412+ // Add peer via announce and complete handshake
413+ auto announce = Discovery::build_announce_packet (" peer_a" , {});
414+ disc.process_announce (peer_addr, announce.data (), announce.size ());
415+ disc.mark_syn_sent (peer_addr);
416+ auto connect_result = disc.process_connect (peer_addr, SYN | CON_ACK );
417+ REQUIRE (connect_result.just_connected );
418+ REQUIRE (disc.is_connected (peer_addr));
419+
420+ // Peer is fully connected — no retransmit needed
421+ auto result = disc.process_announce (peer_addr, announce.data (), announce.size ());
422+ REQUIRE_FALSE (result.is_new );
423+ REQUIRE (result.response_flags == CON_ACK );
424+ }
425+
426+ SCENARIO (" Discovery no retransmit for IDLE peer (not yet sent SYN)" , " [nuclearnet][discovery]" ) {
427+ Discovery disc (std::chrono::seconds (5 ));
428+
429+ sock_t peer_addr = make_addr (0x0A000001 , 5000 );
430+
431+ // First announce — new peer, handshake IDLE
432+ auto announce = Discovery::build_announce_packet (" peer_a" , {});
433+ auto result = disc.process_announce (peer_addr, announce.data (), announce.size ());
434+ REQUIRE (result.is_new );
435+ REQUIRE (disc.get_peer (peer_addr)->handshake == HandshakeState::IDLE );
436+
437+ // Second announce — peer still in IDLE (we haven't sent SYN yet)
438+ // Should indicate SYN needed
439+ result = disc.process_announce (peer_addr, announce.data (), announce.size ());
440+ REQUIRE_FALSE (result.is_new );
441+ REQUIRE (result.response_flags == SYN );
442+ }
0 commit comments