Skip to content

Commit a990cb1

Browse files
committed
Enhance Starknet compatibility and testing framework
- Updated the main CI workflow to include testing for the client with mocks. - Modified subscription handling in `subscription.js` to accommodate the new `subscription_id` structure from Starknet. - Refactored `provider_test.go` to include WebSocket provider support and improved test configurations. - Introduced a new test file `websocket_test.go` to validate WebSocket subscriptions for new block headers, ensuring robust error handling and compatibility with the testnet environment. These changes improve the overall robustness of the RPC client and enhance compatibility with Starknet's API.
1 parent 4b7360a commit a990cb1

File tree

4 files changed

+150
-14
lines changed

4 files changed

+150
-14
lines changed

.github/workflows/main_ci_check.yml

+4
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,7 @@ jobs:
7171
cd ../simpleCall && go build
7272
cd ../simpleInvoke && go build
7373
cd ../deployContractUDC && go build
74+
75+
# Test client on mock
76+
- name: Test client with mocks
77+
run: cd client && go test -v

client/testdata/subscription.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
--> {"jsonrpc":"2.0","id":1,"method":"nftest_subscribe","params":["someSubscription",5,1]}
44
<-- {"jsonrpc":"2.0","id":1,"result":"0x1"}
5-
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":1}}
6-
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":2}}
7-
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":3}}
8-
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":4}}
9-
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":5}}
5+
// changed from {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":1}} to accomodate the new subscription_id from starknet
6+
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription_id":0,"subscription":"0x1","result":1}}
7+
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription_id":0,"subscription":"0x1","result":2}}
8+
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription_id":0,"subscription":"0x1","result":3}}
9+
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription_id":0,"subscription":"0x1","result":4}}
10+
<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription_id":0,"subscription":"0x1","result":5}}
1011

1112
--> {"jsonrpc":"2.0","id":2,"method":"nftest_echo","params":[11]}
1213
<-- {"jsonrpc":"2.0","id":2,"result":11}

rpc/provider_test.go

