@@ -47,6 +47,11 @@ func (m *mockContentRouter) FindPeers(ctx context.Context, pid peer.ID, limit in
4747 return args .Get (0 ).(iter.ResultIter [* types.PeerRecord ]), args .Error (1 )
4848}
4949
50+ func (m * mockContentRouter ) GetClosestPeers (ctx context.Context , peerID , closerThan peer.ID , count int ) (iter.ResultIter [* types.PeerRecord ], error ) {
51+ args := m .Called (ctx , peerID , closerThan , count )
52+ return args .Get (0 ).(iter.ResultIter [* types.PeerRecord ]), args .Error (1 )
53+ }
54+
5055func (m * mockContentRouter ) GetIPNS (ctx context.Context , name ipns.Name ) (* ipns.Record , error ) {
5156 args := m .Called (ctx , name )
5257 rec , _ := args .Get (0 ).(* ipns.Record )
@@ -676,6 +681,146 @@ func TestClient_FindPeers(t *testing.T) {
676681 }
677682}
678683
684+ func TestClient_GetClosestPeers (t * testing.T ) {
685+ bitswapPeerRecord := makePeerRecord ([]string {"transport-bitswap" })
686+ httpPeerRecord := makePeerRecord ([]string {"transport-ipfs-gateway-http" })
687+
688+ peerRecords := []iter.Result [* types.PeerRecord ]{
689+ {Val : & bitswapPeerRecord },
690+ {Val : & httpPeerRecord },
691+ }
692+
693+ pid := * bitswapPeerRecord .ID
694+
695+ cases := []struct {
696+ name string
697+ httpStatusCode int
698+ stopServer bool
699+ routerResult []iter.Result [* types.PeerRecord ]
700+ routerErr error
701+ clientRequiresStreaming bool
702+ serverStreamingDisabled bool
703+ closerThan peer.ID
704+ count int
705+
706+ expErrContains osErrContains
707+ expResult []iter.Result [* types.PeerRecord ]
708+ expStreamingResponse bool
709+ expJSONResponse bool
710+ }{
711+ {
712+ name : "happy case" ,
713+ routerResult : peerRecords ,
714+ expResult : peerRecords ,
715+ expStreamingResponse : true ,
716+ },
717+ {
718+ name : "server doesn't support streaming" ,
719+ routerResult : peerRecords ,
720+ expResult : peerRecords ,
721+ serverStreamingDisabled : true ,
722+ expJSONResponse : true ,
723+ },
724+ {
725+ name : "client requires streaming but server doesn't support it" ,
726+ serverStreamingDisabled : true ,
727+ clientRequiresStreaming : true ,
728+ expErrContains : osErrContains {expContains : "HTTP error with StatusCode=400: no supported content types" },
729+ },
730+ {
731+ name : "returns an error if there's a non-200 response" ,
732+ httpStatusCode : 500 ,
733+ expErrContains : osErrContains {expContains : "HTTP error with StatusCode=500" },
734+ },
735+ {
736+ name : "returns an error if the HTTP client returns a non-HTTP error" ,
737+ stopServer : true ,
738+ expErrContains : osErrContains {
739+ expContains : "connect: connection refused" ,
740+ expContainsWin : "connectex: No connection could be made because the target machine actively refused it." ,
741+ },
742+ },
743+ {
744+ name : "returns no providers if the HTTP server returns a 404 response" ,
745+ httpStatusCode : 404 ,
746+ expResult : nil ,
747+ },
748+ {
749+ name : "passes count and closerThan along" ,
750+ expStreamingResponse : true ,
751+ routerResult : peerRecords ,
752+ expResult : peerRecords ,
753+ count : 10 ,
754+ closerThan : pid ,
755+ },
756+ }
757+ for _ , c := range cases {
758+ t .Run (c .name , func (t * testing.T ) {
759+ var clientOpts []Option
760+ var serverOpts []server.Option
761+ var onRespReceived []func (* http.Response )
762+ var onReqReceived []func (* http.Request )
763+
764+ if c .serverStreamingDisabled {
765+ serverOpts = append (serverOpts , server .WithStreamingResultsDisabled ())
766+ }
767+
768+ if c .clientRequiresStreaming {
769+ clientOpts = append (clientOpts , WithStreamResultsRequired ())
770+ onReqReceived = append (onReqReceived , func (r * http.Request ) {
771+ assert .Equal (t , mediaTypeNDJSON , r .Header .Get ("Accept" ))
772+ })
773+ }
774+
775+ if c .expStreamingResponse {
776+ onRespReceived = append (onRespReceived , func (r * http.Response ) {
777+ assert .Equal (t , mediaTypeNDJSON , r .Header .Get ("Content-Type" ))
778+ })
779+ }
780+
781+ if c .expJSONResponse {
782+ onRespReceived = append (onRespReceived , func (r * http.Response ) {
783+ assert .Equal (t , mediaTypeJSON , r .Header .Get ("Content-Type" ))
784+ })
785+ }
786+
787+ deps := makeTestDeps (t , clientOpts , serverOpts )
788+
789+ deps .recordingHTTPClient .f = append (deps .recordingHTTPClient .f , onRespReceived ... )
790+ deps .recordingHandler .f = append (deps .recordingHandler .f , onReqReceived ... )
791+
792+ client := deps .client
793+ router := deps .router
794+
795+ ctx , cancel := context .WithCancel (context .Background ())
796+ t .Cleanup (cancel )
797+
798+ if c .httpStatusCode != 0 {
799+ deps .server .Config .Handler = http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
800+ w .WriteHeader (c .httpStatusCode )
801+ })
802+ }
803+
804+ if c .stopServer {
805+ deps .server .Close ()
806+ }
807+
808+ count := c .count
809+ if count == 0 {
810+ count = 20
811+ }
812+ routerResultIter := iter .FromSlice (c .routerResult )
813+ router .On ("GetClosestPeers" , mock .Anything , pid , c .closerThan , count ).Return (routerResultIter , c .routerErr )
814+
815+ resultIter , err := client .GetClosestPeers (ctx , pid , c .closerThan , c .count )
816+ c .expErrContains .errContains (t , err )
817+
818+ results := iter .ReadAll (resultIter )
819+ assert .Equal (t , c .expResult , results )
820+ })
821+ }
822+ }
823+
679824func TestNormalizeBaseURL (t * testing.T ) {
680825 cases := []struct {
681826 name string
0 commit comments