Extract e2e tests to a separate package (#375)
This change moves our e2e tests into a separate package to make room for proper unit and integration tests. This is Phase 1 described in #371. Changes: Makefile has separate directives to run unit/integration tests and e2e tests, CI runs unit/integration tests first and then e2e tests, E2e tests are in reliability order, i.e. the least reliable tests are run in the end to be sure that nothing else is broken, Some tests are fixed or quarantined.
This commit is contained in:
parent
90acfedf7a
commit
281b304edb
|
@ -11,7 +11,7 @@ install:
|
|||
- go get golang.org/x/tools/cmd/cover
|
||||
|
||||
script:
|
||||
- make ci
|
||||
- travis_wait make ci
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
|
80
Makefile
80
Makefile
|
@ -30,6 +30,8 @@ help: ##@other Show this help
|
|||
|
||||
# Main targets
|
||||
|
||||
UNIT_TEST_PACKAGES := $(shell go list ./... | grep -v /vendor | grep -v /e2e | grep -v /cmd)
|
||||
|
||||
statusgo: ##@build Build status-go as statusd server
|
||||
build/env.sh go build -i -o $(GOBIN)/statusd -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
||||
@echo "\nCompilation done.\nRun \"build/bin/statusd help\" to view available commands."
|
||||
|
@ -72,15 +74,6 @@ statusgo-ios-simulator-mainnet: xgo
|
|||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./cmd/statusd
|
||||
@echo "iOS framework cross compilation done (mainnet)."
|
||||
|
||||
ci: mock
|
||||
build/env.sh go test -timeout 40m -v ./geth/api/...
|
||||
build/env.sh go test -timeout 40m -v ./geth/common
|
||||
build/env.sh go test -timeout 40m -v ./geth/jail
|
||||
build/env.sh go test -timeout 40m -v ./geth/node
|
||||
build/env.sh go test -timeout 40m -v ./geth/params
|
||||
build/env.sh go test -timeout 40m -v ./extkeys
|
||||
build/env.sh go test -timeout 1m -v ./helpers/...
|
||||
|
||||
generate: ##@other Regenerate assets and other auto-generated stuff
|
||||
cp ./node_modules/web3/dist/web3.js ./static/scripts/web3.js
|
||||
build/env.sh go generate ./static
|
||||
|
@ -137,65 +130,30 @@ lint: ##@tests Run meta linter on code
|
|||
@echo "Linter: gosimple\n--------------------"
|
||||
@gometalinter --disable-all --deadline 45s --enable=gosimple extkeys cmd/... geth/... | grep -v -f ./static/config/linter_exclude_list.txt || echo "OK!"
|
||||
|
||||
mock-install:
|
||||
mock-install: ##@other Install mocking tools
|
||||
go get -u github.com/golang/mock/mockgen
|
||||
|
||||
mock: mock-install ##@other Regenerate mocks
|
||||
mock: ##@other Regenerate mocks
|
||||
mockgen -source=geth/common/types.go -destination=geth/common/types_mock.go -package=common
|
||||
|
||||
test: ##@tests Run tests
|
||||
@build/env.sh echo "mode: set" > coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth/api
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth/common
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth/jail
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth/node
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth/params
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./extkeys
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./cmd/statusd
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
@build/env.sh go tool cover -html=coverage-all.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage-all.out
|
||||
test: ##@tests Run unit and integration tests
|
||||
build/env.sh go test $(UNIT_TEST_PACKAGES)
|
||||
|
||||
test-api:
|
||||
build/env.sh go test -v -coverprofile=coverage.out -coverpkg=./geth/node ./geth/api
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
test-coverage: ##@tests Run unit and integration tests with covevare
|
||||
build/env.sh go test -coverpkg= $(UNIT_TEST_PACKAGES)
|
||||
|
||||
test-common:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./geth/common
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
test-e2e: ##@tests Run e2e tests
|
||||
# order: reliability then alphabetical
|
||||
build/env.sh go test -timeout 1m ./e2e/accounts/...
|
||||
build/env.sh go test -timeout 1m ./e2e/api/...
|
||||
build/env.sh go test -timeout 1m ./e2e/node/...
|
||||
build/env.sh go test -timeout 15m ./e2e/jail/...
|
||||
build/env.sh go test -timeout 20m ./e2e/rpc/...
|
||||
build/env.sh go test -timeout 10m ./e2e/whisper/...
|
||||
build/env.sh go test -timeout 10m ./e2e/transactions/...
|
||||
build/env.sh go test -timeout 10m ./cmd/statusd
|
||||
|
||||
test-jail:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./geth/jail
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-node:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./geth/node
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-params:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./geth/params
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-extkeys:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./extkeys
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-cmd:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./cmd/statusd
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
ci: mock-install mock test-coverage test-e2e ##@tests Run all tests in CI
|
||||
|
||||
clean: ##@other Cleanup
|
||||
rm -fr build/bin/*
|
||||
|
|
|
@ -23,9 +23,9 @@ import (
|
|||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
"github.com/status-im/status-go/static"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -201,7 +201,10 @@ func testGetDefaultConfig(t *testing.T) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// @TODO(adam): quarantined this test until it uses a different directory.
|
||||
func testResetChainData(t *testing.T) bool {
|
||||
t.Skip()
|
||||
|
||||
resetChainDataResponse := common.APIResponse{}
|
||||
rawResponse := ResetChainData()
|
||||
|
||||
|
@ -322,7 +325,7 @@ func testStopResumeNode(t *testing.T) bool {
|
|||
}
|
||||
|
||||
func testCallRPC(t *testing.T) bool {
|
||||
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}` + "\n"
|
||||
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}`
|
||||
rawResponse := CallRPC(C.CString(`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`))
|
||||
received := C.GoString(rawResponse)
|
||||
if expected != received {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
e2e
|
||||
===
|
||||
|
||||
This package contains all e2e tests divided into subpackages which represents (or should represent) business domains like transactions, chat etc.
|
||||
|
||||
These tests are run against public testnets: Ropsten and Rinkeby.
|
||||
|
||||
e2e package contains a few utilities which are described in a [godoc](https://godoc.org/github.com/status-im/status-go/e2e).
|
||||
|
||||
## Run
|
||||
|
||||
`make test-e2e`
|
|
@ -0,0 +1,64 @@
|
|||
package accounts
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestAccountsRPCTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AccountsRPCTestSuite))
|
||||
}
|
||||
|
||||
type AccountsRPCTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestRPCEthAccounts() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
rpcClient := s.Backend.NodeManager().RPCClient()
|
||||
s.NotNil(rpcClient)
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.Address) + `"]}`
|
||||
resp := rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_accounts",
|
||||
"params": []
|
||||
}`)
|
||||
s.Equal(expectedResponse, resp)
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestRPCEthAccountsWithUpstream() {
|
||||
s.StartTestBackend(
|
||||
params.RopstenNetworkID,
|
||||
e2e.WithUpstream("https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"),
|
||||
)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
rpcClient := s.Backend.NodeManager().RPCClient()
|
||||
s.NotNil(rpcClient)
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.Address) + `"]}`
|
||||
resp := rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_accounts",
|
||||
"params": []
|
||||
}`)
|
||||
s.Equal(expectedResponse, resp)
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestAccountsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AccountsTestSuite))
|
||||
}
|
||||
|
||||
type AccountsTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestAccountsList() {
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
accounts, err := s.Backend.AccountManager().Accounts()
|
||||
s.NoError(err)
|
||||
|
||||
// make sure that we start with empty accounts list (nobody has logged in yet)
|
||||
s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||
|
||||
// create an account
|
||||
address, _, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
// ensure that there is still no accounts returned
|
||||
accounts, err = s.Backend.AccountManager().Accounts()
|
||||
s.NoError(err)
|
||||
s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||
|
||||
// select account (sub-accounts will be created for this key)
|
||||
err = s.Backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||
s.NoError(err, "account selection failed")
|
||||
|
||||
// at this point main account should show up
|
||||
accounts, err = s.Backend.AccountManager().Accounts()
|
||||
s.NoError(err)
|
||||
s.Equal(1, len(accounts), "exactly single account is expected (main account)")
|
||||
s.Equal(string(accounts[0].Hex()), address,
|
||||
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+address))
|
||||
|
||||
// create sub-account 1
|
||||
subAccount1, subPubKey1, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot create sub-account")
|
||||
|
||||
// now we expect to see both main account and sub-account 1
|
||||
accounts, err = s.Backend.AccountManager().Accounts()
|
||||
s.NoError(err)
|
||||
s.Equal(2, len(accounts), "exactly 2 accounts are expected (main + sub-account 1)")
|
||||
s.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key")
|
||||
s.Equal(string(accounts[1].Hex()), subAccount1, "subAcount1 not returned")
|
||||
|
||||
// create sub-account 2, index automatically progresses
|
||||
subAccount2, subPubKey2, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot create sub-account")
|
||||
s.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||
|
||||
// finally, all 3 accounts should show up (main account, sub-accounts 1 and 2)
|
||||
accounts, err = s.Backend.AccountManager().Accounts()
|
||||
s.NoError(err)
|
||||
s.Equal(3, len(accounts), "unexpected number of accounts")
|
||||
s.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key")
|
||||
|
||||
subAccount1MatchesKey1 := string(accounts[1].Hex()) != "0x"+subAccount1
|
||||
subAccount1MatchesKey2 := string(accounts[2].Hex()) != "0x"+subAccount1
|
||||
s.False(!subAccount1MatchesKey1 && !subAccount1MatchesKey2, "subAcount1 not returned")
|
||||
|
||||
subAccount2MatchesKey1 := string(accounts[1].Hex()) != "0x"+subAccount2
|
||||
subAccount2MatchesKey2 := string(accounts[2].Hex()) != "0x"+subAccount2
|
||||
s.False(!subAccount2MatchesKey1 && !subAccount2MatchesKey2, "subAcount2 not returned")
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestCreateChildAccount() {
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
keyStore, err := s.Backend.NodeManager().AccountKeyStore()
|
||||
s.NoError(err)
|
||||
s.NotNil(keyStore)
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
acct, err := common.ParseAccountString(address)
|
||||
s.NoError(err, "can not get account from address")
|
||||
|
||||
// obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present
|
||||
_, key, err := keyStore.AccountDecryptedKey(acct, TestConfig.Account1.Password)
|
||||
s.NoError(err, "can not obtain decrypted account key")
|
||||
s.NotNil(key.ExtendedKey, "CKD#2 has not been generated for new account")
|
||||
|
||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||
_, _, err = s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error(), "expected error is not returned (tried to create sub-account w/o login)")
|
||||
|
||||
err = s.Backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot select account")
|
||||
|
||||
// try to create sub-account with wrong password
|
||||
_, _, err = s.Backend.AccountManager().CreateChildAccount("", "wrong password")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
s.EqualError(expectedErr, err.Error(), "create sub-account with wrong password")
|
||||
|
||||
// create sub-account (from implicit parent)
|
||||
subAccount1, subPubKey1, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot create sub-account")
|
||||
|
||||
// make sure that sub-account index automatically progresses
|
||||
subAccount2, subPubKey2, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||
|
||||
// create sub-account (from explicit parent)
|
||||
subAccount3, subPubKey3, err := s.Backend.AccountManager().CreateChildAccount(subAccount2, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.False(subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3)
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestRecoverAccount() {
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
keyStore, err := s.Backend.NodeManager().AccountKeyStore()
|
||||
s.NoError(err)
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
// try recovering using password + mnemonic
|
||||
addressCheck, pubKeyCheck, err := s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
s.NoError(err, "recover account failed")
|
||||
s.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect accound details recovered")
|
||||
|
||||
// now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device
|
||||
account, err := common.ParseAccountString(address)
|
||||
s.NoError(err, "can not get account from address")
|
||||
|
||||
account, key, err := keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
||||
s.NoError(err, "can not obtain decrypted account key")
|
||||
extChild2String := key.ExtendedKey.String()
|
||||
|
||||
s.NoError(keyStore.Delete(account, TestConfig.Account1.Password), "cannot remove account")
|
||||
|
||||
addressCheck, pubKeyCheck, err = s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
s.NoError(err, "recover account failed (for non-cached account)")
|
||||
s.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||
"incorrect account details recovered (for non-cached account)")
|
||||
|
||||
// make sure that extended key exists and is imported ok too
|
||||
_, key, err = keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch")
|
||||
|
||||
// make sure that calling import several times, just returns from cache (no error is expected)
|
||||
addressCheck, pubKeyCheck, err = s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
s.NoError(err, "recover account failed (for non-cached account)")
|
||||
s.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||
"incorrect account details recovered (for non-cached account)")
|
||||
|
||||
// time to login with recovered data
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
s.False(whisperService.HasKeyPair(pubKeyCheck), "identity already present in whisper")
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(addressCheck, TestConfig.Account1.Password))
|
||||
s.True(whisperService.HasKeyPair(pubKeyCheck), "identity not injected into whisper")
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestSelectAccount() {
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// test to see if the account was injected in whisper
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// create an account
|
||||
address1, pubKey1, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||
|
||||
// try selecting with wrong password
|
||||
err = s.Backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
s.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used")
|
||||
|
||||
err = s.Backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
s.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||
s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
|
||||
// we need to make sure that selected account is injected as identity into Whisper
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// create test accounts
|
||||
address1, pubKey1, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
address2, pubKey2, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||
|
||||
// make sure that no account is selected by default
|
||||
selectedAccount, err := s.Backend.AccountManager().SelectedAccount()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||
s.Nil(selectedAccount)
|
||||
|
||||
// select account
|
||||
err = s.Backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
s.EqualError(expectedErr, err.Error())
|
||||
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password))
|
||||
s.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
s.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||
s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||
|
||||
// stop node (and all of its sub-protocols)
|
||||
nodeConfig, err := s.Backend.NodeManager().NodeConfig()
|
||||
s.NoError(err)
|
||||
preservedNodeConfig := *nodeConfig
|
||||
nodeStoped, err := s.Backend.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStoped
|
||||
|
||||
// make sure that account is still selected
|
||||
selectedAccount, err = s.Backend.AccountManager().SelectedAccount()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedAccount)
|
||||
s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
|
||||
|
||||
// resume node
|
||||
nodeStarted, err := s.Backend.StartNode(&preservedNodeConfig)
|
||||
s.NoError(err)
|
||||
<-nodeStarted
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedAccount, err = s.Backend.AccountManager().SelectedAccount()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedAccount)
|
||||
s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
|
||||
|
||||
// make sure that Whisper gets identity re-injected
|
||||
whisperService = s.WhisperService()
|
||||
s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
// now restart node using RestartNode() method, and make sure that account is still available
|
||||
s.RestartTestNode()
|
||||
defer s.StopTestBackend()
|
||||
|
||||
whisperService = s.WhisperService()
|
||||
s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
// now logout, and make sure that on restart no account is selected (i.e. logout works properly)
|
||||
s.NoError(s.Backend.AccountManager().Logout())
|
||||
s.RestartTestNode()
|
||||
whisperService = s.WhisperService()
|
||||
s.False(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
selectedAccount, err = s.Backend.AccountManager().SelectedAccount()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
s.Nil(selectedAccount)
|
||||
}
|
|
@ -8,10 +8,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/api"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
|
@ -25,19 +25,13 @@ type APITestSuite struct {
|
|||
}
|
||||
|
||||
func (s *APITestSuite) SetupTest() {
|
||||
require := s.Require()
|
||||
statusAPI := api.NewStatusAPI()
|
||||
require.NotNil(statusAPI)
|
||||
require.IsType(&api.StatusAPI{}, statusAPI)
|
||||
s.api = statusAPI
|
||||
s.api = api.NewStatusAPI()
|
||||
s.NotNil(s.api)
|
||||
}
|
||||
|
||||
func (s *APITestSuite) TestCHTUpdate() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.api)
|
||||
|
||||
tmpDir, err := ioutil.TempDir(os.TempDir(), "cht-updates")
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
configJSON := `{
|
||||
|
@ -48,7 +42,7 @@ func (s *APITestSuite) TestCHTUpdate() {
|
|||
}`
|
||||
//nodeConfig, err := params.LoadNodeConfig(configJSON)
|
||||
_, err = params.LoadNodeConfig(configJSON)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
|
||||
// start node
|
||||
//nodeConfig.DevMode = true
|
||||
|
@ -58,18 +52,15 @@ func (s *APITestSuite) TestCHTUpdate() {
|
|||
}
|
||||
|
||||
func (s *APITestSuite) TestRaceConditions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.api)
|
||||
|
||||
cnt := 25
|
||||
progress := make(chan struct{}, cnt)
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig1, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig2, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||
|
||||
|
@ -92,12 +83,14 @@ func (s *APITestSuite) TestRaceConditions() {
|
|||
s.T().Logf("RestartNodeAsync(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("ResetChainDataAsync()")
|
||||
_, err := s.api.ResetChainDataAsync()
|
||||
s.T().Logf("ResetChainDataAsync(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
// TODO(adam): quarantined until it uses a different datadir
|
||||
// as otherwise it wipes out cached blockchain data.
|
||||
// func(config *params.NodeConfig) {
|
||||
// log.Info("ResetChainDataAsync()")
|
||||
// _, err := s.api.ResetChainDataAsync()
|
||||
// s.T().Logf("ResetChainDataAsync(), error: %v", err)
|
||||
// progress <- struct{}{}
|
||||
// },
|
||||
}
|
||||
|
||||
// increase StartNode()/StopNode() population
|
|
@ -0,0 +1,277 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestAPIBackendTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(APIBackendTestSuite))
|
||||
}
|
||||
|
||||
type APIBackendTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *APIBackendTestSuite) TestRaceConditions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.Backend)
|
||||
|
||||
cnt := 25
|
||||
progress := make(chan struct{}, cnt)
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
nodeConfig1, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeConfig2, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||
|
||||
var funcsToTest = []func(*params.NodeConfig){
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("StartNode()")
|
||||
_, err := s.Backend.StartNode(config)
|
||||
s.T().Logf("StartNode() for network: %d, error: %v", config.NetworkID, err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("StopNode()")
|
||||
_, err := s.Backend.StopNode()
|
||||
s.T().Logf("StopNode() for network: %d, error: %v", config.NetworkID, err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
// func(config *params.NodeConfig) {
|
||||
// log.Info("ResetChainData()")
|
||||
// _, err := s.Backend.ResetChainData()
|
||||
// s.T().Logf("ResetChainData(), error: %v", err)
|
||||
// progress <- struct{}{}
|
||||
// },
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("RestartNode()")
|
||||
_, err := s.Backend.RestartNode()
|
||||
s.T().Logf("RestartNode(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("NodeManager()")
|
||||
instance := s.Backend.NodeManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&node.NodeManager{}, instance)
|
||||
s.T().Logf("NodeManager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("AccountManager()")
|
||||
instance := s.Backend.AccountManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&account.Manager{}, instance)
|
||||
s.T().Logf("Manager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("JailManager()")
|
||||
instance := s.Backend.JailManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&jail.Jail{}, instance)
|
||||
s.T().Logf("JailManager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CreateAccount()")
|
||||
address, pubKey, mnemonic, err := s.Backend.AccountManager().CreateAccount("password")
|
||||
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v, mnemonic: %v)", err, address, pubKey, mnemonic)
|
||||
if err == nil {
|
||||
// SelectAccount
|
||||
log.Info("CreateAccount()")
|
||||
err = s.Backend.AccountManager().SelectAccount(address, "password")
|
||||
s.T().Logf("SelectAccount(%v, %v), error: %v", address, "password", err)
|
||||
|
||||
// CreateChildAccount
|
||||
log.Info("CreateChildAccount()")
|
||||
address, pubKey, err := s.Backend.AccountManager().CreateChildAccount(address, "password")
|
||||
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||
|
||||
// RecoverAccount
|
||||
log.Info("RecoverAccount()")
|
||||
address, pubKey, err = s.Backend.AccountManager().RecoverAccount("password", mnemonic)
|
||||
s.T().Logf("RecoverAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||
}
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("VerifyAccountPassword()")
|
||||
_, err := s.Backend.AccountManager().VerifyAccountPassword(config.KeyStoreDir, "0x0", "bar")
|
||||
s.T().Logf("VerifyAccountPassword(), err: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("Logout()")
|
||||
s.T().Logf("Logout(), result: %v", s.Backend.AccountManager().Logout())
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("IsNodeRunning()")
|
||||
s.T().Logf("IsNodeRunning(), result: %v", s.Backend.IsNodeRunning())
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CompleteTransaction()")
|
||||
_, err := s.Backend.CompleteTransaction("id", "password")
|
||||
s.T().Logf("CompleteTransaction(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("DiscardTransaction()")
|
||||
s.T().Logf("DiscardTransaction(), error: %v", s.Backend.DiscardTransaction("id"))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CompleteTransactions()")
|
||||
ids := []common.QueuedTxID{"id1", "id2"}
|
||||
s.T().Logf("CompleteTransactions(), result: %v", s.Backend.CompleteTransactions(ids, "password"))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("DiscardTransactions()")
|
||||
ids := []common.QueuedTxID{"id1", "id2"}
|
||||
s.T().Logf("DiscardTransactions(), result: %v", s.Backend.DiscardTransactions(ids))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
}
|
||||
|
||||
// increase StartNode()/StopNode() population
|
||||
for i := 0; i < 5; i++ {
|
||||
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
|
||||
}
|
||||
|
||||
for i := 0; i < cnt; i++ {
|
||||
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))]
|
||||
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))]
|
||||
|
||||
if rnd.Intn(100) > 75 { // introduce random delays
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
go randFunc(randConfig)
|
||||
}
|
||||
|
||||
for range progress {
|
||||
cnt -= 1
|
||||
if cnt <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // so that we see some logs
|
||||
nodeStopped, _ := s.Backend.StopNode() // just in case we have a node running
|
||||
if nodeStopped != nil {
|
||||
<-nodeStopped
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *APIBackendTestSuite) TestNetworkSwitching() {
|
||||
// get Ropsten config
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
nodeStarted, err := s.Backend.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
|
||||
<-nodeStarted // wait till node is started
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
|
||||
firstHash, err := e2e.FirstBlockHash(s.Backend.NodeManager())
|
||||
s.NoError(err)
|
||||
s.Equal("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", firstHash)
|
||||
|
||||
// now stop node, and make sure that a new node, on different network can be started
|
||||
nodeStopped, err := s.Backend.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
|
||||
// start new node with completely different config
|
||||
nodeConfig, err = e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
nodeStarted, err = s.Backend.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
|
||||
<-nodeStarted
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
|
||||
// make sure we are on another network indeed
|
||||
firstHash, err = e2e.FirstBlockHash(s.Backend.NodeManager())
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
|
||||
nodeStopped, err = s.Backend.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *APIBackendTestSuite) TestResetChainData() {
|
||||
s.T().Skip()
|
||||
|
||||
require := s.Require()
|
||||
require.NotNil(s.Backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(2 * time.Second) // allow to sync for some time
|
||||
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
nodeReady, err := s.Backend.ResetChainData()
|
||||
require.NoError(err)
|
||||
<-nodeReady
|
||||
s.True(s.Backend.IsNodeRunning()) // new node, with previous config should be running
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
firstHash, err := e2e.FirstBlockHash(s.Backend.NodeManager())
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *APIBackendTestSuite) TestRestartNode() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.Backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
firstHash, err := e2e.FirstBlockHash(s.Backend.NodeManager())
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
nodeRestarted, err := s.Backend.RestartNode()
|
||||
require.NoError(err)
|
||||
<-nodeRestarted
|
||||
s.True(s.Backend.IsNodeRunning()) // new node, with previous config should be running
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
firstHash, err = e2e.FirstBlockHash(s.Backend.NodeManager())
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
package jail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestJailRPCTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(JailRPCTestSuite))
|
||||
}
|
||||
|
||||
type JailRPCTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
|
||||
jail common.JailManager
|
||||
}
|
||||
|
||||
func (s *JailRPCTestSuite) SetupTest() {
|
||||
s.BackendTestSuite.SetupTest()
|
||||
s.jail = s.Backend.JailManager()
|
||||
s.NotNil(s.jail)
|
||||
}
|
||||
|
||||
func (s *JailRPCTestSuite) TestJailRPCSend() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
s.jail.BaseJS(baseStatusJSCode)
|
||||
s.jail.Parse(testChatID, ``)
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
s.NotNil(cell)
|
||||
|
||||
// internally (since we replaced `web3.send` with `jail.Send`)
|
||||
// all requests to web3 are forwarded to `jail.Send`
|
||||
_, err = cell.Run(`
|
||||
var balance = web3.eth.getBalance("` + TestConfig.Account1.Address + `");
|
||||
var sendResult = web3.fromWei(balance, "ether")
|
||||
`)
|
||||
s.NoError(err)
|
||||
|
||||
value, err := cell.Get("sendResult")
|
||||
s.NoError(err, "cannot obtain result of balance check operation")
|
||||
|
||||
balance, err := value.ToFloat()
|
||||
s.NoError(err)
|
||||
|
||||
s.T().Logf("Balance of %.2f ETH found on '%s' account", balance, TestConfig.Account1.Address)
|
||||
s.False(balance < 100, "wrong balance (there should be lots of test Ether on that account)")
|
||||
}
|
||||
|
||||
func (s *JailRPCTestSuite) TestIsConnected() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
s.jail.Parse(testChatID, "")
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = web3.isConnected();
|
||||
responseValue = JSON.stringify(responseValue);
|
||||
`)
|
||||
s.NoError(err)
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
s.NoError(err, "cannot obtain result of isConnected()")
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
s.NoError(err, "cannot parse result")
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","result":true}`
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
// regression test: eth_getTransactionReceipt with invalid transaction hash should return null
|
||||
func (s *JailRPCTestSuite) TestRegressionGetTransactionReceipt() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
rpcClient := s.Backend.NodeManager().RPCClient()
|
||||
s.NotNil(rpcClient)
|
||||
|
||||
// note: transaction hash is assumed to be invalid
|
||||
got := rpcClient.CallRaw(`{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0xbbebf28d0a3a3cbb38e6053a5b21f08f82c62b0c145a17b1c4313cac3f68ae7c"],"id":7}`)
|
||||
expected := `{"jsonrpc":"2.0","id":7,"result":null}`
|
||||
s.Equal(expected, got)
|
||||
}
|
||||
|
||||
func (s *JailRPCTestSuite) TestContractDeployment() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// Allow to sync, otherwise you'll get "Nonce too low."
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
s.jail.Parse(testChatID, "")
|
||||
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
var err error
|
||||
|
||||
err = json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||
s.NoError(err, "cannot unmarshal JSON: %s", jsonEvent)
|
||||
|
||||
if envelope.Type == txqueue.EventTransactionQueued {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
s.T().Logf("transaction queued and will be completed shortly, id: %v", event["id"])
|
||||
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
txID := event["id"].(string)
|
||||
txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
if s.NoError(err, event["id"]) {
|
||||
s.T().Logf("contract transaction complete, URL: %s", "https://ropsten.etherscan.io/tx/"+txHash.Hex())
|
||||
}
|
||||
|
||||
close(completeQueuedTransaction)
|
||||
}
|
||||
})
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = null;
|
||||
var errorValue = null;
|
||||
var testContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"a","type":"int256"}],"name":"double","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"}]);
|
||||
var test = testContract.new(
|
||||
{
|
||||
from: '` + TestConfig.Account1.Address + `',
|
||||
data: '0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029',
|
||||
gas: '` + strconv.Itoa(params.DefaultGas) + `'
|
||||
}, function (e, contract) {
|
||||
// NOTE: The callback will fire twice!
|
||||
if (e) {
|
||||
errorValue = e;
|
||||
return;
|
||||
}
|
||||
// Once the contract has the transactionHash property set and once its deployed on an address.
|
||||
if (!contract.address) {
|
||||
responseValue = contract.transactionHash;
|
||||
}
|
||||
})
|
||||
`)
|
||||
s.NoError(err)
|
||||
|
||||
select {
|
||||
case <-completeQueuedTransaction:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("test timed out")
|
||||
}
|
||||
|
||||
// Wait until callback is fired and `responseValue` is set. Hacky but simple.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
errorValue, err := cell.Get("errorValue")
|
||||
s.NoError(err)
|
||||
s.Equal("null", errorValue.String())
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
s.NoError(err)
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
s.NoError(err)
|
||||
|
||||
expectedResponse := txHash.Hex()
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailRPCTestSuite) TestJailVMPersistence() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot select account: %v", TestConfig.Account1.Address)
|
||||
|
||||
type testCase struct {
|
||||
command string
|
||||
params string
|
||||
validator func(response string) error
|
||||
}
|
||||
var testCases = []testCase{
|
||||
{
|
||||
`["sendTestTx"]`,
|
||||
`{"amount": "0.000001", "from": "` + TestConfig.Account1.Address + `"}`,
|
||||
func(response string) error {
|
||||
if strings.Contains(response, "error") {
|
||||
return fmt.Errorf("unexpected response: %v", response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["sendTestTx"]`,
|
||||
`{"amount": "0.000002", "from": "` + TestConfig.Account1.Address + `"}`,
|
||||
func(response string) error {
|
||||
if strings.Contains(response, "error") {
|
||||
return fmt.Errorf("unexpected response: %v", response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["ping"]`,
|
||||
`{"pong": "Ping1", "amount": 0.42}`,
|
||||
func(response string) error {
|
||||
expectedResponse := `{"result": "Ping1"}`
|
||||
if response != expectedResponse {
|
||||
return fmt.Errorf("unexpected response, expected: %v, got: %v", expectedResponse, response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["ping"]`,
|
||||
`{"pong": "Ping2", "amount": 0.42}`,
|
||||
func(response string) error {
|
||||
expectedResponse := `{"result": "Ping2"}`
|
||||
if response != expectedResponse {
|
||||
return fmt.Errorf("unexpected response, expected: %v, got: %v", expectedResponse, response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
jail := s.Backend.JailManager()
|
||||
jail.BaseJS(baseStatusJSCode)
|
||||
|
||||
parseResult := jail.Parse(testChatID, `
|
||||
var total = 0;
|
||||
_status_catalog['ping'] = function(params) {
|
||||
total += Number(params.amount);
|
||||
return params.pong;
|
||||
}
|
||||
|
||||
_status_catalog['sendTestTx'] = function(params) {
|
||||
var amount = params.amount;
|
||||
var transaction = {
|
||||
"from": params.from,
|
||||
"to": "`+TestConfig.Account2.Address+`",
|
||||
"value": web3.toWei(amount, "ether")
|
||||
};
|
||||
web3.eth.sendTransaction(transaction, function (error, result) {
|
||||
if(!error) {
|
||||
total += Number(amount);
|
||||
}
|
||||
});
|
||||
}
|
||||
`)
|
||||
s.NotContains(parseResult, "error", "further will fail if initial parsing failed")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||
s.T().Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||
return
|
||||
}
|
||||
if envelope.Type == txqueue.EventTransactionQueued {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
s.T().Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
||||
|
||||
//var txHash common.Hash
|
||||
txID := event["id"].(string)
|
||||
txHash, err := s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot complete queued transaction[%v]: %v", event["id"], err)
|
||||
|
||||
s.T().Logf("Transaction complete: https://ropsten.etherscan.io/tx/%s", txHash.Hex())
|
||||
}
|
||||
})
|
||||
|
||||
// run commands concurrently
|
||||
for _, tc := range testCases {
|
||||
wg.Add(1)
|
||||
go func(tc testCase) {
|
||||
defer wg.Done() // ensure we don't forget it
|
||||
|
||||
s.T().Logf("CALL START: %v %v", tc.command, tc.params)
|
||||
response := jail.Call(testChatID, tc.command, tc.params)
|
||||
if err := tc.validator(response); err != nil {
|
||||
s.T().Errorf("failed test validation: %v, err: %v", tc.command, err)
|
||||
}
|
||||
s.T().Logf("CALL END: %v %v", tc.command, tc.params)
|
||||
}(tc)
|
||||
}
|
||||
|
||||
finishTestCases := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(finishTestCases)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-finishTestCases:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("some tests failed to finish in time")
|
||||
}
|
||||
|
||||
// Wait till eth_sendTransaction callbacks have been executed.
|
||||
// FIXME(tiabc): more reliable means of testing that.
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Validate total.
|
||||
cell, err := jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
|
||||
totalOtto, err := cell.Get("total")
|
||||
s.NoError(err)
|
||||
|
||||
total, err := totalOtto.ToFloat()
|
||||
s.NoError(err)
|
||||
|
||||
s.T().Log(total)
|
||||
s.InDelta(0.840003, total, 0.0000001)
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package jail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
"github.com/status-im/status-go/static"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
const (
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
var (
|
||||
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
|
||||
txJSCode = string(static.MustAsset("testdata/jail/tx-send/tx-send.js"))
|
||||
)
|
||||
|
||||
func TestJailTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(JailTestSuite))
|
||||
}
|
||||
|
||||
type JailTestSuite struct {
|
||||
suite.Suite
|
||||
jail common.JailManager
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) SetupTest() {
|
||||
s.jail = jail.New(nil)
|
||||
s.NotNil(s.jail)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestInit() {
|
||||
errorWrapper := func(err error) string {
|
||||
return `{"error":"` + err.Error() + `"}`
|
||||
}
|
||||
|
||||
// get cell VM w/o defining cell first
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
|
||||
s.EqualError(err, "cell["+testChatID+"] doesn't exist")
|
||||
s.Nil(cell)
|
||||
|
||||
// create VM (w/o properly initializing base JS script)
|
||||
err = errors.New("ReferenceError: '_status_catalog' is not defined")
|
||||
s.Equal(errorWrapper(err), s.jail.Parse(testChatID, ``))
|
||||
err = errors.New("ReferenceError: 'call' is not defined")
|
||||
s.Equal(errorWrapper(err), s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`))
|
||||
|
||||
// get existing cell (even though we got errors, cell was still created)
|
||||
cell, err = s.jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
s.NotNil(cell)
|
||||
|
||||
statusJS := baseStatusJSCode + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
s.jail.BaseJS(statusJS)
|
||||
|
||||
// now no error should occur
|
||||
response := s.jail.Parse(testChatID, ``)
|
||||
expectedResponse := `{"result": {"commands":{},"responses":{}}}`
|
||||
s.Equal(expectedResponse, response)
|
||||
|
||||
// make sure that Call succeeds even w/o running node
|
||||
response = s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse = `{"result": 144}`
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestParse() {
|
||||
extraCode := `
|
||||
var _status_catalog = {
|
||||
foo: 'bar'
|
||||
};`
|
||||
response := s.jail.Parse("newChat", extraCode)
|
||||
expectedResponse := `{"result": {"foo":"bar"}}`
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestFunctionCall() {
|
||||
// load Status JS and add test command to it
|
||||
statusJS := baseStatusJSCode + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
s.jail.Parse(testChatID, statusJS)
|
||||
|
||||
// call with wrong chat id
|
||||
response := s.jail.Call("chatIDNonExistent", "", "")
|
||||
expectedError := `{"error":"cell[chatIDNonExistent] doesn't exist"}`
|
||||
s.Equal(expectedError, response)
|
||||
|
||||
// call extraFunc()
|
||||
response = s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse := `{"result": 144}`
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestEventSignal() {
|
||||
s.jail.Parse(testChatID, "")
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
s.NoError(err)
|
||||
|
||||
testData := "foobar"
|
||||
opCompletedSuccessfully := make(chan struct{}, 1)
|
||||
|
||||
// replace transaction notification handler
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
err := json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||
s.NoError(err)
|
||||
|
||||
if envelope.Type == jail.EventSignal {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
chatID, ok := event["chat_id"].(string)
|
||||
s.True(ok, "chat id is required, but not found")
|
||||
s.Equal(testChatID, chatID, "incorrect chat ID")
|
||||
|
||||
actualData, ok := event["data"].(string)
|
||||
s.True(ok, "data field is required, but not found")
|
||||
s.Equal(testData, actualData, "incorrect data")
|
||||
|
||||
close(opCompletedSuccessfully)
|
||||
}
|
||||
})
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = statusSignals.sendSignal("` + testData + `");
|
||||
responseValue = JSON.stringify(responseValue);
|
||||
`)
|
||||
s.NoError(err)
|
||||
|
||||
// make sure that signal is sent (and its parameters are correct)
|
||||
select {
|
||||
case <-opCompletedSuccessfully:
|
||||
// pass
|
||||
case <-time.After(5 * time.Second):
|
||||
s.FailNow("test timed out")
|
||||
}
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
s.NoError(err, "cannot obtain result of localStorage.set()")
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
s.NoError(err, "cannot parse result")
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","result":true}`
|
||||
s.Equal(expectedResponse, response)
|
||||
}
|
|
@ -2,7 +2,6 @@ package node_test
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -13,11 +12,11 @@ import (
|
|||
gethnode "github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
|
@ -26,20 +25,15 @@ func TestManagerTestSuite(t *testing.T) {
|
|||
}
|
||||
|
||||
type ManagerTestSuite struct {
|
||||
BaseTestSuite
|
||||
e2e.NodeManagerTestSuite
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) SetupTest() {
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
s.Require().NotNil(s.NodeManager)
|
||||
s.Require().IsType(&node.NodeManager{}, s.NodeManager)
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestReferences() {
|
||||
s.Require().NotNil(s.NodeManager)
|
||||
|
||||
// test for nil values of nodeManager
|
||||
var noNodeTests = []struct {
|
||||
func (s *ManagerTestSuite) TestReferencesWithoutStartedNode() {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
initFn func() (interface{}, error)
|
||||
expectedErr error
|
||||
|
@ -122,17 +116,19 @@ func (s *ManagerTestSuite) TestReferences() {
|
|||
nil,
|
||||
},
|
||||
}
|
||||
for _, testCase := range noNodeTests {
|
||||
s.T().Log(testCase.name)
|
||||
obj, err := testCase.initFn()
|
||||
for _, tc := range testCases {
|
||||
s.T().Log(tc.name)
|
||||
obj, err := tc.initFn()
|
||||
s.Nil(obj)
|
||||
s.Equal(testCase.expectedErr, err)
|
||||
s.Equal(tc.expectedErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
// test with node fully started
|
||||
func (s *ManagerTestSuite) TestReferencesWithStartedNode() {
|
||||
s.StartTestNode(params.RinkebyNetworkID)
|
||||
defer s.StopTestNode()
|
||||
var nodeReadyTestCases = []struct {
|
||||
|
||||
var testCases = []struct {
|
||||
name string
|
||||
initFn func() (interface{}, error)
|
||||
expectedType interface{}
|
||||
|
@ -187,142 +183,163 @@ func (s *ManagerTestSuite) TestReferences() {
|
|||
&rpc.Client{},
|
||||
},
|
||||
}
|
||||
for _, testCase := range nodeReadyTestCases {
|
||||
obj, err := testCase.initFn()
|
||||
s.T().Log(testCase.name)
|
||||
for _, tc := range testCases {
|
||||
s.T().Log(tc.name)
|
||||
obj, err := tc.initFn()
|
||||
s.NoError(err)
|
||||
s.NotNil(obj)
|
||||
s.IsType(testCase.expectedType, obj)
|
||||
s.IsType(tc.expectedType, obj)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestNodeStartStop() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
// try stopping non-started node
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
_, err = s.NodeManager.StopNode()
|
||||
require.EqualError(err, node.ErrNoRunningNode.Error())
|
||||
s.Equal(err, node.ErrNoRunningNode)
|
||||
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
// start node
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
<-nodeStarted // wait till node is started
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
s.NoError(err)
|
||||
// wait till node is started
|
||||
<-nodeStarted
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// try starting another node (w/o stopping the previously started node)
|
||||
_, err = s.NodeManager.StartNode(nodeConfig)
|
||||
require.EqualError(err, node.ErrNodeExists.Error())
|
||||
s.Equal(err, node.ErrNodeExists)
|
||||
|
||||
// now stop node, and make sure that a new node, on different network can be started
|
||||
// now stop node
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// start new node with exactly the same config
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
nodeStarted, err = s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
s.NoError(err)
|
||||
// wait till node is started
|
||||
<-nodeStarted
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
s.StopTestNode()
|
||||
// finally stop the node
|
||||
nodeStopped, err = s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestNetworkSwitching() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
// get Ropsten config
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
// wait till node is started
|
||||
<-nodeStarted
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
<-nodeStarted // wait till node is started
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
FirstBlockHash(require, s.NodeManager, "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
firstHash, err := e2e.FirstBlockHash(s.NodeManager)
|
||||
s.NoError(err)
|
||||
s.Equal("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", firstHash)
|
||||
|
||||
// now stop node, and make sure that a new node, on different network can be started
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// start new node with completely different config
|
||||
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
nodeConfig, err = e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
nodeStarted, err = s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
s.NoError(err)
|
||||
// wait till node is started
|
||||
<-nodeStarted
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// make sure we are on another network indeed
|
||||
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
firstHash, err = e2e.FirstBlockHash(s.NodeManager)
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
|
||||
s.StopTestNode()
|
||||
nodeStopped, err = s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestStartNodeWithUpstreamEnabled() {
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfig.UpstreamConfig.Enabled = true
|
||||
nodeConfig.UpstreamConfig.URL = "https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
<-nodeStarted
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
}
|
||||
|
||||
// TODO(adam): fix this test to not use a different directory for blockchain data
|
||||
func (s *ManagerTestSuite) TestResetChainData() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
s.T().Skip()
|
||||
|
||||
s.StartTestNode(params.RinkebyNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
time.Sleep(2 * time.Second) // allow to sync for some time
|
||||
// allow to sync for some time
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
// reset chain data
|
||||
nodeReady, err := s.NodeManager.ResetChainData()
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
// new node, with previous config should be running
|
||||
<-nodeReady
|
||||
s.True(s.NodeManager.IsNodeRunning()) // new node, with previous config should be running
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
firstHash, err := e2e.FirstBlockHash(s.NodeManager)
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestRestartNode() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
s.StartTestNode(params.RinkebyNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
nodeReady, err := s.NodeManager.RestartNode()
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
// new node, with previous config should be running
|
||||
<-nodeReady
|
||||
s.True(s.NodeManager.IsNodeRunning()) // new node, with previous config should be running
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
FirstBlockHash(require, s.NodeManager, "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
firstHash, err := e2e.FirstBlockHash(s.NodeManager)
|
||||
s.NoError(err)
|
||||
s.Equal("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177", firstHash)
|
||||
}
|
||||
|
||||
// TODO(adam): race conditions should be tested with -race flag and unit tests, if possible.
|
||||
// Research if it's possible to do the same with unit tests.
|
||||
func (s *ManagerTestSuite) TestRaceConditions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
cnt := 25
|
||||
progress := make(chan struct{}, cnt)
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig1, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig2, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||
|
||||
|
@ -355,12 +372,14 @@ func (s *ManagerTestSuite) TestRaceConditions() {
|
|||
s.T().Logf("PopulateStaticPeers(), error: %v", s.NodeManager.PopulateStaticPeers())
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("ResetChainData()")
|
||||
_, err := s.NodeManager.ResetChainData()
|
||||
s.T().Logf("ResetChainData(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
// TODO(adam): quarantined until it uses a different datadir
|
||||
// as otherwise it wipes out cached blockchain data.
|
||||
// func(config *params.NodeConfig) {
|
||||
// log.Info("ResetChainData()")
|
||||
// _, err := s.NodeManager.ResetChainData()
|
||||
// s.T().Logf("ResetChainData(), error: %v", err)
|
||||
// progress <- struct{}{}
|
||||
// },
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("RestartNode()")
|
||||
_, err := s.NodeManager.RestartNode()
|
||||
|
@ -434,48 +453,53 @@ func (s *ManagerTestSuite) TestRaceConditions() {
|
|||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestNodeStartCrash() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
// start node outside the manager (on the same port), so that manager node.Start() method fails
|
||||
outsideNode, err := node.MakeNode(nodeConfig)
|
||||
require.NoError(outsideNode.Start())
|
||||
|
||||
// let's listen for node.crashed signal
|
||||
signalReceived := false
|
||||
signalReceived := make(chan struct{})
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
log.Info("Notification Received", "event", jsonEvent)
|
||||
var envelope signal.Envelope
|
||||
err := json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||
s.NoError(err, fmt.Sprintf("cannot unmarshal JSON: %s", jsonEvent))
|
||||
s.NoError(err)
|
||||
|
||||
if envelope.Type == signal.EventNodeCrashed {
|
||||
signalReceived = true
|
||||
close(signalReceived)
|
||||
}
|
||||
})
|
||||
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
// start node outside the manager (on the same port), so that manager node.Start() method fails
|
||||
outsideNode, err := node.MakeNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
err = outsideNode.Start()
|
||||
s.NoError(err)
|
||||
|
||||
// now try starting using node manager
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err) // no error is thrown, as node is started in separate routine
|
||||
<-nodeStarted // no deadlock either, as manager should close the channel on error
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
s.NoError(err) // no error is thrown, as node is started in separate routine
|
||||
<-nodeStarted // no deadlock either, as manager should close the channel on error
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
|
||||
time.Sleep(2 * time.Second) // allow signal to propagate
|
||||
require.True(signalReceived, "node crash signal is expected")
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
s.FailNow("timed out waiting for signal")
|
||||
case <-signalReceived:
|
||||
}
|
||||
|
||||
// stop outside node, and re-try
|
||||
require.NoError(outsideNode.Stop())
|
||||
signalReceived = false
|
||||
err = outsideNode.Stop()
|
||||
s.NoError(err)
|
||||
signalReceived = make(chan struct{})
|
||||
nodeStarted, err = s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err) // again, no error
|
||||
<-nodeStarted // no deadlock, and no signal this time, manager should be able to start node
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
s.NoError(err) // again, no error
|
||||
<-nodeStarted // no deadlock, and no signal this time, manager should be able to start node
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
|
||||
time.Sleep(2 * time.Second) // allow signal to propagate
|
||||
require.False(signalReceived, "node should start w/o crash signal")
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-signalReceived:
|
||||
s.FailNow("signal should not be received")
|
||||
}
|
||||
|
||||
// cleanup
|
||||
s.NodeManager.StopNode()
|
|
@ -0,0 +1,63 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type RPCClientTestSuite struct {
|
||||
e2e.NodeManagerTestSuite
|
||||
}
|
||||
|
||||
func TestRPCClientTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(RPCClientTestSuite))
|
||||
}
|
||||
|
||||
func (s *RPCClientTestSuite) SetupTest() {
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
s.NotNil(s.NodeManager)
|
||||
}
|
||||
|
||||
func (s *RPCClientTestSuite) TestNewClient() {
|
||||
config, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(config)
|
||||
s.NoError(err)
|
||||
<-nodeStarted
|
||||
|
||||
node, err := s.NodeManager.Node()
|
||||
s.NoError(err)
|
||||
|
||||
// upstream disabled, local node ok
|
||||
s.False(config.UpstreamConfig.Enabled)
|
||||
_, err = rpc.NewClient(node, config.UpstreamConfig)
|
||||
s.NoError(err)
|
||||
|
||||
// upstream enabled with incorrect URL, local node ok
|
||||
upstreamBad := config.UpstreamConfig
|
||||
upstreamBad.Enabled = true
|
||||
upstreamBad.URL = "///__httphh://///incorrect_urlxxx"
|
||||
_, err = rpc.NewClient(node, upstreamBad)
|
||||
s.Error(err)
|
||||
|
||||
// upstream enabled with correct URL, local node ok
|
||||
upstreamGood := config.UpstreamConfig
|
||||
upstreamGood.Enabled = true
|
||||
upstreamGood.URL = "http://example.com/rpc"
|
||||
_, err = rpc.NewClient(node, upstreamGood)
|
||||
s.NoError(err)
|
||||
|
||||
// upstream disabled, local node failed (stopped)
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
|
||||
_, err = rpc.NewClient(node, config.UpstreamConfig)
|
||||
s.Error(err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rpc_test
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -9,10 +9,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
|
@ -36,100 +35,18 @@ func TestRPCTestSuite(t *testing.T) {
|
|||
}
|
||||
|
||||
type RPCTestSuite struct {
|
||||
BaseTestSuite
|
||||
e2e.NodeManagerTestSuite
|
||||
}
|
||||
|
||||
func (s *RPCTestSuite) SetupTest() {
|
||||
require := s.Require()
|
||||
|
||||
nodeManager := node.NewNodeManager()
|
||||
require.NotNil(nodeManager)
|
||||
s.NodeManager = nodeManager
|
||||
}
|
||||
|
||||
func (s *RPCTestSuite) TestNewClient() {
|
||||
require := s.Require()
|
||||
|
||||
config, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(config)
|
||||
require.NoError(err)
|
||||
require.NotNil(config)
|
||||
|
||||
<-nodeStarted
|
||||
|
||||
node, err := s.NodeManager.Node()
|
||||
require.NoError(err)
|
||||
|
||||
// upstream disabled, local node ok
|
||||
_, err = rpc.NewClient(node, config.UpstreamConfig)
|
||||
require.NoError(err)
|
||||
|
||||
// upstream enabled with incorrect URL, local node ok
|
||||
upstreamBad := config.UpstreamConfig
|
||||
upstreamBad.Enabled = true
|
||||
upstreamBad.URL = "///__httphh://///incorrect_urlxxx"
|
||||
_, err = rpc.NewClient(node, upstreamBad)
|
||||
require.NotNil(err)
|
||||
|
||||
// upstream enabled with correct URL, local node ok
|
||||
upstreamGood := config.UpstreamConfig
|
||||
upstreamGood.Enabled = true
|
||||
upstreamGood.URL = "http://example.com/rpc"
|
||||
_, err = rpc.NewClient(node, upstreamGood)
|
||||
require.Nil(err)
|
||||
|
||||
// upstream disabled, local node failed (stopped)
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
require.NoError(err)
|
||||
|
||||
<-nodeStopped
|
||||
|
||||
_, err = rpc.NewClient(node, config.UpstreamConfig)
|
||||
require.NotNil(err)
|
||||
}
|
||||
|
||||
func (s *RPCTestSuite) TestRPCClientHandler() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestNode(params.RopstenNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
rpcClient := s.NodeManager.RPCClient()
|
||||
require.NotNil(rpcClient)
|
||||
|
||||
rpcClient.RegisterHandler("eth_sendTransaction", func(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
return map[string]interface{}{"done": true}, nil
|
||||
})
|
||||
|
||||
response := rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id":10,
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": "` + TestConfig.Account1.Address + `",
|
||||
"to": "` + TestConfig.Account2.Address + `",
|
||||
"value": "0x200",
|
||||
"nonce": "0x100",
|
||||
"data": "` + hexutil.Encode([]byte("Will-power")) + `",
|
||||
"gasPrice": "0x4a817c800",
|
||||
"gasLimit": "0x5208",
|
||||
"chainId": 3391
|
||||
}]
|
||||
}`)
|
||||
require.Equal(`{"jsonrpc":"2.0","id":10,"result":{"done":true}}`, response)
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
s.NotNil(s.NodeManager)
|
||||
}
|
||||
|
||||
func (s *RPCTestSuite) TestCallRPC() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
for _, upstreamEnabled := range []bool{false, true} {
|
||||
s.T().Logf("TestCallRPC with upstream: %t", upstreamEnabled)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfig.IPCEnabled = false
|
||||
nodeConfig.WSEnabled = false
|
||||
|
@ -141,12 +58,11 @@ func (s *RPCTestSuite) TestCallRPC() {
|
|||
}
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
s.NoError(err)
|
||||
<-nodeStarted
|
||||
|
||||
rpcClient := s.NodeManager.RPCClient()
|
||||
require.NotNil(rpcClient)
|
||||
s.NotNil(rpcClient)
|
||||
|
||||
type rpcCall struct {
|
||||
inputJSON string
|
||||
|
@ -208,47 +124,51 @@ func (s *RPCTestSuite) TestCallRPC() {
|
|||
|
||||
select {
|
||||
case <-time.After(time.Second * 30):
|
||||
s.NodeManager.StopNode()
|
||||
s.FailNow("test timed out")
|
||||
s.Fail("test timed out")
|
||||
case <-done:
|
||||
s.NodeManager.StopNode()
|
||||
}
|
||||
|
||||
stoppedNode, err := s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-stoppedNode
|
||||
}
|
||||
}
|
||||
|
||||
// TestCallRawResult checks if returned response is a valid JSON-RPC response.
|
||||
func (s *RPCTestSuite) TestCallRawResult() {
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
nodeConfig, err := e2e.MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
defer s.NodeManager.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
|
||||
client := s.NodeManager.RPCClient()
|
||||
s.NotNil(client)
|
||||
|
||||
jsonResult := client.CallRaw(`{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`)
|
||||
s.Equal(`{"jsonrpc":"2.0","id":67,"result":"5.0"}`, jsonResult)
|
||||
|
||||
s.NodeManager.StopNode()
|
||||
}
|
||||
|
||||
// TestCallContextResult checks if result passed to CallContext
|
||||
// is set accordingly to its underlying memory layout.
|
||||
func (s *RPCTestSuite) TestCallContextResult() {
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
defer s.NodeManager.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
s.StartTestNode(
|
||||
params.RopstenNetworkID,
|
||||
e2e.WithUpstream("https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"),
|
||||
)
|
||||
defer s.StopTestNode()
|
||||
|
||||
client := s.NodeManager.RPCClient()
|
||||
s.NotNil(client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
var blockNumber hexutil.Uint
|
||||
err = client.CallContext(context.Background(), &blockNumber, "eth_blockNumber")
|
||||
err := client.CallContext(ctx, &blockNumber, "eth_blockNumber")
|
||||
s.NoError(err)
|
||||
s.True(blockNumber > 0, "blockNumber should be higher than 0")
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/api"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// NodeManagerTestSuite defines a test suit with NodeManager.
|
||||
type NodeManagerTestSuite struct {
|
||||
suite.Suite
|
||||
NodeManager common.NodeManager
|
||||
}
|
||||
|
||||
// StartTestNode initiazes a NodeManager instances with configuration retrieved
|
||||
// from the test config.
|
||||
func (s *NodeManagerTestSuite) StartTestNode(networkID int, opts ...TestNodeOption) {
|
||||
nodeConfig, err := MakeTestNodeConfig(networkID)
|
||||
s.NoError(err)
|
||||
|
||||
// Apply any options altering node config.
|
||||
for i := range opts {
|
||||
opts[i](nodeConfig)
|
||||
}
|
||||
|
||||
// import account keys
|
||||
s.NoError(importTestAccouns(nodeConfig.KeyStoreDir))
|
||||
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
s.NotNil(nodeStarted)
|
||||
<-nodeStarted
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
}
|
||||
|
||||
// StopTestNode attempts to stop initialized NodeManager.
|
||||
func (s *NodeManagerTestSuite) StopTestNode() {
|
||||
s.NotNil(s.NodeManager)
|
||||
s.True(s.NodeManager.IsNodeRunning())
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
s.NoError(err)
|
||||
<-nodeStopped
|
||||
s.False(s.NodeManager.IsNodeRunning())
|
||||
}
|
||||
|
||||
// BackendTestSuite is a test suite with api.StatusBackend initialized
|
||||
// and a few utility methods to start and stop node or get various services.
|
||||
type BackendTestSuite struct {
|
||||
suite.Suite
|
||||
Backend *api.StatusBackend
|
||||
}
|
||||
|
||||
// SetupTest initializes Backend.
|
||||
func (s *BackendTestSuite) SetupTest() {
|
||||
s.Backend = api.NewStatusBackend()
|
||||
s.NotNil(s.Backend)
|
||||
}
|
||||
|
||||
// TearDownTest cleans up the packages state.
|
||||
func (s *BackendTestSuite) TearDownTest() {
|
||||
signal.ResetDefaultNodeNotificationHandler()
|
||||
}
|
||||
|
||||
// StartTestBackend imports some keys and starts a node.
|
||||
func (s *BackendTestSuite) StartTestBackend(networkID int, opts ...TestNodeOption) {
|
||||
nodeConfig, err := MakeTestNodeConfig(networkID)
|
||||
s.NoError(err)
|
||||
|
||||
// Apply any options altering node config.
|
||||
for i := range opts {
|
||||
opts[i](nodeConfig)
|
||||
}
|
||||
|
||||
// import account keys
|
||||
s.NoError(importTestAccouns(nodeConfig.KeyStoreDir))
|
||||
|
||||
// start node
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
nodeStarted, err := s.Backend.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
<-nodeStarted
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
// StopTestBackend stops the node.
|
||||
func (s *BackendTestSuite) StopTestBackend() {
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
backendStopped, err := s.Backend.StopNode()
|
||||
s.NoError(err)
|
||||
<-backendStopped
|
||||
s.False(s.Backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
// RestartTestNode restarts a currently running node.
|
||||
func (s *BackendTestSuite) RestartTestNode() {
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
nodeRestarted, err := s.Backend.RestartNode()
|
||||
s.NoError(err)
|
||||
<-nodeRestarted
|
||||
s.True(s.Backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
// WhisperService returns a reference to the Whisper service.
|
||||
func (s *BackendTestSuite) WhisperService() *whisper.Whisper {
|
||||
whisperService, err := s.Backend.NodeManager().WhisperService()
|
||||
s.NoError(err)
|
||||
s.NotNil(whisperService)
|
||||
|
||||
return whisperService
|
||||
}
|
||||
|
||||
// LightEthereumService returns a reference to the LES service.
|
||||
func (s *BackendTestSuite) LightEthereumService() *les.LightEthereum {
|
||||
lightEthereum, err := s.Backend.NodeManager().LightEthereumService()
|
||||
s.NoError(err)
|
||||
s.NotNil(lightEthereum)
|
||||
|
||||
return lightEthereum
|
||||
}
|
||||
|
||||
// TxQueueManager returns a reference to the TxQueueManager.
|
||||
func (s *BackendTestSuite) TxQueueManager() common.TxQueueManager {
|
||||
return s.Backend.TxQueueManager()
|
||||
}
|
||||
|
||||
func importTestAccouns(keyStoreDir string) (err error) {
|
||||
err = common.ImportTestAccount(keyStoreDir, "test-account1.pk")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return common.ImportTestAccount(keyStoreDir, "test-account2.pk")
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
)
|
||||
|
||||
// TestNodeOption is a callback passed to StartTestNode which alters its config.
|
||||
type TestNodeOption func(config *params.NodeConfig)
|
||||
|
||||
// WithUpstream returns TestNodeOption which enabled UpstreamConfig.
|
||||
func WithUpstream(url string) TestNodeOption {
|
||||
return func(config *params.NodeConfig) {
|
||||
config.UpstreamConfig.Enabled = true
|
||||
config.UpstreamConfig.URL = url
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTestNodeConfig defines a function to return a giving params.NodeConfig
|
||||
// where specific network addresses are assigned based on provieded network id.
|
||||
func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
||||
testDir := filepath.Join(TestDataDir, TestNetworkNames[networkID])
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
testDir = filepath.ToSlash(testDir)
|
||||
}
|
||||
|
||||
// run tests with "INFO" log level only
|
||||
// when `go test` invoked with `-v` flag
|
||||
errorLevel := "ERROR"
|
||||
if testing.Verbose() {
|
||||
errorLevel = "INFO"
|
||||
}
|
||||
|
||||
configJSON := `{
|
||||
"NetworkId": ` + strconv.Itoa(networkID) + `,
|
||||
"DataDir": "` + testDir + `",
|
||||
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
|
||||
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
|
||||
"LogLevel": "` + errorLevel + `"
|
||||
}`
|
||||
|
||||
nodeConfig, err := params.LoadNodeConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
// FirstBlockHash validates Attach operation for the NodeManager.
|
||||
func FirstBlockHash(nodeManager common.NodeManager) (string, error) {
|
||||
// obtain RPC client for running node
|
||||
runningNode, err := nodeManager.Node()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rpcClient, err := runningNode.Attach()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// get first block
|
||||
var firstBlock struct {
|
||||
Hash gethcommon.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
err = rpcClient.CallContext(context.Background(), &firstBlock, "eth_getBlockByNumber", "0x0", true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return firstBlock.Hash.Hex(), nil
|
||||
}
|
|
@ -1,36 +1,156 @@
|
|||
package api_test
|
||||
package transactions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/log"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// FIXME(tiabc): Sometimes it fails due to "no suitable peers found".
|
||||
func (s *BackendTestSuite) TestSendContractTx() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
const (
|
||||
txSendFolder = "testdata/jail/tx-send/"
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
func TestTransactionsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(TransactionsTestSuite))
|
||||
}
|
||||
|
||||
type TransactionsTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
}
|
||||
|
||||
func (s *TransactionsTestSuite) TestCallRPCSendTransaction() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// Allow to sync the blockchain.
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
transactionCompleted := make(chan struct{})
|
||||
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(rawSignal string) {
|
||||
var signal signal.Envelope
|
||||
err := json.Unmarshal([]byte(rawSignal), &signal)
|
||||
s.NoError(err)
|
||||
|
||||
if signal.Type == txqueue.EventTransactionQueued {
|
||||
event := signal.Event.(map[string]interface{})
|
||||
txID := event["id"].(string)
|
||||
txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot complete queued transaction %s", txID)
|
||||
|
||||
close(transactionCompleted)
|
||||
}
|
||||
})
|
||||
|
||||
result := s.Backend.CallRPC(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": "` + TestConfig.Account1.Address + `",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"value": "0x9184e72a"
|
||||
}]
|
||||
}`)
|
||||
s.NotContains(result, "error")
|
||||
|
||||
select {
|
||||
case <-transactionCompleted:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("sending transaction timed out")
|
||||
}
|
||||
|
||||
s.Equal(`{"jsonrpc":"2.0","id":1,"result":"`+txHash.String()+`"}`, result)
|
||||
}
|
||||
|
||||
func (s *TransactionsTestSuite) TestCallRPCSendTransactionUpstream() {
|
||||
s.StartTestBackend(
|
||||
params.RopstenNetworkID,
|
||||
e2e.WithUpstream("https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"),
|
||||
)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// Allow to sync the blockchain.
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account2.Address, TestConfig.Account2.Password)
|
||||
s.NoError(err)
|
||||
|
||||
transactionCompleted := make(chan struct{})
|
||||
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(rawSignal string) {
|
||||
var signal signal.Envelope
|
||||
err := json.Unmarshal([]byte(rawSignal), &signal)
|
||||
s.NoError(err)
|
||||
|
||||
if signal.Type == txqueue.EventTransactionQueued {
|
||||
event := signal.Event.(map[string]interface{})
|
||||
txID := event["id"].(string)
|
||||
|
||||
// Complete with a wrong passphrase.
|
||||
txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), "some-invalid-passphrase")
|
||||
s.EqualError(err, keystore.ErrDecrypt.Error(), "should return an error as the passphrase was invalid")
|
||||
|
||||
// Complete with a correct passphrase.
|
||||
txHash, err = s.Backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account2.Password)
|
||||
s.NoError(err, "cannot complete queued transaction %s", txID)
|
||||
|
||||
close(transactionCompleted)
|
||||
}
|
||||
})
|
||||
|
||||
result := s.Backend.CallRPC(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": "` + TestConfig.Account2.Address + `",
|
||||
"to": "` + TestConfig.Account1.Address + `",
|
||||
"value": "0x9184e72a"
|
||||
}]
|
||||
}`)
|
||||
s.NotContains(result, "error")
|
||||
|
||||
select {
|
||||
case <-transactionCompleted:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("sending transaction timed out")
|
||||
}
|
||||
|
||||
s.Equal(`{"jsonrpc":"2.0","id":1,"result":"`+txHash.String()+`"}`, result)
|
||||
}
|
||||
|
||||
// FIXME(tiabc): Sometimes it fails due to "no suitable peers found".
|
||||
func (s *TransactionsTestSuite) TestSendContractTx() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
sampleAddress, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
sampleAddress, _, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
|
@ -47,7 +167,7 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
|||
|
||||
// the first call will fail (we are not logged in, but trying to complete tx)
|
||||
log.Info("trying to complete with no user logged in")
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -59,9 +179,9 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
|||
|
||||
// the second call will also fail (we are logged in as different user)
|
||||
log.Info("trying to complete with invalid user")
|
||||
err = s.backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -73,8 +193,8 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
|||
|
||||
// the third call will work as expected (as we are logged in with correct credentials)
|
||||
log.Info("trying to complete with correct user, this should succeed")
|
||||
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -88,9 +208,9 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
|||
|
||||
// this call blocks, up until Complete Transaction is called
|
||||
byteCode, err := hexutil.Decode(`0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029`)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: nil, // marker, contract creation is expected
|
||||
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), gethcommon.Ether)),
|
||||
|
@ -110,21 +230,18 @@ func (s *BackendTestSuite) TestSendContractTx() {
|
|||
s.Zero(s.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestSendEtherTx() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestSendEtherTx() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// create an account
|
||||
sampleAddress, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
sampleAddress, _, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
|
@ -141,7 +258,7 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
|||
|
||||
// the first call will fail (we are not logged in, but trying to complete tx)
|
||||
log.Info("trying to complete with no user logged in")
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -153,9 +270,9 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
|||
|
||||
// the second call will also fail (we are logged in as different user)
|
||||
log.Info("trying to complete with invalid user")
|
||||
err = s.backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.AccountManager().SelectAccount(sampleAddress, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)), TestConfig.Account1.Password)
|
||||
s.EqualError(
|
||||
err,
|
||||
|
@ -165,8 +282,8 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
|||
|
||||
// the third call will work as expected (as we are logged in with correct credentials)
|
||||
log.Info("trying to complete with correct user, this should succeed")
|
||||
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -179,7 +296,7 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
|||
})
|
||||
|
||||
// this call blocks, up until Complete Transaction is called
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
|
@ -194,16 +311,19 @@ func (s *BackendTestSuite) TestSendEtherTx() {
|
|||
|
||||
s.Equal(txHashCheck.Hex(), txHash.Hex(), "transaction hash returned from SendTransaction is invalid")
|
||||
s.False(reflect.DeepEqual(txHashCheck, gethcommon.Hash{}), "transaction was never queued or completed")
|
||||
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.Zero(s.Backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestSendEtherTxUpstream() {
|
||||
s.StartTestBackend(params.RopstenNetworkID, WithUpstream("https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"))
|
||||
func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
|
||||
s.StartTestBackend(
|
||||
params.RopstenNetworkID,
|
||||
e2e.WithUpstream("https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"),
|
||||
)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
@ -219,7 +339,7 @@ func (s *BackendTestSuite) TestSendEtherTxUpstream() {
|
|||
event := envelope.Event.(map[string]interface{})
|
||||
log.Info("transaction queued (will be completed shortly)", "id", event["id"].(string))
|
||||
|
||||
txHash, err = s.backend.CompleteTransaction(
|
||||
txHash, err = s.Backend.CompleteTransaction(
|
||||
common.QueuedTxID(event["id"].(string)),
|
||||
TestConfig.Account1.Password,
|
||||
)
|
||||
|
@ -232,7 +352,7 @@ func (s *BackendTestSuite) TestSendEtherTxUpstream() {
|
|||
|
||||
// This call blocks, up until Complete Transaction is called.
|
||||
// Explicitly not setting Gas to get it estimated.
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
GasPrice: (*hexutil.Big)(big.NewInt(28000000000)),
|
||||
|
@ -247,23 +367,20 @@ func (s *BackendTestSuite) TestSendEtherTxUpstream() {
|
|||
}
|
||||
|
||||
s.Equal(txHash.Hex(), txHashCheck.Hex(), "transaction hash returned from SendTransaction is invalid")
|
||||
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.Zero(s.Backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestDoubleCompleteQueuedTransactions() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
|
@ -282,13 +399,13 @@ func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
|
|||
|
||||
// try with wrong password
|
||||
// make sure that tx is NOT removed from the queue (by re-trying with the correct password)
|
||||
_, err = s.backend.CompleteTransaction(txID, TestConfig.Account1.Password+"wrong")
|
||||
_, err = s.Backend.CompleteTransaction(txID, TestConfig.Account1.Password+"wrong")
|
||||
s.EqualError(err, keystore.ErrDecrypt.Error())
|
||||
|
||||
s.Equal(1, s.TxQueueManager().TransactionQueue().Count(), "txqueue cannot be empty, as tx has failed")
|
||||
|
||||
// now try to complete transaction, but with the correct password
|
||||
txHash, err = s.backend.CompleteTransaction(txID, TestConfig.Account1.Password)
|
||||
txHash, err = s.Backend.CompleteTransaction(txID, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
log.Info("transaction complete", "URL", "https://rinkeby.etherscan.io/tx/"+txHash.Hex())
|
||||
|
@ -311,7 +428,7 @@ func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
|
|||
})
|
||||
|
||||
// this call blocks, and should return on *second* attempt to CompleteTransaction (w/ the correct password)
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
|
@ -326,27 +443,24 @@ func (s *BackendTestSuite) TestDoubleCompleteQueuedTransactions() {
|
|||
|
||||
s.Equal(txHashCheck.Hex(), txHash.Hex(), "transaction hash returned from SendTransaction is invalid")
|
||||
s.False(reflect.DeepEqual(txHashCheck, gethcommon.Hash{}), "transaction was never queued or completed")
|
||||
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.Zero(s.Backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.True(txFailedEventCalled, "expected tx failure signal is not received")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestDiscardQueuedTransaction() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestDiscardQueuedTransaction() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// reset queue
|
||||
s.backend.TxQueueManager().TransactionQueue().Reset()
|
||||
s.Backend.TxQueueManager().TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
|
@ -362,18 +476,18 @@ func (s *BackendTestSuite) TestDiscardQueuedTransaction() {
|
|||
txID := common.QueuedTxID(event["id"].(string))
|
||||
log.Info("transaction queued (will be discarded soon)", "id", txID)
|
||||
|
||||
s.True(s.backend.TxQueueManager().TransactionQueue().Has(txID), "txqueue should still have test tx")
|
||||
s.True(s.Backend.TxQueueManager().TransactionQueue().Has(txID), "txqueue should still have test tx")
|
||||
|
||||
// discard
|
||||
err := s.backend.DiscardTransaction(txID)
|
||||
err := s.Backend.DiscardTransaction(txID)
|
||||
s.NoError(err, "cannot discard tx")
|
||||
|
||||
// try completing discarded transaction
|
||||
_, err = s.backend.CompleteTransaction(txID, TestConfig.Account1.Password)
|
||||
_, err = s.Backend.CompleteTransaction(txID, TestConfig.Account1.Password)
|
||||
s.EqualError(err, "transaction hash not found", "expects tx not found, but call to CompleteTransaction succeeded")
|
||||
|
||||
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||
s.False(s.backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
s.False(s.Backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
fmt.Sprintf("txqueue should not have test tx at this point (it should be discarded): %s", txID))
|
||||
|
||||
close(completeQueuedTransaction)
|
||||
|
@ -395,7 +509,7 @@ func (s *BackendTestSuite) TestDiscardQueuedTransaction() {
|
|||
})
|
||||
|
||||
// this call blocks, and should return when DiscardQueuedTransaction() is called
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
|
@ -409,14 +523,11 @@ func (s *BackendTestSuite) TestDiscardQueuedTransaction() {
|
|||
}
|
||||
|
||||
s.True(reflect.DeepEqual(txHashCheck, gethcommon.Hash{}), "transaction returned hash, while it shouldn't")
|
||||
s.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.Zero(s.Backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.True(txFailedEventCalled, "expected tx failure signal is not received")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestCompleteMultipleQueuedTransactions() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
|
@ -426,8 +537,8 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
|||
s.TxQueueManager().TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
testTxCount := 3
|
||||
txIDs := make(chan common.QueuedTxID, testTxCount)
|
||||
|
@ -450,7 +561,7 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
|||
|
||||
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||
sendTx := func() {
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
|
@ -462,16 +573,16 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
|||
// wait for transactions, and complete them in a single call
|
||||
completeTxs := func(txIDs []common.QueuedTxID) {
|
||||
txIDs = append(txIDs, "invalid-tx-id")
|
||||
results := s.backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
require.Len(results, testTxCount+1)
|
||||
require.EqualError(results["invalid-tx-id"].Error, "transaction hash not found")
|
||||
results := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
s.Len(results, testTxCount+1)
|
||||
s.EqualError(results["invalid-tx-id"].Error, "transaction hash not found")
|
||||
|
||||
for txID, txResult := range results {
|
||||
require.False(
|
||||
s.False(
|
||||
txResult.Error != nil && txID != "invalid-tx-id",
|
||||
"invalid error for %s", txID,
|
||||
)
|
||||
require.False(
|
||||
s.False(
|
||||
txResult.Hash == (gethcommon.Hash{}) && txID != "invalid-tx-id",
|
||||
"invalid hash (expected non empty hash): %s", txID,
|
||||
)
|
||||
|
@ -481,8 +592,8 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
|||
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||
|
||||
for _, txID := range txIDs {
|
||||
require.False(
|
||||
s.backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
s.False(
|
||||
s.Backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
"txqueue should not have test tx at this point (it should be completed)",
|
||||
)
|
||||
}
|
||||
|
@ -508,26 +619,23 @@ func (s *BackendTestSuite) TestCompleteMultipleQueuedTransactions() {
|
|||
s.FailNow("test timed out")
|
||||
}
|
||||
|
||||
require.Zero(s.TxQueueManager().TransactionQueue().Count(), "queue should be empty")
|
||||
s.Zero(s.TxQueueManager().TransactionQueue().Count(), "queue should be empty")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestDiscardMultipleQueuedTransactions() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// reset queue
|
||||
s.backend.TxQueueManager().TransactionQueue().Reset()
|
||||
s.Backend.TxQueueManager().TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
testTxCount := 3
|
||||
txIDs := make(chan common.QueuedTxID, testTxCount)
|
||||
|
@ -544,7 +652,7 @@ func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
|||
txID := common.QueuedTxID(event["id"].(string))
|
||||
log.Info("transaction queued (will be discarded soon)", "id", txID)
|
||||
|
||||
s.True(s.backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
s.True(s.Backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
"txqueue should still have test tx")
|
||||
txIDs <- txID
|
||||
}
|
||||
|
@ -569,7 +677,7 @@ func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
|||
|
||||
// this call blocks, and should return when DiscardQueuedTransaction() for a given tx id is called
|
||||
sendTx := func() {
|
||||
txHashCheck, err := s.backend.SendTransaction(nil, common.SendTxArgs{
|
||||
txHashCheck, err := s.Backend.SendTransaction(nil, common.SendTxArgs{
|
||||
From: common.FromAddress(TestConfig.Account1.Address),
|
||||
To: common.ToAddress(TestConfig.Account2.Address),
|
||||
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
||||
|
@ -584,24 +692,24 @@ func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
|||
txIDs = append(txIDs, "invalid-tx-id")
|
||||
|
||||
// discard
|
||||
discardResults := s.backend.DiscardTransactions(txIDs)
|
||||
require.Len(discardResults, 1, "cannot discard txs: %v", discardResults)
|
||||
require.Error(discardResults["invalid-tx-id"].Error, "transaction hash not found", "cannot discard txs: %v", discardResults)
|
||||
discardResults := s.Backend.DiscardTransactions(txIDs)
|
||||
s.Len(discardResults, 1, "cannot discard txs: %v", discardResults)
|
||||
s.Error(discardResults["invalid-tx-id"].Error, "transaction hash not found", "cannot discard txs: %v", discardResults)
|
||||
|
||||
// try completing discarded transaction
|
||||
completeResults := s.backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
require.Len(completeResults, testTxCount+1, "unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||
completeResults := s.Backend.CompleteTransactions(txIDs, TestConfig.Account1.Password)
|
||||
s.Len(completeResults, testTxCount+1, "unexpected number of errors (call to CompleteTransaction should not succeed)")
|
||||
|
||||
for _, txResult := range completeResults {
|
||||
require.Error(txResult.Error, "transaction hash not found", "invalid error for %s", txResult.Hash.Hex())
|
||||
require.Equal("0x0000000000000000000000000000000000000000000000000000000000000000", txResult.Hash.Hex(), "invalid hash (expected zero): %s", txResult.Hash.Hex())
|
||||
s.Error(txResult.Error, "transaction hash not found", "invalid error for %s", txResult.Hash.Hex())
|
||||
s.Equal("0x0000000000000000000000000000000000000000000000000000000000000000", txResult.Hash.Hex(), "invalid hash (expected zero): %s", txResult.Hash.Hex())
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second) // make sure that tx complete signal propagates
|
||||
|
||||
for _, txID := range txIDs {
|
||||
require.False(
|
||||
s.backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
s.False(
|
||||
s.Backend.TxQueueManager().TransactionQueue().Has(txID),
|
||||
"txqueue should not have test tx at this point (it should be discarded): %s",
|
||||
txID,
|
||||
)
|
||||
|
@ -624,54 +732,48 @@ func (s *BackendTestSuite) TestDiscardMultipleQueuedTransactions() {
|
|||
select {
|
||||
case <-allTestTxDiscarded:
|
||||
case <-time.After(1 * time.Minute):
|
||||
require.FailNow("test timed out")
|
||||
s.FailNow("test timed out")
|
||||
}
|
||||
|
||||
require.Zero(s.backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
s.Zero(s.Backend.TxQueueManager().TransactionQueue().Count(), "tx queue must be empty at this point")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestNonExistentQueuedTransactions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestNonExistentQueuedTransactions() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
// replace transaction notification handler
|
||||
signal.SetDefaultNodeNotificationHandler(func(string) {})
|
||||
|
||||
// try completing non-existing transaction
|
||||
_, err := s.backend.CompleteTransaction("some-bad-transaction-id", TestConfig.Account1.Password)
|
||||
_, err := s.Backend.CompleteTransaction("some-bad-transaction-id", TestConfig.Account1.Password)
|
||||
s.Error(err, "error expected and not received")
|
||||
s.EqualError(err, txqueue.ErrQueuedTxIDNotFound.Error())
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestEvictionOfQueuedTransactions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
func (s *TransactionsTestSuite) TestEvictionOfQueuedTransactions() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
backend := s.LightEthereumService().StatusBackend
|
||||
require.NotNil(backend)
|
||||
s.NotNil(backend)
|
||||
|
||||
// reset queue
|
||||
s.backend.TxQueueManager().TransactionQueue().Reset()
|
||||
s.Backend.TxQueueManager().TransactionQueue().Reset()
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
txQueue := s.backend.TxQueueManager().TransactionQueue()
|
||||
txQueue := s.Backend.TxQueueManager().TransactionQueue()
|
||||
var i = 0
|
||||
txIDs := [txqueue.DefaultTxQueueCap + 5 + 10]common.QueuedTxID{}
|
||||
s.backend.TxQueueManager().SetTransactionQueueHandler(func(queuedTx *common.QueuedTx) {
|
||||
s.Backend.TxQueueManager().SetTransactionQueueHandler(func(queuedTx *common.QueuedTx) {
|
||||
log.Info("tx enqueued", "i", i+1, "queue size", txQueue.Count(), "id", queuedTx.ID)
|
||||
txIDs[i] = queuedTx.ID
|
||||
i++
|
||||
|
@ -680,7 +782,7 @@ func (s *BackendTestSuite) TestEvictionOfQueuedTransactions() {
|
|||
s.Zero(txQueue.Count(), "transaction count should be zero")
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go s.backend.SendTransaction(nil, common.SendTxArgs{}) // nolint: errcheck
|
||||
go s.Backend.SendTransaction(nil, common.SendTxArgs{}) // nolint: errcheck
|
||||
}
|
||||
time.Sleep(2 * time.Second) // FIXME(tiabc): more reliable synchronization to ensure all transactions are enqueued
|
||||
|
||||
|
@ -690,15 +792,15 @@ func (s *BackendTestSuite) TestEvictionOfQueuedTransactions() {
|
|||
s.Equal(10, txQueue.Count(), "transaction count should be 10")
|
||||
|
||||
for i := 0; i < txqueue.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines
|
||||
go s.backend.SendTransaction(nil, common.SendTxArgs{}) // nolint: errcheck
|
||||
go s.Backend.SendTransaction(nil, common.SendTxArgs{}) // nolint: errcheck
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
require.True(txQueue.Count() <= txqueue.DefaultTxQueueCap, "transaction count should be %d (or %d): got %d", txqueue.DefaultTxQueueCap, txqueue.DefaultTxQueueCap-1, txQueue.Count())
|
||||
s.True(txQueue.Count() <= txqueue.DefaultTxQueueCap, "transaction count should be %d (or %d): got %d", txqueue.DefaultTxQueueCap, txqueue.DefaultTxQueueCap-1, txQueue.Count())
|
||||
|
||||
for _, txID := range txIDs {
|
||||
txQueue.Remove(txID)
|
||||
}
|
||||
|
||||
require.Zero(txQueue.Count(), "transaction count should be zero: %d", txQueue.Count())
|
||||
s.Zero(txQueue.Count(), "transaction count should be zero: %d", txQueue.Count())
|
||||
}
|
|
@ -0,0 +1,431 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/static"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
const (
|
||||
whisperMessage1 = `test message 1 (K1 -> K2, signed+encrypted, from us)`
|
||||
whisperMessage2 = `test message 3 (K1 -> "", signed broadcast)`
|
||||
whisperMessage3 = `test message 4 ("" -> "", anon broadcast)`
|
||||
whisperMessage4 = `test message 5 ("" -> K1, encrypted anon broadcast)`
|
||||
whisperMessage5 = `test message 6 (K2 -> K1, signed+encrypted, to us)`
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
var (
|
||||
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
|
||||
)
|
||||
|
||||
func TestWhisperJailTestSuite(t *testing.T) {
|
||||
s := new(WhisperJailTestSuite)
|
||||
s.Timeout = time.Minute * 5
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type WhisperJailTestSuite struct {
|
||||
e2e.BackendTestSuite
|
||||
|
||||
Timeout time.Duration
|
||||
WhisperAPI *whisper.PublicWhisperAPI
|
||||
Jail common.JailManager
|
||||
}
|
||||
|
||||
func (s *WhisperJailTestSuite) StartTestBackend(networkID int, opts ...e2e.TestNodeOption) {
|
||||
s.BackendTestSuite.StartTestBackend(networkID, opts...)
|
||||
|
||||
s.WhisperAPI = whisper.NewPublicWhisperAPI(s.WhisperService())
|
||||
s.Jail = s.Backend.JailManager()
|
||||
s.NotNil(s.Jail)
|
||||
s.Jail.BaseJS(baseStatusJSCode)
|
||||
}
|
||||
|
||||
func (s *WhisperJailTestSuite) GetAccountKey(account struct {
|
||||
Address string
|
||||
Password string
|
||||
}) (*keystore.Key, string, error) {
|
||||
accountManager := s.Backend.AccountManager()
|
||||
|
||||
_, accountKey1, err := accountManager.AddressToDecryptedAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
accountKey1Hex := gethcommon.ToHex(crypto.FromECDSAPub(&accountKey1.PrivateKey.PublicKey))
|
||||
|
||||
_, err = s.WhisperService().AddKeyPair(accountKey1.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if ok := s.WhisperAPI.HasKeyPair(context.Background(), accountKey1Hex); !ok {
|
||||
return nil, "", errors.New("KeyPair should be injected in Whisper")
|
||||
}
|
||||
|
||||
return accountKey1, accountKey1Hex, nil
|
||||
}
|
||||
|
||||
func (s *WhisperJailTestSuite) TestJailWhisper() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
_, accountKey1Hex, err := s.GetAccountKey(TestConfig.Account1)
|
||||
s.NoError(err)
|
||||
|
||||
_, accountKey2Hex, err := s.GetAccountKey(TestConfig.Account2)
|
||||
s.NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
code string
|
||||
useFilter bool
|
||||
}{
|
||||
{
|
||||
"test 0: ensure correct version of Whisper is used",
|
||||
`
|
||||
var expectedVersion = '5.0';
|
||||
if (web3.version.whisper != expectedVersion) {
|
||||
throw 'unexpected shh version, expected: ' + expectedVersion + ', got: ' + web3.version.whisper;
|
||||
}
|
||||
`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"test 1: encrypted signed message from us (From != nil && To != nil)",
|
||||
`
|
||||
var identity1 = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity1)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var identity2 = '` + accountKey2Hex + `';
|
||||
if (!shh.hasKeyPair(identity2)) {
|
||||
throw 'identitity "` + accountKey2Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage1 + `';
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
sig: identity1,
|
||||
privateKeyID: identity2,
|
||||
topics: [topic]
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
sig: identity1,
|
||||
pubKey: identity2,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 2: signed (known sender) broadcast (From != nil && To == nil)",
|
||||
`
|
||||
var identity = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage2 + `';
|
||||
|
||||
// generate symmetric key
|
||||
var keyid = shh.newSymKey();
|
||||
if (!shh.hasSymKey(keyid)) {
|
||||
throw new Error('key not found');
|
||||
}
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
sig: identity,
|
||||
topics: [topic],
|
||||
symKeyID: keyid
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
sig: identity,
|
||||
symKeyID: keyid,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 3: anonymous broadcast (From == nil && To == nil)",
|
||||
`
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage3 + `';
|
||||
|
||||
// generate symmetric key
|
||||
var keyid = shh.newSymKey();
|
||||
if (!shh.hasSymKey(keyid)) {
|
||||
throw new Error('key not found');
|
||||
}
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
topics: [topic],
|
||||
symKeyID: keyid
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
symKeyID: keyid,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
// @TODO(adam): quarantined as always failing. Check out TestEncryptedAnonymousMessage
|
||||
// as an equivalent test in pure Go which passes. Bug in web3?
|
||||
// {
|
||||
// "test 4: encrypted anonymous message (From == nil && To != nil)",
|
||||
// `
|
||||
// var identity = '` + accountKey2Hex + `';
|
||||
// if (!shh.hasKeyPair(identity)) {
|
||||
// throw 'idenitity "` + accountKey2Hex + `" not found in whisper';
|
||||
// }
|
||||
|
||||
// var topic = makeTopic();
|
||||
// var payload = '` + whisperMessage4 + `';
|
||||
|
||||
// // start watching for messages
|
||||
// var filter = shh.newMessageFilter({
|
||||
// privateKeyID: identity,
|
||||
// topics: [topic],
|
||||
// });
|
||||
|
||||
// // post message
|
||||
// var message = {
|
||||
// ttl: 20,
|
||||
// powTarget: 0.01,
|
||||
// powTime: 20,
|
||||
// topic: topic,
|
||||
// pubKey: identity,
|
||||
// payload: web3.toHex(payload),
|
||||
// };
|
||||
|
||||
// var sent = shh.post(message)
|
||||
// if (!sent) {
|
||||
// throw 'message not sent: ' + JSON.stringify(message);
|
||||
// }
|
||||
// `,
|
||||
// true,
|
||||
// },
|
||||
{
|
||||
"test 5: encrypted signed response to us (From != nil && To != nil)",
|
||||
`
|
||||
var identity1 = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity1)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
var identity2 = '` + accountKey2Hex + `';
|
||||
if (!shh.hasKeyPair(identity2)) {
|
||||
throw 'idenitity "` + accountKey2Hex + `" not found in whisper';
|
||||
}
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage5 + `';
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
privateKeyID: identity1,
|
||||
sig: identity2,
|
||||
topics: [topic],
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
sig: identity2,
|
||||
pubKey: identity1,
|
||||
topic: topic,
|
||||
payload: web3.toHex(payload),
|
||||
ttl: 20,
|
||||
powTime: 20,
|
||||
powTarget: 0.01,
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + message;
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.T().Log(tc.name)
|
||||
|
||||
chatID := crypto.Keccak256Hash([]byte(tc.name)).Hex()
|
||||
|
||||
s.Jail.Parse(chatID, `
|
||||
var shh = web3.shh;
|
||||
// topic must be 4-byte long
|
||||
var makeTopic = function () {
|
||||
var topic = '0x';
|
||||
for (var i = 0; i < 8; i++) {
|
||||
topic += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return topic;
|
||||
};
|
||||
`)
|
||||
|
||||
cell, err := s.Jail.Cell(chatID)
|
||||
s.NoError(err, "cannot get VM")
|
||||
|
||||
// Setup filters and post messages.
|
||||
_, err = cell.Run(tc.code)
|
||||
s.NoError(err)
|
||||
|
||||
if !tc.useFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
timedOut := make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(s.Timeout):
|
||||
close(timedOut)
|
||||
}
|
||||
}()
|
||||
|
||||
poll_loop:
|
||||
for {
|
||||
// Use polling because:
|
||||
// (1) filterId is not assigned immediately,
|
||||
// (2) messages propagate with some delay.
|
||||
select {
|
||||
case <-done:
|
||||
break poll_loop
|
||||
case <-timedOut:
|
||||
s.FailNow("polling for messages timed out")
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
|
||||
filter, err := cell.Get("filter")
|
||||
s.NoError(err, "cannot get filter")
|
||||
filterID, err := filter.Object().Get("filterId")
|
||||
s.NoError(err, "cannot get filterId")
|
||||
|
||||
// FilterID is not assigned yet.
|
||||
if filterID.IsNull() {
|
||||
continue
|
||||
}
|
||||
|
||||
payload, err := cell.Get("payload")
|
||||
s.NoError(err, "cannot get payload")
|
||||
|
||||
messages, err := s.WhisperAPI.GetFilterMessages(filterID.String())
|
||||
s.NoError(err)
|
||||
for _, m := range messages {
|
||||
s.Equal(payload.String(), string(m.Payload))
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WhisperJailTestSuite) TestEncryptedAnonymousMessage() {
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
accountKey2, accountKey2Hex, err := s.GetAccountKey(TestConfig.Account2)
|
||||
s.NoError(err)
|
||||
|
||||
topicSlice := make([]byte, whisper.TopicLength)
|
||||
_, err = rand.Read(topicSlice)
|
||||
s.NoError(err)
|
||||
|
||||
topic := whisper.BytesToTopic(topicSlice)
|
||||
|
||||
filter, err := s.WhisperAPI.NewMessageFilter(whisper.Criteria{
|
||||
PrivateKeyID: accountKey2Hex,
|
||||
Topics: []whisper.TopicType{topic},
|
||||
})
|
||||
s.NoError(err)
|
||||
|
||||
ok, err := s.WhisperAPI.Post(context.Background(), whisper.NewMessage{
|
||||
TTL: 20,
|
||||
PowTarget: 0.01,
|
||||
PowTime: 20,
|
||||
Topic: topic,
|
||||
PublicKey: crypto.FromECDSAPub(&accountKey2.PrivateKey.PublicKey),
|
||||
Payload: []byte(whisperMessage4),
|
||||
})
|
||||
s.NoError(err)
|
||||
s.True(ok)
|
||||
|
||||
done := make(chan struct{})
|
||||
timedOut := make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(s.Timeout):
|
||||
close(timedOut)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-timedOut:
|
||||
s.FailNow("polling for messages timed out")
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
|
||||
messages, err := s.WhisperAPI.GetFilterMessages(filter)
|
||||
s.NoError(err)
|
||||
for _, m := range messages {
|
||||
s.Equal(whisperMessage4, string(m.Payload))
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package node_test
|
||||
package whisper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,10 +6,11 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
|
@ -18,49 +19,45 @@ func TestWhisperTestSuite(t *testing.T) {
|
|||
}
|
||||
|
||||
type WhisperTestSuite struct {
|
||||
BaseTestSuite
|
||||
e2e.NodeManagerTestSuite
|
||||
}
|
||||
|
||||
func (s *WhisperTestSuite) SetupTest() {
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
s.Require().NotNil(s.NodeManager)
|
||||
s.Require().IsType(&node.NodeManager{}, s.NodeManager)
|
||||
s.NotNil(s.NodeManager)
|
||||
}
|
||||
|
||||
// TODO(adam): can anyone explain what this test is testing?
|
||||
// I don't see any race condition testing here.
|
||||
func (s *WhisperTestSuite) TestWhisperFilterRace() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
s.StartTestNode(params.RinkebyNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
whisperService, err := s.NodeManager.WhisperService()
|
||||
require.NoError(err)
|
||||
require.NotNil(whisperService)
|
||||
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperService)
|
||||
require.NotNil(whisperAPI)
|
||||
s.NoError(err)
|
||||
|
||||
accountManager := account.NewManager(s.NodeManager)
|
||||
require.NotNil(accountManager)
|
||||
s.NotNil(accountManager)
|
||||
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperService)
|
||||
|
||||
// account1
|
||||
_, accountKey1, err := accountManager.AddressToDecryptedAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
accountKey1Byte := crypto.FromECDSAPub(&accountKey1.PrivateKey.PublicKey)
|
||||
|
||||
key1ID, err := whisperService.AddKeyPair(accountKey1.PrivateKey)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
ok := whisperAPI.HasKeyPair(context.Background(), key1ID)
|
||||
require.True(ok, "identity not injected")
|
||||
s.True(ok, "identity not injected")
|
||||
|
||||
// account2
|
||||
_, accountKey2, err := accountManager.AddressToDecryptedAccount(TestConfig.Account2.Address, TestConfig.Account2.Password)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
key2ID, err := whisperService.AddKeyPair(accountKey2.PrivateKey)
|
||||
require.NoError(err)
|
||||
s.NoError(err)
|
||||
ok = whisperAPI.HasKeyPair(context.Background(), key2ID)
|
||||
require.True(ok, "identity not injected")
|
||||
s.True(ok, "identity not injected")
|
||||
|
||||
// race filter addition
|
||||
filterAdded := make(chan struct{})
|
||||
|
@ -96,3 +93,26 @@ func (s *WhisperTestSuite) TestWhisperFilterRace() {
|
|||
|
||||
<-allFiltersAdded
|
||||
}
|
||||
|
||||
func (s *WhisperTestSuite) TestLogout() {
|
||||
s.StartTestNode(params.RinkebyNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
whisperService, err := s.NodeManager.WhisperService()
|
||||
s.NoError(err)
|
||||
|
||||
accountManager := account.NewManager(s.NodeManager)
|
||||
s.NotNil(accountManager)
|
||||
|
||||
// create an account
|
||||
address, pubKey, _, err := accountManager.CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
// make sure that identity doesn't exist (yet) in Whisper
|
||||
s.False(whisperService.HasKeyPair(pubKey), "identity already present in whisper")
|
||||
s.NoError(accountManager.SelectAccount(address, TestConfig.Account1.Password))
|
||||
s.True(whisperService.HasKeyPair(pubKey), "identity not injected into whisper")
|
||||
|
||||
s.NoError(accountManager.Logout())
|
||||
s.False(whisperService.HasKeyPair(pubKey), "identity not cleared from whisper")
|
||||
}
|
|
@ -12,44 +12,23 @@ import (
|
|||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/stretchr/testify/suite"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAccountsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AccountsTestSuite))
|
||||
}
|
||||
|
||||
type AccountsTestSuite struct {
|
||||
BaseTestSuite
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) SetupTest() {
|
||||
require := s.Require()
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
require.NotNil(s.NodeManager)
|
||||
require.IsType(&node.NodeManager{}, s.NodeManager)
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestVerifyAccountPassword() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
accountManager := account.NewManager(nil)
|
||||
require.NotNil(accountManager)
|
||||
|
||||
func TestVerifyAccountPassword(t *testing.T) {
|
||||
acctManager := account.NewManager(nil)
|
||||
keyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts")
|
||||
require.NoError(err)
|
||||
defer os.RemoveAll(keyStoreDir) // nolint: errcheck
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(keyStoreDir)
|
||||
|
||||
emptyKeyStoreDir, err := ioutil.TempDir(os.TempDir(), "empty")
|
||||
require.NoError(err)
|
||||
defer os.RemoveAll(emptyKeyStoreDir) // nolint: errcheck
|
||||
emptyKeyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts_empty")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(emptyKeyStoreDir)
|
||||
|
||||
// import account keys
|
||||
require.NoError(common.ImportTestAccount(keyStoreDir, "test-account1.pk"))
|
||||
require.NoError(common.ImportTestAccount(keyStoreDir, "test-account2.pk"))
|
||||
require.NoError(t, common.ImportTestAccount(keyStoreDir, "test-account1.pk"))
|
||||
require.NoError(t, common.ImportTestAccount(keyStoreDir, "test-account2.pk"))
|
||||
|
||||
account1Address := gethcommon.BytesToAddress(gethcommon.FromHex(TestConfig.Account1.Address))
|
||||
|
||||
|
@ -97,18 +76,17 @@ func (s *AccountsTestSuite) TestVerifyAccountPassword() {
|
|||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
s.T().Log(testCase.name)
|
||||
accountKey, err := accountManager.VerifyAccountPassword(testCase.keyPath, testCase.address, testCase.password)
|
||||
accountKey, err := acctManager.VerifyAccountPassword(testCase.keyPath, testCase.address, testCase.password)
|
||||
if !reflect.DeepEqual(err, testCase.expectedError) {
|
||||
s.FailNow(fmt.Sprintf("unexpected error: expected \n'%v', got \n'%v'", testCase.expectedError, err))
|
||||
require.FailNow(t, fmt.Sprintf("unexpected error: expected \n'%v', got \n'%v'", testCase.expectedError, err))
|
||||
}
|
||||
if err == nil {
|
||||
if accountKey == nil {
|
||||
s.T().Error("no error reported, but account key is missing")
|
||||
require.Fail(t, "no error reported, but account key is missing")
|
||||
}
|
||||
accountAddress := gethcommon.BytesToAddress(gethcommon.FromHex(testCase.address))
|
||||
if accountKey.Address != accountAddress {
|
||||
s.T().Fatalf("account mismatch: have %s, want %s", accountKey.Address.Hex(), accountAddress.Hex())
|
||||
require.Fail(t, "account mismatch: have %s, want %s", accountKey.Address.Hex(), accountAddress.Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,18 +94,18 @@ func (s *AccountsTestSuite) TestVerifyAccountPassword() {
|
|||
|
||||
// TestVerifyAccountPasswordWithAccountBeforeEIP55 verifies if VerifyAccountPassword
|
||||
// can handle accounts before introduction of EIP55.
|
||||
func (s *AccountsTestSuite) TestVerifyAccountPasswordWithAccountBeforeEIP55() {
|
||||
func TestVerifyAccountPasswordWithAccountBeforeEIP55(t *testing.T) {
|
||||
keyStoreDir, err := ioutil.TempDir("", "status-accounts-test")
|
||||
s.NoError(err)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(keyStoreDir)
|
||||
|
||||
// Import keys and make sure one was created before EIP55 introduction.
|
||||
err = common.ImportTestAccount(keyStoreDir, "test-account1-before-eip55.pk")
|
||||
s.NoError(err)
|
||||
require.NoError(t, err)
|
||||
|
||||
acctManager := account.NewManager(nil)
|
||||
|
||||
address := gethcommon.HexToAddress(TestConfig.Account1.Address)
|
||||
_, err = acctManager.VerifyAccountPassword(keyStoreDir, address.Hex(), TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -118,14 +118,14 @@ func (m *StatusBackend) StopNode() (<-chan struct{}, error) {
|
|||
}
|
||||
<-m.nodeReady
|
||||
|
||||
m.txQueueManager.Stop()
|
||||
m.jailManager.Stop()
|
||||
|
||||
nodeStopped, err := m.nodeManager.StopNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.txQueueManager.Stop()
|
||||
m.jailManager.Stop()
|
||||
|
||||
backendStopped := make(chan struct{}, 1)
|
||||
go func() {
|
||||
<-nodeStopped
|
||||
|
|
|
@ -1,394 +0,0 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
)
|
||||
|
||||
func (s *BackendTestSuite) TestAccountsList() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
runningNode, err := s.backend.NodeManager().Node()
|
||||
require.NoError(err)
|
||||
require.NotNil(runningNode)
|
||||
|
||||
accounts, err := s.backend.AccountManager().Accounts()
|
||||
require.NoError(err)
|
||||
|
||||
// make sure that we start with empty accounts list (nobody has logged in yet)
|
||||
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||
|
||||
// create an account
|
||||
address, _, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
|
||||
// ensure that there is still no accounts returned
|
||||
accounts, err = s.backend.AccountManager().Accounts()
|
||||
require.NoError(err)
|
||||
require.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
|
||||
|
||||
// select account (sub-accounts will be created for this key)
|
||||
err = s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||
require.NoError(err, "account selection failed")
|
||||
|
||||
// at this point main account should show up
|
||||
accounts, err = s.backend.AccountManager().Accounts()
|
||||
require.NoError(err)
|
||||
require.Equal(1, len(accounts), "exactly single account is expected (main account)")
|
||||
require.Equal(string(accounts[0].Hex()), address,
|
||||
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), address))
|
||||
|
||||
// create sub-account 1
|
||||
subAccount1, subPubKey1, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot create sub-account")
|
||||
|
||||
// now we expect to see both main account and sub-account 1
|
||||
accounts, err = s.backend.AccountManager().Accounts()
|
||||
require.NoError(err)
|
||||
require.Equal(2, len(accounts), "exactly 2 accounts are expected (main + sub-account 1)")
|
||||
require.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key")
|
||||
require.Equal(string(accounts[1].Hex()), subAccount1, "subAcount1 not returned")
|
||||
|
||||
// create sub-account 2, index automatically progresses
|
||||
subAccount2, subPubKey2, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot create sub-account")
|
||||
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||
|
||||
// finally, all 3 accounts should show up (main account, sub-accounts 1 and 2)
|
||||
accounts, err = s.backend.AccountManager().Accounts()
|
||||
require.NoError(err)
|
||||
require.Equal(3, len(accounts), "unexpected number of accounts")
|
||||
require.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key")
|
||||
|
||||
subAccount1MatchesKey1 := string(accounts[1].Hex()) != subAccount1
|
||||
subAccount1MatchesKey2 := string(accounts[2].Hex()) != subAccount1
|
||||
require.False(!subAccount1MatchesKey1 && !subAccount1MatchesKey2, "subAcount1 not returned")
|
||||
|
||||
subAccount2MatchesKey1 := string(accounts[1].Hex()) != subAccount2
|
||||
subAccount2MatchesKey2 := string(accounts[2].Hex()) != subAccount2
|
||||
require.False(!subAccount2MatchesKey1 && !subAccount2MatchesKey2, "subAcount2 not returned")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestCreateChildAccount() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
keyStore, err := s.backend.NodeManager().AccountKeyStore()
|
||||
require.NoError(err)
|
||||
require.NotNil(keyStore)
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
acct, err := common.ParseAccountString(address)
|
||||
require.NoError(err, "can not get account from address")
|
||||
|
||||
// obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present
|
||||
_, key, err := keyStore.AccountDecryptedKey(acct, TestConfig.Account1.Password)
|
||||
require.NoError(err, "can not obtain decrypted account key")
|
||||
require.NotNil(key.ExtendedKey, "CKD#2 has not been generated for new account")
|
||||
|
||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||
_, _, err = s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
require.EqualError(account.ErrNoAccountSelected, err.Error(), "expected error is not returned (tried to create sub-account w/o login)")
|
||||
|
||||
err = s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot select account")
|
||||
|
||||
// try to create sub-account with wrong password
|
||||
_, _, err = s.backend.AccountManager().CreateChildAccount("", "wrong password")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
require.EqualError(expectedErr, err.Error(), "create sub-account with wrong password")
|
||||
|
||||
// create sub-account (from implicit parent)
|
||||
subAccount1, subPubKey1, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot create sub-account")
|
||||
|
||||
// make sure that sub-account index automatically progresses
|
||||
subAccount2, subPubKey2, err := s.backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
require.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed")
|
||||
|
||||
// create sub-account (from explicit parent)
|
||||
subAccount3, subPubKey3, err := s.backend.AccountManager().CreateChildAccount(subAccount2, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
require.False(subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3)
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestRecoverAccount() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
keyStore, err := s.backend.NodeManager().AccountKeyStore()
|
||||
require.NoError(err)
|
||||
require.NotNil(keyStore)
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
// try recovering using password + mnemonic
|
||||
addressCheck, pubKeyCheck, err := s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
require.NoError(err, "recover account failed")
|
||||
require.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect accound details recovered")
|
||||
|
||||
// now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device
|
||||
account, err := common.ParseAccountString(address)
|
||||
require.NoError(err, "can not get account from address")
|
||||
|
||||
account, key, err := keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
||||
require.NoError(err, "can not obtain decrypted account key")
|
||||
extChild2String := key.ExtendedKey.String()
|
||||
|
||||
require.NoError(keyStore.Delete(account, TestConfig.Account1.Password), "cannot remove account")
|
||||
|
||||
addressCheck, pubKeyCheck, err = s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
require.NoError(err, "recover account failed (for non-cached account)")
|
||||
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||
"incorrect account details recovered (for non-cached account)")
|
||||
|
||||
// make sure that extended key exists and is imported ok too
|
||||
_, key, err = keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
require.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch")
|
||||
|
||||
// make sure that calling import several times, just returns from cache (no error is expected)
|
||||
addressCheck, pubKeyCheck, err = s.backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic)
|
||||
require.NoError(err, "recover account failed (for non-cached account)")
|
||||
require.False(address != addressCheck || pubKey != pubKeyCheck,
|
||||
"incorrect account details recovered (for non-cached account)")
|
||||
|
||||
// time to login with recovered data
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
require.False(whisperService.HasKeyPair(pubKeyCheck), "identity already present in whisper")
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(addressCheck, TestConfig.Account1.Password))
|
||||
require.True(whisperService.HasKeyPair(pubKeyCheck), "identity not injected into whisper")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestSelectAccount() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// test to see if the account was injected in whisper
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// create an account
|
||||
address1, pubKey1, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
s.T().Logf("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||
|
||||
// try selecting with wrong password
|
||||
err = s.backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
require.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used")
|
||||
|
||||
err = s.backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestLogout() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// create an account
|
||||
address, pubKey, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
|
||||
// make sure that identity doesn't exist (yet) in Whisper
|
||||
require.False(whisperService.HasKeyPair(pubKey), "identity already present in whisper")
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(address, TestConfig.Account1.Password))
|
||||
require.True(whisperService.HasKeyPair(pubKey), "identity not injected into whisper")
|
||||
|
||||
require.NoError(s.backend.AccountManager().Logout())
|
||||
require.False(whisperService.HasKeyPair(pubKey), "identity not cleared from whisper")
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestSelectedAccountOnRestart() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
|
||||
// we need to make sure that selected account is injected as identity into Whisper
|
||||
whisperService := s.WhisperService()
|
||||
|
||||
// create test accounts
|
||||
address1, pubKey1, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
address2, pubKey2, _, err := s.backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper")
|
||||
|
||||
// make sure that no account is selected by default
|
||||
selectedAccount, err := s.backend.AccountManager().SelectedAccount()
|
||||
require.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||
require.Nil(selectedAccount)
|
||||
|
||||
// select account
|
||||
err = s.backend.AccountManager().SelectAccount(address1, "wrongPassword")
|
||||
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase")
|
||||
require.EqualError(expectedErr, err.Error())
|
||||
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(address1, TestConfig.Account1.Password))
|
||||
require.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper")
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
require.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper")
|
||||
require.NoError(s.backend.AccountManager().SelectAccount(address2, TestConfig.Account1.Password))
|
||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper")
|
||||
|
||||
// stop node (and all of its sub-protocols)
|
||||
nodeConfig, err := s.backend.NodeManager().NodeConfig()
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeConfig)
|
||||
preservedNodeConfig := *nodeConfig
|
||||
nodeStoped, err := s.backend.StopNode()
|
||||
require.NoError(err)
|
||||
<-nodeStoped
|
||||
|
||||
// make sure that account is still selected
|
||||
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||
require.NoError(err)
|
||||
require.NotNil(selectedAccount)
|
||||
require.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
|
||||
|
||||
// resume node
|
||||
nodeStarted, err := s.backend.StartNode(&preservedNodeConfig)
|
||||
require.NoError(err)
|
||||
<-nodeStarted
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||
require.NoError(err)
|
||||
require.NotNil(selectedAccount)
|
||||
require.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected")
|
||||
|
||||
// make sure that Whisper gets identity re-injected
|
||||
whisperService = s.WhisperService()
|
||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
// now restart node using RestartNode() method, and make sure that account is still available
|
||||
s.RestartTestNode()
|
||||
defer s.StopTestBackend()
|
||||
|
||||
whisperService = s.WhisperService()
|
||||
require.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
// now logout, and make sure that on restart no account is selected (i.e. logout works properly)
|
||||
require.NoError(s.backend.AccountManager().Logout())
|
||||
s.RestartTestNode()
|
||||
whisperService = s.WhisperService()
|
||||
require.False(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper")
|
||||
require.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
selectedAccount, err = s.backend.AccountManager().SelectedAccount()
|
||||
require.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
require.Nil(selectedAccount)
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestRPCEthAccounts() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
|
||||
rpcClient := s.backend.NodeManager().RPCClient()
|
||||
|
||||
expected := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.Address) + `"]}`
|
||||
resp := rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_accounts",
|
||||
"params": []
|
||||
}`)
|
||||
require.Equal(expected, resp)
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestRPCEthAccountsWithUpstream() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID, WithUpstream("https://ropsten.infura.io/z6GCTmjdP3FETEJmMBI4"))
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
|
||||
rpcClient := s.backend.NodeManager().RPCClient()
|
||||
|
||||
expected := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.Address) + `"]}`
|
||||
resp := rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_accounts",
|
||||
"params": []
|
||||
}`)
|
||||
require.Equal(expected, resp)
|
||||
}
|
||||
|
||||
// regression test: eth_getTransactionReceipt with invalid transaction hash should return null
|
||||
func (s *BackendTestSuite) TestRegressionGetTransactionReceipt() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
rpcClient := s.backend.NodeManager().RPCClient()
|
||||
|
||||
// note: transaction hash is assumed to be invalid
|
||||
got := rpcClient.CallRaw(`{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0xbbebf28d0a3a3cbb38e6053a5b21f08f82c62b0c145a17b1c4313cac3f68ae7c"],"id":7}`)
|
||||
expected := `{"jsonrpc":"2.0","id":7,"result":null}`
|
||||
require.Equal(expected, got)
|
||||
}
|
|
@ -1,628 +0,0 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
"github.com/status-im/status-go/static"
|
||||
)
|
||||
|
||||
const (
|
||||
whisperMessage1 = `test message 1 (K1 -> K2, signed+encrypted, from us)`
|
||||
whisperMessage2 = `test message 3 (K1 -> "", signed broadcast)`
|
||||
whisperMessage3 = `test message 4 ("" -> "", anon broadcast)`
|
||||
whisperMessage4 = `test message 5 ("" -> K1, encrypted anon broadcast)`
|
||||
whisperMessage5 = `test message 6 (K2 -> K1, signed+encrypted, to us)`
|
||||
txSendFolder = "testdata/jail/tx-send/"
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
var (
|
||||
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
|
||||
)
|
||||
|
||||
func (s *BackendTestSuite) TestContractDeployment() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
// Allow to sync, otherwise you'll get "Nonce too low."
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
jailInstance := s.backend.JailManager()
|
||||
jailInstance.Parse(testChatID, "")
|
||||
|
||||
cell, err := jailInstance.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
|
||||
completeQueuedTransaction := make(chan struct{})
|
||||
|
||||
// replace transaction notification handler
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
var err error
|
||||
err = json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||
require.NoError(err, fmt.Sprintf("cannot unmarshal JSON: %s", jsonEvent))
|
||||
|
||||
if envelope.Type == txqueue.EventTransactionQueued {
|
||||
// Use s.* for assertions - require leaves the channel unclosed.
|
||||
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
s.T().Logf("transaction queued and will be completed shortly, id: %v", event["id"])
|
||||
|
||||
s.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
||||
|
||||
txID := event["id"].(string)
|
||||
txHash, err = s.backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
if s.NoError(err, event["id"]) {
|
||||
s.T().Logf("contract transaction complete, URL: %s", "https://ropsten.etherscan.io/tx/"+txHash.Hex())
|
||||
}
|
||||
|
||||
close(completeQueuedTransaction)
|
||||
}
|
||||
})
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = null;
|
||||
var errorValue = null;
|
||||
var testContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"a","type":"int256"}],"name":"double","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"}]);
|
||||
var test = testContract.new(
|
||||
{
|
||||
from: '` + TestConfig.Account1.Address + `',
|
||||
data: '0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029',
|
||||
gas: '` + strconv.Itoa(params.DefaultGas) + `'
|
||||
}, function (e, contract) {
|
||||
// NOTE: The callback will fire twice!
|
||||
if (e) {
|
||||
errorValue = e;
|
||||
return
|
||||
}
|
||||
// Once the contract has the transactionHash property set and once its deployed on an address.
|
||||
if (!contract.address) {
|
||||
responseValue = contract.transactionHash;
|
||||
}
|
||||
})
|
||||
`)
|
||||
require.NoError(err)
|
||||
|
||||
select {
|
||||
case <-completeQueuedTransaction:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("test timed out")
|
||||
}
|
||||
|
||||
// Wait until callback is fired and `responseValue` is set. Hacky but simple.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
errorValue, err := cell.Get("errorValue")
|
||||
require.NoError(err)
|
||||
require.Equal("null", errorValue.String())
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
require.NoError(err)
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
require.NoError(err)
|
||||
|
||||
expectedResponse := txHash.Hex()
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestJailWhisper() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
jailInstance := s.backend.JailManager()
|
||||
require.NotNil(jailInstance)
|
||||
|
||||
jailInstance.BaseJS(baseStatusJSCode)
|
||||
|
||||
whisperService := s.WhisperService()
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperService)
|
||||
|
||||
accountManager := s.backend.AccountManager()
|
||||
|
||||
// account1
|
||||
_, accountKey1, err := accountManager.AddressToDecryptedAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err)
|
||||
accountKey1Hex := gethcommon.ToHex(crypto.FromECDSAPub(&accountKey1.PrivateKey.PublicKey))
|
||||
|
||||
_, err = whisperService.AddKeyPair(accountKey1.PrivateKey)
|
||||
require.NoError(err, fmt.Sprintf("identity not injected: %v", accountKey1Hex))
|
||||
|
||||
if ok := whisperAPI.HasKeyPair(context.Background(), accountKey1Hex); !ok {
|
||||
require.FailNow(fmt.Sprintf("identity not injected: %v", accountKey1Hex))
|
||||
}
|
||||
|
||||
// account2
|
||||
_, accountKey2, err := accountManager.AddressToDecryptedAccount(TestConfig.Account2.Address, TestConfig.Account2.Password)
|
||||
require.NoError(err)
|
||||
accountKey2Hex := gethcommon.ToHex(crypto.FromECDSAPub(&accountKey2.PrivateKey.PublicKey))
|
||||
|
||||
_, err = whisperService.AddKeyPair(accountKey2.PrivateKey)
|
||||
require.NoError(err, fmt.Sprintf("identity not injected: %v", accountKey2Hex))
|
||||
|
||||
if ok := whisperAPI.HasKeyPair(context.Background(), accountKey2Hex); !ok {
|
||||
require.FailNow(fmt.Sprintf("identity not injected: %v", accountKey2Hex))
|
||||
}
|
||||
|
||||
passedTests := map[string]bool{
|
||||
whisperMessage1: false,
|
||||
whisperMessage2: false,
|
||||
whisperMessage3: false,
|
||||
whisperMessage4: false,
|
||||
whisperMessage5: false,
|
||||
}
|
||||
installedFilters := map[string]string{
|
||||
whisperMessage1: "",
|
||||
whisperMessage2: "",
|
||||
whisperMessage3: "",
|
||||
whisperMessage4: "",
|
||||
whisperMessage5: "",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
testCode string
|
||||
useFilter bool
|
||||
}{
|
||||
{
|
||||
"test 0: ensure correct version of Whisper is used",
|
||||
`
|
||||
var expectedVersion = '5.0';
|
||||
if (web3.version.whisper != expectedVersion) {
|
||||
throw 'unexpected shh version, expected: ' + expectedVersion + ', got: ' + web3.version.whisper;
|
||||
}
|
||||
`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"test 1: encrypted signed message from us (From != nil && To != nil)",
|
||||
`
|
||||
var identity1 = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity1)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var identity2 = '` + accountKey2Hex + `';
|
||||
if (!shh.hasKeyPair(identity2)) {
|
||||
throw 'identitity "` + accountKey2Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage1 + `';
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
sig: identity1,
|
||||
privateKeyID: identity2,
|
||||
topics: [topic]
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
sig: identity1,
|
||||
pubKey: identity2,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
|
||||
var filterName = '` + whisperMessage1 + `';
|
||||
var filterId = filter.filterId;
|
||||
if (!filterId) {
|
||||
throw 'filter not installed properly';
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 2: signed (known sender) broadcast (From != nil && To == nil)",
|
||||
`
|
||||
var identity = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage2 + `';
|
||||
|
||||
// generate symmetric key
|
||||
var keyid = shh.newSymKey();
|
||||
if (!shh.hasSymKey(keyid)) {
|
||||
throw new Error('key not found');
|
||||
}
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
sig: identity,
|
||||
topics: [topic],
|
||||
symKeyID: keyid
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
sig: identity,
|
||||
symKeyID: keyid,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
|
||||
var filterName = '` + whisperMessage2 + `';
|
||||
var filterId = filter.filterId;
|
||||
if (!filterId) {
|
||||
throw 'filter not installed properly';
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 3: anonymous broadcast (From == nil && To == nil)",
|
||||
`
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage3 + `';
|
||||
|
||||
// generate symmetric key
|
||||
var keyid = shh.newSymKey();
|
||||
if (!shh.hasSymKey(keyid)) {
|
||||
throw new Error('key not found');
|
||||
}
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
topics: [topic],
|
||||
symKeyID: keyid
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
symKeyID: keyid,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
|
||||
var filterName = '` + whisperMessage3 + `';
|
||||
var filterId = filter.filterId;
|
||||
if (!filterId) {
|
||||
throw 'filter not installed properly';
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 4: encrypted anonymous message (From == nil && To != nil)",
|
||||
`
|
||||
var identity = '` + accountKey2Hex + `';
|
||||
if (!shh.hasKeyPair(identity)) {
|
||||
throw 'idenitity "` + accountKey2Hex + `" not found in whisper';
|
||||
}
|
||||
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage4 + `';
|
||||
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
privateKeyID: identity,
|
||||
topics: [topic],
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
ttl: 20,
|
||||
powTarget: 0.01,
|
||||
powTime: 20,
|
||||
topic: topic,
|
||||
pubKey: identity,
|
||||
payload: web3.toHex(payload),
|
||||
};
|
||||
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + JSON.stringify(message);
|
||||
}
|
||||
|
||||
var filterName = '` + whisperMessage4 + `';
|
||||
var filterId = filter.filterId;
|
||||
if (!filterId) {
|
||||
throw 'filter not installed properly';
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test 5: encrypted signed response to us (From != nil && To != nil)",
|
||||
`
|
||||
var identity1 = '` + accountKey1Hex + `';
|
||||
if (!shh.hasKeyPair(identity1)) {
|
||||
throw 'idenitity "` + accountKey1Hex + `" not found in whisper';
|
||||
}
|
||||
var identity2 = '` + accountKey2Hex + `';
|
||||
if (!shh.hasKeyPair(identity2)) {
|
||||
throw 'idenitity "` + accountKey2Hex + `" not found in whisper';
|
||||
}
|
||||
var topic = makeTopic();
|
||||
var payload = '` + whisperMessage5 + `';
|
||||
// start watching for messages
|
||||
var filter = shh.newMessageFilter({
|
||||
privateKeyID: identity1,
|
||||
sig: identity2,
|
||||
topics: [topic],
|
||||
});
|
||||
|
||||
// post message
|
||||
var message = {
|
||||
sig: identity2,
|
||||
pubKey: identity1,
|
||||
topic: topic,
|
||||
payload: web3.toHex(payload),
|
||||
ttl: 20,
|
||||
powTime: 20,
|
||||
powTarget: 0.01,
|
||||
};
|
||||
var sent = shh.post(message)
|
||||
if (!sent) {
|
||||
throw 'message not sent: ' + message;
|
||||
}
|
||||
var filterName = '` + whisperMessage5 + `';
|
||||
var filterId = filter.filterId;
|
||||
if (!filterId) {
|
||||
throw 'filter not installed properly';
|
||||
}
|
||||
`,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
s.T().Log(testCase.name)
|
||||
testCaseKey := crypto.Keccak256Hash([]byte(testCase.name)).Hex()
|
||||
|
||||
jailInstance.Parse(testCaseKey, `
|
||||
var shh = web3.shh;
|
||||
// topic must be 4-byte long
|
||||
var makeTopic = function () {
|
||||
var topic = '0x';
|
||||
for (var i = 0; i < 8; i++) {
|
||||
topic += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return topic;
|
||||
};
|
||||
`)
|
||||
|
||||
cell, err := jailInstance.Cell(testCaseKey)
|
||||
require.NoError(err, "cannot get VM")
|
||||
|
||||
// post messages
|
||||
_, err = cell.Run(testCase.testCode)
|
||||
require.NoError(err)
|
||||
|
||||
if !testCase.useFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
// update installed filters
|
||||
filterId, err := cell.Get("filterId")
|
||||
require.NoError(err, "cannot get filterId")
|
||||
|
||||
filterName, err := cell.Get("filterName")
|
||||
require.NoError(err, "cannot get filterName")
|
||||
|
||||
_, ok := installedFilters[filterName.String()]
|
||||
require.True(ok, "unrecognized filter")
|
||||
|
||||
installedFilters[filterName.String()] = filterId.String()
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // allow whisper to poll
|
||||
|
||||
for testKey, filter := range installedFilters {
|
||||
if filter != "" {
|
||||
s.T().Logf("filter found: %v", filter)
|
||||
messages, err := whisperAPI.GetFilterMessages(filter)
|
||||
require.NoError(err)
|
||||
for _, message := range messages {
|
||||
s.T().Logf("message found: %s", string(message.Payload))
|
||||
passedTests[testKey] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for testName, passedTest := range passedTests {
|
||||
s.True(passedTest, "test not passed: %v", testName)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestJailVMPersistence() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestBackend(params.RopstenNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second) // allow to sync
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
err := s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot select account: %v", TestConfig.Account1.Address)
|
||||
|
||||
type testCase struct {
|
||||
command string
|
||||
params string
|
||||
validator func(response string) error
|
||||
}
|
||||
var testCases = []testCase{
|
||||
{
|
||||
`["sendTestTx"]`,
|
||||
`{"amount": "0.000001", "from": "` + TestConfig.Account1.Address + `"}`,
|
||||
func(response string) error {
|
||||
if strings.Contains(response, "error") {
|
||||
return fmt.Errorf("unexpected response: %v", response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["sendTestTx"]`,
|
||||
`{"amount": "0.000002", "from": "` + TestConfig.Account1.Address + `"}`,
|
||||
func(response string) error {
|
||||
if strings.Contains(response, "error") {
|
||||
return fmt.Errorf("unexpected response: %v", response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["ping"]`,
|
||||
`{"pong": "Ping1", "amount": 0.42}`,
|
||||
func(response string) error {
|
||||
expectedResponse := `{"result": "Ping1"}`
|
||||
if response != expectedResponse {
|
||||
return fmt.Errorf("unexpected response, expected: %v, got: %v", expectedResponse, response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
`["ping"]`,
|
||||
`{"pong": "Ping2", "amount": 0.42}`,
|
||||
func(response string) error {
|
||||
expectedResponse := `{"result": "Ping2"}`
|
||||
if response != expectedResponse {
|
||||
return fmt.Errorf("unexpected response, expected: %v, got: %v", expectedResponse, response)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
jailInstance := s.backend.JailManager()
|
||||
jailInstance.BaseJS(baseStatusJSCode)
|
||||
|
||||
parseResult := jailInstance.Parse(testChatID, `
|
||||
var total = 0;
|
||||
_status_catalog['ping'] = function(params) {
|
||||
total += Number(params.amount);
|
||||
return params.pong;
|
||||
}
|
||||
|
||||
_status_catalog['sendTestTx'] = function(params) {
|
||||
var amount = params.amount;
|
||||
var transaction = {
|
||||
"from": params.from,
|
||||
"to": "`+TestConfig.Account2.Address+`",
|
||||
"value": web3.toWei(amount, "ether")
|
||||
};
|
||||
web3.eth.sendTransaction(transaction, function (error, result) {
|
||||
if(!error) {
|
||||
total += Number(amount);
|
||||
}
|
||||
});
|
||||
}
|
||||
`)
|
||||
require.NotContains(parseResult, "error", "further will fail if initial parsing failed")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||
s.T().Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||
return
|
||||
}
|
||||
if envelope.Type == txqueue.EventTransactionQueued {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
s.T().Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
||||
|
||||
//var txHash common.Hash
|
||||
txID := event["id"].(string)
|
||||
txHash, err := s.backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
require.NoError(err, "cannot complete queued transaction[%v]: %v", event["id"], err)
|
||||
|
||||
s.T().Logf("Transaction complete: https://ropsten.etherscan.io/tx/%s", txHash.Hex())
|
||||
}
|
||||
})
|
||||
|
||||
// run commands concurrently
|
||||
for _, tc := range testCases {
|
||||
wg.Add(1)
|
||||
go func(tc testCase) {
|
||||
defer wg.Done() // ensure we don't forget it
|
||||
|
||||
s.T().Logf("CALL START: %v %v", tc.command, tc.params)
|
||||
response := jailInstance.Call(testChatID, tc.command, tc.params)
|
||||
if err := tc.validator(response); err != nil {
|
||||
s.T().Errorf("failed test validation: %v, err: %v", tc.command, err)
|
||||
}
|
||||
s.T().Logf("CALL END: %v %v", tc.command, tc.params)
|
||||
}(tc)
|
||||
}
|
||||
|
||||
finishTestCases := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(finishTestCases)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-finishTestCases:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("some tests failed to finish in time")
|
||||
}
|
||||
|
||||
// Wait till eth_sendTransaction callbacks have been executed.
|
||||
// FIXME(tiabc): more reliable means of testing that.
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Validate total.
|
||||
cell, err := jailInstance.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
|
||||
totalOtto, err := cell.Get("total")
|
||||
require.NoError(err)
|
||||
|
||||
total, err := totalOtto.ToFloat()
|
||||
require.NoError(err)
|
||||
|
||||
s.T().Log(total)
|
||||
require.InDelta(0.840003, total, 0.0000001)
|
||||
}
|
|
@ -1,626 +0,0 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/api"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestBackendTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(BackendTestSuite))
|
||||
}
|
||||
|
||||
type BackendTestSuite struct {
|
||||
suite.Suite
|
||||
backend *api.StatusBackend
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) SetupTest() {
|
||||
require := s.Require()
|
||||
backend := api.NewStatusBackend()
|
||||
require.NotNil(backend)
|
||||
require.IsType(&api.StatusBackend{}, backend)
|
||||
s.backend = backend
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) StartTestBackend(networkID int, opts ...TestNodeOption) {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(networkID)
|
||||
require.NoError(err)
|
||||
|
||||
// Apply any options altering node config.
|
||||
for i := range opts {
|
||||
opts[i](nodeConfig)
|
||||
}
|
||||
|
||||
// import account keys
|
||||
require.NoError(common.ImportTestAccount(nodeConfig.KeyStoreDir, "test-account1.pk"))
|
||||
require.NoError(common.ImportTestAccount(nodeConfig.KeyStoreDir, "test-account2.pk"))
|
||||
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
<-nodeStarted
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) StopTestBackend() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
backendStopped, err := s.backend.StopNode()
|
||||
require.NoError(err)
|
||||
<-backendStopped
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) WhisperService() *whisper.Whisper {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
whisperService, err := s.backend.NodeManager().WhisperService()
|
||||
require.NoError(err)
|
||||
require.NotNil(whisperService)
|
||||
|
||||
return whisperService
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) LightEthereumService() *les.LightEthereum {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
lightEthereum, err := s.backend.NodeManager().LightEthereumService()
|
||||
require.NoError(err)
|
||||
require.NotNil(lightEthereum)
|
||||
|
||||
return lightEthereum
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TxQueueManager() common.TxQueueManager {
|
||||
return s.backend.TxQueueManager()
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) RestartTestNode() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
nodeRestarted, err := s.backend.RestartNode()
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeRestarted)
|
||||
<-nodeRestarted
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestNewBackend() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestNodeStartStop() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
// try stopping non-started node
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStopped, err := s.backend.StopNode()
|
||||
require.EqualError(err, node.ErrNoRunningNode.Error())
|
||||
require.Nil(nodeStopped)
|
||||
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeStarted)
|
||||
|
||||
<-nodeStarted // wait till node is started
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
|
||||
// try starting another node (w/o stopping the previously started node)
|
||||
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
||||
require.EqualError(err, node.ErrNodeExists.Error())
|
||||
require.Nil(nodeStarted)
|
||||
|
||||
// now stop node, and make sure that a new node, on different network can be started
|
||||
nodeStopped, err = s.backend.StopNode()
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeStopped)
|
||||
<-nodeStopped
|
||||
|
||||
// start new node with exactly the same config
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeStarted)
|
||||
defer s.backend.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestStartNodeWithUpstreamEnabled() {
|
||||
require := s.Require()
|
||||
|
||||
backend := api.NewStatusBackend()
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeConfig.UpstreamConfig.Enabled = true
|
||||
|
||||
nodeStarted, err := backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
defer backend.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
require.True(backend.IsNodeRunning())
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/rpc_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *BackendTestSuite) TestCallRPC() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeStarted)
|
||||
defer s.backend.StopNode()
|
||||
<-nodeStarted
|
||||
|
||||
progress := make(chan struct{}, 25)
|
||||
type rpcCall struct {
|
||||
inputJSON string
|
||||
validator func(resultJSON string)
|
||||
}
|
||||
var rpcCalls = []rpcCall{
|
||||
{
|
||||
`{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{
|
||||
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x76c0",
|
||||
"gasPrice": "0x9184e72a000",
|
||||
"value": "0x9184e72a",
|
||||
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}`,
|
||||
func(resultJSON string) {
|
||||
log.Info("eth_sendTransaction")
|
||||
s.NotContains(resultJSON, "error")
|
||||
progress <- struct{}{}
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"jsonrpc":"2.0","method":"shh_version","params":[],"id":67}`,
|
||||
func(resultJSON string) {
|
||||
expected := `{"jsonrpc":"2.0","id":67,"result":"5.0"}`
|
||||
s.Equal(expected, resultJSON)
|
||||
s.T().Log("shh_version: ", resultJSON)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`,
|
||||
func(resultJSON string) {
|
||||
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}`
|
||||
s.Equal(expected, resultJSON)
|
||||
s.T().Log("web3_sha3: ", resultJSON)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"jsonrpc":"2.0","method":"net_version","params":[],"id":67}`,
|
||||
func(resultJSON string) {
|
||||
expected := `{"jsonrpc":"2.0","id":67,"result":"4"}`
|
||||
s.Equal(expected, resultJSON)
|
||||
s.T().Log("net_version: ", resultJSON)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cnt := len(rpcCalls) - 1 // send transaction blocks up until complete/discarded/times out
|
||||
for _, r := range rpcCalls {
|
||||
go func(r rpcCall) {
|
||||
s.T().Logf("Run test: %v", r.inputJSON)
|
||||
resultJSON := s.backend.CallRPC(r.inputJSON)
|
||||
r.validator(resultJSON)
|
||||
}(r)
|
||||
}
|
||||
|
||||
for range progress {
|
||||
cnt -= 1
|
||||
if cnt <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestCallRPCSendTransaction() {
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
defer s.backend.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
|
||||
// Allow to sync the blockchain.
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
err = s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
|
||||
transactionCompleted := make(chan struct{})
|
||||
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(rawSignal string) {
|
||||
var signal signal.Envelope
|
||||
err := json.Unmarshal([]byte(rawSignal), &signal)
|
||||
s.NoError(err)
|
||||
|
||||
if signal.Type == txqueue.EventTransactionQueued {
|
||||
event := signal.Event.(map[string]interface{})
|
||||
txID := event["id"].(string)
|
||||
txHash, err = s.backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
||||
s.NoError(err, "cannot complete queued transaction %s", txID)
|
||||
|
||||
close(transactionCompleted)
|
||||
}
|
||||
})
|
||||
|
||||
result := s.backend.CallRPC(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": "` + TestConfig.Account1.Address + `",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"value": "0x9184e72a"
|
||||
}]
|
||||
}`)
|
||||
s.NotContains(result, "error")
|
||||
|
||||
select {
|
||||
case <-transactionCompleted:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("sending transaction timed out")
|
||||
}
|
||||
|
||||
s.Equal(`{"jsonrpc":"2.0","id":1,"result":"`+txHash.String()+`"}`, result)
|
||||
}
|
||||
|
||||
func (s *BackendTestSuite) TestCallRPCSendTransactionUpstream() {
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
s.NoError(err)
|
||||
|
||||
nodeConfig.UpstreamConfig.Enabled = true
|
||||
nodeConfig.UpstreamConfig.URL = "https://ropsten.infura.io/nKmXgiFgc2KqtoQ8BCGJ"
|
||||
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
s.NoError(err)
|
||||
defer s.backend.StopNode()
|
||||
|
||||
<-nodeStarted
|
||||
|
||||
// Allow to sync the blockchain.
|
||||
time.Sleep(TestConfig.Node.SyncSeconds * time.Second)
|
||||
|
||||
err = s.backend.AccountManager().SelectAccount(TestConfig.Account2.Address, TestConfig.Account2.Password)
|
||||
s.NoError(err)
|
||||
|
||||
transactionCompleted := make(chan struct{})
|
||||
|
||||
var txHash gethcommon.Hash
|
||||
signal.SetDefaultNodeNotificationHandler(func(rawSignal string) {
|
||||
var signal signal.Envelope
|
||||
err := json.Unmarshal([]byte(rawSignal), &signal)
|
||||
s.NoError(err)
|
||||
|
||||
if signal.Type == txqueue.EventTransactionQueued {
|
||||
event := signal.Event.(map[string]interface{})
|
||||
txID := event["id"].(string)
|
||||
|
||||
// Complete with a wrong passphrase.
|
||||
txHash, err = s.backend.CompleteTransaction(common.QueuedTxID(txID), "some-invalid-passphrase")
|
||||
s.EqualError(err, keystore.ErrDecrypt.Error(), "should return an error as the passphrase was invalid")
|
||||
|
||||
// Complete with a correct passphrase.
|
||||
txHash, err = s.backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account2.Password)
|
||||
s.NoError(err, "cannot complete queued transaction %s", txID)
|
||||
|
||||
close(transactionCompleted)
|
||||
}
|
||||
})
|
||||
|
||||
result := s.backend.CallRPC(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": "` + TestConfig.Account2.Address + `",
|
||||
"to": "` + TestConfig.Account1.Address + `",
|
||||
"value": "0x9184e72a"
|
||||
}]
|
||||
}`)
|
||||
s.NotContains(result, "error")
|
||||
|
||||
select {
|
||||
case <-transactionCompleted:
|
||||
case <-time.After(time.Minute):
|
||||
s.FailNow("sending transaction timed out")
|
||||
}
|
||||
|
||||
s.Equal(`{"jsonrpc":"2.0","id":1,"result":"`+txHash.String()+`"}`, result)
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *BackendTestSuite) TestRaceConditions() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
cnt := 25
|
||||
progress := make(chan struct{}, cnt)
|
||||
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
nodeConfig1, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeConfig2, err := MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
|
||||
|
||||
var funcsToTest = []func(*params.NodeConfig){
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("StartNode()")
|
||||
_, err := s.backend.StartNode(config)
|
||||
s.T().Logf("StartNode() for network: %d, error: %v", config.NetworkID, err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("StopNode()")
|
||||
_, err := s.backend.StopNode()
|
||||
s.T().Logf("StopNode() for network: %d, error: %v", config.NetworkID, err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("ResetChainData()")
|
||||
_, err := s.backend.ResetChainData()
|
||||
s.T().Logf("ResetChainData(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("RestartNode()")
|
||||
_, err := s.backend.RestartNode()
|
||||
s.T().Logf("RestartNode(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("NodeManager()")
|
||||
instance := s.backend.NodeManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&node.NodeManager{}, instance)
|
||||
s.T().Logf("NodeManager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("AccountManager()")
|
||||
instance := s.backend.AccountManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&account.Manager{}, instance)
|
||||
s.T().Logf("Manager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("JailManager()")
|
||||
instance := s.backend.JailManager()
|
||||
s.NotNil(instance)
|
||||
s.IsType(&jail.Jail{}, instance)
|
||||
s.T().Logf("JailManager(), result: %v", instance)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CreateAccount()")
|
||||
address, pubKey, mnemonic, err := s.backend.AccountManager().CreateAccount("password")
|
||||
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v, mnemonic: %v)", err, address, pubKey, mnemonic)
|
||||
if err == nil {
|
||||
// SelectAccount
|
||||
log.Info("CreateAccount()")
|
||||
err = s.backend.AccountManager().SelectAccount(address, "password")
|
||||
s.T().Logf("SelectAccount(%v, %v), error: %v", address, "password", err)
|
||||
|
||||
// CreateChildAccount
|
||||
log.Info("CreateChildAccount()")
|
||||
address, pubKey, err := s.backend.AccountManager().CreateChildAccount(address, "password")
|
||||
s.T().Logf("CreateAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||
|
||||
// RecoverAccount
|
||||
log.Info("RecoverAccount()")
|
||||
address, pubKey, err = s.backend.AccountManager().RecoverAccount("password", mnemonic)
|
||||
s.T().Logf("RecoverAccount(), error: %v (address: %v, pubKey: %v)", err, address, pubKey)
|
||||
}
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("VerifyAccountPassword()")
|
||||
_, err := s.backend.AccountManager().VerifyAccountPassword(config.KeyStoreDir, "0x0", "bar")
|
||||
s.T().Logf("VerifyAccountPassword(), err: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("Logout()")
|
||||
s.T().Logf("Logout(), result: %v", s.backend.AccountManager().Logout())
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("IsNodeRunning()")
|
||||
s.T().Logf("IsNodeRunning(), result: %v", s.backend.IsNodeRunning())
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CompleteTransaction()")
|
||||
_, err := s.backend.CompleteTransaction("id", "password")
|
||||
s.T().Logf("CompleteTransaction(), error: %v", err)
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("DiscardTransaction()")
|
||||
s.T().Logf("DiscardTransaction(), error: %v", s.backend.DiscardTransaction("id"))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("CompleteTransactions()")
|
||||
ids := []common.QueuedTxID{"id1", "id2"}
|
||||
s.T().Logf("CompleteTransactions(), result: %v", s.backend.CompleteTransactions(ids, "password"))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
func(config *params.NodeConfig) {
|
||||
log.Info("DiscardTransactions()")
|
||||
ids := []common.QueuedTxID{"id1", "id2"}
|
||||
s.T().Logf("DiscardTransactions(), result: %v", s.backend.DiscardTransactions(ids))
|
||||
progress <- struct{}{}
|
||||
},
|
||||
}
|
||||
|
||||
// increase StartNode()/StopNode() population
|
||||
for i := 0; i < 5; i++ {
|
||||
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
|
||||
}
|
||||
|
||||
for i := 0; i < cnt; i++ {
|
||||
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))]
|
||||
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))]
|
||||
|
||||
if rnd.Intn(100) > 75 { // introduce random delays
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
go randFunc(randConfig)
|
||||
}
|
||||
|
||||
for range progress {
|
||||
cnt -= 1
|
||||
if cnt <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // so that we see some logs
|
||||
nodeStopped, _ := s.backend.StopNode() // just in case we have a node running
|
||||
if nodeStopped != nil {
|
||||
<-nodeStopped
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *BackendTestSuite) TestNetworkSwitching() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
// get Ropsten config
|
||||
nodeConfig, err := MakeTestNodeConfig(params.RopstenNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStarted, err := s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
<-nodeStarted // wait till node is started
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
|
||||
FirstBlockHash(require, s.backend.NodeManager(), "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
|
||||
// now stop node, and make sure that a new node, on different network can be started
|
||||
nodeStopped, err := s.backend.StopNode()
|
||||
require.NoError(err)
|
||||
<-nodeStopped
|
||||
|
||||
// start new node with completely different config
|
||||
nodeConfig, err = MakeTestNodeConfig(params.RinkebyNetworkID)
|
||||
require.NoError(err)
|
||||
|
||||
require.False(s.backend.IsNodeRunning())
|
||||
nodeStarted, err = s.backend.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
|
||||
<-nodeStarted
|
||||
require.True(s.backend.IsNodeRunning())
|
||||
|
||||
// make sure we are on another network indeed
|
||||
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
|
||||
nodeStopped, err = s.backend.StopNode()
|
||||
require.NoError(err)
|
||||
<-nodeStopped
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *BackendTestSuite) TestResetChainData() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
time.Sleep(2 * time.Second) // allow to sync for some time
|
||||
|
||||
s.True(s.backend.IsNodeRunning())
|
||||
nodeReady, err := s.backend.ResetChainData()
|
||||
require.NoError(err)
|
||||
<-nodeReady
|
||||
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
}
|
||||
|
||||
// FIXME(tiabc): There's also a test with the same name in geth/node/manager_test.go
|
||||
// so this test should only check StatusBackend logic with a mocked version of the underlying NodeManager.
|
||||
func (s *BackendTestSuite) TestRestartNode() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.backend)
|
||||
|
||||
s.StartTestBackend(params.RinkebyNetworkID)
|
||||
defer s.StopTestBackend()
|
||||
|
||||
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
|
||||
s.True(s.backend.IsNodeRunning())
|
||||
nodeRestarted, err := s.backend.RestartNode()
|
||||
require.NoError(err)
|
||||
<-nodeRestarted
|
||||
s.True(s.backend.IsNodeRunning()) // new node, with previous config should be running
|
||||
|
||||
// make sure we can read the first byte, and it is valid (for Rinkeby)
|
||||
FirstBlockHash(require, s.backend.NodeManager(), "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
}
|
|
@ -6,40 +6,30 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/robertkrimen/otto"
|
||||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/static"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func (s *JailTestSuite) TestJailTimeoutFailure() {
|
||||
require := s.Require()
|
||||
const (
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
cell, err := s.jail.NewCell(testChatID)
|
||||
require.NoError(err)
|
||||
require.NotNil(cell)
|
||||
defer cell.Stop()
|
||||
var (
|
||||
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
|
||||
)
|
||||
|
||||
// Attempt to run a timeout string against a Cell.
|
||||
_, err = cell.Run(`
|
||||
var timerCounts = 0;
|
||||
setTimeout(function(n){
|
||||
if (Date.now() - n < 50) {
|
||||
throw new Error("Timed out");
|
||||
}
|
||||
|
||||
timerCounts++;
|
||||
}, 30, Date.now());
|
||||
`)
|
||||
require.NoError(err)
|
||||
|
||||
// wait at least 10x longer to decrease probability
|
||||
// of false negatives as we using real clock here
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
value, err := cell.Get("timerCounts")
|
||||
require.NoError(err)
|
||||
require.True(value.IsNumber())
|
||||
require.Equal("0", value.String())
|
||||
type CellTestSuite struct {
|
||||
suite.Suite
|
||||
jail *jail.Jail
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestJailTimeout() {
|
||||
func (s *CellTestSuite) SetupTest() {
|
||||
s.jail = jail.New(nil)
|
||||
s.NotNil(s.jail)
|
||||
}
|
||||
|
||||
func (s *CellTestSuite) TestJailTimeout() {
|
||||
require := s.Require()
|
||||
|
||||
cell, err := s.jail.NewCell(testChatID)
|
||||
|
@ -50,7 +40,7 @@ func (s *JailTestSuite) TestJailTimeout() {
|
|||
// Attempt to run a timeout string against a Cell.
|
||||
_, err = cell.Run(`
|
||||
var timerCounts = 0;
|
||||
setTimeout(function(n){
|
||||
setTimeout(function(n){
|
||||
if (Date.now() - n < 50) {
|
||||
throw new Error("Timed out");
|
||||
}
|
||||
|
@ -70,7 +60,7 @@ func (s *JailTestSuite) TestJailTimeout() {
|
|||
require.Equal("1", value.String())
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestJailLoopInCall() {
|
||||
func (s *CellTestSuite) TestJailLoopInCall() {
|
||||
require := s.Require()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
|
@ -113,7 +103,7 @@ func (s *JailTestSuite) TestJailLoopInCall() {
|
|||
|
||||
// TestJailLoopRace tests multiple setTimeout callbacks,
|
||||
// supposed to be run with '-race' flag.
|
||||
func (s *JailTestSuite) TestJailLoopRace() {
|
||||
func (s *CellTestSuite) TestJailLoopRace() {
|
||||
require := s.Require()
|
||||
|
||||
cell, err := s.jail.NewCell(testChatID)
|
||||
|
@ -152,7 +142,7 @@ func (s *JailTestSuite) TestJailLoopRace() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestJailFetchPromise() {
|
||||
func (s *CellTestSuite) TestJailFetchPromise() {
|
||||
body := `{"key": "value"}`
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
@ -196,7 +186,7 @@ func (s *JailTestSuite) TestJailFetchPromise() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestJailFetchCatch() {
|
||||
func (s *CellTestSuite) TestJailFetchCatch() {
|
||||
require := s.Require()
|
||||
|
||||
cell, err := s.jail.NewCell(testChatID)
|
||||
|
@ -239,7 +229,7 @@ func (s *JailTestSuite) TestJailFetchCatch() {
|
|||
|
||||
// TestJailFetchRace tests multiple fetch callbacks,
|
||||
// supposed to be run with '-race' flag.
|
||||
func (s *JailTestSuite) TestJailFetchRace() {
|
||||
func (s *CellTestSuite) TestJailFetchRace() {
|
||||
body := `{"key": "value"}`
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
@ -302,7 +292,7 @@ func (s *JailTestSuite) TestJailFetchRace() {
|
|||
|
||||
// TestJailLoopCancel tests that cell.Stop() really cancels event
|
||||
// loop and pending tasks.
|
||||
func (s *JailTestSuite) TestJailLoopCancel() {
|
||||
func (s *CellTestSuite) TestJailLoopCancel() {
|
||||
require := s.Require()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package fetch_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -210,7 +211,7 @@ func (s *FetchSuite) SetupTest() {
|
|||
s.vm = vm.New(o)
|
||||
s.loop = loop.New(s.vm)
|
||||
|
||||
go s.loop.Run()
|
||||
go s.loop.Run(context.Background())
|
||||
}
|
||||
|
||||
func (s *FetchSuite) TearDownSuite() {
|
||||
|
|
|
@ -115,6 +115,12 @@ func (c CallTask) Cancel() {}
|
|||
// pushing the resultant return value and error (or nil) into the associated
|
||||
// channels. If the call results in an error, it will return that error.
|
||||
func (c CallTask) Execute(vm *vm.VM, l *loop.Loop) error {
|
||||
// vm is not used directly here, but underlying
|
||||
// FunctionCall in CallTask likely does use it,
|
||||
// so we must to guard it here
|
||||
vm.Lock()
|
||||
defer vm.Unlock()
|
||||
|
||||
v, err := c.Function.Call(otto.NullValue(), c.Args...)
|
||||
c.Value <- v
|
||||
c.Error <- err
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package promise_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -88,7 +89,7 @@ func (s *PromiseSuite) SetupTest() {
|
|||
s.vm = vm.New(o)
|
||||
s.loop = loop.New(s.vm)
|
||||
|
||||
go s.loop.Run()
|
||||
go s.loop.Run(context.Background())
|
||||
|
||||
err := promise.Define(s.vm, s.loop)
|
||||
s.NoError(err)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package timers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -106,7 +107,7 @@ func (s *TimersSuite) SetupTest() {
|
|||
s.vm = vm.New(o)
|
||||
s.loop = loop.New(s.vm)
|
||||
|
||||
go s.loop.Run()
|
||||
go s.loop.Run(context.Background())
|
||||
|
||||
err := timers.Define(s.vm, s.loop)
|
||||
s.NoError(err)
|
||||
|
|
|
@ -35,9 +35,6 @@ type Jail struct {
|
|||
// New returns new Jail environment with the associated NodeManager.
|
||||
// It's caller responsibility to call jail.Stop() when jail is not needed.
|
||||
func New(nodeManager common.NodeManager) *Jail {
|
||||
if nodeManager == nil {
|
||||
panic("Jail is missing mandatory dependencies")
|
||||
}
|
||||
return &Jail{
|
||||
nodeManager: nodeManager,
|
||||
cells: make(map[string]*Cell),
|
||||
|
|
|
@ -1,345 +0,0 @@
|
|||
package jail_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/geth/account"
|
||||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
"github.com/status-im/status-go/static"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
const (
|
||||
testChatID = "testChat"
|
||||
)
|
||||
|
||||
var (
|
||||
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
|
||||
txJSCode = string(static.MustAsset("testdata/jail/tx-send/tx-send.js"))
|
||||
)
|
||||
|
||||
func TestJailTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(JailTestSuite))
|
||||
}
|
||||
|
||||
type JailTestSuite struct {
|
||||
BaseTestSuite
|
||||
jail *jail.Jail
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) SetupTest() {
|
||||
require := s.Require()
|
||||
|
||||
nodeManager := node.NewNodeManager()
|
||||
require.NotNil(nodeManager)
|
||||
|
||||
accountManager := account.NewManager(nodeManager)
|
||||
require.NotNil(accountManager)
|
||||
|
||||
jail := jail.New(nodeManager)
|
||||
require.NotNil(jail)
|
||||
|
||||
s.jail = jail
|
||||
s.NodeManager = nodeManager
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TearDownTest() {
|
||||
s.jail.Stop()
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestInit() {
|
||||
require := s.Require()
|
||||
|
||||
errorWrapper := func(err error) string {
|
||||
return `{"error":"` + err.Error() + `"}`
|
||||
}
|
||||
|
||||
// get cell VM w/o defining cell first
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
require.EqualError(err, "cell[testChat] doesn't exist")
|
||||
require.Nil(cell)
|
||||
|
||||
// create VM (w/o properly initializing base JS script)
|
||||
err = errors.New("ReferenceError: '_status_catalog' is not defined")
|
||||
require.Equal(errorWrapper(err), s.jail.Parse(testChatID, ``))
|
||||
err = errors.New("ReferenceError: 'call' is not defined")
|
||||
require.Equal(errorWrapper(err), s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`))
|
||||
|
||||
// get existing cell (even though we got errors, cell was still created)
|
||||
cell, err = s.jail.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
require.NotNil(cell)
|
||||
|
||||
statusJS := baseStatusJSCode + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
s.jail.BaseJS(statusJS)
|
||||
|
||||
// now no error should occur
|
||||
response := s.jail.Parse(testChatID, ``)
|
||||
expectedResponse := `{"result": {"commands":{},"responses":{}}}`
|
||||
require.Equal(expectedResponse, response)
|
||||
|
||||
// make sure that Call succeeds even w/o running node
|
||||
response = s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse = `{"result": 144}`
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestParse() {
|
||||
require := s.Require()
|
||||
|
||||
extraCode := `
|
||||
var _status_catalog = {
|
||||
foo: 'bar'
|
||||
};
|
||||
`
|
||||
response := s.jail.Parse("newChat", extraCode)
|
||||
expectedResponse := `{"result": {"foo":"bar"}}`
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestFunctionCall() {
|
||||
require := s.Require()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
statusJS := baseStatusJSCode + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
s.jail.Parse(testChatID, statusJS)
|
||||
|
||||
// call with wrong chat id
|
||||
response := s.jail.Call("chatIDNonExistent", "", "")
|
||||
expectedError := `{"error":"cell[chatIDNonExistent] doesn't exist"}`
|
||||
require.Equal(expectedError, response)
|
||||
|
||||
// call extraFunc()
|
||||
response = s.jail.Call(testChatID, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse := `{"result": 144}`
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
// TestJailRPCAsyncSend was written to catch race conditions with a weird error message
|
||||
// starting from `ReferenceError` as if otto vm were losing symbols.
|
||||
func (s *JailTestSuite) TestJailRPCAsyncSend() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestNode(params.RopstenNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
s.jail.BaseJS(baseStatusJSCode)
|
||||
s.jail.Parse(testChatID, txJSCode)
|
||||
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
require.NotNil(cell)
|
||||
|
||||
// internally (since we replaced `web3.send` with `jail.Send`)
|
||||
// all requests to web3 are forwarded to `jail.Send`
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
_, err = cell.Run(`_status_catalog.commands.sendAsync({
|
||||
"from": "` + TestConfig.Account1.Address + `",
|
||||
"to": "` + TestConfig.Account2.Address + `",
|
||||
"value": "0.000001"
|
||||
})`)
|
||||
require.NoError(err, "Request failed to process")
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// TODO(divan): revisit this test. sendAsync now returns immediately,
|
||||
// and we need no way here to halt jail loop, which executes actual
|
||||
// transaction send in background. For now, just wait a couple of secs
|
||||
// to let tests pass.
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestJailRPCSend() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestNode(params.RopstenNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
// load Status JS and add test command to it
|
||||
s.jail.BaseJS(baseStatusJSCode)
|
||||
s.jail.Parse(testChatID, ``)
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
require.NotNil(cell)
|
||||
|
||||
// internally (since we replaced `web3.send` with `jail.Send`)
|
||||
// all requests to web3 are forwarded to `jail.Send`
|
||||
_, err = cell.Run(`
|
||||
var balance = web3.eth.getBalance("` + TestConfig.Account1.Address + `");
|
||||
var sendResult = web3.fromWei(balance, "ether")
|
||||
`)
|
||||
require.NoError(err)
|
||||
|
||||
value, err := cell.Get("sendResult")
|
||||
require.NoError(err, "cannot obtain result of balance check operation")
|
||||
|
||||
balance, err := value.ToFloat()
|
||||
require.NoError(err)
|
||||
|
||||
require.False(balance < 100, "wrong balance (there should be lots of test Ether on that account)")
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestIsConnected() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestNode(params.RopstenNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
s.jail.Parse(testChatID, "")
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = web3.isConnected();
|
||||
responseValue = JSON.stringify(responseValue);
|
||||
`)
|
||||
require.NoError(err)
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
require.NoError(err, "cannot obtain result of isConnected()")
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
require.NoError(err, "cannot parse result")
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","result":true}`
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
func (s *JailTestSuite) TestEventSignal() {
|
||||
require := s.Require()
|
||||
|
||||
s.jail.Parse(testChatID, "")
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
cell, err := s.jail.Cell(testChatID)
|
||||
require.NoError(err)
|
||||
|
||||
testData := "foobar"
|
||||
|
||||
opCompletedSuccessfully := make(chan struct{}, 1)
|
||||
|
||||
// replace transaction notification handler
|
||||
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||
var envelope signal.Envelope
|
||||
err := json.Unmarshal([]byte(jsonEvent), &envelope)
|
||||
require.NoError(err)
|
||||
|
||||
if envelope.Type == jail.EventSignal {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
chatID, ok := event["chat_id"].(string)
|
||||
require.True(ok, "chat id is required, but not found")
|
||||
require.Equal(testChatID, chatID, "incorrect chat ID")
|
||||
|
||||
actualData, ok := event["data"].(string)
|
||||
require.True(ok, "data field is required, but not found")
|
||||
require.Equal(testData, actualData, "incorrect data")
|
||||
|
||||
s.T().Logf("event processed: %s", jsonEvent)
|
||||
close(opCompletedSuccessfully)
|
||||
}
|
||||
})
|
||||
|
||||
_, err = cell.Run(`
|
||||
var responseValue = statusSignals.sendSignal("` + testData + `");
|
||||
responseValue = JSON.stringify(responseValue);
|
||||
`)
|
||||
s.NoError(err)
|
||||
|
||||
// make sure that signal is sent (and its parameters are correct)
|
||||
select {
|
||||
case <-opCompletedSuccessfully:
|
||||
// pass
|
||||
case <-time.After(3 * time.Second):
|
||||
require.Fail("operation timed out")
|
||||
}
|
||||
|
||||
responseValue, err := cell.Get("responseValue")
|
||||
require.NoError(err, "cannot obtain result of localStorage.set()")
|
||||
|
||||
response, err := responseValue.ToString()
|
||||
require.NoError(err, "cannot parse result")
|
||||
|
||||
expectedResponse := `{"jsonrpc":"2.0","result":true}`
|
||||
require.Equal(expectedResponse, response)
|
||||
}
|
||||
|
||||
// TestCallResponseOrder tests for problem in
|
||||
// https://github.com/status-im/status-go/issues/372
|
||||
func (s *JailTestSuite) TestCallResponseOrder() {
|
||||
require := s.Require()
|
||||
|
||||
s.StartTestNode(params.RopstenNetworkID)
|
||||
defer s.StopTestNode()
|
||||
|
||||
statusJS := baseStatusJSCode + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};
|
||||
_status_catalog.commands["calculateGasPrice"] = function (n) {
|
||||
var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
|
||||
var price = 211000000000;
|
||||
try {
|
||||
price = web3.eth.gasPrice;
|
||||
} catch (err) {}
|
||||
|
||||
return price * gasMultiplicator;
|
||||
};
|
||||
`
|
||||
s.jail.Parse(testChatID, statusJS)
|
||||
|
||||
N := 1000
|
||||
errCh := make(chan error, N)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(2)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
res := s.jail.Call(testChatID, `["commands", "testCommand"]`, fmt.Sprintf(`{"val": %d}`, i))
|
||||
if !strings.Contains(res, fmt.Sprintf("result\": %d", i*i)) {
|
||||
errCh <- fmt.Errorf("result should be '%d', got %s", i*i, res)
|
||||
}
|
||||
}(i)
|
||||
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
res := s.jail.Call(testChatID, `["commands", "calculateGasPrice"]`, fmt.Sprintf(`%d`, i))
|
||||
if strings.Contains(res, "error") {
|
||||
errCh <- fmt.Errorf("result should not contain 'error', got %s", res)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
close(errCh)
|
||||
for e := range errCh {
|
||||
require.NoError(e)
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
gethparams "github.com/ethereum/go-ethereum/params"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -245,6 +245,7 @@ var loadConfigTestCases = []struct {
|
|||
"enode://ce6854c2c77a8800fcc12600206c344b8053bb90ee3ba280e6c4f18f3141cdc5ee80bcc3bdb24cbc0e96dffd4b38d7b57546ed528c00af6cd604ab65c4d528f6@163.172.153.124:30303",
|
||||
"enode://00ae60771d9815daba35766d463a82a7b360b3a80e35ab2e0daa25bdc6ca6213ff4c8348025e7e1a908a8f58411a364fe02a0fb3c2aa32008304f063d8aaf1a2@163.172.132.85:30303",
|
||||
"enode://86ebc843aa51669e08e27400e435f957918e39dc540b021a2f3291ab776c88bbda3d97631639219b6e77e375ab7944222c47713bdeb3251b25779ce743a39d70@212.47.254.155:30303",
|
||||
"enode://a1ef9ba5550d5fac27f7cbd4e8d20a643ad75596f307c91cd6e7f85b548b8a6bf215cca436d6ee436d6135f9fe51398f8dd4c0bd6c6a0c332ccb41880f33ec12@51.15.218.125:30303",
|
||||
}
|
||||
require.Equal(t, expectedEnodes, enodes)
|
||||
},
|
||||
|
@ -295,6 +296,7 @@ var loadConfigTestCases = []struct {
|
|||
"enode://ce6854c2c77a8800fcc12600206c344b8053bb90ee3ba280e6c4f18f3141cdc5ee80bcc3bdb24cbc0e96dffd4b38d7b57546ed528c00af6cd604ab65c4d528f6@163.172.153.124:30303",
|
||||
"enode://00ae60771d9815daba35766d463a82a7b360b3a80e35ab2e0daa25bdc6ca6213ff4c8348025e7e1a908a8f58411a364fe02a0fb3c2aa32008304f063d8aaf1a2@163.172.132.85:30303",
|
||||
"enode://86ebc843aa51669e08e27400e435f957918e39dc540b021a2f3291ab776c88bbda3d97631639219b6e77e375ab7944222c47713bdeb3251b25779ce743a39d70@212.47.254.155:30303",
|
||||
"enode://a1ef9ba5550d5fac27f7cbd4e8d20a643ad75596f307c91cd6e7f85b548b8a6bf215cca436d6ee436d6135f9fe51398f8dd4c0bd6c6a0c332ccb41880f33ec12@51.15.218.125:30303",
|
||||
}
|
||||
require.Equal(t, expectedEnodes, enodes)
|
||||
},
|
||||
|
@ -457,7 +459,7 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
refConfigData = strings.Replace(refConfigData, "$TMPDIR", nodeConfig.DataDir, -1)
|
||||
refConfigData = strings.Replace(refConfigData, "$VERSION", params.Version, -1)
|
||||
|
||||
require.EqualValues(t, refConfigData, loadedConfigData)
|
||||
require.Equal(t, strings.TrimSpace(string(refConfigData[:])), strings.TrimSpace(string(loadedConfigData[:])))
|
||||
}
|
||||
|
||||
configReadWrite(params.RinkebyNetworkID, "testdata/config.rinkeby.json")
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,45 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetResultFromRPCResponse(t *testing.T) {
|
||||
var err error
|
||||
|
||||
var resultRawMessage json.RawMessage
|
||||
err = setResultFromRPCResponse(&resultRawMessage, []string{"one", "two", "three"})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, json.RawMessage(`["one","two","three"]`), resultRawMessage)
|
||||
|
||||
var resultSlice []int
|
||||
err = setResultFromRPCResponse(&resultSlice, []int{1, 2, 3})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []int{1, 2, 3}, resultSlice)
|
||||
|
||||
var resultMap map[string]interface{}
|
||||
err = setResultFromRPCResponse(&resultMap, map[string]interface{}{"test": true})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[string]interface{}{"test": true}, resultMap)
|
||||
|
||||
var resultStruct struct {
|
||||
A int
|
||||
B string
|
||||
}
|
||||
err = setResultFromRPCResponse(&resultStruct, struct {
|
||||
A int
|
||||
B string
|
||||
}{5, "test"})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, struct {
|
||||
A int
|
||||
B string
|
||||
}{5, "test"}, resultStruct)
|
||||
|
||||
var resultIncorrectType []int
|
||||
err = setResultFromRPCResponse(&resultIncorrectType, []string{"a", "b"})
|
||||
require.Error(t, err)
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
// Package testing implements the base level testing types needed.
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
assertions "github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
// TestConfig defines the default config usable at package-level.
|
||||
TestConfig *common.TestConfig
|
||||
|
||||
// RootDir is the main application directory
|
||||
RootDir string
|
||||
|
||||
// TestDataDir is data directory used for tests
|
||||
TestDataDir string
|
||||
|
||||
// TestNetworkNames network ID to name mapping
|
||||
TestNetworkNames = map[int]string{
|
||||
params.MainNetworkID: "Mainnet",
|
||||
params.RopstenNetworkID: "Ropsten",
|
||||
params.RinkebyNetworkID: "Rinkeby",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// setup root directory
|
||||
RootDir = filepath.Dir(pwd)
|
||||
if strings.HasSuffix(RootDir, "geth") || strings.HasSuffix(RootDir, "cmd") { // we need to hop one more level
|
||||
RootDir = filepath.Join(RootDir, "..")
|
||||
}
|
||||
|
||||
// setup auxiliary directories
|
||||
TestDataDir = filepath.Join(RootDir, ".ethereumtest")
|
||||
|
||||
TestConfig, err = common.LoadTestConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// BaseTestSuite defines a base tests suit which others suites can embedded to
|
||||
// access initialization methods useful for testing.
|
||||
type BaseTestSuite struct {
|
||||
suite.Suite
|
||||
NodeManager common.NodeManager
|
||||
}
|
||||
|
||||
// StartTestNode initiazes a NodeManager instances with configuration retrieved
|
||||
// from the test config.
|
||||
func (s *BaseTestSuite) StartTestNode(networkID int, opts ...TestNodeOption) {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
|
||||
nodeConfig, err := MakeTestNodeConfig(networkID)
|
||||
require.NoError(err)
|
||||
|
||||
// Apply any options altering node config.
|
||||
for i := range opts {
|
||||
opts[i](nodeConfig)
|
||||
}
|
||||
|
||||
keyStoreDir := filepath.Join(TestDataDir, TestNetworkNames[networkID], "keystore")
|
||||
require.NoError(common.ImportTestAccount(keyStoreDir, "test-account1.pk"))
|
||||
require.NoError(common.ImportTestAccount(keyStoreDir, "test-account2.pk"))
|
||||
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
nodeStarted, err := s.NodeManager.StartNode(nodeConfig)
|
||||
require.NoError(err)
|
||||
require.NotNil(nodeStarted)
|
||||
<-nodeStarted
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
}
|
||||
|
||||
// TestNodeOption is a callback passed to StartTestNode which alters its config.
|
||||
type TestNodeOption func(config *params.NodeConfig)
|
||||
|
||||
// WithUpstream returns TestNodeOption which enabled UpstreamConfig.
|
||||
func WithUpstream(url string) TestNodeOption {
|
||||
return func(config *params.NodeConfig) {
|
||||
config.UpstreamConfig.Enabled = true
|
||||
config.UpstreamConfig.URL = url
|
||||
}
|
||||
}
|
||||
|
||||
// StopTestNode attempts to stop initialized NodeManager.
|
||||
func (s *BaseTestSuite) StopTestNode() {
|
||||
require := s.Require()
|
||||
require.NotNil(s.NodeManager)
|
||||
require.True(s.NodeManager.IsNodeRunning())
|
||||
nodeStopped, err := s.NodeManager.StopNode()
|
||||
require.NoError(err)
|
||||
<-nodeStopped
|
||||
require.False(s.NodeManager.IsNodeRunning())
|
||||
}
|
||||
|
||||
// FirstBlockHash validates Attach operation for the NodeManager.
|
||||
func FirstBlockHash(require *assertions.Assertions, nodeManager common.NodeManager, expectedHash string) {
|
||||
require.NotNil(nodeManager)
|
||||
|
||||
var firstBlock struct {
|
||||
Hash gethcommon.Hash `json:"hash"`
|
||||
}
|
||||
|
||||
// obtain RPC client for running node
|
||||
runningNode, err := nodeManager.Node()
|
||||
require.NoError(err)
|
||||
require.NotNil(runningNode)
|
||||
|
||||
rpcClient, err := runningNode.Attach()
|
||||
require.NoError(err)
|
||||
|
||||
// get first block
|
||||
err = rpcClient.CallContext(context.Background(), &firstBlock, "eth_getBlockByNumber", "0x0", true)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(expectedHash, firstBlock.Hash.Hex())
|
||||
}
|
||||
|
||||
// MakeTestNodeConfig defines a function to return a giving params.NodeConfig
|
||||
// where specific network addresses are assigned based on provieded network id.
|
||||
func MakeTestNodeConfig(networkID int) (*params.NodeConfig, error) {
|
||||
testDir := filepath.Join(TestDataDir, TestNetworkNames[networkID])
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
testDir = filepath.ToSlash(testDir)
|
||||
}
|
||||
|
||||
// run tests with "INFO" log level only
|
||||
// when `go test` invoked with `-v` flag
|
||||
errorLevel := "ERROR"
|
||||
if testing.Verbose() {
|
||||
errorLevel = "INFO"
|
||||
}
|
||||
|
||||
configJSON := `{
|
||||
"NetworkId": ` + strconv.Itoa(networkID) + `,
|
||||
"DataDir": "` + testDir + `",
|
||||
"HTTPPort": ` + strconv.Itoa(TestConfig.Node.HTTPPort) + `,
|
||||
"WSPort": ` + strconv.Itoa(TestConfig.Node.WSPort) + `,
|
||||
"LogLevel": "` + errorLevel + `"
|
||||
}`
|
||||
|
||||
nodeConfig, err := params.LoadNodeConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nodeConfig, nil
|
||||
}
|
||||
|
||||
// LoadFromFile is useful for loading test data, from testdata/filename into a variable
|
||||
// nolint: errcheck
|
||||
func LoadFromFile(filename string) string {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
io.Copy(buf, f)
|
||||
f.Close()
|
||||
|
||||
return string(buf.Bytes())
|
||||
}
|
|
@ -198,7 +198,10 @@ func (m *Manager) completeLocalTransaction(queuedTx *common.QueuedTx, password s
|
|||
return gethcommon.Hash{}, err
|
||||
}
|
||||
|
||||
return les.StatusBackend.SendTransaction(context.Background(), status.SendTxArgs(queuedTx.Args), password)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
return les.StatusBackend.SendTransaction(ctx, status.SendTxArgs(queuedTx.Args), password)
|
||||
}
|
||||
|
||||
func (m *Manager) completeRemoteTransaction(queuedTx *common.QueuedTx, password string) (gethcommon.Hash, error) {
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
. "github.com/status-im/status-go/geth/testing"
|
||||
. "github.com/status-im/status-go/testing"
|
||||
)
|
||||
|
||||
var errTxAssumedSent = errors.New("assume tx is done")
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
)
|
||||
|
||||
var (
|
||||
// TestConfig defines the default config usable at package-level.
|
||||
TestConfig *common.TestConfig
|
||||
|
||||
// RootDir is the main application directory
|
||||
RootDir string
|
||||
|
||||
// TestDataDir is data directory used for tests
|
||||
TestDataDir string
|
||||
|
||||
// TestNetworkNames network ID to name mapping
|
||||
TestNetworkNames = map[int]string{
|
||||
params.MainNetworkID: "Mainnet",
|
||||
params.RopstenNetworkID: "Ropsten",
|
||||
params.RinkebyNetworkID: "Rinkeby",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// setup root directory
|
||||
RootDir = filepath.Dir(pwd)
|
||||
if strings.HasSuffix(RootDir, "geth") || strings.HasSuffix(RootDir, "cmd") { // we need to hop one more level
|
||||
RootDir = filepath.Join(RootDir, "..")
|
||||
}
|
||||
|
||||
// setup auxiliary directories
|
||||
TestDataDir = filepath.Join(RootDir, ".ethereumtest")
|
||||
|
||||
TestConfig, err = common.LoadTestConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadFromFile is useful for loading test data, from testdata/filename into a variable
|
||||
// nolint: errcheck
|
||||
func LoadFromFile(filename string) string {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
io.Copy(buf, f)
|
||||
f.Close()
|
||||
|
||||
return string(buf.Bytes())
|
||||
}
|
Loading…
Reference in New Issue