From e503b36b152b29cb4c55ad5485015b6011c9bf58 Mon Sep 17 00:00:00 2001 From: paolocappelletti Date: Wed, 23 Sep 2020 16:47:56 +0200 Subject: [PATCH 1/3] added qa python test --- qa/README.md | 46 ++++++ qa/basicTest.py | 103 ++++++++++++++ qa/httpCalls/__init__.py | 1 + qa/httpCalls/block/__init__.py | 0 qa/httpCalls/block/best.py | 7 + qa/httpCalls/carApi/__init__.py | 0 qa/httpCalls/carApi/acceptCarSellOrder.py | 18 +++ qa/httpCalls/carApi/createCar.py | 26 ++++ qa/httpCalls/carApi/createCarSellOrder.py | 20 +++ qa/httpCalls/transaction/__init__.py | 0 .../transaction/sendCoinsToAddress.py | 17 +++ qa/httpCalls/transaction/spendForgingStake.py | 26 ++++ qa/httpCalls/wallet/__init__.py | 0 qa/httpCalls/wallet/allBoxes.py | 7 + qa/httpCalls/wallet/balance.py | 7 + qa/httpCalls/wallet/createPrivateKey25519.py | 8 ++ qa/lambo_test1_standardWorkflow.py | 131 ++++++++++++++++++ qa/lambo_test2_duplicateVin.py | 69 +++++++++ qa/resources/__init__.py | 0 qa/resources/template.conf | 50 +++++++ qa/resources/testdata.py | 17 +++ qa/run_all.py | 33 +++++ qa/utils/__init__.py | 0 qa/utils/searchBoxListByAttributes.py | 36 +++++ qa/utils/searchTransactionInBlock.py | 9 ++ 25 files changed, 631 insertions(+) create mode 100644 qa/README.md create mode 100644 qa/basicTest.py create mode 100644 qa/httpCalls/__init__.py create mode 100644 qa/httpCalls/block/__init__.py create mode 100644 qa/httpCalls/block/best.py create mode 100644 qa/httpCalls/carApi/__init__.py create mode 100644 qa/httpCalls/carApi/acceptCarSellOrder.py create mode 100644 qa/httpCalls/carApi/createCar.py create mode 100644 qa/httpCalls/carApi/createCarSellOrder.py create mode 100644 qa/httpCalls/transaction/__init__.py create mode 100644 qa/httpCalls/transaction/sendCoinsToAddress.py create mode 100644 qa/httpCalls/transaction/spendForgingStake.py create mode 100644 qa/httpCalls/wallet/__init__.py create mode 100644 qa/httpCalls/wallet/allBoxes.py create mode 100644 qa/httpCalls/wallet/balance.py create mode 100644 qa/httpCalls/wallet/createPrivateKey25519.py create mode 100644 qa/lambo_test1_standardWorkflow.py create mode 100644 qa/lambo_test2_duplicateVin.py create mode 100644 qa/resources/__init__.py create mode 100644 qa/resources/template.conf create mode 100644 qa/resources/testdata.py create mode 100644 qa/run_all.py create mode 100644 qa/utils/__init__.py create mode 100644 qa/utils/searchBoxListByAttributes.py create mode 100644 qa/utils/searchTransactionInBlock.py diff --git a/qa/README.md b/qa/README.md new file mode 100644 index 0000000..6d042a3 --- /dev/null +++ b/qa/README.md @@ -0,0 +1,46 @@ +**Application Test suite** +-------------------------- +**Requirements** + +- Install Python 2.7 +- Donwload Sidechain SDK + +**Additional settings** + +Setup these environment variables: +``` +BITCOINCLI: path to zen-cli +BITCOIND: path to zend +APP_JAR: sidechain app jar +APP_LIB: sidechain app lib folder +APP_MAIN: main sidechain class to run +SIDECHAIN_SDK: path to SDK folder +``` + +**Execution** + +You can run all tests using command. + +``` +python run_all.py +``` + +Or run individual test using command like this: + +``` +python lambo_test1_standardWorkflow.py +``` + +Example for Linux environment: + +``` +export BITCOINCLI=/var/horizen/lamboRegistry/zend_oo/src/zen-cli +export BITCOIND=/var/horizen/lamboRegistry/zend_oo/src/zend +export SIDECHAIN_SDK=/var/horizen/lamboRegistry/Sidechains-SDK +export APP_JAR=/var/horizen/lamboRegistry/lambo-registry/target/lambo-registry-0.1.0.jar +export APP_LIB=/var/horizen/lamboRegistry/lambo-registry/target/lib/* +export APP_MAIN=io.horizen.lambo.CarRegistryApp + +python run_all.py --tmpdir=./_tmp +``` + diff --git a/qa/basicTest.py b/qa/basicTest.py new file mode 100644 index 0000000..e4fde2f --- /dev/null +++ b/qa/basicTest.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python2 +import sys +import os +sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') +from SidechainTestFramework.sc_test_framework import SidechainTestFramework +from SidechainTestFramework.sc_boostrap_info import SCNodeConfiguration, SCCreationInfo, MCConnectionInfo, \ + SCNetworkConfiguration +from test_framework.util import assert_true, initialize_chain_clean, start_nodes, \ + websocket_port_by_mc_node_index, connect_nodes_bi, disconnect_nodes_bi +from SidechainTestFramework.scutil import bootstrap_sidechain_nodes, start_sc_nodes, generate_next_blocks, connect_sc_nodes, initialize_default_sc_chain_clean +from SidechainTestFramework.sc_forging_util import * +from httpCalls.transaction.spendForgingStake import spendForgingStake + +""" +Basic class for test - extend this to create your own test +This basic class handles the bootstrap of the sidechain and exposes a bounch of commonly used methods (convertInitialForging and generateOneBlock) +""" +class BasicTest(SidechainTestFramework): + + def __init__(self, sc_nodes = 1): + print "---------" + print "Initializing test " + str(self.__class__) + #check variable configuration + assert_true(sc_nodes > 0, "Sidechain nodes must be > 0") + assert_true(len(os.getenv("BITCOINCLI", "")) > 0, "BITCOINCLI env var must be set") + assert_true(len(os.getenv("BITCOIND", "")) > 0, "BITCOIND env var must be set") + assert_true(len(os.getenv("SIDECHAIN_SDK", "")) > 0, "SIDECHAIN_SDK env var must be set") + assert_true(len(os.getenv("APP_JAR", "")) > 0, "APP_JAR env var must be set") + assert_true(len(os.getenv("APP_LIB", "")) > 0, "APP_LIB env var must be set") + assert_true(len(os.getenv("APP_MAIN", "")) > 0, "APP_MAIN env var must be set") + print "Configuration of this test:" + print " 1 mainchain node" + print " {} sidechains node".format(sc_nodes) + self.number_of_mc_nodes = 1 + self.number_of_sidechain_nodes = sc_nodes + + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, self.number_of_mc_nodes) + + def setup_network(self, split = False): + print("Initializing Mainchain nodes...") + self.nodes = self.setup_nodes() + self.sync_all() + print("OK\n") + + def setup_nodes(self): + return start_nodes(self.number_of_mc_nodes, self.options.tmpdir) + + def sc_setup_chain(self): + mc_node_1 = self.nodes[0] + + sc_node_configuration = [] + for x in range(1, self.number_of_sidechain_nodes +1): + sc_node_configuration.append( + SCNodeConfiguration( + MCConnectionInfo(address="ws://{0}:{1}".format(mc_node_1.hostname, websocket_port_by_mc_node_index(0))) + ) + ) + network = SCNetworkConfiguration(SCCreationInfo(mc_node_1, 600, 1000), *sc_node_configuration) + bootstrap_sidechain_nodes(self.options.tmpdir, network) + + + + def sc_setup_network(self, split = False): + print("Initializing {} Sidechain nodes...".format(self.number_of_sidechain_nodes)) + self.sc_nodes = self.sc_setup_nodes() + if (self.number_of_sidechain_nodes > 1): + for x in range(1, self.number_of_sidechain_nodes): + print('Connecting sidechain node node0 with node{}'.format(x)) + connect_sc_nodes(self.sc_nodes[0], x) + self.sc_sync_all() + print("OK\n") + + def sc_setup_nodes(self): + lib_separator = ":" + if sys.platform.startswith('win'): + lib_separator = ";" + app_jar = os.getenv("APP_JAR", "") + app_lib = os.getenv("APP_LIB", "") + app_main = os.getenv("APP_MAIN", "") + + path = [] + for x in range(1, self.number_of_sidechain_nodes + 1): + path .append(app_jar + lib_separator + app_lib + " " + app_main) + + return start_sc_nodes(self.number_of_sidechain_nodes, self.options.tmpdir, None, None, path) + + #spends initial forgin stake to create a stadard coinbox + def convertInitialForging(self): + sc_node1 = self.sc_nodes[0] + response = sc_node1.wallet_allBoxes() + box = response["result"]["boxes"][0] + box_id = box["id"] + publicKey = box["proposition"]["publicKey"] + blockSignPublicKey = box["blockSignProposition"]["publicKey"] + vrfPubKey = box["vrfPubKey"]["publicKey"] + convertedForgingStakeValue = box["value"] + #spends forgin stake to create a stadard coinbox + spendForgingStake(sc_node1, box_id, convertedForgingStakeValue, 0, publicKey, blockSignPublicKey, vrfPubKey) + return publicKey, convertedForgingStakeValue + + def generateOneBlock(self, sidechainNode): + return generate_next_blocks(sidechainNode, "", 1)[0] \ No newline at end of file diff --git a/qa/httpCalls/__init__.py b/qa/httpCalls/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/qa/httpCalls/__init__.py @@ -0,0 +1 @@ + diff --git a/qa/httpCalls/block/__init__.py b/qa/httpCalls/block/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/httpCalls/block/best.py b/qa/httpCalls/block/best.py new file mode 100644 index 0000000..911c51d --- /dev/null +++ b/qa/httpCalls/block/best.py @@ -0,0 +1,7 @@ +#executes a block/best call +def http_block_best(sidechainNode): + response = sidechainNode.block_best() + return response['result']['block'] + + + diff --git a/qa/httpCalls/carApi/__init__.py b/qa/httpCalls/carApi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/httpCalls/carApi/acceptCarSellOrder.py b/qa/httpCalls/carApi/acceptCarSellOrder.py new file mode 100644 index 0000000..7e98866 --- /dev/null +++ b/qa/httpCalls/carApi/acceptCarSellOrder.py @@ -0,0 +1,18 @@ +import json +#create and send a custom transaction carApi/acceptCarSellOrder +def acceptCarSellOrder(sidechainNode, carSellOrderId, fee): + j = {\ + "carSellOrderId": carSellOrderId,\ + "fee": fee \ + } + request = json.dumps(j) + response = sidechainNode.carApi_acceptCarSellOrder(request) + transactionBytes = response["result"]["transactionBytes"] + j = {\ + "transactionBytes": transactionBytes,\ + } + request = json.dumps(j) + response = sidechainNode.transaction_sendTransaction(request) + return response["result"]["transactionId"] + + diff --git a/qa/httpCalls/carApi/createCar.py b/qa/httpCalls/carApi/createCar.py new file mode 100644 index 0000000..fed0a21 --- /dev/null +++ b/qa/httpCalls/carApi/createCar.py @@ -0,0 +1,26 @@ +import json +#create and send a custom transaction carApi/createCar +#returns (, ) +def createCar(sidechainNode, vin, year, model, color, proposition, fee): + j = {\ + "vin": vin,\ + "year": year,\ + "model": model,\ + "color": color, \ + "proposition": proposition, \ + "fee": fee \ + } + request = json.dumps(j) + response = sidechainNode.carApi_createCar(request) + if ("error" in response): + return (False, None) + else: + transactionBytes = response["result"]["transactionBytes"] + j = {\ + "transactionBytes": transactionBytes,\ + } + request = json.dumps(j) + response = sidechainNode.transaction_sendTransaction(request) + return (True, response["result"]["transactionId"]) + + diff --git a/qa/httpCalls/carApi/createCarSellOrder.py b/qa/httpCalls/carApi/createCarSellOrder.py new file mode 100644 index 0000000..abf4877 --- /dev/null +++ b/qa/httpCalls/carApi/createCarSellOrder.py @@ -0,0 +1,20 @@ +import json +#create and send a custom transaction carApi/createCarSellOrder +def createCarSellOrder(sidechainNode, carBoxId, buyerProposition, sellPrice, fee): + j = {\ + "carBoxId": carBoxId,\ + "buyerProposition": buyerProposition,\ + "sellPrice": sellPrice,\ + "fee": fee \ + } + request = json.dumps(j) + response = sidechainNode.carApi_createCarSellOrder(request) + transactionBytes = response["result"]["transactionBytes"] + j = {\ + "transactionBytes": transactionBytes,\ + } + request = json.dumps(j) + response = sidechainNode.transaction_sendTransaction(request) + return response["result"]["transactionId"] + + diff --git a/qa/httpCalls/transaction/__init__.py b/qa/httpCalls/transaction/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/httpCalls/transaction/sendCoinsToAddress.py b/qa/httpCalls/transaction/sendCoinsToAddress.py new file mode 100644 index 0000000..82b4487 --- /dev/null +++ b/qa/httpCalls/transaction/sendCoinsToAddress.py @@ -0,0 +1,17 @@ +import json +#execute a transaction/sendCoinsToAddress call +def sendCoinsToAddress(sidechainNode, address, amount, fee): + j = {\ + "outputs": [ \ + { \ + "publicKey": address, \ + "value": amount \ + } \ + ], \ + "fee": fee \ + } + request = json.dumps(j) + response = sidechainNode.transaction_sendCoinsToAddress(request) + return response["result"]["transactionId"] + + diff --git a/qa/httpCalls/transaction/spendForgingStake.py b/qa/httpCalls/transaction/spendForgingStake.py new file mode 100644 index 0000000..397a72b --- /dev/null +++ b/qa/httpCalls/transaction/spendForgingStake.py @@ -0,0 +1,26 @@ +import json +#execute a transaction/spendForgingStake call +def spendForgingStake(sidechainNode, inputBoxId, valueRegular, valueForging, publicKey, blockSignPublicKey, vrfPubKey): + j = {\ + "transactionInputs": [ \ + { \ + "boxId": inputBoxId \ + } \ + ],\ + "regularOutputs": [\ + {\ + "publicKey": publicKey,\ + "value": valueRegular\ + }\ + ],\ + "forgerOutputs": [\ + {\ + "publicKey": publicKey,\ + "blockSignPublicKey": blockSignPublicKey,\ + "vrfPubKey": vrfPubKey,\ + "value": valueForging\ + }\ + ],\ + } + request = json.dumps(j) + sidechainNode.transaction_spendForgingStake(request) diff --git a/qa/httpCalls/wallet/__init__.py b/qa/httpCalls/wallet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/httpCalls/wallet/allBoxes.py b/qa/httpCalls/wallet/allBoxes.py new file mode 100644 index 0000000..9b3e790 --- /dev/null +++ b/qa/httpCalls/wallet/allBoxes.py @@ -0,0 +1,7 @@ +#executes a wallet/allBoxes call +def http_wallet_allBoxes(sidechainNode): + response = sidechainNode.wallet_allBoxes() + return response['result']['boxes'] + + + diff --git a/qa/httpCalls/wallet/balance.py b/qa/httpCalls/wallet/balance.py new file mode 100644 index 0000000..e179ede --- /dev/null +++ b/qa/httpCalls/wallet/balance.py @@ -0,0 +1,7 @@ +#executes a wallet/balance call +def http_wallet_balance(sidechainNode): + response = sidechainNode.wallet_balance() + return response['result']['balance'] + + + diff --git a/qa/httpCalls/wallet/createPrivateKey25519.py b/qa/httpCalls/wallet/createPrivateKey25519.py new file mode 100644 index 0000000..235e82c --- /dev/null +++ b/qa/httpCalls/wallet/createPrivateKey25519.py @@ -0,0 +1,8 @@ +import json +#create and send a custom transaction block/best +def http_wallet_createPrivateKey25519(sidechainNode): + response = sidechainNode.wallet_createPrivateKey25519() + return response['result']['proposition']['publicKey'] + + + diff --git a/qa/lambo_test1_standardWorkflow.py b/qa/lambo_test1_standardWorkflow.py new file mode 100644 index 0000000..40a6922 --- /dev/null +++ b/qa/lambo_test1_standardWorkflow.py @@ -0,0 +1,131 @@ +import sys +import os +sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') +from test_framework.util import assert_equal, assert_true, assert_false, fail +from httpCalls.transaction.sendCoinsToAddress import sendCoinsToAddress +from httpCalls.carApi.createCar import createCar +from httpCalls.carApi.createCarSellOrder import createCarSellOrder +from httpCalls.carApi.acceptCarSellOrder import acceptCarSellOrder +from httpCalls.block.best import http_block_best +from httpCalls.wallet.allBoxes import http_wallet_allBoxes +from httpCalls.wallet.balance import http_wallet_balance +from httpCalls.wallet.createPrivateKey25519 import http_wallet_createPrivateKey25519 +from utils.searchBoxListByAttributes import searchBoxListByAttributes +from utils.searchTransactionInBlock import searchTransactionInBlock +from basicTest import BasicTest +from resources.testdata import CAR, BOXTYPE_STANDARD, BOXTYPE_CUSTOM + +""" +This test checks a standard workflow: creating a car and selling it to another wallet + +Network Configuration: + 1 MC nodes and 2 SC nodes + +Workflow modelled in this test: + SCNode1: spendForgingStake + SCNode1: createCar + SCNode2: createPrivateKey25519 of UserB (buyer) + SCNode1: createCarSellOrder to the UserB + SCNode2: acceptCarSellOrder +""" +class LamboTest1(BasicTest): + + def __init__(self): + #setup network with 2 sidechain nodes + super(LamboTest1, self).__init__(2) + + def run_test(self): + print "Starting test" + sc_node1 = self.sc_nodes[0] + sc_node2 = self.sc_nodes[1] + self.sc_sync_all() + + #convert initial forging amount to standard coinbox and returns the public key owning it and the amount + (publicKey, convertedForgingStakeValue) = self.convertInitialForging() + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check that the stadard coinbox is present in wallet + boxes = http_wallet_allBoxes(sc_node1) + (searchBoxFound, boxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_STANDARD.REGULAR, + 'value', convertedForgingStakeValue, + ) + assert_true(searchBoxFound) + + #declare a new car + (success, transactionid) = createCar(sc_node1, CAR.VIN, CAR.YEAR, CAR.MODEL, CAR.COLOR, publicKey, 1000) + assert_true(success) + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check that the declared car is present in wallet + boxes = http_wallet_allBoxes(sc_node1) + (searchBoxFound, carBoxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_CUSTOM.CAR, + 'vin', CAR.VIN + ) + assert_true(searchBoxFound) + + #generate new key in sidechain node 2 (will be the buyer) + publiKeyNode2 = http_wallet_createPrivateKey25519(sc_node2) + + #create a car sell order for the buyer + txId = createCarSellOrder(sc_node1, carBoxId, publiKeyNode2, 10000000, 1000) + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check that the transaction was included in last block + bestBlock = http_block_best(sc_node1) + (searchFound, foundTx) = searchTransactionInBlock(bestBlock, txId) + assert_true(searchFound) + + #check that the sell order was created correctly + (searchBoxFound, sellOrderBoxId) = searchBoxListByAttributes(foundTx['newBoxes'], + 'typeId', 2, + 'vin', CAR.VIN, + ) + assert_true(searchBoxFound) + + #send some coin to the user on sidechain node 2 + sendCoinsToAddress(sc_node1, publiKeyNode2, 50000000, 1000) + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check the user on sidechain node2 has received the money + assert_equal(http_wallet_balance(sc_node2), 50000000) + + #user on sidechain node2 accepts the sell order + acceptCarSellOrder(sc_node2, sellOrderBoxId, 1000) + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #user on sidechain node 2 now should own the car + boxes = http_wallet_allBoxes(sc_node2) + (searchBoxFound, sellOrderBoxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_CUSTOM.CAR, + 'vin', CAR.VIN, + ) + assert_true(searchBoxFound) + + #user on sidechain node 1 (the seller) shold not own the car anymore + boxes = http_wallet_allBoxes(sc_node1) + (searchBoxFound, sellOrderBoxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_CUSTOM.CAR, + 'vin', CAR.VIN, + ) + assert_false(searchBoxFound) + + #user on sidechain node 2 has correct balance (initial - car price - feee + car value (actually 1 hardcoded in code)) + assert_equal(http_wallet_balance(sc_node2), 39999001) + + + + +if __name__ == "__main__": + LamboTest1().main() \ No newline at end of file diff --git a/qa/lambo_test2_duplicateVin.py b/qa/lambo_test2_duplicateVin.py new file mode 100644 index 0000000..2a35bd8 --- /dev/null +++ b/qa/lambo_test2_duplicateVin.py @@ -0,0 +1,69 @@ +import sys +import os +sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') +from test_framework.util import assert_equal, assert_true, assert_false, fail +from httpCalls.carApi.createCar import createCar +from httpCalls.wallet.allBoxes import http_wallet_allBoxes +from utils.searchBoxListByAttributes import searchBoxListByAttributes +from basicTest import BasicTest +from resources.testdata import CAR, BOXTYPE_STANDARD, BOXTYPE_CUSTOM + +""" +This test checks that is not admitted to create more than one car with the same VIN + +Network Configuration: + 1 MC nodes and 1 SC node + +Workflow modelled in this test: + SCNode1: spendForgingStake + SCNode1: createCar + SCNode1: createCar (with the same VIN) : checks that the create has not success +""" +class LamboTest2(BasicTest): + + + def __init__(self): + #setup network with 1 sidechain node + super(LamboTest2, self).__init__(1) + + + def run_test(self): + sc_node1 = self.sc_nodes[0] + self.sc_sync_all() + + #convert initial forging amount to standard coinbox and returns the public key owning it + (publicKey, convertedForgingStakeValue) = self.convertInitialForging() + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check that the stadard coinbox is present in wallet + boxes = http_wallet_allBoxes(sc_node1) + (searchBoxFound, boxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_STANDARD.REGULAR, + 'value', convertedForgingStakeValue, + ) + assert_true(searchBoxFound) + + #declare a new car + (success, transactionid) = createCar(sc_node1, CAR.VIN, CAR.YEAR, CAR.MODEL, CAR.COLOR, publicKey, 1000) + assert_true(success) + self.sc_sync_all() + self.generateOneBlock(sc_node1) + self.sc_sync_all() + + #check that the declared car is present in wallet + boxes = http_wallet_allBoxes(sc_node1) + (searchBoxFound, carBoxId) = searchBoxListByAttributes(boxes, + 'typeId', BOXTYPE_CUSTOM.CAR, + 'vin', CAR.VIN, + ) + assert_true(searchBoxFound) + + #declare anoter car with the same VIN + (success, transactionid) = createCar(sc_node1, CAR.VIN, "2018", "Lamborghini Aventador 2", "black", publicKey, 1000) + assert_false(success) + + +if __name__ == "__main__": + LamboTest2().main() \ No newline at end of file diff --git a/qa/resources/__init__.py b/qa/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/resources/template.conf b/qa/resources/template.conf new file mode 100644 index 0000000..e7080db --- /dev/null +++ b/qa/resources/template.conf @@ -0,0 +1,50 @@ +scorex { + dataDir = "%(DIRECTORY)s/sc_node%(NODE_NUMBER)s/blockchain" + logDir = "%(DIRECTORY)s/sc_node%(NODE_NUMBER)s/log" + + restApi { + bindAddress = "%(API_ADDRESS)s:%(API_PORT)s" + api-key-hash = "" + timeout = 5s + } + + network { + nodeName = "node%(NODE_NUMBER)s" + bindAddress = "%(API_ADDRESS)s:%(BIND_PORT)s" + knownPeers = [] + agentName = "2-Hop" + } + + websocket { + address = "%(WEBSOCKET_ADDRESS)s" + connectionTimeout = %(CONNECTION_TIMEOUT)d milliseconds + reconnectionDelay = %(RECONNECTION_DELAY)d seconds + reconnectionMaxAttempts = %(RECONNECTION_MAX_ATTEMPS)d + zencliCommandLine = "%(ZEN_CLI)s" + zencliCommandLineArguments = %(ZEN_CLI_ARGS)s + } + + wallet { + seed = "%(WALLET_SEED)s" + genesisSecrets = %(GENESIS_SECRETS)s + } + + genesis { + scGenesisBlockHex = "%(GENESIS_DATA)s" + scId = "%(SIDECHAIN_ID)s" + powData = "%(POW_DATA)s" + mcBlockHeight = %(BLOCK_HEIGHT)d + mcNetwork = %(NETWORK)s + withdrawalEpochLength = %(WITHDRAWAL_EPOCH_LENGTH)d + } + + withdrawalEpochCertificate { + submitterIsEnabled = true + signersPublicKeys = %(SIGNER_PUBLIC_KEY)s + signersThreshold = %(THRESHOLD)d + signersSecrets = %(SIGNER_PRIVATE_KEY)s + provingKeyFilePath = "../src/main/resources/sample_proving_key_7_keys_with_threshold_5" + verificationKeyFilePath = "../src/main/resources/sample_vk_7_keys_with_threshold_5" + } + +} diff --git a/qa/resources/testdata.py b/qa/resources/testdata.py new file mode 100644 index 0000000..ebf1884 --- /dev/null +++ b/qa/resources/testdata.py @@ -0,0 +1,17 @@ +class BOXTYPE_STANDARD(): + REGULAR = 1 + WITHDRAWAL_REQUEST = 2 + FORGER = 3 + +class BOXTYPE_CUSTOM(): + CAR = 1 + +class CAR(): + VIN = "NDWERR23423RAS" + YEAR = "2019" + MODEL = "Lamborghini Aventador" + COLOR = "yellow" + + + + diff --git a/qa/run_all.py b/qa/run_all.py new file mode 100644 index 0000000..e6539bf --- /dev/null +++ b/qa/run_all.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python2 +import sys +import os +sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') +from test_framework.util import assert_equal, assert_true, assert_false, fail +from lambo_test1_standardWorkflow import LamboTest1 +from lambo_test2_duplicateVin import LamboTest2 + +def run_test(test): + try: + + test.main() + except SystemExit as e: + return e.code + return 0 + +def run_tests(log_file): + print "Running all tests " + original = sys.stdout + sys.stdout = log_file + + result = run_test(LamboTest1()) + assert_equal(0, result, "LamboTest1 test failed!") + + result = run_test(LamboTest2()) + assert_equal(0, result, "LamboTest2 test failed!") + + + sys.stdout = original + print "Test suite completed! - see log file: " + log_file.name +if __name__ == "__main__": + log_file = open("sc_test.log", "w") + run_tests(log_file) diff --git a/qa/utils/__init__.py b/qa/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qa/utils/searchBoxListByAttributes.py b/qa/utils/searchBoxListByAttributes.py new file mode 100644 index 0000000..e74843b --- /dev/null +++ b/qa/utils/searchBoxListByAttributes.py @@ -0,0 +1,36 @@ +""" +search inside a given box list for a box having specific attribute values. +Up to 3 different attributes can be specified: if present, they are checked in AND (all conditions must be true) +Return format: (, ) +""" +def searchBoxListByAttributes(boxes, + attribute1, attribute1Value, + attribute2 = None, attribute2Value = None, + attribute3 = None, attribute3Value = None + ): + + conditions = [TestCondition(attribute1, attribute1Value)] + if (attribute2 != None): + conditions.append(TestCondition(attribute2, attribute2Value)) + if (attribute3 != None): + conditions.append(TestCondition(attribute3, attribute3Value)) + + for box in boxes: + found = True + for cond in conditions: + if (cond.test(box) == False): + found = False + if (found): + return (True, box['id']) + return (False, None) + +class TestCondition: + def __init__(self, attributeName, attributeValue): + self.attributeName = attributeName + self.attributeValue = attributeValue + + def test(self, box): + if (self.attributeName in box and box[self.attributeName] == self.attributeValue): + return True + else: + return False diff --git a/qa/utils/searchTransactionInBlock.py b/qa/utils/searchTransactionInBlock.py new file mode 100644 index 0000000..939ac3d --- /dev/null +++ b/qa/utils/searchTransactionInBlock.py @@ -0,0 +1,9 @@ +""" +search for a specific transaction in a block +Return format: (, ) +""" +def searchTransactionInBlock(block, transactionId): + for tx in block["sidechainTransactions"]: + if (tx["id"] == transactionId): + return (True, tx) + return (False, None) From 37dcbff121b512ba70ff036eca0f2ba102d2fdea Mon Sep 17 00:00:00 2001 From: paolocappelletti Date: Wed, 23 Sep 2020 17:31:13 +0200 Subject: [PATCH 2/3] small fix --- qa/lambo_test1_standardWorkflow.py | 2 +- qa/resources/testdata.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/lambo_test1_standardWorkflow.py b/qa/lambo_test1_standardWorkflow.py index 40a6922..98c4a65 100644 --- a/qa/lambo_test1_standardWorkflow.py +++ b/qa/lambo_test1_standardWorkflow.py @@ -85,7 +85,7 @@ def run_test(self): #check that the sell order was created correctly (searchBoxFound, sellOrderBoxId) = searchBoxListByAttributes(foundTx['newBoxes'], - 'typeId', 2, + 'typeId', BOXTYPE_CUSTOM.SELL_ORDER, 'vin', CAR.VIN, ) assert_true(searchBoxFound) diff --git a/qa/resources/testdata.py b/qa/resources/testdata.py index ebf1884..84fd035 100644 --- a/qa/resources/testdata.py +++ b/qa/resources/testdata.py @@ -5,6 +5,7 @@ class BOXTYPE_STANDARD(): class BOXTYPE_CUSTOM(): CAR = 1 + SELL_ORDER = 2 class CAR(): VIN = "NDWERR23423RAS" From 18ed873bfafd0e179efd6339525e45aecd8a6f41 Mon Sep 17 00:00:00 2001 From: paolocappelletti Date: Thu, 24 Sep 2020 12:05:30 +0200 Subject: [PATCH 3/3] - forward transfer instead of convertInitialForging - fixed typo in readme --- qa/README.md | 2 +- qa/basicTest.py | 2 +- qa/lambo_test1_standardWorkflow.py | 25 ++++++++++++------------- qa/lambo_test2_duplicateVin.py | 25 +++++++++++++++---------- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/qa/README.md b/qa/README.md index 6d042a3..6529c21 100644 --- a/qa/README.md +++ b/qa/README.md @@ -3,7 +3,7 @@ **Requirements** - Install Python 2.7 -- Donwload Sidechain SDK +- Download Sidechain SDK **Additional settings** diff --git a/qa/basicTest.py b/qa/basicTest.py index e4fde2f..3b3c13b 100644 --- a/qa/basicTest.py +++ b/qa/basicTest.py @@ -57,7 +57,7 @@ def sc_setup_chain(self): ) ) network = SCNetworkConfiguration(SCCreationInfo(mc_node_1, 600, 1000), *sc_node_configuration) - bootstrap_sidechain_nodes(self.options.tmpdir, network) + self.sc_nodes_bootstrap_info = bootstrap_sidechain_nodes(self.options.tmpdir, network) diff --git a/qa/lambo_test1_standardWorkflow.py b/qa/lambo_test1_standardWorkflow.py index 98c4a65..1da49fe 100644 --- a/qa/lambo_test1_standardWorkflow.py +++ b/qa/lambo_test1_standardWorkflow.py @@ -1,7 +1,7 @@ import sys import os sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') -from test_framework.util import assert_equal, assert_true, assert_false, fail +from test_framework.util import assert_equal, assert_true, assert_false, fail, forward_transfer_to_sidechain from httpCalls.transaction.sendCoinsToAddress import sendCoinsToAddress from httpCalls.carApi.createCar import createCar from httpCalls.carApi.createCarSellOrder import createCarSellOrder @@ -36,26 +36,27 @@ def __init__(self): def run_test(self): print "Starting test" + mc_node = self.nodes[0] sc_node1 = self.sc_nodes[0] sc_node2 = self.sc_nodes[1] + publicKeySeller = self.sc_nodes_bootstrap_info.genesis_account.publicKey self.sc_sync_all() - #convert initial forging amount to standard coinbox and returns the public key owning it and the amount - (publicKey, convertedForgingStakeValue) = self.convertInitialForging() + #we need regular coins (the genesis account balance is locked into forging stake), so we perform a + #forward transfer to sidechain for an amount equals to the genesis_account_balance + forward_transfer_to_sidechain(self.sc_nodes_bootstrap_info.sidechain_id, + mc_node, + publicKeySeller, + self.sc_nodes_bootstrap_info.genesis_account_balance) self.sc_sync_all() self.generateOneBlock(sc_node1) self.sc_sync_all() - #check that the stadard coinbox is present in wallet - boxes = http_wallet_allBoxes(sc_node1) - (searchBoxFound, boxId) = searchBoxListByAttributes(boxes, - 'typeId', BOXTYPE_STANDARD.REGULAR, - 'value', convertedForgingStakeValue, - ) - assert_true(searchBoxFound) + #check that the wallet balance is doubled now (forging stake + the forward transfer) (we need to convert to zentoshi also) + assert_equal(http_wallet_balance(sc_node1), (self.sc_nodes_bootstrap_info.genesis_account_balance * 2) * 100000000) #declare a new car - (success, transactionid) = createCar(sc_node1, CAR.VIN, CAR.YEAR, CAR.MODEL, CAR.COLOR, publicKey, 1000) + (success, transactionid) = createCar(sc_node1, CAR.VIN, CAR.YEAR, CAR.MODEL, CAR.COLOR, publicKeySeller, 1000) assert_true(success) self.sc_sync_all() self.generateOneBlock(sc_node1) @@ -125,7 +126,5 @@ def run_test(self): assert_equal(http_wallet_balance(sc_node2), 39999001) - - if __name__ == "__main__": LamboTest1().main() \ No newline at end of file diff --git a/qa/lambo_test2_duplicateVin.py b/qa/lambo_test2_duplicateVin.py index 2a35bd8..8722535 100644 --- a/qa/lambo_test2_duplicateVin.py +++ b/qa/lambo_test2_duplicateVin.py @@ -1,9 +1,10 @@ import sys import os sys.path.append(os.getenv("SIDECHAIN_SDK", "") + '/qa/') -from test_framework.util import assert_equal, assert_true, assert_false, fail +from test_framework.util import assert_equal, assert_true, assert_false, fail, forward_transfer_to_sidechain from httpCalls.carApi.createCar import createCar from httpCalls.wallet.allBoxes import http_wallet_allBoxes +from httpCalls.wallet.balance import http_wallet_balance from utils.searchBoxListByAttributes import searchBoxListByAttributes from basicTest import BasicTest from resources.testdata import CAR, BOXTYPE_STANDARD, BOXTYPE_CUSTOM @@ -28,22 +29,24 @@ def __init__(self): def run_test(self): + mc_node = self.nodes[0] sc_node1 = self.sc_nodes[0] + publicKey = self.sc_nodes_bootstrap_info.genesis_account.publicKey self.sc_sync_all() - #convert initial forging amount to standard coinbox and returns the public key owning it - (publicKey, convertedForgingStakeValue) = self.convertInitialForging() + + #we need regular coins (the genesis account balance is locked into forging stake), so we perform a + #forward transfer to sidechain for an amount equals to the genesis_account_balance + forward_transfer_to_sidechain(self.sc_nodes_bootstrap_info.sidechain_id, + mc_node, + publicKey, + self.sc_nodes_bootstrap_info.genesis_account_balance) self.sc_sync_all() self.generateOneBlock(sc_node1) self.sc_sync_all() - #check that the stadard coinbox is present in wallet - boxes = http_wallet_allBoxes(sc_node1) - (searchBoxFound, boxId) = searchBoxListByAttributes(boxes, - 'typeId', BOXTYPE_STANDARD.REGULAR, - 'value', convertedForgingStakeValue, - ) - assert_true(searchBoxFound) + #check that the wallet balance is doubled now (forging stake + the forward transfer) (we need to convert to zentoshi also) + assert_equal(http_wallet_balance(sc_node1), (self.sc_nodes_bootstrap_info.genesis_account_balance * 2) * 100000000) #declare a new car (success, transactionid) = createCar(sc_node1, CAR.VIN, CAR.YEAR, CAR.MODEL, CAR.COLOR, publicKey, 1000) @@ -62,6 +65,8 @@ def run_test(self): #declare anoter car with the same VIN (success, transactionid) = createCar(sc_node1, CAR.VIN, "2018", "Lamborghini Aventador 2", "black", publicKey, 1000) + + #check the call failed assert_false(success)