@@ -35,6 +35,15 @@ def update_missing
3535 end
3636 end
3737
38+ class CapturingActorChannel < SolidObjects ::ActorChannel
39+ attr_reader :stream_callback , :stream_coder
40+
41+ def stream_from ( _broadcasting , callback = nil , coder : nil , &block )
42+ @stream_callback = callback || block
43+ @stream_coder = coder
44+ end
45+ end
46+
3847 setup do
3948 SolidObjects . reset!
4049 ChannelActor . ensure_registered!
@@ -115,6 +124,71 @@ def update_missing
115124 worker &.stop
116125 end
117126
127+ test "decodes Action Cable broadcasts before reactive processing" do
128+ reference = ChannelActor . ref ( "actor-1" )
129+ SolidObjects . configuration . authorize_subscription = -> ( **) { true }
130+ connection = ActionCable ::Channel ::ConnectionStub . new
131+ channel = CapturingActorChannel . new (
132+ connection ,
133+ "actor-channel" ,
134+ {
135+ token : SolidObjects ::StreamToken . generate (
136+ reference ,
137+ observables : %w[ missing ]
138+ ) ,
139+ components : JSON . generate (
140+ [
141+ component_token (
142+ reference ,
143+ component_name : "summary" ,
144+ dependencies : %w[ missing ] ,
145+ revision : 0
146+ )
147+ ]
148+ )
149+ } . with_indifferent_access
150+ )
151+ channel . subscribe_to_channel
152+
153+ assert_equal ActiveSupport ::JSON , channel . stream_coder
154+ connection . transmissions . clear
155+
156+ reference . async ( :update_missing )
157+ worker = SolidObjects ::Worker . new
158+ worker . run_until_idle
159+ broadcast = SolidObjects ::Broadcast . find_by! ( observable_name : "missing" )
160+ SolidObjects ::ActionCableBroadcastAdapter . new . call ( broadcast )
161+ stream_name = SolidObjects ::StreamName . for ( reference )
162+ encoded_stream = ActionCable . server . pubsub . broadcasts ( stream_name ) . sole
163+ handler = channel . __send__ (
164+ :stream_handler ,
165+ stream_name ,
166+ channel . stream_callback ,
167+ coder : channel . stream_coder
168+ )
169+
170+ handler . call ( encoded_stream )
171+
172+ channel_transmissions = connection . transmissions . filter_map do |transmission |
173+ transmission [ "message" ]
174+ end
175+ scalar_target = SolidObjects ::DomIdentity . observable ( reference , :missing )
176+ scalar_update = channel_transmissions . find do |transmission |
177+ transmission . include? ( scalar_target ) &&
178+ transmission . include? ( ">1</span>" )
179+ end
180+ assert scalar_update
181+ assert_equal 1 ,
182+ component_refreshes (
183+ reference ,
184+ :summary ,
185+ messages : channel_transmissions
186+ ) . length
187+ refute channel_transmissions . any? { |transmission | transmission . start_with? ( "\" " ) }
188+ ensure
189+ worker &.stop
190+ end
191+
118192 test "streams only after token verification and host authorization" do
119193 reference = ChannelActor . ref ( "actor-1" )
120194 SolidObjects . configuration . authorize_subscription = lambda do |actor_type :, actor_id :, authorization_context :|
@@ -371,9 +445,9 @@ def component_token(reference, component_name:, dependencies:, revision:)
371445 )
372446 end
373447
374- def component_refreshes ( reference , component_name )
448+ def component_refreshes ( reference , component_name , messages : transmissions )
375449 target = SolidObjects ::DomIdentity . component ( reference , component_name )
376- transmissions . select do |transmission |
450+ messages . select do |transmission |
377451 transmission . include? ( %(target="#{ target } ") ) &&
378452 transmission . include? ( "<turbo-frame" )
379453 end
0 commit comments