Skip to content

Commit 0d13380

Browse files
authored
Add test suite (#7)
1 parent e7f0a00 commit 0d13380

File tree

6 files changed

+182
-37
lines changed

6 files changed

+182
-37
lines changed

client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ func (c *Client) callQueryMethod(method Method) (io.ReadCloser, int, error) {
148148
// doRequestWithRetry sends the HTTP request with retry according to the configured retry options.
149149
func (c *Client) doRequestWithRetry(req *http.Request) (io.ReadCloser, int, error) {
150150
attempt := 0
151+
151152
for {
152153
attempt++
153154
resp, err := c.HTTPClient.Do(req)

methods_check_by_address.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,9 @@ var (
5858

5959
// CheckContractAddress represents the contract address and associated chain IDs and statuses.
6060
type CheckContractAddress struct {
61-
Address common.Address `json:"address"` // The contract address.
62-
ChainIds []struct {
63-
ChainID string `json:"chainId"` // The chain ID.
64-
Status string `json:"status"` // The status of the contract.
65-
} `json:"chainIds"` // The associated chain IDs and statuses.
61+
Address common.Address `json:"address"` // The contract address.
62+
Status string `json:"status"` // The status of the contract.
63+
ChainIDs []string `json:"chainIds"` // The chain ID.
6664
}
6765

6866
// CheckContractByAddresses retrieves the available verified contract addresses for the given chain ID.

methods_check_by_address_test.go

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,9 @@ func TestCheckContractByAddresses(t *testing.T) {
1717
// Simulate a successful response with sample contract addresses
1818
contractAddresses := []*CheckContractAddress{
1919
{
20-
Address: common.HexToAddress("0x1234567890abcdef"),
21-
ChainIds: []struct {
22-
ChainID string `json:"chainId"`
23-
Status string `json:"status"`
24-
}{
25-
{
26-
ChainID: "1",
27-
Status: "verified",
28-
},
29-
{
30-
ChainID: "2",
31-
Status: "not verified",
32-
},
33-
},
20+
Address: common.HexToAddress("0x1234567890abcdef"),
21+
Status: "verified",
22+
ChainIDs: []string{"1", "2"},
3423
},
3524
}
3625

@@ -59,20 +48,9 @@ func TestCheckContractByAddresses(t *testing.T) {
5948

6049
expectedContractAddresses := []*CheckContractAddress{
6150
{
62-
Address: common.HexToAddress("0x1234567890abcdef"),
63-
ChainIds: []struct {
64-
ChainID string `json:"chainId"`
65-
Status string `json:"status"`
66-
}{
67-
{
68-
ChainID: "1",
69-
Status: "verified",
70-
},
71-
{
72-
ChainID: "2",
73-
Status: "not verified",
74-
},
75-
},
51+
Address: common.HexToAddress("0x1234567890abcdef"),
52+
Status: "verified",
53+
ChainIDs: []string{"1", "2"},
7654
},
7755
}
7856

methods_source.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package sourcify
33
import (
44
"encoding/json"
55
"fmt"
6+
"io"
67
"net/http"
8+
"strings"
79

810
"github.com/ethereum/go-ethereum/common"
911
)
@@ -104,12 +106,29 @@ func GetContractSourceCode(client *Client, chainId int, contract common.Address,
104106
// You'll have memory leaks if you don't do this!
105107
defer response.Close()
106108

109+
body, readBodyErr := io.ReadAll(response)
110+
if readBodyErr != nil {
111+
return nil, fmt.Errorf("failure to read body: %s", readBodyErr)
112+
}
113+
107114
if statusCode != http.StatusOK {
108115
return nil, fmt.Errorf("unexpected status code: %d", statusCode)
109116
}
110117

111-
var toReturn *SourceCodes
112-
if err := json.NewDecoder(response).Decode(&toReturn); err != nil {
118+
toReturn := &SourceCodes{}
119+
120+
if err := json.Unmarshal(body, &toReturn); err != nil {
121+
// Sometimes, response will not be a JSON object, but an array.
122+
// In this case, we'll get an error, but we can still return the code.
123+
// This is a workaround for the Sourcify API.
124+
// Ugly, but it works.
125+
if strings.Contains(err.Error(), "cannot unmarshal array into Go value") {
126+
toReturn.Status = "unknown"
127+
if err := json.Unmarshal(body, &toReturn.Code); err != nil {
128+
return nil, err
129+
}
130+
return toReturn, nil
131+
}
113132
return nil, err
114133
}
115134

methods_tree.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package sourcify
33
import (
44
"encoding/json"
55
"fmt"
6+
"io"
67
"net/http"
8+
"strings"
79

810
"github.com/ethereum/go-ethereum/common"
911
)
@@ -98,12 +100,29 @@ func GetContractFiles(client *Client, chainId int, contract common.Address, matc
98100
// You'll have memory leaks if you don't do this!
99101
defer response.Close()
100102

103+
body, readBodyErr := io.ReadAll(response)
104+
if readBodyErr != nil {
105+
return nil, fmt.Errorf("failure to read body: %s", readBodyErr)
106+
}
107+
101108
if statusCode != http.StatusOK {
102109
return nil, fmt.Errorf("unexpected status code: %d", statusCode)
103110
}
104111

105-
var toReturn *FileTree
106-
if err := json.NewDecoder(response).Decode(&toReturn); err != nil {
112+
toReturn := &FileTree{}
113+
114+
if err := json.Unmarshal(body, &toReturn); err != nil {
115+
// Sometimes, response will not be a JSON object, but an array.
116+
// In this case, we'll get an error, but we can still return the code.
117+
// This is a workaround for the Sourcify API.
118+
// Ugly, but it works.
119+
if strings.Contains(err.Error(), "cannot unmarshal array into Go value") {
120+
toReturn.Status = "unknown"
121+
if err := json.Unmarshal(body, &toReturn.Files); err != nil {
122+
return nil, err
123+
}
124+
return toReturn, nil
125+
}
107126
return nil, err
108127
}
109128

sourcify_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package sourcify
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/ethereum/go-ethereum/common"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/suite"
10+
)
11+
12+
// SourcifySuite is a struct that embeds suite.Suite and contains fields necessary for
13+
// executing tests. This includes a Client to interact with the sourcify service, a list of
14+
// addresses to check, a list of chain IDs to use in testing, and specific instances
15+
// of an address and a chain ID.
16+
type SourcifySuite struct {
17+
suite.Suite
18+
client *Client
19+
Addresses []string
20+
ChainIDs []int
21+
SpecificChainID int
22+
SpecificAddress common.Address
23+
}
24+
25+
// SetupTest initializes the test suite before each test. It creates a new Client with
26+
// specified options and initializes the addresses and chain IDs to be used in the tests.
27+
func (suite *SourcifySuite) SetupTest() {
28+
suite.client = NewClient(
29+
WithBaseURL("https://sourcify.dev/server"),
30+
WithRetryOptions(
31+
WithMaxRetries(3),
32+
WithDelay(2*time.Second),
33+
),
34+
//WithRateLimit(20, 1*time.Second),
35+
)
36+
suite.Addresses = []string{"0x054B2223509D430269a31De4AE2f335890be5C8F"}
37+
suite.ChainIDs = []int{1, 56, 137}
38+
suite.SpecificChainID = 56 // Add the specific chain ID for the two methods
39+
suite.SpecificAddress = common.HexToAddress("0x054B2223509D430269a31De4AE2f335890be5C8F") // Add the specific address for the two methods
40+
}
41+
42+
// TestGetContractMetadata tests the GetContractMetadata function. It asserts that no error
43+
// is returned and the metadata is not nil.
44+
func (suite *SourcifySuite) TestGetContractMetadata() {
45+
// Act
46+
metadata, err := GetContractMetadata(suite.client, suite.SpecificChainID, suite.SpecificAddress, MethodMatchTypeFull)
47+
48+
// Assert
49+
assert := assert.New(suite.T())
50+
assert.NoError(err, "GetContractMetadata should not return an error")
51+
assert.NotNil(metadata, "metadata should not be nil")
52+
}
53+
54+
// TestGetContractSourceCode tests the GetContractSourceCode function. It asserts that no error
55+
// is returned, the source code is not nil, and the length of the source code is 2.
56+
func (suite *SourcifySuite) TestGetContractSourceCode() {
57+
sourceCode, err := GetContractSourceCode(suite.client, suite.SpecificChainID, suite.SpecificAddress, MethodMatchTypeFull)
58+
59+
assert := assert.New(suite.T())
60+
assert.NoError(err, "Expected GetContractSourceCode to run without error")
61+
assert.NotNil(sourceCode, "source code should not be nil")
62+
assert.Equal(len(sourceCode.Code), 2, "Expected source code to have 2 files")
63+
}
64+
65+
// TestGetChains tests the GetChains function. It asserts that no error is returned, the chains
66+
// are not nil, and the length of the chains is at least 98.
67+
func (suite *SourcifySuite) TestGetChains() {
68+
chains, err := GetChains(suite.client)
69+
70+
assert := assert.New(suite.T())
71+
assert.NoError(err, "Expected GetChains to run without error")
72+
assert.NotNil(chains, "source code should not be nil")
73+
assert.GreaterOrEqual(len(chains), 98, "Expected source code to have at least 98 chains")
74+
}
75+
76+
// TestGetAvailableContractAddresses tests the GetAvailableContractAddresses function. It asserts
77+
// that no error is returned, the addresses are not nil, and the length of the full and partial addresses
78+
// are at least 1000 each.
79+
func (suite *SourcifySuite) TestGetAvailableContractAddresses() {
80+
// Act
81+
addresses, err := GetAvailableContractAddresses(suite.client, suite.SpecificChainID)
82+
83+
// Assert
84+
assert := assert.New(suite.T())
85+
assert.NoError(err, "GetAvailableContractAddresses should not return an error")
86+
assert.NotNil(addresses, "addresses should not be nil")
87+
assert.GreaterOrEqual(len(addresses.Full), 1000, "Expected source code to have at least 1000 addresses")
88+
assert.GreaterOrEqual(len(addresses.Partial), 1000, "Expected source code to have at least 1000 addresses")
89+
}
90+
91+
// TestCheckContractByAddresses tests the CheckContractByAddresses function. It asserts that no error
92+
// is returned and the checks are not nil.
93+
func (suite *SourcifySuite) TestCheckContractByAddresses() {
94+
// Act
95+
checks, err := CheckContractByAddresses(suite.client, suite.Addresses, suite.ChainIDs, MethodMatchTypeFull)
96+
97+
// Assert
98+
assert := assert.New(suite.T())
99+
assert.NoError(err, "CheckContractByAddresses should not return an error")
100+
assert.NotNil(checks, "checks should not be nil")
101+
}
102+
103+
// TestGetHealth tests the GetHealth function. It asserts that no error is returned and the status
104+
// returned is true.
105+
func (suite *SourcifySuite) TestGetHealth() {
106+
// Act
107+
status, err := GetHealth(suite.client)
108+
109+
// Assert
110+
assert := assert.New(suite.T())
111+
assert.NoError(err, "GetHealth should not return an error")
112+
assert.True(status, "status should be true")
113+
}
114+
115+
// TestGetContractFiles tests the GetContractFiles function. It asserts that no error is returned,
116+
// the tree is not nil, and the length of the files is 2.
117+
func (suite *SourcifySuite) TestGetContractFiles() {
118+
tree, err := GetContractFiles(suite.client, suite.SpecificChainID, suite.SpecificAddress, MethodMatchTypeFull)
119+
120+
assert := assert.New(suite.T())
121+
assert.NoError(err, "Expected GetContractSourceCode to run without error")
122+
assert.NotNil(tree, "tree code should not be nil")
123+
assert.Equal(len(tree.Files), 2, "Expected tree to have 2 files")
124+
}
125+
126+
// TestGetContractFiles tests the GetContractFiles function. It asserts that no error is returned,
127+
// the tree is not nil, and the length of the files is 2.
128+
func TestSourcifySuite(t *testing.T) {
129+
suite.Run(t, new(SourcifySuite))
130+
}

0 commit comments

Comments
 (0)