mirror of
https://github.com/status-im/status-go.git
synced 2025-01-23 21:19:48 +00:00
8153d935d2
The goal of this PR is to make geth/api tests to finally pass from the beginning to the end. I tried to achieve it here by: Removing calls to common.PanicAfter so that we know which tests fail the most, Better sync of some tests using channels, Small test improvements.
783 lines
22 KiB
Go
783 lines
22 KiB
Go
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/log"
|
|
"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/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) TestJailSendQueuedTransaction() {
|
|
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
|
|
require.NoError(s.backend.AccountManager().SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password))
|
|
|
|
txParams := `{
|
|
"from": "` + TestConfig.Account1.Address + `",
|
|
"to": "` + TestConfig.Account2.Address + `",
|
|
"value": "0.000001"
|
|
}`
|
|
|
|
txHashes := make(chan gethcommon.Hash, 1)
|
|
|
|
// replace transaction notification handler
|
|
requireMessageId := false
|
|
node.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
|
var envelope node.SignalEnvelope
|
|
err := json.Unmarshal([]byte(jsonEvent), &envelope)
|
|
s.NoError(err, fmt.Sprintf("cannot unmarshal JSON: %s", jsonEvent))
|
|
|
|
if envelope.Type == node.EventTransactionQueued {
|
|
event := envelope.Event.(map[string]interface{})
|
|
messageId, ok := event["message_id"].(string)
|
|
s.True(ok, "Message id is required, but not found")
|
|
if requireMessageId {
|
|
require.NotEmpty(messageId, "Message id is required, but not provided")
|
|
} else {
|
|
require.Empty(messageId, "Message id is not required, but provided")
|
|
}
|
|
|
|
txID := event["id"].(string)
|
|
txHash, err := s.backend.CompleteTransaction(common.QueuedTxID(txID), TestConfig.Account1.Password)
|
|
require.NoError(err, "cannot complete queued transaction[%v]", event["id"])
|
|
|
|
log.Info("Transaction complete", "URL", "https://ropsten.etherscan.io/tx/%s"+txHash.Hex())
|
|
|
|
txHashes <- txHash
|
|
}
|
|
})
|
|
|
|
type testCommand struct {
|
|
command string
|
|
params string
|
|
expectedResponse string
|
|
}
|
|
type testCase struct {
|
|
name string
|
|
file string
|
|
requireMessageId bool
|
|
commands []testCommand
|
|
}
|
|
|
|
tests := []testCase{
|
|
{
|
|
// no context or message id
|
|
name: "Case 1: no message id or context in inited JS",
|
|
file: "no-message-id-or-context.js",
|
|
requireMessageId: false,
|
|
commands: []testCommand{
|
|
{
|
|
`["commands", "send"]`,
|
|
txParams,
|
|
`{"result": {"transaction-hash":"TX_HASH"}}`,
|
|
},
|
|
{
|
|
`["commands", "getBalance"]`,
|
|
`{"address": "` + TestConfig.Account1.Address + `"}`,
|
|
`{"result": {"balance":42}}`,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// context is present in inited JS (but no message id is there)
|
|
name: "Case 2: context is present in inited JS (but no message id is there)",
|
|
file: "context-no-message-id.js",
|
|
requireMessageId: false,
|
|
commands: []testCommand{
|
|
{
|
|
`["commands", "send"]`,
|
|
txParams,
|
|
`{"result": {"context":{"` + params.SendTransactionMethodName + `":true},"result":{"transaction-hash":"TX_HASH"}}}`,
|
|
},
|
|
{
|
|
`["commands", "getBalance"]`,
|
|
`{"address": "` + TestConfig.Account1.Address + `"}`,
|
|
`{"result": {"context":{},"result":{"balance":42}}}`, // note empty (but present) context!
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// message id is present in inited JS, but no context is there
|
|
name: "Case 3: message id is present, context is not present",
|
|
file: "message-id-no-context.js",
|
|
requireMessageId: true,
|
|
commands: []testCommand{
|
|
{
|
|
`["commands", "send"]`,
|
|
txParams,
|
|
`{"result": {"transaction-hash":"TX_HASH"}}`,
|
|
},
|
|
{
|
|
`["commands", "getBalance"]`,
|
|
`{"address": "` + TestConfig.Account1.Address + `"}`,
|
|
`{"result": {"balance":42}}`, // note empty context!
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// both message id and context are present in inited JS (this UC is what we normally expect to see)
|
|
name: "Case 4: both message id and context are present",
|
|
file: "tx-send.js",
|
|
requireMessageId: true,
|
|
commands: []testCommand{
|
|
{
|
|
`["commands", "getBalance"]`,
|
|
`{"address": "` + TestConfig.Account1.Address + `"}`,
|
|
`{"result": {"context":{"message_id":"42"},"result":{"balance":42}}}`, // message id in context, but default one is used!
|
|
},
|
|
{
|
|
`["commands", "send"]`,
|
|
txParams,
|
|
`{"result": {"context":{"eth_sendTransaction":true,"message_id":"foobar"},"result":{"transaction-hash":"TX_HASH"}}}`,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
jailInstance := s.backend.JailManager()
|
|
for _, test := range tests {
|
|
jailInstance.BaseJS(string(static.MustAsset(txSendFolder + test.file)))
|
|
jailInstance.Parse(testChatID, ``)
|
|
|
|
// used by notification handler
|
|
requireMessageId = test.requireMessageId
|
|
|
|
for _, command := range test.commands {
|
|
s.T().Logf("%s: %s", test.name, command.command)
|
|
response := jailInstance.Call(testChatID, command.command, command.params)
|
|
|
|
var txHash gethcommon.Hash
|
|
if command.command == `["commands", "send"]` {
|
|
select {
|
|
case txHash = <-txHashes:
|
|
case <-time.After(time.Minute):
|
|
s.FailNow("test timed out: %s", test.name)
|
|
}
|
|
}
|
|
|
|
expectedResponse := strings.Replace(command.expectedResponse, "TX_HASH", txHash.Hex(), 1)
|
|
require.Equal(expectedResponse, response)
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
node.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
|
var envelope node.SignalEnvelope
|
|
var err error
|
|
err = json.Unmarshal([]byte(jsonEvent), &envelope)
|
|
require.NoError(err, fmt.Sprintf("cannot unmarshal JSON: %s", jsonEvent))
|
|
|
|
if envelope.Type == node.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 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!
|
|
// Once the contract has the transactionHash property set and once its deployed on an address.
|
|
if (!e) {
|
|
if (!contract.address) {
|
|
responseValue = contract.transactionHash;
|
|
}
|
|
}
|
|
})
|
|
`)
|
|
require.NoError(err)
|
|
|
|
select {
|
|
case <-completeQueuedTransaction:
|
|
case <-time.After(time.Minute):
|
|
s.FailNow("test timed out")
|
|
}
|
|
|
|
responseValue, err := cell.Get("responseValue")
|
|
require.NoError(err)
|
|
|
|
response, err := responseValue.ToString()
|
|
require.NoError(err)
|
|
|
|
expectedResponse := txHash.Hex()
|
|
require.Equal(expectedResponse, response)
|
|
s.T().Logf("estimation complete: %s", 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;
|
|
var makeTopic = function () {
|
|
var min = 1;
|
|
var max = Math.pow(16, 8);
|
|
var randInt = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
return web3.toHex(randInt);
|
|
};
|
|
`)
|
|
|
|
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
|
|
node.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
|
var envelope node.SignalEnvelope
|
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
|
s.T().Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
|
return
|
|
}
|
|
if envelope.Type == node.EventTransactionQueued {
|
|
event := envelope.Event.(map[string]interface{})
|
|
s.T().Logf("Transaction queued (will be completed shortly): {id: %s}\n", event["id"].(string))
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
//if err := geth.DiscardTransaction(event["id"].(string)); err != nil {
|
|
// t.Errorf("cannot discard: %v", err)
|
|
// progress <- "tx discarded"
|
|
// return
|
|
//}
|
|
|
|
//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")
|
|
}
|
|
|
|
// 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)
|
|
}
|