package cli import ( "context" "fmt" "testing" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" mocks "github.com/filecoin-project/lotus/api/mocks" types "github.com/filecoin-project/lotus/chain/types" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) type markerKeyType struct{} var markerKey = markerKeyType{} type contextMatcher struct { marker *int } // Matches returns whether x is a match. func (cm contextMatcher) Matches(x interface{}) bool { ctx, ok := x.(context.Context) if !ok { return false } maybeMarker, ok := ctx.Value(markerKey).(*int) if !ok { return false } return cm.marker == maybeMarker } func (cm contextMatcher) String() string { return fmt.Sprintf("Context with Value(%v/%T, %p)", markerKey, markerKey, cm.marker) } func ContextWithMarker(ctx context.Context) (context.Context, gomock.Matcher) { marker := new(int) outCtx := context.WithValue(ctx, markerKey, marker) return outCtx, contextMatcher{marker: marker} } func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) { mockCtrl := gomock.NewController(t) mockApi := mocks.NewMockFullNode(mockCtrl) srvcs := &ServicesImpl{ api: mockApi, closer: mockCtrl.Finish, } return srvcs, mockApi } // linter doesn't like dead code, so these are commented out. func fakeSign(msg *types.Message) *types.SignedMessage { return &types.SignedMessage{ Message: *msg, Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, } } //func makeMessageSigner() (*cid.Cid, interface{}) { //smCid := cid.Undef //return &smCid, //func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { //sm := fakeSign(msg) //smCid = sm.Cid() //return sm, nil //} //} type MessageMatcher SendParams var _ gomock.Matcher = MessageMatcher{} // Matches returns whether x is a match. func (mm MessageMatcher) Matches(x interface{}) bool { proto, ok := x.(*api.MessagePrototype) if !ok { return false } m := &proto.Message if mm.From != address.Undef && mm.From != m.From { return false } if mm.To != address.Undef && mm.To != m.To { return false } if types.BigCmp(mm.Val, m.Value) != 0 { return false } if mm.Nonce != nil && *mm.Nonce != m.Nonce { return false } if mm.GasPremium != nil && big.Cmp(*mm.GasPremium, m.GasPremium) != 0 { return false } if mm.GasPremium == nil && m.GasPremium.Sign() != 0 { return false } if mm.GasFeeCap != nil && big.Cmp(*mm.GasFeeCap, m.GasFeeCap) != 0 { return false } if mm.GasFeeCap == nil && m.GasFeeCap.Sign() != 0 { return false } if mm.GasLimit != nil && *mm.GasLimit != m.GasLimit { return false } if mm.GasLimit == nil && m.GasLimit != 0 { return false } // handle rest of options return true } // String describes what the matcher matches. func (mm MessageMatcher) String() string { return fmt.Sprintf("%#v", SendParams(mm)) } func TestSendService(t *testing.T) { addrGen := address.NewForTestGetter() a1 := addrGen() a2 := addrGen() const balance = 10000 params := SendParams{ From: a1, To: a2, Val: types.NewInt(balance - 100), } ctx, ctxM := ContextWithMarker(context.Background()) t.Run("happy", func(t *testing.T) { params := params srvcs, _ := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) assert.True(t, MessageMatcher(params).Matches(proto)) }) t.Run("default-from", func(t *testing.T) { params := params params.From = address.Undef mm := MessageMatcher(params) mm.From = a1 srvcs, mockApi := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck gomock.InOrder( mockApi.EXPECT().WalletDefaultAddress(ctxM).Return(a1, nil), ) proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) assert.True(t, mm.Matches(proto)) }) t.Run("set-nonce", func(t *testing.T) { params := params n := uint64(5) params.Nonce = &n mm := MessageMatcher(params) srvcs, _ := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) assert.True(t, mm.Matches(proto)) }) t.Run("gas-params", func(t *testing.T) { params := params limit := int64(1) params.GasLimit = &limit gfc := big.NewInt(100) params.GasFeeCap = &gfc gp := big.NewInt(10) params.GasPremium = &gp mm := MessageMatcher(params) srvcs, _ := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) assert.True(t, mm.Matches(proto)) }) }