@@ -145,26 +145,75 @@ public async Task CanUseClientResultsWithIHubContextT()
145145 var connectionHandler = serviceProvider . GetService < HubConnectionHandler < HubT > > ( ) ;
146146
147147 using var client = new TestClient ( ) ;
148+ var connectionId = client . Connection . ConnectionId ;
148149
149150 var connectionHandlerTask = await client . ConnectAsync ( connectionHandler ) ;
150151
151152 // Wait for a connection, or for the endpoint to fail.
152153 await client . Connected . OrThrowIfOtherFails ( connectionHandlerTask ) . DefaultTimeout ( ) ;
153154
154- var context = serviceProvider . GetRequiredService < IHubContext < HubT , Test > > ( ) ;
155- var resultTask = context . Clients . Single ( client . Connection . ConnectionId ) . GetClientResult ( 1 ) ;
155+ var context = serviceProvider . GetRequiredService < IHubContext < HubT , ITest > > ( ) ;
156156
157- var message = await client . ReadAsync ( ) . DefaultTimeout ( ) ;
158- var invocation = Assert . IsType < InvocationMessage > ( message ) ;
157+ async Task AssertClientResult ( Task < int > resultTask )
158+ {
159+ var message = await client . ReadAsync ( ) . DefaultTimeout ( ) ;
160+ var invocation = Assert . IsType < InvocationMessage > ( message ) ;
159161
160- Assert . Single ( invocation . Arguments ) ;
161- Assert . Equal ( 1L , invocation . Arguments [ 0 ] ) ;
162- Assert . Equal ( "GetClientResult" , invocation . Target ) ;
162+ Assert . Single ( invocation . Arguments ) ;
163+ Assert . Equal ( 1L , invocation . Arguments [ 0 ] ) ;
164+ Assert . Equal ( "GetClientResult" , invocation . Target ) ;
163165
164- await client . SendHubMessageAsync ( CompletionMessage . WithResult ( invocation . InvocationId , 2 ) ) . DefaultTimeout ( ) ;
166+ await client . SendHubMessageAsync ( CompletionMessage . WithResult ( invocation . InvocationId , 2 ) ) . DefaultTimeout ( ) ;
165167
166- var result = await resultTask . DefaultTimeout ( ) ;
167- Assert . Equal ( 2 , result ) ;
168+ var result = await resultTask . DefaultTimeout ( ) ;
169+ Assert . Equal ( 2 , result ) ;
170+ }
171+
172+ await AssertClientResult ( context . Clients . Single ( connectionId ) . GetClientResult ( 1 ) ) ;
173+ await AssertClientResult ( context . Clients . Client ( connectionId ) . GetClientResult ( 1 ) ) ;
168174 }
169175 }
176+
177+ [ Fact ]
178+ public async Task CanReturnClientResultToTypedHubThreeWays ( )
179+ {
180+ using ( StartVerifiableLog ( ) )
181+ {
182+ var serviceProvider = HubConnectionHandlerTestUtils . CreateServiceProvider ( builder =>
183+ {
184+ // Waiting for a client result blocks the hub dispatcher pipeline, need to allow multiple invocations
185+ builder . AddSignalR ( o => o . MaximumParallelInvocationsPerClient = 2 ) ;
186+ } , LoggerFactory ) ;
187+ var connectionHandler = serviceProvider . GetService < HubConnectionHandler < HubT > > ( ) ;
188+
189+ using var client = new TestClient ( invocationBinder : new GetClientResultThreeWaysInvocationBinder ( ) ) ;
190+
191+ var connectionHandlerTask = await client . ConnectAsync ( connectionHandler ) . DefaultTimeout ( ) ;
192+
193+ var invocationId = await client . SendHubMessageAsync ( new InvocationMessage (
194+ invocationId : "1" ,
195+ nameof ( HubT . GetClientResultThreeWays ) ,
196+ new object [ ] { 5 , 6 , 7 } ) ) . DefaultTimeout ( ) ;
197+
198+ // Send back "value + 4" to all three invocations.
199+ for ( int i = 0 ; i < 3 ; i ++ )
200+ {
201+ // Hub asks client for a result, this is an invocation message with an ID.
202+ var invocationMessage = Assert . IsType < InvocationMessage > ( await client . ReadAsync ( ) . DefaultTimeout ( ) ) ;
203+ Assert . NotNull ( invocationMessage . InvocationId ) ;
204+ var res = 4 + ( int ) invocationMessage . Arguments [ 0 ] ;
205+ await client . SendHubMessageAsync ( CompletionMessage . WithResult ( invocationMessage . InvocationId , res ) ) . DefaultTimeout ( ) ;
206+ }
207+
208+ var completion = Assert . IsType < CompletionMessage > ( await client . ReadAsync ( ) . DefaultTimeout ( ) ) ;
209+ Assert . Equal ( new ClientResults ( 9 , 10 , 11 ) , completion . Result ) ;
210+ }
211+ }
212+
213+ private class GetClientResultThreeWaysInvocationBinder : IInvocationBinder
214+ {
215+ public IReadOnlyList < Type > GetParameterTypes ( string methodName ) => new [ ] { typeof ( int ) } ;
216+ public Type GetReturnType ( string invocationId ) => typeof ( ClientResults ) ;
217+ public Type GetStreamItemType ( string streamId ) => throw new NotImplementedException ( ) ;
218+ }
170219}
0 commit comments