mirror of
https://github.com/planetmint/planetmint.git
synced 2026-03-05 16:48:41 +00:00
Initial Migration
This commit is contained in:
1
acceptance/python/.gitignore
vendored
Normal file
1
acceptance/python/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
docs
|
||||
9
acceptance/python/Dockerfile
Normal file
9
acceptance/python/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM python:3.6.3
|
||||
|
||||
RUN mkdir -p /src
|
||||
RUN pip install --upgrade \
|
||||
pycco \
|
||||
websocket-client~=0.47.0 \
|
||||
pytest~=3.0 \
|
||||
bigchaindb-driver~=0.6.2 \
|
||||
blns
|
||||
125
acceptance/python/src/test_basic.py
Normal file
125
acceptance/python/src/test_basic.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# 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
|
||||
|
||||
# # Basic Acceptance Test
|
||||
# Here we check that the primitives of the system behave as expected.
|
||||
# As you will see, this script tests basic stuff like:
|
||||
#
|
||||
# - create a transaction
|
||||
# - check if the transaction is stored
|
||||
# - check for the outputs of a given public key
|
||||
# - transfer the transaction to another key
|
||||
#
|
||||
# We run a series of checks for each steps, that is retrieving the transaction from
|
||||
# the remote system, and also checking the `outputs` of a given public key.
|
||||
#
|
||||
# This acceptance test is a rip-off of our
|
||||
# [tutorial](https://docs.bigchaindb.com/projects/py-driver/en/latest/usage.html).
|
||||
|
||||
# ## Imports
|
||||
# We need some utils from the `os` package, we will interact with
|
||||
# env variables.
|
||||
import os
|
||||
|
||||
# For this test case we import and use the Python Driver.
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
|
||||
|
||||
def test_basic():
|
||||
# ## Set up a connection to Planetmint
|
||||
# To use BighainDB we need a connection. Here we create one. By default we
|
||||
# connect to localhost, but you can override this value using the env variable
|
||||
# called `PLANETMINT_ENDPOINT`, a valid value must include the schema:
|
||||
# `https://example.com:9984`
|
||||
bdb = BigchainDB(os.environ.get('PLANETMINT_ENDPOINT'))
|
||||
|
||||
# ## Create keypairs
|
||||
# This test requires the interaction between two actors with their own keypair.
|
||||
# The two keypairs will be called—drum roll—Alice and Bob.
|
||||
alice, bob = generate_keypair(), generate_keypair()
|
||||
|
||||
# ## Alice registers her bike in Planetmint
|
||||
# Alice has a nice bike, and here she creates the "digital twin"
|
||||
# of her bike.
|
||||
bike = {'data': {'bicycle': {'serial_number': 420420}}}
|
||||
|
||||
# She prepares a `CREATE` transaction...
|
||||
prepared_creation_tx = bdb.transactions.prepare(
|
||||
operation='CREATE',
|
||||
signers=alice.public_key,
|
||||
asset=bike)
|
||||
|
||||
# ... and she fulfills it with her private key.
|
||||
fulfilled_creation_tx = bdb.transactions.fulfill(
|
||||
prepared_creation_tx,
|
||||
private_keys=alice.private_key)
|
||||
|
||||
# We will use the `id` of this transaction several time, so we store it in
|
||||
# a variable with a short and easy name
|
||||
bike_id = fulfilled_creation_tx['id']
|
||||
|
||||
# Now she is ready to send it to the Planetmint Network.
|
||||
sent_transfer_tx = bdb.transactions.send_commit(fulfilled_creation_tx)
|
||||
|
||||
# And just to be 100% sure, she also checks if she can retrieve
|
||||
# it from the Planetmint node.
|
||||
assert bdb.transactions.retrieve(bike_id), 'Cannot find transaction {}'.format(bike_id)
|
||||
|
||||
# Alice is now the proud owner of one unspent asset.
|
||||
assert len(bdb.outputs.get(alice.public_key, spent=False)) == 1
|
||||
assert bdb.outputs.get(alice.public_key)[0]['transaction_id'] == bike_id
|
||||
|
||||
# ## Alice transfers her bike to Bob
|
||||
# After registering her bike, Alice is ready to transfer it to Bob.
|
||||
# She needs to create a new `TRANSFER` transaction.
|
||||
|
||||
# A `TRANSFER` transaction contains a pointer to the original asset. The original asset
|
||||
# is identified by the `id` of the `CREATE` transaction that defined it.
|
||||
transfer_asset = {'id': bike_id}
|
||||
|
||||
# Alice wants to spend the one and only output available, the one with index `0`.
|
||||
output_index = 0
|
||||
output = fulfilled_creation_tx['outputs'][output_index]
|
||||
|
||||
# Here, she defines the `input` of the `TRANSFER` transaction. The `input` contains
|
||||
# several keys:
|
||||
#
|
||||
# - `fulfillment`, taken from the previous `CREATE` transaction.
|
||||
# - `fulfills`, that specifies which condition she is fulfilling.
|
||||
# - `owners_before`.
|
||||
transfer_input = {'fulfillment': output['condition']['details'],
|
||||
'fulfills': {'output_index': output_index,
|
||||
'transaction_id': fulfilled_creation_tx['id']},
|
||||
'owners_before': output['public_keys']}
|
||||
|
||||
# Now that all the elements are set, she creates the actual transaction...
|
||||
prepared_transfer_tx = bdb.transactions.prepare(
|
||||
operation='TRANSFER',
|
||||
asset=transfer_asset,
|
||||
inputs=transfer_input,
|
||||
recipients=bob.public_key)
|
||||
|
||||
# ... and signs it with her private key.
|
||||
fulfilled_transfer_tx = bdb.transactions.fulfill(
|
||||
prepared_transfer_tx,
|
||||
private_keys=alice.private_key)
|
||||
|
||||
# She finally sends the transaction to a Planetmint node.
|
||||
sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx)
|
||||
|
||||
# And just to be 100% sure, she also checks if she can retrieve
|
||||
# it from the Planetmint node.
|
||||
assert bdb.transactions.retrieve(fulfilled_transfer_tx['id']) == sent_transfer_tx
|
||||
|
||||
# Now Alice has zero unspent transactions.
|
||||
assert len(bdb.outputs.get(alice.public_key, spent=False)) == 0
|
||||
|
||||
# While Bob has one.
|
||||
assert len(bdb.outputs.get(bob.public_key, spent=False)) == 1
|
||||
|
||||
# Bob double checks what he got was the actual bike.
|
||||
bob_tx_id = bdb.outputs.get(bob.public_key, spent=False)[0]['transaction_id']
|
||||
assert bdb.transactions.retrieve(bob_tx_id) == sent_transfer_tx
|
||||
181
acceptance/python/src/test_divisible_asset.py
Normal file
181
acceptance/python/src/test_divisible_asset.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# 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
|
||||
|
||||
# # Divisible assets integration testing
|
||||
# This test checks if we can successfully divide assets.
|
||||
# The script tests various things like:
|
||||
#
|
||||
# - create a transaction with a divisible asset and issue them to someone
|
||||
# - check if the transaction is stored and has the right amount of tokens
|
||||
# - spend some tokens
|
||||
# - try to spend more tokens than available
|
||||
#
|
||||
# We run a series of checks for each step, that is retrieving
|
||||
# the transaction from the remote system, and also checking the `amount`
|
||||
# of a given transaction.
|
||||
#
|
||||
# This integration test is a rip-off of our
|
||||
# [tutorial](https://docs.bigchaindb.com/projects/py-driver/en/latest/usage.html).
|
||||
|
||||
# ## Imports
|
||||
# We need some utils from the `os` package, we will interact with
|
||||
# env variables.
|
||||
# We need the `pytest` package to catch the `BadRequest` exception properly.
|
||||
# And of course, we also need the `BadRequest`.
|
||||
import os
|
||||
import pytest
|
||||
from bigchaindb_driver.exceptions import BadRequest
|
||||
|
||||
# For this test case we import and use the Python Driver.
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
|
||||
|
||||
def test_divisible_assets():
|
||||
# ## Set up a connection to Planetmint
|
||||
# Check [test_basic.py](./test_basic.html) to get some more details
|
||||
# about the endpoint.
|
||||
bdb = BigchainDB(os.environ.get('PLANETMINT_ENDPOINT'))
|
||||
|
||||
# Oh look, it is Alice again and she brought her friend Bob along.
|
||||
alice, bob = generate_keypair(), generate_keypair()
|
||||
|
||||
# ## Alice creates a time sharing token
|
||||
# Alice wants to go on vacation, while Bobs bike just broke down.
|
||||
# Alice decides to rent her bike to Bob while she is gone.
|
||||
# So she prepares a `CREATE` transaction to issues 10 tokens.
|
||||
# First, she prepares an asset for a time sharing token. As you can see in
|
||||
# the description, Bob and Alice agree that each token can be used to ride
|
||||
# the bike for one hour.
|
||||
|
||||
bike_token = {
|
||||
'data': {
|
||||
'token_for': {
|
||||
'bike': {
|
||||
'serial_number': 420420
|
||||
}
|
||||
},
|
||||
'description': 'Time share token. Each token equals one hour of riding.',
|
||||
},
|
||||
}
|
||||
|
||||
# She prepares a `CREATE` transaction and issues 10 tokens.
|
||||
# Here, Alice defines in a tuple that she wants to assign
|
||||
# these 10 tokens to Bob.
|
||||
prepared_token_tx = bdb.transactions.prepare(
|
||||
operation='CREATE',
|
||||
signers=alice.public_key,
|
||||
recipients=[([bob.public_key], 10)],
|
||||
asset=bike_token)
|
||||
|
||||
# She fulfills and sends the transaction.
|
||||
fulfilled_token_tx = bdb.transactions.fulfill(
|
||||
prepared_token_tx,
|
||||
private_keys=alice.private_key)
|
||||
|
||||
bdb.transactions.send_commit(fulfilled_token_tx)
|
||||
|
||||
# We store the `id` of the transaction to use it later on.
|
||||
bike_token_id = fulfilled_token_tx['id']
|
||||
|
||||
# Let's check if the transaction was successful.
|
||||
assert bdb.transactions.retrieve(bike_token_id), \
|
||||
'Cannot find transaction {}'.format(bike_token_id)
|
||||
|
||||
# Bob owns 10 tokens now.
|
||||
assert bdb.transactions.retrieve(bike_token_id)['outputs'][0][
|
||||
'amount'] == '10'
|
||||
|
||||
# ## Bob wants to use the bike
|
||||
# Now that Bob got the tokens and the sun is shining, he wants to get out
|
||||
# with the bike for three hours.
|
||||
# To use the bike he has to send the tokens back to Alice.
|
||||
# To learn about the details of transferring a transaction check out
|
||||
# [test_basic.py](./test_basic.html)
|
||||
transfer_asset = {'id': bike_token_id}
|
||||
|
||||
output_index = 0
|
||||
output = fulfilled_token_tx['outputs'][output_index]
|
||||
transfer_input = {'fulfillment': output['condition']['details'],
|
||||
'fulfills': {'output_index': output_index,
|
||||
'transaction_id': fulfilled_token_tx[
|
||||
'id']},
|
||||
'owners_before': output['public_keys']}
|
||||
|
||||
# To use the tokens Bob has to reassign 7 tokens to himself and the
|
||||
# amount he wants to use to Alice.
|
||||
prepared_transfer_tx = bdb.transactions.prepare(
|
||||
operation='TRANSFER',
|
||||
asset=transfer_asset,
|
||||
inputs=transfer_input,
|
||||
recipients=[([alice.public_key], 3), ([bob.public_key], 7)])
|
||||
|
||||
# He signs and sends the transaction.
|
||||
fulfilled_transfer_tx = bdb.transactions.fulfill(
|
||||
prepared_transfer_tx,
|
||||
private_keys=bob.private_key)
|
||||
|
||||
sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx)
|
||||
|
||||
# First, Bob checks if the transaction was successful.
|
||||
assert bdb.transactions.retrieve(
|
||||
fulfilled_transfer_tx['id']) == sent_transfer_tx
|
||||
# There are two outputs in the transaction now.
|
||||
# The first output shows that Alice got back 3 tokens...
|
||||
assert bdb.transactions.retrieve(
|
||||
fulfilled_transfer_tx['id'])['outputs'][0]['amount'] == '3'
|
||||
|
||||
# ... while Bob still has 7 left.
|
||||
assert bdb.transactions.retrieve(
|
||||
fulfilled_transfer_tx['id'])['outputs'][1]['amount'] == '7'
|
||||
|
||||
# ## Bob wants to ride the bike again
|
||||
# It's been a week and Bob wants to right the bike again.
|
||||
# Now he wants to ride for 8 hours, that's a lot Bob!
|
||||
# He prepares the transaction again.
|
||||
|
||||
transfer_asset = {'id': bike_token_id}
|
||||
# This time we need an `output_index` of 1, since we have two outputs
|
||||
# in the `fulfilled_transfer_tx` we created before. The first output with
|
||||
# index 0 is for Alice and the second output is for Bob.
|
||||
# Since Bob wants to spend more of his tokens he has to provide the
|
||||
# correct output with the correct amount of tokens.
|
||||
output_index = 1
|
||||
|
||||
output = fulfilled_transfer_tx['outputs'][output_index]
|
||||
|
||||
transfer_input = {'fulfillment': output['condition']['details'],
|
||||
'fulfills': {'output_index': output_index,
|
||||
'transaction_id': fulfilled_transfer_tx['id']},
|
||||
'owners_before': output['public_keys']}
|
||||
|
||||
# This time Bob only provides Alice in the `recipients` because he wants
|
||||
# to spend all his tokens
|
||||
prepared_transfer_tx = bdb.transactions.prepare(
|
||||
operation='TRANSFER',
|
||||
asset=transfer_asset,
|
||||
inputs=transfer_input,
|
||||
recipients=[([alice.public_key], 8)])
|
||||
|
||||
fulfilled_transfer_tx = bdb.transactions.fulfill(
|
||||
prepared_transfer_tx,
|
||||
private_keys=bob.private_key)
|
||||
|
||||
# Oh Bob, what have you done?! You tried to spend more tokens than you had.
|
||||
# Remember Bob, last time you spent 3 tokens already,
|
||||
# so you only have 7 left.
|
||||
with pytest.raises(BadRequest) as error:
|
||||
bdb.transactions.send_commit(fulfilled_transfer_tx)
|
||||
|
||||
# Now Bob gets an error saying that the amount he wanted to spent is
|
||||
# higher than the amount of tokens he has left.
|
||||
assert error.value.args[0] == 400
|
||||
message = 'Invalid transaction (AmountError): The amount used in the ' \
|
||||
'inputs `7` needs to be same as the amount used in the ' \
|
||||
'outputs `8`'
|
||||
assert error.value.args[2]['message'] == message
|
||||
|
||||
# We have to stop this test now, I am sorry, but Bob is pretty upset
|
||||
# about his mistake. See you next time :)
|
||||
48
acceptance/python/src/test_double_spend.py
Normal file
48
acceptance/python/src/test_double_spend.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# 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
|
||||
|
||||
# # Double Spend testing
|
||||
# This test challenge the system with double spends.
|
||||
|
||||
import os
|
||||
from uuid import uuid4
|
||||
from threading import Thread
|
||||
import queue
|
||||
|
||||
import bigchaindb_driver.exceptions
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
|
||||
|
||||
def test_double_create():
|
||||
bdb = BigchainDB(os.environ.get('PLANETMINT_ENDPOINT'))
|
||||
alice = generate_keypair()
|
||||
|
||||
results = queue.Queue()
|
||||
|
||||
tx = bdb.transactions.fulfill(
|
||||
bdb.transactions.prepare(
|
||||
operation='CREATE',
|
||||
signers=alice.public_key,
|
||||
asset={'data': {'uuid': str(uuid4())}}),
|
||||
private_keys=alice.private_key)
|
||||
|
||||
def send_and_queue(tx):
|
||||
try:
|
||||
bdb.transactions.send_commit(tx)
|
||||
results.put('OK')
|
||||
except bigchaindb_driver.exceptions.TransportError as e:
|
||||
results.put('FAIL')
|
||||
|
||||
t1 = Thread(target=send_and_queue, args=(tx, ))
|
||||
t2 = Thread(target=send_and_queue, args=(tx, ))
|
||||
|
||||
t1.start()
|
||||
t2.start()
|
||||
|
||||
results = [results.get(timeout=2), results.get(timeout=2)]
|
||||
|
||||
assert results.count('OK') == 1
|
||||
assert results.count('FAIL') == 1
|
||||
126
acceptance/python/src/test_multiple_owners.py
Normal file
126
acceptance/python/src/test_multiple_owners.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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
|
||||
|
||||
# # Multiple owners 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
|
||||
# [tutorial](https://docs.bigchaindb.com/projects/py-driver/en/latest/usage.html).
|
||||
|
||||
# ## Imports
|
||||
# We need some utils from the `os` package, we will interact with
|
||||
# env variables.
|
||||
import os
|
||||
|
||||
# For this test case we import and use the Python Driver.
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
|
||||
|
||||
def test_multiple_owners():
|
||||
# ## Set up a connection to Planetmint
|
||||
# Check [test_basic.py](./test_basic.html) to get some more details
|
||||
# about the endpoint.
|
||||
bdb = BigchainDB(os.environ.get('PLANETMINT_ENDPOINT'))
|
||||
|
||||
# Hey Alice and Bob, nice to see you again!
|
||||
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 = bdb.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 = bdb.transactions.fulfill(
|
||||
prepared_dw_tx,
|
||||
private_keys=[alice.private_key, bob.private_key])
|
||||
|
||||
bdb.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 check if the transaction was successful.
|
||||
assert bdb.transactions.retrieve(dw_id), \
|
||||
'Cannot find transaction {}'.format(dw_id)
|
||||
|
||||
# The transaction should have two public keys in the outputs.
|
||||
assert len(
|
||||
bdb.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 = bdb.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 = bdb.transactions.fulfill(
|
||||
prepared_transfer_tx,
|
||||
private_keys=[alice.private_key, bob.private_key])
|
||||
|
||||
sent_transfer_tx = bdb.transactions.send_commit(fulfilled_transfer_tx)
|
||||
|
||||
# They check if the transaction was successful.
|
||||
assert bdb.transactions.retrieve(
|
||||
fulfilled_transfer_tx['id']) == sent_transfer_tx
|
||||
|
||||
# The owners before should include both Alice and Bob.
|
||||
assert len(
|
||||
bdb.transactions.retrieve(fulfilled_transfer_tx['id'])['inputs'][0][
|
||||
'owners_before']) == 2
|
||||
|
||||
# While the new owner is Carol.
|
||||
assert bdb.transactions.retrieve(fulfilled_transfer_tx['id'])[
|
||||
'outputs'][0]['public_keys'][0] == carol.public_key
|
||||
101
acceptance/python/src/test_naughty_strings.py
Normal file
101
acceptance/python/src/test_naughty_strings.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# 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
|
||||
|
||||
# ## Testing potentially hazardous strings
|
||||
# This test uses a library of `naughty` strings (code injections, weird unicode chars., etc.) as both keys and values.
|
||||
# We look for either a successful tx, or in the case that we use a naughty string as a key, and it violates some key
|
||||
# constraints, we expect to receive a well formatted error message.
|
||||
|
||||
# ## Imports
|
||||
# We need some utils from the `os` package, we will interact with
|
||||
# env variables.
|
||||
import os
|
||||
|
||||
# Since the naughty strings get encoded and decoded in odd ways,
|
||||
# we'll use a regex to sweep those details under the rug.
|
||||
import re
|
||||
|
||||
# We'll use a nice library of naughty strings...
|
||||
from blns import blns
|
||||
|
||||
# And parameterize our test so each one is treated as a separate test case
|
||||
import pytest
|
||||
|
||||
# For this test case we import and use the Python Driver.
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
from bigchaindb_driver.exceptions import BadRequest
|
||||
|
||||
naughty_strings = blns.all()
|
||||
|
||||
|
||||
# This is our base test case, but we'll reuse it to send naughty strings as both keys and values.
|
||||
def send_naughty_tx(asset, metadata):
|
||||
# ## Set up a connection to Planetmint
|
||||
# Check [test_basic.py](./test_basic.html) to get some more details
|
||||
# about the endpoint.
|
||||
bdb = BigchainDB(os.environ.get('PLANETMINT_ENDPOINT'))
|
||||
|
||||
# Here's Alice.
|
||||
alice = generate_keypair()
|
||||
|
||||
# Alice is in a naughty mood today, so she creates a tx with some naughty strings
|
||||
prepared_transaction = bdb.transactions.prepare(
|
||||
operation='CREATE',
|
||||
signers=alice.public_key,
|
||||
asset=asset,
|
||||
metadata=metadata)
|
||||
|
||||
# She fulfills the transaction
|
||||
fulfilled_transaction = bdb.transactions.fulfill(
|
||||
prepared_transaction,
|
||||
private_keys=alice.private_key)
|
||||
|
||||
# The fulfilled tx gets sent to the BDB network
|
||||
try:
|
||||
sent_transaction = bdb.transactions.send_commit(fulfilled_transaction)
|
||||
except BadRequest as e:
|
||||
sent_transaction = e
|
||||
|
||||
# If her key contained a '.', began with a '$', or contained a NUL character
|
||||
regex = '.*\..*|\$.*|.*\x00.*'
|
||||
key = next(iter(metadata))
|
||||
if re.match(regex, key):
|
||||
# Then she expects a nicely formatted error code
|
||||
status_code = sent_transaction.status_code
|
||||
error = sent_transaction.error
|
||||
regex = (
|
||||
r'\{\s*\n*'
|
||||
r'\s*"message":\s*"Invalid transaction \(ValidationError\):\s*'
|
||||
r'Invalid key name.*The key name cannot contain characters.*\n*'
|
||||
r'\s*"status":\s*400\n*'
|
||||
r'\s*\}\n*')
|
||||
assert status_code == 400
|
||||
assert re.fullmatch(regex, error), sent_transaction
|
||||
# Otherwise, she expects to see her transaction in the database
|
||||
elif 'id' in sent_transaction.keys():
|
||||
tx_id = sent_transaction['id']
|
||||
assert bdb.transactions.retrieve(tx_id)
|
||||
# If neither condition was true, then something weird happened...
|
||||
else:
|
||||
raise TypeError(sent_transaction)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
|
||||
def test_naughty_keys(naughty_string):
|
||||
|
||||
asset = {'data': {naughty_string: 'nice_value'}}
|
||||
metadata = {naughty_string: 'nice_value'}
|
||||
|
||||
send_naughty_tx(asset, metadata)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("naughty_string", naughty_strings, ids=naughty_strings)
|
||||
def test_naughty_values(naughty_string):
|
||||
|
||||
asset = {'data': {'nice_key': naughty_string}}
|
||||
metadata = {'nice_key': naughty_string}
|
||||
|
||||
send_naughty_tx(asset, metadata)
|
||||
132
acceptance/python/src/test_stream.py
Normal file
132
acceptance/python/src/test_stream.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# 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
|
||||
|
||||
# # Stream Acceptance Test
|
||||
# This test checks if the event stream works correctly. The basic idea of this
|
||||
# test is to generate some random **valid** transaction, send them to a
|
||||
# Planetmint node, and expect those transactions to be returned by the valid
|
||||
# transactions Stream API. During this test, two threads work together,
|
||||
# sharing a queue to exchange events.
|
||||
#
|
||||
# - The *main thread* first creates and sends the transactions to Planetmint;
|
||||
# then it run through all events in the shared queue to check if all
|
||||
# transactions sent have been validated by Planetmint.
|
||||
# - The *listen thread* listens to the events coming from Planetmint and puts
|
||||
# them in a queue shared with the main thread.
|
||||
|
||||
import os
|
||||
import queue
|
||||
import json
|
||||
from threading import Thread, Event
|
||||
from uuid import uuid4
|
||||
|
||||
# For this script, we need to set up a websocket connection, that's the reason
|
||||
# we import the
|
||||
# [websocket](https://github.com/websocket-client/websocket-client) module
|
||||
from websocket import create_connection
|
||||
|
||||
from bigchaindb_driver import BigchainDB
|
||||
from bigchaindb_driver.crypto import generate_keypair
|
||||
|
||||
|
||||
def test_stream():
|
||||
# ## Set up the test
|
||||
# We use the env variable `BICHAINDB_ENDPOINT` to know where to connect.
|
||||
# Check [test_basic.py](./test_basic.html) for more information.
|
||||
BDB_ENDPOINT = os.environ.get('PLANETMINT_ENDPOINT')
|
||||
|
||||
# *That's pretty bad, but let's do like this for now.*
|
||||
WS_ENDPOINT = 'ws://{}:9985/api/v1/streams/valid_transactions'.format(BDB_ENDPOINT.rsplit(':')[0])
|
||||
|
||||
bdb = BigchainDB(BDB_ENDPOINT)
|
||||
|
||||
# Hello to Alice again, she is pretty active in those tests, good job
|
||||
# Alice!
|
||||
alice = generate_keypair()
|
||||
|
||||
# We need few variables to keep the state, specifically we need `sent` to
|
||||
# keep track of all transactions Alice sent to Planetmint, while `received`
|
||||
# are the transactions Planetmint validated and sent back to her.
|
||||
sent = []
|
||||
received = queue.Queue()
|
||||
|
||||
# In this test we use a websocket. The websocket must be started **before**
|
||||
# sending transactions to Planetmint, otherwise we might lose some
|
||||
# transactions. The `ws_ready` event is used to synchronize the main thread
|
||||
# with the listen thread.
|
||||
ws_ready = Event()
|
||||
|
||||
# ## Listening to events
|
||||
# This is the function run by the complementary thread.
|
||||
def listen():
|
||||
# First we connect to the remote endpoint using the WebSocket protocol.
|
||||
ws = create_connection(WS_ENDPOINT)
|
||||
|
||||
# After the connection has been set up, we can signal the main thread
|
||||
# to proceed (continue reading, it should make sense in a second.)
|
||||
ws_ready.set()
|
||||
|
||||
# It's time to consume all events coming from the Planetmint stream API.
|
||||
# Every time a new event is received, it is put in the queue shared
|
||||
# with the main thread.
|
||||
while True:
|
||||
result = ws.recv()
|
||||
received.put(result)
|
||||
|
||||
# Put `listen` in a thread, and start it. Note that `listen` is a local
|
||||
# function and it can access all variables in the enclosing function.
|
||||
t = Thread(target=listen, daemon=True)
|
||||
t.start()
|
||||
|
||||
# ## Pushing the transactions to Planetmint
|
||||
# After starting the listen thread, we wait for it to connect, and then we
|
||||
# proceed.
|
||||
ws_ready.wait()
|
||||
|
||||
# Here we prepare, sign, and send ten different `CREATE` transactions. To
|
||||
# make sure each transaction is different from the other, we generate a
|
||||
# random `uuid`.
|
||||
for _ in range(10):
|
||||
tx = bdb.transactions.fulfill(
|
||||
bdb.transactions.prepare(
|
||||
operation='CREATE',
|
||||
signers=alice.public_key,
|
||||
asset={'data': {'uuid': str(uuid4())}}),
|
||||
private_keys=alice.private_key)
|
||||
# We don't want to wait for each transaction to be in a block. By using
|
||||
# `async` mode, we make sure that the driver returns as soon as the
|
||||
# transaction is pushed to the Planetmint API. Remember: we expect all
|
||||
# transactions to be in the shared queue: this is a two phase test,
|
||||
# first we send a bunch of transactions, then we check if they are
|
||||
# valid (and, in this case, they should).
|
||||
bdb.transactions.send_async(tx)
|
||||
|
||||
# The `id` of every sent transaction is then stored in a list.
|
||||
sent.append(tx['id'])
|
||||
|
||||
# ## Check the valid transactions coming from Planetmint
|
||||
# Now we are ready to check if Planetmint did its job. A simple way to
|
||||
# check if all sent transactions have been processed is to **remove** from
|
||||
# `sent` the transactions we get from the *listen thread*. At one point in
|
||||
# time, `sent` should be empty, and we exit the test.
|
||||
while sent:
|
||||
# To avoid waiting forever, we have an arbitrary timeout of 5
|
||||
# seconds: it should be enough time for Planetmint to create
|
||||
# blocks, in fact a new block is created every second. If we hit
|
||||
# the timeout, then game over ¯\\\_(ツ)\_/¯
|
||||
try:
|
||||
event = received.get(timeout=5)
|
||||
txid = json.loads(event)['transaction_id']
|
||||
except queue.Empty:
|
||||
assert False, 'Did not receive all expected transactions'
|
||||
|
||||
# Last thing is to try to remove the `txid` from the set of sent
|
||||
# transactions. If this test is running in parallel with others, we
|
||||
# might get a transaction id of another test, and `remove` can fail.
|
||||
# It's OK if this happens.
|
||||
try:
|
||||
sent.remove(txid)
|
||||
except ValueError:
|
||||
pass
|
||||
Reference in New Issue
Block a user