+27-9
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ const (
2121

2222
// testConfiguration is a type that is used to configure tests
2323
type testConfiguration struct {
24-
provider *Provider
25-
base string
24+
provider *Provider
25+
wsProvider *WsProvider
26+
base string
27+
wsBase string
2628
}
2729

2830
var (
@@ -31,15 +33,16 @@ var (
3133

3234
// testConfigurations are predefined test configurations
3335
testConfigurations = map[string]testConfiguration{
34-
// Requires a Mainnet Starknet JSON-RPC compliant node (e.g. pathfinder)
35-
// (ref: https://github.com/eqlabs/pathfinder)
36+
// Requires a Mainnet Starknet JSON-RPC compliant node (e.g. Juno)
37+
// (ref: https://github.com/NethermindEth/juno)
3638
"mainnet": {
3739
base: "https://free-rpc.nethermind.io/mainnet-juno",
3840
},
39-
// Requires a Testnet Starknet JSON-RPC compliant node (e.g. pathfinder)
40-
// (ref: https://github.com/eqlabs/pathfinder)
41+
// Requires a Testnet Starknet JSON-RPC compliant node (e.g. Juno)
42+
// (ref: https://github.com/NethermindEth/juno)
4143
"testnet": {
4244
base: "https://free-rpc.nethermind.io/sepolia-juno",
45+
// wsBase: "ws://localhost:6061",
4346
},
4447
// Requires a Devnet configuration running locally
4548
// (ref: https://github.com/0xSpaceShard/starknet-devnet-rs)
@@ -96,15 +99,30 @@ func beforeEach(t *testing.T) *testConfiguration {
9699
if base != "" {
97100
testConfig.base = base
98101
}
99-
c, err := NewProvider(testConfig.base)
102+
103+
client, err := NewProvider(testConfig.base)
100104
if err != nil {
101105
t.Fatal("connect should succeed, instead:", err)
102106
}
103-
104-
testConfig.provider = c
107+
testConfig.provider = client
105108
t.Cleanup(func() {
106109
testConfig.provider.c.Close()
107110
})
111+
112+
wsBase := os.Getenv("WS_PROVIDER_URL")
113+
if wsBase != "" {
114+
testConfig.wsBase = wsBase
115+
116+
wsClient, err := NewWebsocketProvider(testConfig.wsBase)
117+
if err != nil {
118+
t.Fatal("connect should succeed, instead:", err)
119+
}
120+
testConfig.wsProvider = wsClient
121+
t.Cleanup(func() {
122+
testConfig.wsProvider.c.Close()
123+
})
124+
}
125+
108126
return &testConfig
109127
}
110128

rpc/websocket_test.go

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package rpc
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/NethermindEth/starknet.go/client"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestSubscribeNewHeads(t *testing.T) {
13+
if testEnv != "testnet" {
14+
t.Skip("Skipping test as it requires a testnet environment")
15+
}
16+
17+
testConfig := beforeEach(t)
18+
require.NotNil(t, testConfig.wsBase, "wsProvider base is not set")
19+
20+
type testSetType struct {
21+
headers chan *BlockHeader
22+
blockID []BlockID
23+
counter int
24+
isErrorExpected bool
25+
}
26+
27+
provider := testConfig.provider
28+
blockNumber, err := provider.BlockNumber(context.Background())
29+
require.NoError(t, err)
30+
31+
latestBlockNumbers := []uint64{blockNumber, blockNumber + 1} // for the case the latest block number is updated
32+
33+
testSet := map[string][]testSetType{
34+
"testnet": {
35+
{ // normal
36+
headers: make(chan *BlockHeader),
37+
isErrorExpected: false,
38+
},
39+
{ // with tag latest
40+
headers: make(chan *BlockHeader),
41+
blockID: []BlockID{WithBlockTag("latest")},
42+
isErrorExpected: false,
43+
},
44+
{ // with tag pending
45+
headers: make(chan *BlockHeader),
46+
blockID: []BlockID{WithBlockTag("pending")},
47+
isErrorExpected: true,
48+
},
49+
{ // with block number within the range of 1024 blocks
50+
headers: make(chan *BlockHeader),
51+
blockID: []BlockID{WithBlockNumber(blockNumber - 100)},
52+
counter: 100,
53+
isErrorExpected: false,
54+
},
55+
{ // invalid, with block number out of the range of 1024 blocks
56+
headers: make(chan *BlockHeader),
57+
blockID: []BlockID{WithBlockNumber(blockNumber - 1025)},
58+
isErrorExpected: true,
59+
},
60+
{ // invalid, more than one blockID parameter
61+
headers: make(chan *BlockHeader),
62+
blockID: []BlockID{WithBlockTag("latest"), WithBlockTag("latest")},
63+
isErrorExpected: true,
64+
},
65+
},
66+
}[testEnv]
67+
68+
for index, test := range testSet {
69+
t.Run(fmt.Sprintf("test %d", index+1), func(t *testing.T) {
70+
71+
wsProvider, err := NewWebsocketProvider(testConfig.wsBase)
72+
require.NoError(t, err)
73+
defer wsProvider.Close()
74+
75+
var sub *client.ClientSubscription
76+
if len(test.blockID) == 0 {
77+
sub, err = wsProvider.SubscribeNewHeads(context.Background(), test.headers)
78+
} else {
79+
sub, err = wsProvider.SubscribeNewHeads(context.Background(), test.headers, test.blockID...)
80+
}
81+
82+
if test.isErrorExpected {
83+
require.Error(t, err)
84+
return
85+
} else {
86+
require.NoError(t, err)
87+
}
88+
89+
require.NotNil(t, sub)
90+
defer sub.Unsubscribe()
91+
92+
for {
93+
select {
94+
case resp := <-test.headers:
95+
require.IsType(t, &BlockHeader{}, resp)
96+
97+
if test.counter != 0 {
98+
if test.counter == 1 {
99+
require.Contains(t, latestBlockNumbers, resp.BlockNumber+1)
100+
return
101+
} else {
102+
test.counter--
103+
}
104+
} else {
105+
return
106+
}
107+
case err := <-sub.Err():
108+
require.NoError(t, err)
109+
}
110+
}
111+
})
112+
}
113+
}

0 commit comments

Comments
 (0)