-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cover transaction manager with additional test cases #561 #601
Changes from 8 commits
d81554b
4c3d4be
155a424
35a2a31
e3d708a
16a05eb
1b3dbde
70e00f7
347f726
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,9 @@ import ( | |
"github.com/ethereum/go-ethereum/accounts/keystore" | ||
gethcommon "github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/ethereum/go-ethereum/rlp" | ||
gethrpc "github.com/ethereum/go-ethereum/rpc" | ||
"github.com/golang/mock/gomock" | ||
"github.com/stretchr/testify/suite" | ||
|
@@ -61,19 +63,53 @@ func (s *TxQueueTestSuite) TearDownTest() { | |
s.client.Close() | ||
} | ||
|
||
func (s *TxQueueTestSuite) setupTransactionPoolAPI(account *common.SelectedExtKey, nonce hexutil.Uint64, gas hexutil.Big, txErr error) { | ||
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), account.Address, gethrpc.PendingBlockNumber).Return(&nonce, nil) | ||
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(big.NewInt(10), nil) | ||
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(&gas, nil) | ||
s.txServiceMock.EXPECT().SendRawTransaction(gomock.Any(), gomock.Any()).Return(gethcommon.Hash{}, txErr) | ||
func (s *TxQueueTestSuite) setupTransactionPoolAPI(tx *common.QueuedTx, config *params.NodeConfig, account *common.SelectedExtKey, nonce *hexutil.Uint64, gas *hexutil.Big, txErr error) { | ||
// Expect calls to gas functions only if there are no user defined values. | ||
// And also set the expected gas and gas price for RLP encoding the expected tx. | ||
var usedGas, usedGasPrice *big.Int | ||
s.txServiceMock.EXPECT().GetTransactionCount(gomock.Any(), account.Address, gethrpc.PendingBlockNumber).Return(nonce, nil) | ||
if tx.Args.GasPrice == nil { | ||
usedGasPrice = big.NewInt(10) | ||
s.txServiceMock.EXPECT().GasPrice(gomock.Any()).Return(usedGasPrice, nil) | ||
} else { | ||
usedGasPrice = (*big.Int)(tx.Args.GasPrice) | ||
} | ||
if tx.Args.Gas == nil { | ||
s.txServiceMock.EXPECT().EstimateGas(gomock.Any(), gomock.Any()).Return(gas, nil) | ||
usedGas = (*big.Int)(gas) | ||
} else { | ||
usedGas = (*big.Int)(tx.Args.Gas) | ||
} | ||
// Prepare the transaction anD RLP encode it. | ||
data := s.rlpEncodeTx(tx, config, account, nonce, usedGas, usedGasPrice) | ||
// Expect the RLP encoded transaction. | ||
s.txServiceMock.EXPECT().SendRawTransaction(gomock.Any(), data).Return(gethcommon.Hash{}, txErr) | ||
} | ||
|
||
func (s *TxQueueTestSuite) rlpEncodeTx(tx *common.QueuedTx, config *params.NodeConfig, account *common.SelectedExtKey, nonce *hexutil.Uint64, gas, gasPrice *big.Int) hexutil.Bytes { | ||
newTx := types.NewTransaction( | ||
uint64(*nonce), | ||
gethcommon.Address(*tx.Args.To), | ||
tx.Args.Value.ToInt(), | ||
gas, | ||
gasPrice, | ||
tx.Args.Data, | ||
) | ||
chainID := big.NewInt(int64(config.NetworkID)) | ||
signedTx, err := types.SignTx(newTx, types.NewEIP155Signer(chainID), account.AccountKey.PrivateKey) | ||
s.NoError(err) | ||
data, err := rlp.EncodeToBytes(signedTx) | ||
s.NoError(err) | ||
return hexutil.Bytes(data) | ||
} | ||
|
||
func (s *TxQueueTestSuite) setupStatusBackend(account *common.SelectedExtKey, password string, passwordErr error) { | ||
func (s *TxQueueTestSuite) setupStatusBackend(account *common.SelectedExtKey, password string, passwordErr error) *params.NodeConfig { | ||
nodeConfig, nodeErr := params.NewNodeConfig("/tmp", params.RopstenNetworkID, true) | ||
s.nodeManagerMock.EXPECT().NodeConfig().Return(nodeConfig, nodeErr) | ||
s.accountManagerMock.EXPECT().SelectedAccount().Return(account, nil) | ||
s.accountManagerMock.EXPECT().VerifyAccountPassword(nodeConfig.KeyStoreDir, account.Address.String(), password).Return( | ||
nil, passwordErr) | ||
return nodeConfig | ||
} | ||
|
||
func (s *TxQueueTestSuite) TestCompleteTransaction() { | ||
|
@@ -83,41 +119,72 @@ func (s *TxQueueTestSuite) TestCompleteTransaction() { | |
Address: common.FromAddress(TestConfig.Account1.Address), | ||
AccountKey: &keystore.Key{PrivateKey: key}, | ||
} | ||
s.setupStatusBackend(account, password, nil) | ||
|
||
nonce := hexutil.Uint64(10) | ||
gas := hexutil.Big(*big.NewInt(defaultGas + 1)) | ||
s.setupTransactionPoolAPI(account, nonce, gas, nil) | ||
|
||
txQueueManager := NewManager(s.nodeManagerMock, s.accountManagerMock) | ||
|
||
txQueueManager.Start() | ||
defer txQueueManager.Stop() | ||
|
||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ | ||
From: common.FromAddress(TestConfig.Account1.Address), | ||
To: common.ToAddress(TestConfig.Account2.Address), | ||
}) | ||
|
||
s.NoError(txQueueManager.QueueTransaction(tx)) | ||
w := make(chan struct{}) | ||
var ( | ||
hash gethcommon.Hash | ||
err error | ||
) | ||
go func() { | ||
hash, err = txQueueManager.CompleteTransaction(tx.ID, password) | ||
s.NoError(err) | ||
close(w) | ||
}() | ||
testCases := []struct { | ||
name string | ||
gas *hexutil.Big | ||
gasPrice *hexutil.Big | ||
}{ | ||
{ | ||
"noGasDef", | ||
nil, | ||
nil, | ||
}, | ||
{ | ||
"gasDefined", | ||
(*hexutil.Big)(big.NewInt(100)), | ||
nil, | ||
}, | ||
{ | ||
"gasPriceDefined", | ||
nil, | ||
(*hexutil.Big)(big.NewInt(100)), | ||
}, | ||
} | ||
|
||
rst := txQueueManager.WaitForTransaction(tx) | ||
// Check that error is assigned to the transaction. | ||
s.NoError(rst.Error) | ||
// Transaction should be already removed from the queue. | ||
s.False(txQueueManager.TransactionQueue().Has(tx.ID)) | ||
s.NoError(WaitClosed(w, time.Second)) | ||
s.Equal(hash, rst.Hash) | ||
for _, testCase := range testCases { | ||
s.T().Run(testCase.name, func(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. never used it with testify, will it allow to use testify.m the same way as before? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like it doesn't detect that. But IMHO, it's certainly more readable & DRY and all tests run in few seconds anyway. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks good i think
|
||
s.SetupTest() | ||
config := s.setupStatusBackend(account, password, nil) | ||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ | ||
From: common.FromAddress(TestConfig.Account1.Address), | ||
To: common.ToAddress(TestConfig.Account2.Address), | ||
Gas: testCase.gas, | ||
GasPrice: testCase.gasPrice, | ||
}) | ||
s.setupTransactionPoolAPI(tx, config, account, &nonce, &gas, nil) | ||
|
||
txQueueManager := NewManager(s.nodeManagerMock, s.accountManagerMock) | ||
txQueueManager.completionTimeout = time.Second | ||
txQueueManager.rpcCallTimeout = time.Second | ||
|
||
txQueueManager.Start() | ||
defer txQueueManager.Stop() | ||
|
||
s.NoError(txQueueManager.QueueTransaction(tx)) | ||
w := make(chan struct{}) | ||
var ( | ||
hash gethcommon.Hash | ||
err error | ||
) | ||
go func() { | ||
hash, err = txQueueManager.CompleteTransaction(tx.ID, password) | ||
s.NoError(err) | ||
close(w) | ||
}() | ||
|
||
rst := txQueueManager.WaitForTransaction(tx) | ||
// Check that error is assigned to the transaction. | ||
s.NoError(rst.Error) | ||
// Transaction should be already removed from the queue. | ||
s.False(txQueueManager.TransactionQueue().Has(tx.ID)) | ||
s.NoError(WaitClosed(w, time.Second)) | ||
s.Equal(hash, rst.Hash) | ||
}) | ||
} | ||
} | ||
|
||
func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() { | ||
|
@@ -127,23 +194,26 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() { | |
Address: common.FromAddress(TestConfig.Account1.Address), | ||
AccountKey: &keystore.Key{PrivateKey: key}, | ||
} | ||
s.setupStatusBackend(account, password, nil) | ||
config := s.setupStatusBackend(account, password, nil) | ||
|
||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ | ||
From: common.FromAddress(TestConfig.Account1.Address), | ||
To: common.ToAddress(TestConfig.Account2.Address), | ||
}) | ||
|
||
nonce := hexutil.Uint64(10) | ||
gas := hexutil.Big(*big.NewInt(defaultGas + 1)) | ||
s.setupTransactionPoolAPI(account, nonce, gas, nil) | ||
s.setupTransactionPoolAPI(tx, config, account, &nonce, &gas, nil) | ||
|
||
txQueueManager := NewManager(s.nodeManagerMock, s.accountManagerMock) | ||
txQueueManager.completionTimeout = time.Second | ||
txQueueManager.rpcCallTimeout = time.Second | ||
txQueueManager.DisableNotificactions() | ||
txQueueManager.Start() | ||
defer txQueueManager.Stop() | ||
|
||
tx := common.CreateTransaction(context.Background(), common.SendTxArgs{ | ||
From: common.FromAddress(TestConfig.Account1.Address), | ||
To: common.ToAddress(TestConfig.Account2.Address), | ||
}) | ||
|
||
s.NoError(txQueueManager.QueueTransaction(tx)) | ||
err := txQueueManager.QueueTransaction(tx) | ||
s.NoError(err) | ||
|
||
var ( | ||
wg sync.WaitGroup | ||
|
@@ -158,14 +228,14 @@ func (s *TxQueueTestSuite) TestCompleteTransactionMultipleTimes() { | |
defer wg.Done() | ||
_, err := txQueueManager.CompleteTransaction(tx.ID, password) | ||
mu.Lock() | ||
defer mu.Unlock() | ||
if err == nil { | ||
completedTx++ | ||
} else if err == queue.ErrQueuedTxInProgress { | ||
inprogressTx++ | ||
} else { | ||
s.Fail("tx failed with unexpected error: ", err.Error()) | ||
} | ||
mu.Unlock() | ||
}() | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think about preparing tx earlier and passing it to this method? it looks like it will cleanup an interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, what do you mean by "preparing tx earlier and passing it to this method"? Isn't that already how it works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i thought that it is possible to pass types.Transaction instead of
tx *common.QueuedTx, config *params.NodeConfig, account *common.SelectedExtKey, nonce *hexutil.Uint64, gas *hexutil.Big
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean, I may be able to further reduce the code using
types.Transaction
. I'll look into it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second glance, it will just result in moving lines elsewhere because one has to form both
types.Transaction
andcommon.QueuedTx
objects in the same case. In other words, if I define atypes.Transaction
object in the test case and pass it tosetupTransactionPoolAPI
instead, that means I can remove forming a new object froms.rlpEncode
and it doesn't make a difference about how I handle rest of the args such as gas, gas price and nonce.But it's definitely possible to reduce the amount of args there by having default test values so I'm going to do that instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, thanks for checking