planetmint/tests/web/test_websocket_server.py
Lorenz Herzberger 8abbef00fe
GitHub actions (#234)
* creating first github action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fix syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed action, using black stable

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated checkout action on workflow black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* formatted code with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced lint with black service

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed black service added black check to makefile

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pull_request to black actions trigger

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black style checker (#212)

* updated version number to 1.0.0

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* creating first github action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fix syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed action, using black stable

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated checkout action on workflow black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* formatted code with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* version bumpt

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed some comments and unsused import

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced lint with black service

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed black service added black check to makefile

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pull_request to black actions trigger

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* started on unit test workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed run step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed typo

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* testing docker-compose

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* check docker-compose

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* try running pytest

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* check out -f

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed path

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* increased health check retries, added job dependency

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added path to docker-compose.yml to test action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* moved container startup to test step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added checkout step to test job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* different kind of execution

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* checking build step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed missing keyword

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added checkout to build step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* storing artifacts

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added needs

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed Dockerfile-dev to python-slim

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added job matrix back in

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added abci to build job matrix

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated test job steps

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed typo

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced docker exec with docker-compose exec for abci test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added first version of acceptance and integration test action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added runs-on

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* reverted to docker exec

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added copyright notice and env to start container step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* separated abci from non abci test job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed pytest workflow to unit-test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added codecov workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pytest install to codecov step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pip install

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* moved codecov to unit-test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* show files

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed paths

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed debug job steps

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed black to lint, added audit workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* checking if dc down is necessary

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed dc down step from acceptance and integration

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed lint error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added tox documentation to github acitons (#226)

* added documentation job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added docs dependency install to docs workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* add more dependencies

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* install rapidjson manually

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added python-rapidjson to docs requirements text

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed gh config on tox.ini

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added base58 to docs require

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed docs require to dev

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* reversed changes to docs require

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed gh to gh-actions

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* increased verbosity for debugging

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added -e docsroot manually

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed verbosity

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed travis ci files

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed audit step to trigger on schedule

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
Co-authored-by: enesturk <enes.m.turk@gmail.com>
2022-08-18 09:45:51 +02:00

275 lines
9.1 KiB
Python

# 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 asyncio
import json
import queue
import threading
# from unittest.mock import patch
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
import pytest
class MockWebSocket:
def __init__(self):
self.received = []
def send_str(self, s):
self.received.append(s)
def test_eventify_block_works_with_any_transaction():
from planetmint.web.websocket_dispatcher import Dispatcher
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)]).sign([alice.private_key])
tx_transfer = Transfer.generate(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id).sign(
[alice.private_key]
)
block = {"height": 1, "transactions": [tx, tx_transfer]}
expected_events = [
{"height": 1, "asset_id": tx.id, "transaction_id": tx.id},
{"height": 1, "asset_id": tx_transfer.asset["id"], "transaction_id": tx_transfer.id},
]
for event, expected in zip(Dispatcher.eventify_block(block), expected_events):
assert event == expected
def test_simplified_block_works():
from planetmint.web.websocket_dispatcher import Dispatcher
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)]).sign([alice.private_key])
tx_transfer = Transfer.generate(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id).sign(
[alice.private_key]
)
block = {
"height": 1,
"hash": "27E2D48AFA5E4B7FF26AA9C84B5CFCA2A670DBD297740053C0D177EB18962B09",
"transactions": [tx, tx_transfer],
}
expected_event = {
"height": 1,
"hash": "27E2D48AFA5E4B7FF26AA9C84B5CFCA2A670DBD297740053C0D177EB18962B09",
"transaction_ids": [tx.id, tx_transfer.id],
}
blk_event = Dispatcher.simplified_block(block)
assert blk_event == expected_event
@pytest.mark.asyncio
async def test_bridge_sync_async_queue(event_loop):
from planetmint.web.websocket_server import _multiprocessing_to_asyncio
sync_queue = queue.Queue()
async_queue = asyncio.Queue(loop=event_loop)
async_queue2 = asyncio.Queue(loop=event_loop)
bridge = threading.Thread(
target=_multiprocessing_to_asyncio, args=(sync_queue, async_queue, async_queue2, event_loop), daemon=True
)
bridge.start()
sync_queue.put("fahren")
sync_queue.put("auf")
sync_queue.put("der")
sync_queue.put("Autobahn")
result = await async_queue.get()
assert result == "fahren"
result = await async_queue.get()
assert result == "auf"
result = await async_queue.get()
assert result == "der"
result = await async_queue.get()
assert result == "Autobahn"
print(f" queue ({async_queue.qsize()}): {async_queue} ")
assert async_queue.qsize() == 0
# TODO: fix the test and uncomment it
# @patch('threading.Thread')
# @patch('aiohttp.web.run_app')
# @patch('planetmint.web.websocket_server.init_app')
# @patch('asyncio.get_event_loop', return_value='event-loop')
# @patch('asyncio.Queue', return_value='event-queue')
# def test_start_creates_an_event_loop(queue_mock, get_event_loop_mock,
# init_app_mock, run_app_mock,
# thread_mock):
# from planetmint import config
# from planetmint.web.websocket_server import start, _multiprocessing_to_asyncio
#
# start(None)
# #thread_mock.assert_called_once_with(
# # target=_multiprocessing_to_asyncio,
# # args=(None, queue_mock.return_value, queue_mock.return_value, get_event_loop_mock.return_value),
# # daemon=True,
# #)
# thread_mock.return_value.start.assert_called_once_with()
# init_app_mock.assert_called_with('event-queue', 'event-queue', loop='event-loop')
# run_app_mock.assert_called_once_with(
# init_app_mock.return_value,
# host=config['wsserver']['host'],
# port=config['wsserver']['port'],
# )
@pytest.mark.asyncio
async def test_websocket_block_event(aiohttp_client, event_loop):
from planetmint import events
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT_BLOCKS
from planetmint.transactions.common import crypto
user_priv, user_pub = crypto.generate_key_pair()
tx = Create.generate([user_pub], [([user_pub], 1)])
tx = tx.sign([user_priv])
blk_source = asyncio.Queue(loop=event_loop)
tx_source = asyncio.Queue(loop=event_loop)
app = init_app(tx_source, blk_source, loop=event_loop)
client = await aiohttp_client(app)
ws = await client.ws_connect(EVENTS_ENDPOINT_BLOCKS)
block = {
"height": 1,
"hash": "27E2D48AFA5E4B7FF26AA9C84B5CFCA2A670DBD297740053C0D177EB18962B09",
"transactions": [tx],
}
block_event = events.Event(events.EventTypes.BLOCK_VALID, block)
await blk_source.put(block_event)
result = await ws.receive()
json_result = json.loads(result.data)
assert json_result["height"] == block["height"]
assert json_result["hash"] == block["hash"]
assert len(json_result["transaction_ids"]) == 1
assert json_result["transaction_ids"][0] == tx.id
await blk_source.put(events.POISON_PILL)
@pytest.mark.asyncio
async def test_websocket_transaction_event(aiohttp_client, event_loop):
from planetmint import events
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT
from planetmint.transactions.common import crypto
user_priv, user_pub = crypto.generate_key_pair()
tx = Create.generate([user_pub], [([user_pub], 1)])
tx = tx.sign([user_priv])
blk_source = asyncio.Queue(loop=event_loop)
tx_source = asyncio.Queue(loop=event_loop)
app = init_app(tx_source, blk_source, loop=event_loop)
client = await aiohttp_client(app)
ws = await client.ws_connect(EVENTS_ENDPOINT)
block = {"height": 1, "transactions": [tx]}
block_event = events.Event(events.EventTypes.BLOCK_VALID, block)
await tx_source.put(block_event)
for tx in block["transactions"]:
result = await ws.receive()
json_result = json.loads(result.data)
assert json_result["transaction_id"] == tx.id
# Since the transactions are all CREATEs, asset id == transaction id
assert json_result["asset_id"] == tx.id
assert json_result["height"] == block["height"]
await tx_source.put(events.POISON_PILL)
@pytest.mark.asyncio
async def test_websocket_string_event(aiohttp_client, event_loop):
from planetmint.events import POISON_PILL
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT
blk_source = asyncio.Queue(loop=event_loop)
tx_source = asyncio.Queue(loop=event_loop)
app = init_app(tx_source, blk_source, loop=event_loop)
client = await aiohttp_client(app)
ws = await client.ws_connect(EVENTS_ENDPOINT)
await tx_source.put("hack")
await tx_source.put("the")
await tx_source.put("planet!")
result = await ws.receive()
assert result.data == "hack"
result = await ws.receive()
assert result.data == "the"
result = await ws.receive()
assert result.data == "planet!"
await tx_source.put(POISON_PILL)
@pytest.mark.skip("Processes are not stopping properly, and the whole test suite would hang")
def test_integration_from_webapi_to_websocket(monkeypatch, client, loop):
# XXX: I think that the `pytest-aiohttp` plugin is sparkling too much
# magic in the `asyncio` module: running this test without monkey-patching
# `asycio.get_event_loop` (and without the `loop` fixture) raises a:
# RuntimeError: There is no current event loop in thread 'MainThread'.
#
# That's pretty weird because this test doesn't use the pytest-aiohttp
# plugin explicitely.
monkeypatch.setattr("asyncio.get_event_loop", lambda: loop)
import json
import random
import aiohttp
from planetmint.transactions.common import crypto
# TODO processes does not exist anymore, when reactivating this test it
# will fail because of this
from planetmint import processes
# Start Planetmint
processes.start()
loop = asyncio.get_event_loop()
import time
time.sleep(1)
ws_url = client.get("http://localhost:9984/api/v1/").json["_links"]["streams_v1"]
# Connect to the WebSocket endpoint
session = aiohttp.ClientSession()
ws = loop.run_until_complete(session.ws_connect(ws_url))
# Create a keypair and generate a new asset
user_priv, user_pub = crypto.generate_key_pair()
asset = {"random": random.random()}
tx = Create.generate([user_pub], [([user_pub], 1)], asset=asset)
tx = tx.sign([user_priv])
# Post the transaction to the Planetmint Web API
client.post("/api/v1/transactions/", data=json.dumps(tx.to_dict()))
result = loop.run_until_complete(ws.receive())
json_result = json.loads(result.data)
assert json_result["transaction_id"] == tx.id