@@ -11,7 +11,12 @@ import ServerCaller from '@/callers/ServerCaller';
11
11
import ClientCaller from '@/callers/ClientCaller' ;
12
12
import UnaryCaller from '@/callers/UnaryCaller' ;
13
13
import * as rpcUtilsMiddleware from '@/utils/middleware' ;
14
- import { ErrorRPC , ErrorRPCRemote } from '@/errors' ;
14
+ import {
15
+ ErrorRPC ,
16
+ ErrorRPCHandlerFailed ,
17
+ ErrorRPCRemote ,
18
+ ErrorRPCTimedOut ,
19
+ } from '@/errors' ;
15
20
import * as rpcErrors from '@/errors' ;
16
21
import RPCClient from '@/RPCClient' ;
17
22
import RPCServer from '@/RPCServer' ;
@@ -593,18 +598,207 @@ describe('RPC', () => {
593
598
await expect ( rpcServer . destroy ( false ) ) . toResolve ( ) ;
594
599
await rpcClient . destroy ( ) ;
595
600
} ) ;
601
+ testProp (
602
+ 'RPC Client and server timeout concurrently' ,
603
+ [ fc . array ( rpcTestUtils . safeJsonValueArb , { minLength : 1 } ) ] ,
604
+ async ( values ) => {
605
+ const { clientPair, serverPair } = rpcTestUtils . createTapPairs <
606
+ Uint8Array ,
607
+ Uint8Array
608
+ > ( ) ;
609
+ const timeout = 600 ;
610
+ class TestMethod extends DuplexHandler {
611
+ public handle = async function * (
612
+ input : AsyncIterableIterator < JSONValue > ,
613
+ cancel : ( reason ?: any ) => void ,
614
+ meta : Record < string , JSONValue > | undefined ,
615
+ ctx : ContextTimed ,
616
+ ) : AsyncIterableIterator < JSONValue > {
617
+ // Check for abort event
618
+ ctx . signal . throwIfAborted ( ) ;
619
+ const abortProm = utils . promise < never > ( ) ;
620
+ ctx . signal . addEventListener ( 'abort' , ( ) => {
621
+ abortProm . rejectP ( ctx . signal . reason ) ;
622
+ } ) ;
623
+ await abortProm . p ;
624
+ } ;
625
+ }
626
+ const testMethodInstance = new TestMethod ( { } ) ;
627
+ // Set up a client and server with matching timeout settings
628
+ const rpcServer = await RPCServer . createRPCServer ( {
629
+ manifest : {
630
+ testMethod : testMethodInstance ,
631
+ } ,
632
+ logger,
633
+ idGen,
634
+ handlerTimeoutTime : timeout ,
635
+ } ) ;
636
+ rpcServer . handleStream ( {
637
+ ...serverPair ,
638
+ cancel : ( ) => { } ,
639
+ } ) ;
640
+
641
+ const rpcClient = await RPCClient . createRPCClient ( {
642
+ manifest : {
643
+ testMethod : new DuplexCaller ( ) ,
644
+ } ,
645
+ streamFactory : async ( ) => {
646
+ return {
647
+ ...clientPair ,
648
+ cancel : ( ) => { } ,
649
+ } ;
650
+ } ,
651
+ logger,
652
+ idGen,
653
+ streamKeepAliveTimeoutTime : timeout ,
654
+ } ) ;
655
+ const callerInterface = await rpcClient . methods . testMethod ( ) ;
656
+ const writer = callerInterface . writable . getWriter ( ) ;
657
+ const reader = callerInterface . readable . getReader ( ) ;
658
+ await expect ( reader . read ( ) ) . rejects . toThrow (
659
+ 'Timed out waiting for header' ,
660
+ ) ;
661
+ await expect ( writer . write ( ) ) . rejects . toThrow (
662
+ 'Timed out waiting for header' ,
663
+ ) ;
664
+ await rpcServer . destroy ( ) ;
665
+ await rpcClient . destroy ( ) ;
666
+ } ,
667
+ { numRuns : 1 } ,
668
+ ) ;
669
+ testProp (
670
+ 'RPC server times out before client' ,
671
+ [ fc . array ( rpcTestUtils . safeJsonValueArb , { minLength : 1 } ) ] ,
672
+ async ( values ) => {
673
+ const { clientPair, serverPair } = rpcTestUtils . createTapPairs <
674
+ Uint8Array ,
675
+ Uint8Array
676
+ > ( ) ;
677
+ class TestMethod extends DuplexHandler {
678
+ public handle = async function * (
679
+ input : AsyncIterableIterator < JSONValue > ,
680
+ cancel : ( reason ?: any ) => void ,
681
+ meta : Record < string , JSONValue > | undefined ,
682
+ ctx : ContextTimed ,
683
+ ) : AsyncIterableIterator < JSONValue > {
684
+ await utils . sleep ( 7 ) ; // Longer than server's timeout, shorter than client'
685
+ yield * input ;
686
+ } ;
687
+ }
688
+ // Set up a client and server, server has a shorter timeout than client`
689
+ const rpcServer = await RPCServer . createRPCServer ( {
690
+ manifest : {
691
+ testMethod : new TestMethod ( { } ) ,
692
+ } ,
693
+ logger,
694
+ idGen,
695
+ handlerTimeoutTime : 5 ,
696
+ } ) ;
697
+ rpcServer . handleStream ( {
698
+ ...serverPair ,
699
+ cancel : ( ) => { } ,
700
+ } ) ;
596
701
597
- /* test('RPC Client and server timeout concurrentrly'), async () => {};
598
- test('RPC client times out and attempts to reconnect'), async () => {};
702
+ const rpcClient = await RPCClient . createRPCClient ( {
703
+ manifest : {
704
+ testMethod : new DuplexCaller ( ) ,
705
+ } ,
706
+ streamFactory : async ( ) => {
707
+ return {
708
+ ...clientPair ,
709
+ cancel : ( ) => { } ,
710
+ } ;
711
+ } ,
712
+ logger,
713
+ idGen,
714
+ streamKeepAliveTimeoutTime : 10 ,
715
+ } ) ;
716
+ const callerInterface = await rpcClient . methods . testMethod ( ) ;
717
+ const writer = callerInterface . writable . getWriter ( ) ;
718
+ const reader = callerInterface . readable . getReader ( ) ;
719
+
720
+ // Expect the server to time out first
721
+ await expect ( writer . closed ) . rejects . toThrow ( ErrorRPCHandlerFailed ) ;
722
+ await expect ( reader . closed ) . rejects . toThrow ( ErrorRPCHandlerFailed ) ;
723
+
724
+ await rpcServer . destroy ( ) ;
725
+ await rpcClient . destroy ( ) ;
726
+ } ,
727
+ ) ;
728
+ testProp (
729
+ 'Client times out before server' ,
730
+ [ fc . array ( rpcTestUtils . safeJsonValueArb , { minLength : 1 } ) ] ,
731
+ async ( values ) => {
732
+ const { clientPair, serverPair } = rpcTestUtils . createTapPairs <
733
+ Uint8Array ,
734
+ Uint8Array
735
+ > ( ) ;
736
+ class TestMethod extends DuplexHandler {
737
+ public handle = async function * (
738
+ input : AsyncIterableIterator < JSONValue > ,
739
+ cancel : ( reason ?: any ) => void ,
740
+ meta : Record < string , JSONValue > | undefined ,
741
+ ctx : ContextTimed ,
742
+ ) : AsyncIterableIterator < JSONValue > {
743
+ await utils . sleep ( 7 ) ; // Longer than client's timeout, shorter than server's
744
+ yield * input ;
745
+ } ;
746
+ }
747
+ // Set up a client and server with matching timeout settings
748
+ const rpcServer = await RPCServer . createRPCServer ( {
749
+ manifest : {
750
+ testMethod : new TestMethod ( { } ) ,
751
+ } ,
752
+ logger,
753
+ idGen,
754
+ handlerTimeoutTime : 10 ,
755
+ } ) ;
756
+ rpcServer . handleStream ( {
757
+ ...serverPair ,
758
+ cancel : ( ) => { } ,
759
+ } ) ;
760
+
761
+ const rpcClient = await RPCClient . createRPCClient ( {
762
+ manifest : {
763
+ testMethod : new DuplexCaller ( ) ,
764
+ } ,
765
+ streamFactory : async ( ) => {
766
+ return {
767
+ ...clientPair ,
768
+ cancel : ( ) => { } ,
769
+ } ;
770
+ } ,
771
+ logger,
772
+ idGen,
773
+ streamKeepAliveTimeoutTime : 5 ,
774
+ } ) ;
775
+ const callerInterface = await rpcClient . methods . testMethod ( ) ;
776
+ const writer = callerInterface . writable . getWriter ( ) ;
777
+ const reader = callerInterface . readable . getReader ( ) ;
778
+
779
+ // Expect the client to time out first
780
+ await expect ( writer . closed ) . rejects . toThrow ( ErrorRPCHandlerFailed ) ;
781
+ await expect ( reader . closed ) . rejects . toThrow ( ErrorRPCHandlerFailed ) ;
599
782
600
- test('RPC server times out before client'), async () => {};
601
- /!**
783
+ await rpcServer . destroy ( ) ;
784
+ await rpcClient . destroy ( ) ;
785
+ } ,
786
+ ) ;
787
+ /**
602
788
* Hard timeout is absolute time limit, cannot be extended or bypassed. When reached,
603
789
* operation is immediately cancelled.
604
- * Soft timeout has a threshold, but can be extended or bypassed. For e.g if server sends
605
- * partial response, client can extend the timeout to receive the rest of the response.
606
- *!/
607
- test('RPC server and client hard and soft timeout limits'), async () => {};
790
+ * Soft timeout has a threshold, but can be extended or bypassed. For e.g if a server sends a
791
+ * partial response, a client can extend the timeout to receive the rest of the response.
792
+ */
793
+ test ( 'RPC server and client hard and soft timeout limits' , async ( ) => {
794
+ // Set up a client and server with both hard and soft timeout limits
795
+ // Trigger various scenarios where one or both could hit soft or hard limits
796
+ // Expect the system to behave according to the specified limits
797
+ } ) ;
608
798
609
- test('RPC client and server with infinite timeout'), async () => {};*/
799
+ test ( 'RPC client and server with infinite timeout' , async ( ) => {
800
+ // Set up a client and server with infinite timeout settings
801
+ // Trigger a call that will hang indefinitely or for a long time
802
+ // Expect neither to time out and verify that they can still handle other operations
803
+ } ) ;
610
804
} ) ;
0 commit comments