@@ -179,7 +179,7 @@ async fn test_mini_agent_tcp_handles_requests() {
179179
180180 // Start the mini agent
181181 let agent_handle = tokio:: spawn ( async move {
182- let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
182+ let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
183183 let _ = mini_agent. start_mini_agent ( shutdown_rx, None ) . await ;
184184 } ) ;
185185
@@ -240,7 +240,7 @@ async fn test_mini_agent_tcp_handles_requests() {
240240 ) ;
241241
242242 // Test /v0.4/traces endpoint with real trace data
243- let trace_payload = create_test_trace_payload ( ) ;
243+ let trace_payload = create_test_trace_payload ( None ) ;
244244 let trace_response =
245245 send_tcp_request ( test_port, "/v0.4/traces" , "POST" , Some ( trace_payload) , & [ ] )
246246 . await
@@ -279,7 +279,7 @@ async fn test_mini_agent_named_pipe_handles_requests() {
279279
280280 // Start the mini agent
281281 let agent_handle = tokio:: spawn ( async move {
282- let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
282+ let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
283283 let _ = mini_agent. start_mini_agent ( shutdown_rx, None ) . await ;
284284 } ) ;
285285
@@ -322,7 +322,7 @@ async fn test_mini_agent_named_pipe_handles_requests() {
322322 ) ;
323323
324324 // Test /v0.4/traces endpoint with real trace data
325- let trace_payload = create_test_trace_payload ( ) ;
325+ let trace_payload = create_test_trace_payload ( None ) ;
326326 let trace_response =
327327 send_named_pipe_request ( & pipe_path, "/v0.4/traces" , "POST" , Some ( trace_payload) )
328328 . await
@@ -353,7 +353,7 @@ async fn test_mini_agent_tcp_with_real_flushers() {
353353 let ( mini_agent, stats_concentrator_service_handle) =
354354 create_mini_agent_with_real_flushers ( config) ;
355355
356- let ( shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
356+ let ( shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
357357 let agent_handle = tokio:: spawn ( async move {
358358 let _ = mini_agent
359359 . start_mini_agent ( shutdown_rx, Some ( stats_concentrator_service_handle) )
@@ -377,7 +377,7 @@ async fn test_mini_agent_tcp_with_real_flushers() {
377377 ) ;
378378
379379 // Send trace data
380- let trace_payload = create_test_trace_payload ( ) ;
380+ let trace_payload = create_test_trace_payload ( None ) ;
381381 let trace_response =
382382 send_tcp_request ( test_port, "/v0.4/traces" , "POST" , Some ( trace_payload) , & [ ] )
383383 . await
@@ -389,7 +389,7 @@ async fn test_mini_agent_tcp_with_real_flushers() {
389389 verify_trace_request ( & mock_server) ;
390390
391391 // Trigger shutdown to force flush in progress concentrator buckets
392- let _ = shutdown_tx. send ( ( ) ) ;
392+ let _ = shutdown_tx. send ( true ) ;
393393 let _ = agent_handle. await ;
394394 verify_stats_request ( & mock_server) ; // Stats generator should generate stats from trace payload
395395}
@@ -410,7 +410,7 @@ async fn test_concentrator_task_death_shuts_down_mini_agent() {
410410 create_mini_agent_with_real_flushers ( config) ;
411411 let abort_handle = stats_concentrator_service_handle. abort_handle ( ) ;
412412
413- let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
413+ let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
414414 let agent_handle = tokio:: spawn ( async move {
415415 mini_agent
416416 . start_mini_agent ( shutdown_rx, Some ( stats_concentrator_service_handle) )
@@ -463,7 +463,7 @@ async fn test_mini_agent_tcp_with_real_flushers_and_tracer_computed_stats() {
463463 create_mini_agent_with_real_flushers ( config) ;
464464
465465 let agent_handle = tokio:: spawn ( async move {
466- let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
466+ let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
467467 let _ = mini_agent. start_mini_agent ( shutdown_rx, None ) . await ;
468468 } ) ;
469469
@@ -484,7 +484,7 @@ async fn test_mini_agent_tcp_with_real_flushers_and_tracer_computed_stats() {
484484 ) ;
485485
486486 // Send trace data
487- let trace_payload = create_test_trace_payload ( ) ;
487+ let trace_payload = create_test_trace_payload ( None ) ;
488488 let trace_response = send_tcp_request (
489489 test_port,
490490 "/v0.4/traces" ,
@@ -524,7 +524,7 @@ async fn test_mini_agent_named_pipe_with_real_flushers() {
524524 let ( mini_agent, _stats_concentrator_service_handle) =
525525 create_mini_agent_with_real_flushers ( config) ;
526526
527- let ( shutdown_tx, shutdown_rx) = tokio:: sync:: oneshot :: channel :: < ( ) > ( ) ;
527+ let ( shutdown_tx, shutdown_rx) = tokio:: sync:: watch :: channel ( false ) ;
528528 let agent_handle = tokio:: spawn ( async move {
529529 let _ = mini_agent. start_mini_agent ( shutdown_rx, None ) . await ;
530530 } ) ;
@@ -546,7 +546,7 @@ async fn test_mini_agent_named_pipe_with_real_flushers() {
546546 ) ;
547547
548548 // Send trace data via named pipe
549- let trace_payload = create_test_trace_payload ( ) ;
549+ let trace_payload = create_test_trace_payload ( None ) ;
550550 let trace_response =
551551 send_named_pipe_request ( pipe_name, "/v0.4/traces" , "POST" , Some ( trace_payload) )
552552 . await
@@ -558,7 +558,107 @@ async fn test_mini_agent_named_pipe_with_real_flushers() {
558558 verify_trace_request ( & mock_server) ;
559559
560560 // Trigger shutdown to force flush in progress concentrator buckets
561- let _ = shutdown_tx. send ( ( ) ) ;
561+ let _ = shutdown_tx. send ( true ) ;
562562 let _ = agent_handle. await ;
563563 verify_stats_request ( & mock_server) ;
564564}
565+
566+ #[ cfg( all( test, windows, feature = "windows-pipes" ) ) ]
567+ #[ tokio:: test]
568+ #[ serial]
569+ async fn test_mini_agent_dual_transport_with_real_flushers ( ) {
570+ let mock_server = MockServer :: start ( ) . await ;
571+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
572+
573+ let pipe_name = r"\\.\pipe\dd_trace_dual_transport_test" ;
574+ let tcp_port: u16 = 8130 ;
575+
576+ let mut config = create_tcp_test_config ( tcp_port) ;
577+ configure_mock_endpoints ( & mut config, & mock_server. url ( ) ) ;
578+ config. dd_apm_windows_pipe_name = Some ( pipe_name. to_string ( ) ) ;
579+ // Both transports are deliberately set on the same agent: a non-zero TCP
580+ // port AND a pipe name. They must come up concurrently.
581+ let config = Arc :: new ( config) ;
582+
583+ let ( mini_agent, stats_concentrator_service_handle) =
584+ create_mini_agent_with_real_flushers ( config) ;
585+ let agent_handle = tokio:: spawn ( async move {
586+ let ( _shutdown_tx, shutdown_rx) = tokio:: sync:: watch:: channel ( false ) ;
587+ let _ = mini_agent
588+ . start_mini_agent ( shutdown_rx, Some ( stats_concentrator_service_handle) )
589+ . await ;
590+ } ) ;
591+
592+ // Readiness on each transport, sequentially (TCP then pipe).
593+ let mut tcp_ready = false ;
594+ for _ in 0 ..20 {
595+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
596+ if let Ok ( response) = send_tcp_request ( tcp_port, "/info" , "GET" , None , & [ ] ) . await
597+ && response. status ( ) . is_success ( )
598+ {
599+ tcp_ready = true ;
600+ break ;
601+ }
602+ }
603+ assert ! (
604+ tcp_ready,
605+ "TCP listener did not bind on port {tcp_port} when pipe is also configured \
606+ — config may be overriding receiver_port to 0 when a pipe name is set"
607+ ) ;
608+
609+ let mut pipe_ready = false ;
610+ for _ in 0 ..20 {
611+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
612+ if let Ok ( response) = send_named_pipe_request ( pipe_name, "/info" , "GET" , None ) . await
613+ && response. status ( ) . is_success ( )
614+ {
615+ pipe_ready = true ;
616+ break ;
617+ }
618+ }
619+ assert ! (
620+ pipe_ready,
621+ "Named pipe listener did not come up at {pipe_name} when TCP is also configured"
622+ ) ;
623+
624+ // One trace per transport, distinguishable by service name.
625+ let tcp_payload = create_test_trace_payload ( Some ( "dual-tcp-svc" ) ) ;
626+ let pipe_payload = create_test_trace_payload ( Some ( "dual-pipe-svc" ) ) ;
627+
628+ let tcp_response = send_tcp_request ( tcp_port, "/v0.4/traces" , "POST" , Some ( tcp_payload) , & [ ] )
629+ . await
630+ . expect ( "Failed to send /v0.4/traces request over TCP" ) ;
631+ assert_eq ! ( tcp_response. status( ) , StatusCode :: OK ) ;
632+
633+ let pipe_response =
634+ send_named_pipe_request ( pipe_name, "/v0.4/traces" , "POST" , Some ( pipe_payload) )
635+ . await
636+ . expect ( "Failed to send /v0.4/traces request over named pipe" ) ;
637+ assert_eq ! ( pipe_response. status( ) , StatusCode :: OK ) ;
638+
639+ tokio:: time:: sleep ( FLUSH_WAIT_DURATION ) . await ;
640+
641+ // Both payloads must reach the same backend through the shared flusher
642+ // pipeline. The flusher may batch them into one POST or two; either is
643+ // fine, what matters is that both service-name needles show up.
644+ let trace_reqs = mock_server. get_requests_for_path ( "/api/v0.2/traces" ) ;
645+ assert ! (
646+ !trace_reqs. is_empty( ) ,
647+ "no trace POST reached backend; expected traces from both transports"
648+ ) ;
649+ let mut all_bytes = Vec :: new ( ) ;
650+ for req in & trace_reqs {
651+ assert_eq ! ( req. method, "POST" ) ;
652+ all_bytes. extend_from_slice ( & req. body ) ;
653+ }
654+ assert ! (
655+ all_bytes. windows( 12 ) . any( |w| w == b"dual-tcp-svc" ) ,
656+ "TCP-side trace did not reach backend"
657+ ) ;
658+ assert ! (
659+ all_bytes. windows( 13 ) . any( |w| w == b"dual-pipe-svc" ) ,
660+ "pipe-side trace did not reach backend"
661+ ) ;
662+
663+ agent_handle. abort ( ) ;
664+ }
0 commit comments