11# frozen_string_literal: true
22
3+ require "async"
4+ require "async/queue"
35require "rails_helper"
46require "support/script_tag_utils"
57
@@ -303,7 +305,7 @@ def response; end
303305 { html : "<div>Chunk 3: Final content</div>" , consoleReplayScript : "" }
304306 ]
305307 end
306- let ( :chunks_read ) { [ ] }
308+ let ( :chunks_read ) { Async :: Queue . new }
307309 let ( :react_component_specification_tag ) do
308310 <<-SCRIPT . strip_heredoc
309311 <script type="application/json"
@@ -336,7 +338,7 @@ def mock_request_and_response(mock_chunks = chunks, count: 1)
336338 end
337339 clear_stream_mocks
338340
339- chunks_read . clear
341+ chunks_read . dequeue until chunks_read . empty?
340342 mock_streaming_response ( %r{http://localhost:3800/bundles/[a-f0-9]{32}-test/render/[a-f0-9]{32}} , 200 ,
341343 count : count ) do |yielder |
342344 mock_chunks . each do |chunk |
@@ -362,7 +364,7 @@ def mock_request_and_response(mock_chunks = chunks, count: 1)
362364 expect ( initial_result ) . to include ( react_component_div_with_initial_chunk )
363365 expect ( initial_result ) . to include ( chunks . first [ :consoleReplayScript ] )
364366 expect ( initial_result ) . not_to include ( "More content" , "Final content" )
365- expect ( chunks_read . count ) . to eq ( 1 )
367+ expect ( chunks_read . size ) . to eq ( 1 )
366368 end
367369
368370 it "creates a fiber to read subsequent chunks" do
@@ -378,16 +380,16 @@ def mock_request_and_response(mock_chunks = chunks, count: 1)
378380 /#{ Regexp . escape ( chunks [ 1 ] [ :html ] ) } \s +#{ Regexp . escape ( chunks [ 1 ] [ :consoleReplayScript ] ) } /
379381 )
380382 expect ( second_result ) . not_to include ( "Stream React Server Components" , "Final content" )
381- expect ( chunks_read . count ) . to eq ( 2 )
383+ expect ( chunks_read . size ) . to eq ( 2 )
382384
383385 third_result = fiber . resume
384386 expect ( third_result ) . to eq ( chunks [ 2 ] [ :html ] . to_s )
385387 expect ( third_result ) . not_to include ( "Stream React Server Components" , "More content" )
386- expect ( chunks_read . count ) . to eq ( 3 )
388+ expect ( chunks_read . size ) . to eq ( 3 )
387389
388390 expect ( fiber . resume ) . to be_nil
389391 expect ( fiber ) . not_to be_alive
390- expect ( chunks_read . count ) . to eq ( chunks . count )
392+ expect ( chunks_read . size ) . to eq ( chunks . count )
391393 end
392394
393395 it "does not trim whitespaces from html" do
@@ -429,7 +431,7 @@ def mock_request_and_response(mock_chunks = chunks, count: 1)
429431 allow ( mocked_stream ) . to receive ( :write ) do |chunk |
430432 written_chunks << chunk
431433 # Ensures that any chunk received is written immediately to the stream
432- expect ( written_chunks . count ) . to eq ( chunks_read . count ) # rubocop:disable RSpec/ExpectInHook
434+ expect ( written_chunks . count ) . to eq ( chunks_read . size ) # rubocop:disable RSpec/ExpectInHook
433435 end
434436 allow ( mocked_stream ) . to receive ( :close )
435437 mocked_response = instance_double ( ActionDispatch ::Response )
@@ -441,7 +443,7 @@ def mock_request_and_response(mock_chunks = chunks, count: 1)
441443 it "writes the chunk to stream as soon as it is received" do
442444 stream_view_containing_react_components ( template : template_path )
443445 expect ( self ) . to have_received ( :render_to_string ) . once . with ( template : template_path )
444- expect ( chunks_read . count ) . to eq ( chunks . count )
446+ expect ( chunks_read . size ) . to eq ( chunks . count )
445447 expect ( written_chunks . count ) . to eq ( chunks . count )
446448 expect ( mocked_stream ) . to have_received ( :write ) . exactly ( chunks . count ) . times
447449 expect ( mocked_stream ) . to have_received ( :close )
@@ -546,7 +548,7 @@ def stub_render_with_cached_stream(cache_key:, props:, **opts)
546548
547549 def reset_stream_buffers
548550 written_chunks . clear
549- chunks_read . clear
551+ chunks_read . dequeue until chunks_read . empty?
550552 end
551553
552554 def run_stream
@@ -563,15 +565,15 @@ def run_stream
563565
564566 # First render (MISS → write-through)
565567 first_run_chunks = run_stream
566- expect ( chunks_read . count ) . to eq ( chunks . count )
568+ expect ( chunks_read . size ) . to eq ( chunks . count )
567569 expect ( first_run_chunks . first ) . to include ( "<h1>Header Rendered In View</h1>" )
568570
569571 # Second render (HIT → served from cache, no Node call; no new HTTPX chunks)
570572 reset_stream_buffers
571573 # Reset rails context flag to simulate a fresh request lifecycle
572574 @rendered_rails_context = nil
573575 second_run_chunks = run_stream
574- expect ( chunks_read . count ) . to eq ( 0 )
576+ expect ( chunks_read . size ) . to eq ( 0 )
575577 expect ( second_run_chunks ) . to eq ( first_run_chunks )
576578 end
577579
@@ -584,15 +586,15 @@ def run_stream
584586
585587 # First render
586588 run_stream
587- first_call_count = chunks_read . count
589+ first_call_count = chunks_read . size
588590 expect ( first_call_count ) . to eq ( chunks . count )
589591
590592 # Second render (still goes to Node)
591593 reset_stream_buffers
592594 run_stream
593595 reset_stream_buffers
594596 run_stream
595- expect ( chunks_read . count ) . to eq ( chunks . count )
597+ expect ( chunks_read . size ) . to eq ( chunks . count )
596598 end
597599
598600 it "invalidates cache when props change" do
@@ -634,13 +636,13 @@ def run_stream
634636 # With if: false, caching should be disabled - both calls hit Node renderer
635637 render_with_cached_stream ( if : false )
636638 first_run_chunks = run_stream
637- expect ( chunks_read . count ) . to eq ( chunks . count )
639+ expect ( chunks_read . size ) . to eq ( chunks . count )
638640
639641 reset_stream_buffers
640642 @rendered_rails_context = nil
641643 render_with_cached_stream ( if : false )
642644 second_run_chunks = run_stream
643- expect ( chunks_read . count ) . to eq ( chunks . count ) # Both calls went to Node
645+ expect ( chunks_read . size ) . to eq ( chunks . count ) # Both calls went to Node
644646
645647 expect ( second_run_chunks ) . to eq ( first_run_chunks ) # Same template/props, same result
646648 end
0 commit comments