testcases

This commit is contained in:
cary 2026-04-18 16:57:49 +08:00
parent c6cbb984a3
commit 6d059ec73f
9 changed files with 212 additions and 0 deletions

10
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

9
.idea/go-multicall.iml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

10
.idea/go.imports.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GoImports">
<option name="excludedPackages">
<array>
<option value="golang.org/x/net/context" />
</array>
</option>
</component>
</project>

7
.idea/golinter.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GoLinterSettings">
<option name="customConfigFile" value="$PROJECT_DIR$/.golangci.yml" />
<option name="useCustomConfigFile" value="true" />
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/go-multicall.iml" filepath="$PROJECT_DIR$/.idea/go-multicall.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -35,3 +35,19 @@ func TestCall_BadABI(t *testing.T) {
r.Error(err)
r.ErrorContains(err, "unexpected EOF")
}
// TestUnpackResult_SafeTypeAssertion verifies UnpackResult never panics regardless of
// the concrete type stored in Outputs.
func TestUnpackResult_SafeTypeAssertion(t *testing.T) {
r := require.New(t)
// nil Outputs → nil
r.Nil((&Call{}).UnpackResult())
// struct pointer (common case) → nil, no panic
r.Nil((&Call{Outputs: new(struct{ Val bool })}).UnpackResult())
// []interface{} → returns the slice as-is
out := []interface{}{true, "hello"}
r.Equal(out, (&Call{Outputs: out}).UnpackResult())
}

View file

@ -2,6 +2,7 @@ package multicall
import (
"context"
"fmt"
"math/big"
"testing"
@ -102,6 +103,15 @@ func (ms *multicallStub) Aggregate3(opts *bind.CallOpts, calls []contract_multic
return
}
// multicallResultStub allows full control over per-call Success flags and errors.
type multicallResultStub struct {
results func(calls []contract_multicall.Multicall3Call3) ([]contract_multicall.Multicall3Result, error)
}
func (ms *multicallResultStub) Aggregate3(opts *bind.CallOpts, calls []contract_multicall.Multicall3Call3) ([]contract_multicall.Multicall3Result, error) {
return ms.results(calls)
}
func TestCaller_TwoCalls(t *testing.T) {
r := require.New(t)
@ -326,6 +336,136 @@ func TestDial(t *testing.T) {
r.NotNil(caller)
}
// boolOut is a helper output struct used across CanFail tests.
type boolOut struct {
Val1 bool
}
// validBoolData packs and strips the 4-byte selector, returning raw ABI-encoded output for true.
func validBoolData(t *testing.T, c *Contract) []byte {
t.Helper()
packed, err := c.ABI.Pack("testFunc", true)
if err != nil {
t.Fatalf("Pack: %v", err)
}
return packed[4:]
}
// TestCaller_CanFail_BadReturnData verifies that when a CanFail call receives malformed
// return data, only that call is marked Failed; other calls are unaffected and no error
// is returned to the caller.
func TestCaller_CanFail_BadReturnData(t *testing.T) {
r := require.New(t)
c1, err := NewContract(oneValueABI, testAddr1)
r.NoError(err)
c2, err := NewContract(oneValueABI, testAddr2)
r.NoError(err)
valid := validBoolData(t, c1)
call1 := c1.NewCall(new(boolOut), "testFunc", true)
call2 := c2.NewCall(new(boolOut), "testFunc", true).AllowFailure() // bad data below
call3 := c1.NewCall(new(boolOut), "testFunc", true)
caller := &Caller{
contract: &multicallResultStub{
results: func(calls []contract_multicall.Multicall3Call3) ([]contract_multicall.Multicall3Result, error) {
return []contract_multicall.Multicall3Result{
{Success: true, ReturnData: valid},
{Success: true, ReturnData: []byte{0xde, 0xad}}, // garbage — ABI decode fails
{Success: true, ReturnData: valid},
}, nil
},
},
}
calls, err := caller.Call(nil, call1, call2, call3)
r.NoError(err)
r.Len(calls, 3)
r.False(calls[0].Failed)
r.Equal(true, calls[0].Outputs.(*boolOut).Val1)
r.True(calls[1].Failed) // ABI decode failed, CanFail=true → marked Failed, no error
r.False(calls[2].Failed)
r.Equal(true, calls[2].Outputs.(*boolOut).Val1)
}
// TestCaller_CanFail_OnChainFailure verifies that when a CanFail call reverts on-chain
// (Success=false), it is marked Failed and does not affect other calls.
func TestCaller_CanFail_OnChainFailure(t *testing.T) {
r := require.New(t)
c, err := NewContract(oneValueABI, testAddr1)
r.NoError(err)
valid := validBoolData(t, c)
call1 := c.NewCall(new(boolOut), "testFunc", true)
call2 := c.NewCall(new(boolOut), "testFunc", true).AllowFailure()
caller := &Caller{
contract: &multicallResultStub{
results: func(calls []contract_multicall.Multicall3Call3) ([]contract_multicall.Multicall3Result, error) {
return []contract_multicall.Multicall3Result{
{Success: true, ReturnData: valid},
{Success: false, ReturnData: []byte{}}, // on-chain revert
}, nil
},
},
}
calls, err := caller.Call(nil, call1, call2)
r.NoError(err)
r.Len(calls, 2)
r.False(calls[0].Failed)
r.Equal(true, calls[0].Outputs.(*boolOut).Val1)
r.True(calls[1].Failed)
}
// TestCallChunked_ReturnsPartialOnError verifies that when a chunk fails, the results
// from previously successful chunks are returned alongside the error.
func TestCallChunked_ReturnsPartialOnError(t *testing.T) {
r := require.New(t)
c, err := NewContract(oneValueABI, testAddr1)
r.NoError(err)
valid := validBoolData(t, c)
call1 := c.NewCall(new(boolOut), "testFunc", true)
call2 := c.NewCall(new(boolOut), "testFunc", true)
callCount := 0
caller := &Caller{
contract: &multicallResultStub{
results: func(calls []contract_multicall.Multicall3Call3) ([]contract_multicall.Multicall3Result, error) {
callCount++
if callCount == 2 {
return nil, fmt.Errorf("rpc error on second chunk")
}
return []contract_multicall.Multicall3Result{
{Success: true, ReturnData: valid},
}, nil
},
},
}
// chunkSize=1: call1 → chunk 0 (succeeds), call2 → chunk 1 (fails)
result, err := caller.CallChunked(nil, 1, 0, call1, call2)
r.Error(err)
r.ErrorContains(err, "chunk [1]")
// partial results: only the successful first chunk is returned
r.Len(result, 1)
r.False(result[0].Failed)
r.Equal(true, result[0].Outputs.(*boolOut).Val1)
}
func TestChunkInputs(t *testing.T) {
testCases := []struct {
name string