diff --git a/.ci/travis-install.sh b/.ci/travis-install.sh index 34df6e5..083f9bb 100755 --- a/.ci/travis-install.sh +++ b/.ci/travis-install.sh @@ -13,8 +13,6 @@ if [[ -n ${TOXENV} ]]; then pip install --upgrade tox elif [[ ${PLANETMINT_CI_ABCI} == 'enable' ]]; then docker-compose build --no-cache --build-arg abci_status=enable planetmint -elif [[ $PLANETMINT_INTEGRATION_TEST == 'enable' ]]; then - docker-compose build planetmint python-driver else docker-compose build --no-cache planetmint pip install --upgrade codecov diff --git a/.ci/travis_script.sh b/.ci/travis_script.sh index 436805c..5cf1d43 100755 --- a/.ci/travis_script.sh +++ b/.ci/travis_script.sh @@ -13,6 +13,8 @@ elif [[ ${PLANETMINT_CI_ABCI} == 'enable' ]]; then docker-compose exec planetmint pytest -v -m abci elif [[ ${PLANETMINT_ACCEPTANCE_TEST} == 'enable' ]]; then ./run-acceptance-test.sh +elif [[ ${PLANETMINT_INTEGRATION_TEST} == 'enable' ]]; then + ./run-integration-test.sh else docker-compose exec planetmint pytest -v --cov=planetmint --cov-report xml:htmlcov/coverage.xml fi diff --git a/.travis.yml b/.travis.yml index e13715a..e1ec9c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ matrix: - python: 3.9 env: - PLANETMINT_ACCEPTANCE_TEST=enable + - PLANETMINT_INTEGRATION_TEST=enable before_install: sudo .ci/travis-before-install.sh diff --git a/Makefile b/Makefile index 7f09890..6e5327f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help run start stop logs test test-unit test-unit-watch test-acceptance cov doc doc-acceptance clean reset release dist check-deps clean-build clean-pyc clean-test +.PHONY: help run start stop logs test test-unit test-unit-watch test-acceptance test-integration cov doc doc-acceptance clean reset release dist check-deps clean-build clean-pyc clean-test .DEFAULT_GOAL := help @@ -82,6 +82,9 @@ test-unit-watch: check-deps ## Run all tests and wait. Every time you change cod test-acceptance: check-deps ## Run all acceptance tests @./run-acceptance-test.sh +test-integration: check-deps ## Run all integration tests + @./run-integration-test.sh + cov: check-deps ## Check code coverage and open the result in the browser @$(DC) run --rm planetmint pytest -v --cov=planetmint --cov-report html $(BROWSER) htmlcov/index.html diff --git a/docker-compose.yml b/docker-compose.yml index 01f537d..331b4f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,6 +83,17 @@ services: environment: - PLANETMINT_ENDPOINT=planetmint + # Planetmint setup to do integration testing wtih Python + python-integration: + build: + context: . + dockerfile: ./integration/python/Dockerfile + volumes: + - ./integration/python/src:/src + environment: + - PLANETMINT_ENDPOINT_1=https://itest1.planetmint.io + - PLANETMINT_ENDPOINT_2=https://itest2.planetmint.io + # Build docs only # docker-compose build bdocs # docker-compose up -d bdocs diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 0000000..8982e03 --- /dev/null +++ b/integration/README.md @@ -0,0 +1,15 @@ +# Integration test suite +This directory contains the integration test suite for Planetmint. + +The suite uses Docker Compose to run all tests. + +## Running the tests +Run `make test-integration` in the project root directory. + +During development you can run single test use `pytest` inside the `python-integration` container with: + +```bash +docker-compose run --rm python-integration pytest +``` + +Note: The `/src` directory contains all the test within the container. diff --git a/integration/python/Dockerfile b/integration/python/Dockerfile new file mode 100644 index 0000000..109d83f --- /dev/null +++ b/integration/python/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.9 + +RUN mkdir -p /src +RUN pip install --upgrade \ + pytest~=6.2.5 \ + planetmint-driver~=0.9.0 \ No newline at end of file diff --git a/integration/python/src/test_basic.py b/integration/python/src/test_basic.py new file mode 100644 index 0000000..643f3d9 --- /dev/null +++ b/integration/python/src/test_basic.py @@ -0,0 +1,99 @@ +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +# import Planetmint and create object +from planetmint_driver import Planetmint +from planetmint_driver.crypto import generate_keypair +import time +import os + + +def test_basic(): + # Setup up connection to Planetmint integration test nodes + pm_itest1_url = os.environ.get('PLANETMINT_ENDPOINT_1') + pm_itest2_url = os.environ.get('PLANETMINT_ENDPOINT_1') + pm_itest1 = Planetmint(pm_itest1_url) + pm_itest2 = Planetmint(pm_itest2_url) + + # genarate a keypair + alice, bob = generate_keypair(), generate_keypair() + + # create a digital asset for Alice + game_boy_token = { + 'data': { + 'hash': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + 'storageID': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + }, + } + + # prepare the transaction with the digital asset and issue 10 tokens to bob + prepared_creation_tx = pm_itest1.transactions.prepare( + operation='CREATE', + metadata={ + 'hash': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + 'storageID': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',}, + signers=alice.public_key, + recipients=[([alice.public_key], 10)], + asset=game_boy_token) + + # fulfill and send the transaction + fulfilled_creation_tx = pm_itest1.transactions.fulfill( + prepared_creation_tx, + private_keys=alice.private_key) + pm_itest1.transactions.send_commit(fulfilled_creation_tx) + time.sleep(4) + + creation_tx_id = fulfilled_creation_tx['id'] + + # retrieve transactions from both planetmint nodes + creation_tx_itest1 = pm_itest1.transactions.retrieve(creation_tx_id) + creation_tx_itest2 = pm_itest2.transactions.retrieve(creation_tx_id) + + # Assert that transaction is stored on both planetmint nodes + assert creation_tx_itest1 == creation_tx_itest2 + + # Transfer + # create the output and inout for the transaction + transfer_asset = {'id': creation_tx_id} + output_index = 0 + output = fulfilled_creation_tx['outputs'][output_index] + transfer_input = {'fulfillment': output['condition']['details'], + 'fulfills': {'output_index': output_index, + 'transaction_id': transfer_asset['id']}, + 'owners_before': output['public_keys']} + + # prepare the transaction and use 3 tokens + prepared_transfer_tx = pm_itest1.transactions.prepare( + operation='TRANSFER', + asset=transfer_asset, + inputs=transfer_input, + metadata={'hash': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', + 'storageID': '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', }, + recipients=[([alice.public_key], 10)]) + + # fulfill and send the transaction + fulfilled_transfer_tx = pm_itest1.transactions.fulfill( + prepared_transfer_tx, + private_keys=alice.private_key) + sent_transfer_tx = pm_itest1.transactions.send_commit(fulfilled_transfer_tx) + + transfer_tx_id = fulfilled_transfer_tx['id'] + + # retrieve transactions from both planetmint nodes + transfer_tx_itest1 = pm_itest1.transactions.retrieve(transfer_tx_id) + transfer_tx_itest2 = pm_itest2.transactions.retrieve(transfer_tx_id) + + # Assert that transaction is stored on both planetmint nodes + assert transfer_tx_itest1 == transfer_tx_itest2 + + + + + + + + + + diff --git a/integration/python/src/test_multisig.py b/integration/python/src/test_multisig.py new file mode 100644 index 0000000..976fc90 --- /dev/null +++ b/integration/python/src/test_multisig.py @@ -0,0 +1,138 @@ +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +# # Multisignature integration testing +# This test checks if we can successfully create and transfer a transaction +# with multiple owners. +# The script tests various things like: +# +# - create a transaction with multiple owners +# - check if the transaction is stored and has the right amount of public keys +# - transfer the transaction to a third person +# +# We run a series of checks for each step, that is retrieving +# the transaction from the remote system, and also checking the public keys +# of a given transaction. +# +# This integration test is a rip-off of our mutliple signature acceptance tests. + +# ## Imports +# We need some utils from the `os` package, we will interact with +# env variables. +import os + +# For this test case we need import and use the Python driver +from planetmint_driver import Planetmint +from planetmint_driver.crypto import generate_keypair + +def test_multiple_owners(): + # ## Set up a connection to the Planetmint integration test nodes + pm_itest1 = Planetmint(os.environ.get('PLANETMINT_ENDPOINT_1')) + pm_itest2 = Planetmint(os.environ.get('PLANETMINT_ENDPOINT_2')) + + # Generate Keypairs for Alice and Bob! + alice, bob = generate_keypair(), generate_keypair() + + # ## Alice and Bob create a transaction + # Alice and Bob just moved into a shared flat, no one can afford these + # high rents anymore. Bob suggests to get a dish washer for the + # kitchen. Alice agrees and here they go, creating the asset for their + # dish washer. + dw_asset = { + 'data': { + 'dish washer': { + 'serial_number': 1337 + } + } + } + + # They prepare a `CREATE` transaction. To have multiple owners, both + # Bob and Alice need to be the recipients. + prepared_dw_tx = pm_itest1.transactions.prepare( + operation='CREATE', + signers=alice.public_key, + recipients=(alice.public_key, bob.public_key), + asset=dw_asset) + + # Now they both sign the transaction by providing their private keys. + # And send it afterwards. + fulfilled_dw_tx = pm_itest1.transactions.fulfill( + prepared_dw_tx, + private_keys=[alice.private_key, bob.private_key]) + + pm_itest1.transactions.send_commit(fulfilled_dw_tx) + + # We store the `id` of the transaction to use it later on. + dw_id = fulfilled_dw_tx['id'] + + # Let's retrieve the transaction from both nodes + pm_itest1_tx = pm_itest1.transactions.retrieve(dw_id) + pm_itest2_tx = pm_itest2.transactions.retrieve(dw_id) + + # Both retrieved transactions should be the same + assert pm_itest1_tx == pm_itest2_tx + + # Let's check if the transaction was successful. + assert pm_itest1.transactions.retrieve(dw_id), \ + 'Cannot find transaction {}'.format(dw_id) + + # The transaction should have two public keys in the outputs. + assert len( + pm_itest1.transactions.retrieve(dw_id)['outputs'][0]['public_keys']) == 2 + + # ## Alice and Bob transfer a transaction to Carol. + # Alice and Bob save a lot of money living together. They often go out + # for dinner and don't cook at home. But now they don't have any dishes to + # wash, so they decide to sell the dish washer to their friend Carol. + + # Hey Carol, nice to meet you! + carol = generate_keypair() + + # Alice and Bob prepare the transaction to transfer the dish washer to + # Carol. + transfer_asset = {'id': dw_id} + + output_index = 0 + output = fulfilled_dw_tx['outputs'][output_index] + transfer_input = {'fulfillment': output['condition']['details'], + 'fulfills': {'output_index': output_index, + 'transaction_id': fulfilled_dw_tx[ + 'id']}, + 'owners_before': output['public_keys']} + + # Now they create the transaction... + prepared_transfer_tx = pm_itest1.transactions.prepare( + operation='TRANSFER', + asset=transfer_asset, + inputs=transfer_input, + recipients=carol.public_key) + + # ... and sign it with their private keys, then send it. + fulfilled_transfer_tx = pm_itest1.transactions.fulfill( + prepared_transfer_tx, + private_keys=[alice.private_key, bob.private_key]) + + sent_transfer_tx = pm_itest1.transactions.send_commit(fulfilled_transfer_tx) + + # Retrieve the fulfilled transaction from both nodes + pm_itest1_tx = pm_itest1.transactions.retrieve(fulfilled_transfer_tx['id']) + pm_itest2_tx = pm_itest2.transactions.retrieve(fulfilled_transfer_tx['id']) + + # Now compare if both nodes returned the same transaction + assert pm_itest1_tx == pm_itest2_tx + + # They check if the transaction was successful. + assert pm_itest1.transactions.retrieve( + fulfilled_transfer_tx['id']) == sent_transfer_tx + + # The owners before should include both Alice and Bob. + assert len( + pm_itest1.transactions.retrieve(fulfilled_transfer_tx['id'])['inputs'][0][ + 'owners_before']) == 2 + + # While the new owner is Carol. + assert pm_itest1.transactions.retrieve(fulfilled_transfer_tx['id'])[ + 'outputs'][0]['public_keys'][0] == carol.public_key + \ No newline at end of file diff --git a/run-integration-test.sh b/run-integration-test.sh new file mode 100755 index 0000000..6c0d3be --- /dev/null +++ b/run-integration-test.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + + +# Check if both integration test nodes are reachable +check_status () { + OK="200 OK" + + STATUS_1=$(curl -I -s -X GET https://itest1.planetmint.io/ | head -n 1) + STATUS_2=$(curl -I -s -X GET https://itest2.planetmint.io/ | head -n 1) + + # Check if both response status codes return 200 OK + if ! [[ "$STATUS_1" == *"$OK"* ]] || ! [[ "$STATUS_2" == *"$OK"* ]] + then + exit 1 + fi +} + +run_test () { + docker-compose run --rm python-integration pytest /src +} + +teardown () { + docker-compose down +} + +check_status +run_test +exitcode=$? +teardown + +exit $exitcode \ No newline at end of file