@@ -3,6 +3,8 @@ package deployment
33import (
44 "context"
55 "errors"
6+ "net/http"
7+ "net/http/httptest"
68 "testing"
79 "time"
810
@@ -13,14 +15,44 @@ import (
1315 "github.com/smartcontractkit/chainlink-common/pkg/logger"
1416)
1517
18+ // Helper RPC server that always answers with a valid eth_blockNumber response
19+ func newMockRPCServer (t * testing.T ) * httptest.Server {
20+ t .Helper ()
21+
22+ handler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
23+ w .Header ().Set ("Content-Type" , "application/json" )
24+ // Return a valid eth_blockNumber response
25+ _ , _ = w .Write ([]byte (`{"jsonrpc":"2.0","id":1,"result":"0x1"}` ))
26+ })
27+
28+ return httptest .NewServer (handler )
29+ }
30+
31+ // Helper RPC server that always answers with a JSON-RPC error payload
32+ func newBadRPCServer (t * testing.T ) * httptest.Server {
33+ t .Helper ()
34+
35+ handler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
36+ w .Header ().Set ("Content-Type" , "application/json" )
37+ // Standard JSON-RPC error payload
38+ _ , _ = w .Write ([]byte (`{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"internal error"}}` ))
39+ })
40+
41+ return httptest .NewServer (handler )
42+ }
43+
1644// TODO(giogam): This test is incomplete, it should be completed with support for websockets URLS
1745func TestMultiClient (t * testing.T ) {
1846 t .Parallel ()
47+
48+ mockSrv := newMockRPCServer (t )
49+ defer mockSrv .Close ()
50+
1951 var (
2052 lggr = logger .Test (t )
2153 chainSelector uint64 = 16015286601757825753 // "ethereum-testnet-sepolia"
22- wsURL = "ws://example.com"
23- httpURL = "http://example.com"
54+ wsURL = "" // WS unused in this test
55+ httpURL = mockSrv . URL // use mock server for health-check
2456 )
2557
2658 // Expect defaults to be set if not provided.
@@ -43,13 +75,50 @@ func TestMultiClient(t *testing.T) {
4375
4476 // Expect second client to be set as backup.
4577 mc , err = NewMultiClient (lggr , RPCConfig {ChainSelector : chainSelector , RPCs : []RPC {
46- {Name : "test-rpc" , WSURL : wsURL , HTTPURL : httpURL , PreferredURLScheme : URLSchemePreferenceHTTP },
47- {Name : "test-rpc" , WSURL : wsURL , HTTPURL : httpURL , PreferredURLScheme : URLSchemePreferenceHTTP },
78+ {Name : "test-rpc" , WSURL : wsURL , HTTPURL : httpURL , PreferredURLScheme : URLSchemePreferenceHTTP }, //preferred
79+ {Name : "test-rpc" , WSURL : wsURL , HTTPURL : httpURL , PreferredURLScheme : URLSchemePreferenceHTTP }, //backup
4880 }})
4981 require .NoError (t , err )
5082 require .Len (t , mc .Backups , 1 )
5183}
5284
85+ // Verifies that a bad eth_blockNumber response causes MultiClient to skip the
86+ // first RPC and succeed with the next one.
87+ func TestMultiClient_healthCheckSkipsBadRPC (t * testing.T ) {
88+ t .Parallel ()
89+
90+ badSrv := newBadRPCServer (t )
91+ defer badSrv .Close ()
92+
93+ goodSrv := newMockRPCServer (t )
94+ defer goodSrv .Close ()
95+
96+ var (
97+ lggr = logger .Test (t )
98+ chainSelector uint64 = 16015286601757825753
99+ )
100+
101+ mc , err := NewMultiClient (lggr , RPCConfig {ChainSelector : chainSelector , RPCs : []RPC {
102+ // first RPC -> health-check fails
103+ {Name : "bad-rpc" , WSURL : "" , HTTPURL : badSrv .URL , PreferredURLScheme : URLSchemePreferenceHTTP },
104+ // second RPC -> health-check passes
105+ {Name : "good-rpc" , WSURL : "" , HTTPURL : goodSrv .URL , PreferredURLScheme : URLSchemePreferenceHTTP },
106+ }})
107+ require .NoError (t , err )
108+
109+ // Only the good RPC should remain (primary) and there should be no backups.
110+ require .NotNil (t , mc .Client )
111+ require .Empty (t , mc .Backups )
112+
113+ // Sanity-check: calling BlockNumber on the surviving client should succeed.
114+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Second )
115+ defer cancel ()
116+
117+ blockNum , err := mc .BlockNumber (ctx )
118+ require .NoError (t , err )
119+ assert .Equal (t , uint64 (1 ), blockNum )
120+ }
121+
53122func TestMultiClient_dialWithRetry (t * testing.T ) {
54123 t .Parallel ()
55124
0 commit comments