Skip to content

Commit 2acbba6

Browse files
committed
feat(jsonrpc): Add jsonrpc tests
1 parent ff75aee commit 2acbba6

23 files changed

Lines changed: 1949 additions & 26 deletions
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package jsonrpc
5+
6+
import (
7+
"encoding/json"
8+
"errors"
9+
"fmt"
10+
"os"
11+
"strings"
12+
"testing"
13+
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
type jsonrpcSchema struct {
18+
Methods []struct {
19+
Name string
20+
}
21+
}
22+
23+
func methodNameToFileName(methodName string) string {
24+
return "method_" + strings.TrimPrefix(methodName, "cartesi_") + "_test.go"
25+
}
26+
27+
// parse discover file and check if the files for method handlers exist
28+
func Test(t *testing.T) {
29+
data, err := discoverSpec.ReadFile("jsonrpc-discover.json")
30+
assert.Nil(t, err)
31+
32+
var schema jsonrpcSchema
33+
err = json.Unmarshal(data, &schema)
34+
assert.Nil(t, err)
35+
36+
for _, method := range schema.Methods {
37+
testFile := methodNameToFileName(method.Name)
38+
msg := fmt.Sprintf("file not found: %v (%v). assuming there is not test for it.",
39+
testFile, method.Name)
40+
41+
_, err := os.Stat(testFile)
42+
assert.False(t, errors.Is(err, os.ErrNotExist), msg)
43+
}
44+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package jsonrpc
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/cartesi/rollups-node/internal/model"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
// failure: application not in the database -> resource not found
17+
func TestGetApplicationAbsent(t *testing.T) {
18+
s := newTestService(t, functionName())
19+
20+
nr := uint64(0)
21+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
22+
"jsonrpc": "2.0",
23+
"method": "cartesi_getApplication",
24+
"params": { "application": "%v" },
25+
"id": 0
26+
}`, numberToName(nr)))
27+
28+
resp := testRPCResponse[any]{}
29+
assert.Nil(t, json.Unmarshal(body, &resp))
30+
assert.Equal(t, JSONRPC_RESOURCE_NOT_FOUND, resp.Error.Code)
31+
assert.Equal(t, "Application not found", resp.Error.Message)
32+
}
33+
34+
// success: application is in the database -> retrieve application
35+
func TestGetApplicationSuccess(t *testing.T) {
36+
s := newTestService(t, functionName())
37+
ctx := context.Background()
38+
39+
nr := uint64(1)
40+
s.newTestApplication(t, ctx, 0, nr)
41+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
42+
"jsonrpc": "2.0",
43+
"method": "cartesi_getApplication",
44+
"params": { "application": "%v" },
45+
"id": 0
46+
}`, numberToName(nr)))
47+
48+
resp := testRPCResponse[model.Application]{}
49+
assert.Nil(t, json.Unmarshal(body, &resp))
50+
assert.Equal(t, nr, nameToNumber(resp.Result.Data.Name))
51+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package jsonrpc
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/cartesi/rollups-node/internal/evmreader"
13+
"github.com/cartesi/rollups-node/internal/model"
14+
"github.com/cartesi/rollups-node/internal/repository"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func Test_getChainIdFailure(t *testing.T) {
19+
s := newTestService(t, "getChainIdFailure")
20+
21+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
22+
"jsonrpc": "2.0",
23+
"method": "cartesi_getChainId",
24+
"params": {},
25+
"id": 0
26+
}`))
27+
resp := testRPCResponse[any]{}
28+
assert.Nil(t, json.Unmarshal(body, &resp))
29+
assert.Equal(t, JSONRPC_RESOURCE_NOT_FOUND, resp.Error.Code)
30+
assert.Equal(t, "EVM Reader config not found", resp.Error.Message)
31+
}
32+
33+
func Test_getChainIdSuccess(t *testing.T) {
34+
ctx := context.Background()
35+
s := newTestService(t, "getChainIdFailure")
36+
37+
// NodeConfig provision
38+
nr := uint64(0xdeadbeef)
39+
repository.SaveNodeConfig(ctx, s.repository,
40+
&model.NodeConfig[evmreader.PersistentConfig]{
41+
Key: evmreader.EvmReaderConfigKey,
42+
Value: evmreader.PersistentConfig{
43+
ChainID: nr,
44+
},
45+
},
46+
)
47+
48+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
49+
"jsonrpc": "2.0",
50+
"method": "cartesi_getChainId",
51+
"params": {},
52+
"id": 0
53+
}`))
54+
resp := testRPCResponse[hex64]{}
55+
assert.Nil(t, json.Unmarshal(body, &resp))
56+
assert.Equal(t, nr, uint64(resp.Result.Data))
57+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package jsonrpc
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/cartesi/rollups-node/internal/model"
13+
"github.com/ethereum/go-ethereum/common"
14+
"github.com/ethereum/go-ethereum/common/hexutil"
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
// failure: epoch_index not hex encoded -> invalid param
19+
func TestGetEpochMalformedEpochIndex(t *testing.T) {
20+
s := newTestService(t, functionName())
21+
22+
app := uint64(1)
23+
nr := uint64(0)
24+
25+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
26+
"jsonrpc": "2.0",
27+
"method": "cartesi_getEpoch",
28+
"params": {
29+
"application": "%v",
30+
"epoch_index": "%v"
31+
},
32+
"id": 0
33+
}`, numberToName(app), nr))
34+
35+
resp := testRPCResponse[any]{}
36+
assert.Nil(t, json.Unmarshal(body, &resp))
37+
assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code)
38+
assert.Equal(t, "invalid epoch_index: expected hex encoded value", resp.Error.Message)
39+
}
40+
41+
// failure: epoch not in the database -> resource not found
42+
func TestGetEpochAbsent(t *testing.T) {
43+
s := newTestService(t, functionName())
44+
ctx := context.Background()
45+
46+
app := uint64(1)
47+
nr := uint64(0)
48+
49+
appID := s.newTestApplication(t, ctx, 0, app)
50+
err := s.repository.CreateEpoch(ctx, &model.Epoch{
51+
ApplicationID: appID,
52+
Index: nr,
53+
ClaimHash: &common.Hash{},
54+
ClaimTransactionHash: &common.Hash{},
55+
Status: model.EpochStatus_ClaimAccepted,
56+
})
57+
assert.Nil(t, err, "on test case: %v, application: %v, epoch_index: %v", 0, appID, nr)
58+
59+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
60+
"jsonrpc": "2.0",
61+
"method": "cartesi_getEpoch",
62+
"params": {
63+
"application": "%v",
64+
"epoch_index": "%v"
65+
},
66+
"id": 0
67+
}`, numberToName(app), hexutil.EncodeUint64(nr+1)))
68+
69+
resp := testRPCResponse[any]{}
70+
assert.Nil(t, json.Unmarshal(body, &resp))
71+
assert.Equal(t, JSONRPC_RESOURCE_NOT_FOUND, resp.Error.Code)
72+
assert.Equal(t, "Epoch not found", resp.Error.Message)
73+
}
74+
75+
// success: epoch is in the database -> retrieve epoch
76+
func TestGetEpochSuccess(t *testing.T) {
77+
s := newTestService(t, functionName())
78+
ctx := context.Background()
79+
80+
app := uint64(1)
81+
nr := uint64(1)
82+
83+
appID := s.newTestApplication(t, ctx, 0, app)
84+
err := s.repository.CreateEpoch(ctx, &model.Epoch{
85+
ApplicationID: appID,
86+
Index: nr,
87+
ClaimHash: &common.Hash{},
88+
ClaimTransactionHash: &common.Hash{},
89+
Status: model.EpochStatus_ClaimAccepted,
90+
})
91+
assert.Nil(t, err, "on test case: %v, application: %v, epoch_index: %v", 0, appID, nr)
92+
93+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
94+
"jsonrpc": "2.0",
95+
"method": "cartesi_getEpoch",
96+
"params": {
97+
"application": "%v",
98+
"epoch_index": "%v"
99+
},
100+
"id": 0
101+
}`, numberToName(app), hexutil.EncodeUint64(nr)))
102+
103+
resp := testRPCResponse[*model.Epoch]{}
104+
assert.Nil(t, json.Unmarshal(body, &resp))
105+
assert.Equal(t, nr, resp.Result.Data.Index)
106+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package jsonrpc
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/cartesi/rollups-node/internal/model"
13+
"github.com/ethereum/go-ethereum/common/hexutil"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
// failure: input_index not hex encoded -> invalid param
18+
func TestGetInputWithMalformedInputIndex(t *testing.T) {
19+
s := newTestService(t, functionName())
20+
21+
app := uint64(1)
22+
nr := uint64(0)
23+
24+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
25+
"jsonrpc": "2.0",
26+
"method": "cartesi_getInput",
27+
"params": {
28+
"application": "%v",
29+
"input_index": "%v"
30+
},
31+
"id": 0
32+
}`, numberToName(app), nr))
33+
34+
resp := testRPCResponse[any]{}
35+
assert.Nil(t, json.Unmarshal(body, &resp))
36+
assert.Equal(t, JSONRPC_INVALID_PARAMS, resp.Error.Code)
37+
assert.Equal(t, "invalid input_index: expected hex encoded value", resp.Error.Message)
38+
}
39+
40+
// failure: input_index not in database -> absent input
41+
func TestGetInputWithAbsentInput(t *testing.T) {
42+
s := newTestService(t, functionName())
43+
ctx := context.Background()
44+
45+
app := uint64(2)
46+
enr := uint64(1)
47+
inr := uint64(0)
48+
appID := s.newTestApplication(t, ctx, 0, app)
49+
err := s.repository.CreateEpoch(ctx, &model.Epoch{
50+
ApplicationID: appID,
51+
Index: enr,
52+
VirtualIndex: enr,
53+
Status: model.EpochStatus_ClaimAccepted,
54+
})
55+
assert.Nil(t, err, "on test case: %v, application: %v, epoch_index: %v", 0, appID, enr)
56+
57+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
58+
"jsonrpc": "2.0",
59+
"method": "cartesi_getInput",
60+
"params": {
61+
"application": "%v",
62+
"input_index": "%v"
63+
},
64+
"id": 0
65+
}`, numberToName(app), hexutil.EncodeUint64(inr)))
66+
67+
resp := testRPCResponse[*model.Input]{}
68+
assert.Nil(t, json.Unmarshal(body, &resp))
69+
assert.Equal(t, JSONRPC_RESOURCE_NOT_FOUND, resp.Error.Code)
70+
assert.Equal(t, "Input not found", resp.Error.Message)
71+
}
72+
73+
// success: input_index of EvmAdvance in the database -> retrieve input
74+
func TestGetInputWithApplicationAndInput(t *testing.T) {
75+
s := newTestService(t, functionName())
76+
ctx := context.Background()
77+
78+
app := uint64(2)
79+
enr := uint64(1)
80+
inr := uint64(0)
81+
appID := s.newTestApplication(t, ctx, 0, app)
82+
err := s.repository.CreateEpoch(ctx, &model.Epoch{
83+
ApplicationID: appID,
84+
Index: enr,
85+
VirtualIndex: enr,
86+
Status: model.EpochStatus_ClaimAccepted,
87+
})
88+
assert.Nil(t, err, "on test case: %v, application: %v, epoch_index: %v", 0, appID, enr)
89+
90+
err = s.repository.CreateInput(ctx, &model.Input{
91+
EpochApplicationID: appID,
92+
EpochIndex: enr,
93+
Index: inr,
94+
Status: model.InputCompletionStatus_Accepted,
95+
RawData: emptyInput(),
96+
})
97+
assert.Nil(t, err, "on test case: %v, application: %v, input_index: %v", 0, appID, inr)
98+
99+
body := s.doRequest(t, 0, fmt.Appendf([]byte{}, `{
100+
"jsonrpc": "2.0",
101+
"method": "cartesi_getInput",
102+
"params": {
103+
"application": "%v",
104+
"input_index": "%v"
105+
},
106+
"id": 0
107+
}`, numberToName(app), hexutil.EncodeUint64(inr)))
108+
109+
resp := testRPCResponse[*model.Input]{}
110+
assert.Nil(t, json.Unmarshal(body, &resp))
111+
assert.Equal(t, enr, resp.Result.Data.EpochIndex)
112+
assert.Equal(t, inr, resp.Result.Data.Index)
113+
}
114+
115+
// TODO: also test DecodedInput

0 commit comments

Comments
 (0)