diff --git a/_fixtures/src/upload-test/UploadTest.daml b/_fixtures/src/upload-test/UploadTest.daml new file mode 100644 index 00000000..68ab5386 --- /dev/null +++ b/_fixtures/src/upload-test/UploadTest.daml @@ -0,0 +1,14 @@ +-- UploadTest.daml +-- +-- This is ONLY used to test DAR uploading functionality. The test that uploads +-- this DAR assumes it has not been uploaded by any other process before, so it +-- should only be used for that one test. +daml 1.2 + +module UploadTest where + +template XYZ + with + party: Party + where + signatory party diff --git a/_fixtures/src/upload-test/daml.yaml b/_fixtures/src/upload-test/daml.yaml new file mode 100644 index 00000000..d7101a66 --- /dev/null +++ b/_fixtures/src/upload-test/daml.yaml @@ -0,0 +1,10 @@ +sdk-version: 0.13.32 +name: upload-test +version: 1.0.0 +source: UploadTest.daml +parties: +- Alice +- Bob +dependencies: +- daml-prim +- daml-stdlib diff --git a/python/dazl/cli/__init__.py b/python/dazl/cli/__init__.py index ab16ebee..4aac68c7 100644 --- a/python/dazl/cli/__init__.py +++ b/python/dazl/cli/__init__.py @@ -13,14 +13,12 @@ from ._base import CliCommand from .ls import ListAllCommand from .metadata import PrintMetadataCommand -from .sandbox import SandboxCommand from .upload import UploadCommand from .version import VersionCommand COMMANDS = [ ListAllCommand(), PrintMetadataCommand(), - SandboxCommand(), UploadCommand(), VersionCommand(), ] # type: List[CliCommand] diff --git a/python/dazl/cli/sandbox.py b/python/dazl/cli/sandbox.py deleted file mode 100644 index 0b46a18f..00000000 --- a/python/dazl/cli/sandbox.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -from asyncio import get_event_loop -from argparse import ArgumentParser - -from .. import sandbox -from ..util.dar import DamlcPackageError -from ._base import CliCommand - - -class SandboxCommand(CliCommand): - name = 'sandbox' - hidden = True - - def parser(self): - arg_parser = ArgumentParser('dazl sandbox') - arg_parser.add_argument( - 'file', metavar='FILE', help='A .daml file to compile into a package, or a .dar file') - arg_parser.add_argument( - '--port', metavar='PORT', default=7600, type=int) - arg_parser.add_argument( - '--log-level') - return arg_parser - - def execute(self, args): - try: - with sandbox(args.file, args.port): - get_event_loop().run_forever() - except DamlcPackageError as ex: - return ex.exit_code diff --git a/python/poetry.lock b/python/poetry.lock index 2bd38204..c30c0705 100644 --- a/python/poetry.lock +++ b/python/poetry.lock @@ -451,7 +451,7 @@ description = "pytest: simple powerful testing with Python" name = "pytest" optional = false python-versions = ">=3.5" -version = "5.4.1" +version = "5.4.3" [package.dependencies] atomicwrites = ">=1.0" @@ -477,13 +477,13 @@ description = "Pytest support for asyncio." name = "pytest-asyncio" optional = false python-versions = ">= 3.5" -version = "0.10.0" +version = "0.14.0" [package.dependencies] -pytest = ">=3.0.6" +pytest = ">=5.4.0" [package.extras] -testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=3.64)"] +testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=5.7.1)"] [[package]] category = "dev" @@ -810,7 +810,7 @@ pygments = ["pygments"] pytest = [] [metadata] -content-hash = "3a681c4cb3a985ec03e367a317a46808d77661ad1875795c7b4103d754e0a181" +content-hash = "08a9fc39b3d2d05e5a894a3b16127c775295d37f1706b70ecb6876a1da78d406" python-versions = ">=3.6" [metadata.files] @@ -1168,12 +1168,12 @@ pyparsing = [ {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, ] pytest = [ - {file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"}, - {file = "pytest-5.4.1.tar.gz", hash = "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"}, + {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, + {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, ] pytest-asyncio = [ - {file = "pytest-asyncio-0.10.0.tar.gz", hash = "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf"}, - {file = "pytest_asyncio-0.10.0-py3-none-any.whl", hash = "sha256:d734718e25cfc32d2bf78d346e99d33724deeba774cc4afdf491530c6184b63b"}, + {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"}, + {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"}, ] pytest-subtests = [ {file = "pytest-subtests-0.2.1.tar.gz", hash = "sha256:9c0480d4a44dbff5c6ad8ecb283ac66e5bc5ef951c6ba838d51e2548376b3a00"}, diff --git a/python/pyproject.toml b/python/pyproject.toml index bc65c2e4..2ba4dc12 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -29,7 +29,7 @@ typing_extensions = { version = "*", python = "<3.8.0" } grpcio-tools = ">=1.20.1" mypy = "*" pympler = "*" -pytest = "*" +pytest = "^5" pytest-asyncio = "*" setuptools = "==40.8.0" sphinx = "*" diff --git a/python/tests/unit/blocking_setup.py b/python/tests/unit/blocking_setup.py new file mode 100644 index 00000000..10fac977 --- /dev/null +++ b/python/tests/unit/blocking_setup.py @@ -0,0 +1,56 @@ +from asyncio import set_event_loop, new_event_loop +from threading import Thread + +from dazl import Party, Network +from dazl.model.core import Dar + + +def blocking_setup(url: str, dar: Dar) -> 'Party': + """ + Set up a ledger for a test in a completely blocking fashion. + + Used by the tests that test the thread-safe variants of the dazl API where + avoiding contamination of the current async context is more important than + the performance ramifications of calling this function. + + :param url: + The URL of the remote Ledger API implementation to connect to. + :param dar: + A DAR file. + :return: + A newly allocated ``Party`` that is guaranteed to be used by no other + client. + """ + return Setup(url, dar).run() + + +class Setup: + def __init__(self, url, dar): + self.url = url + self.party = None + self.dar = dar + self.network = None + + def run(self): + # upload our DAR and allocate our Party in a completely separate thread as to try to avoid + # polluting the current context + t = Thread(target=self._main) + t.start() + t.join() + return self.party + + def _main(self): + # create a private event loop just for us + set_event_loop(new_event_loop()) + + self.network = Network() + self.network.set_config(url=self.url) + + client = self.network.aio_new_party() + + self.party = client.party + + self.network.run_until_complete(self.upload_dar()) + + async def upload_dar(self): + await self.network.aio_global().ensure_dar(self.dar) diff --git a/python/tests/unit/dars.py b/python/tests/unit/dars.py index 2ab53ddd..ed666282 100644 --- a/python/tests/unit/dars.py +++ b/python/tests/unit/dars.py @@ -42,6 +42,7 @@ def load_dars() -> 'Mapping[str, Path]': PostOffice = DARS['post-office'] Simple = DARS['simple'] TestServer = DARS['test-server'] +UploadTest = DARS['upload-test'] if __name__ == '__main__': diff --git a/python/tests/unit/test_all_party.py b/python/tests/unit/test_all_party.py index 2204ac6a..cd497ffc 100644 --- a/python/tests/unit/test_all_party.py +++ b/python/tests/unit/test_all_party.py @@ -2,30 +2,36 @@ # SPDX-License-Identifier: Apache-2.0 import logging +import uuid -from dazl import sandbox, create, Network +import pytest -from .dars import AllParty +from dazl import async_network, create, Party + +from .dars import AllParty as AllPartyDar -SOME_PARTY = 'SomeParty' -PUBLISHER = 'Publisher' -ALL_PARTY = '000' PrivateContract = 'AllParty.PrivateContract' PublicContract = 'AllParty.PublicContract' -def test_some_party_receives_public_contract(): +@pytest.mark.asyncio +async def test_some_party_receives_public_contract(sandbox): some_party_cids = [] publisher_cids = [] - with sandbox(AllParty) as proc: - network = Network() - network.set_config(url=proc.url, party_groups=[ALL_PARTY]) - some_client = network.aio_party(SOME_PARTY) - some_client.add_ledger_ready(lambda _: create(PrivateContract, {'someParty': SOME_PARTY})) + # TODO: Switch to a Party allocation API when available. + all_party = Party(str(uuid.uuid4())) + + async with async_network(url=sandbox, dars=AllPartyDar) as network: + network.set_config(party_groups=[all_party]) - publisher_client = network.aio_party(PUBLISHER) - publisher_client.add_ledger_ready(lambda _: create(PublicContract, {'publisher': PUBLISHER, 'allParty': ALL_PARTY})) + some_client = network.aio_new_party() + some_client.add_ledger_ready( + lambda _: create(PrivateContract, {'someParty': some_client.party})) + + publisher_client = network.aio_new_party() + publisher_client.add_ledger_ready( + lambda _: create(PublicContract, {'publisher': publisher_client.party, 'allParty': all_party})) some_client.add_ledger_created(PublicContract, lambda e: some_party_cids.append(e.cid)) some_client.add_ledger_created(PrivateContract, lambda e: some_party_cids.append(e.cid)) @@ -33,8 +39,11 @@ def test_some_party_receives_public_contract(): publisher_client.add_ledger_created(PublicContract, lambda e: publisher_cids.append(e.cid)) publisher_client.add_ledger_created(PrivateContract, lambda e: publisher_cids.append(e.cid)) - network.run_until_complete() + network.start() + + logging.info( + 'got to the end with some_party contracts: %s and publisher contracts: %s', + some_party_cids, publisher_cids) - logging.info(f'got to the end with some_party contracts: {some_party_cids} and publisher contracts: {publisher_cids}') assert len(some_party_cids) == 2 assert len(publisher_cids) == 1 diff --git a/python/tests/unit/test_dar_upload.py b/python/tests/unit/test_dar_upload.py index 02d15d27..d6cc0fa4 100644 --- a/python/tests/unit/test_dar_upload.py +++ b/python/tests/unit/test_dar_upload.py @@ -1,56 +1,59 @@ # Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -from asyncio import new_event_loop, set_event_loop, sleep +import pytest +from asyncio import sleep -from dazl import Network, sandbox -from .dars import AllKindsOf +from dazl import Network +from .dars import UploadTest -def test_dar_uploads_near_startup(): - set_event_loop(new_event_loop()) - +@pytest.mark.asyncio +async def test_dar_uploads_near_startup(sandbox): package_ids = [] - with sandbox([]) as proc: - network = Network() - network.set_config(url=proc.url) + network = Network() + network.set_config(url=sandbox) - async def upload_dars_and_verify(): - await upload_test_dars(network) - metadata = await network.aio_global().metadata() - package_ids.extend(metadata.store.package_ids()) + async def upload_dars_and_verify(): + await upload_test_dars(network) + metadata = await network.aio_global().metadata() + package_ids.extend(metadata.store.package_ids()) - network.run_until_complete(upload_dars_and_verify()) + await network.aio_run(upload_dars_and_verify(), keep_open=False) + # Because we use a single sandbox process, it's somewhat difficult to assert that the specific + # DAR we are attempting to upload has indeed been uploaded, because packages are global and + # other tests will upload packages as well. However, we know that we HAVE indeed uploaded + # SOMETHING, and the Sandbox tests are started without any packages at all. So assume that a + # non-zero package ID list means that DAR uploading works. assert len(package_ids) > 0 -def test_package_events(): - set_event_loop(new_event_loop()) - +@pytest.mark.asyncio +async def test_package_events(sandbox): initial_events = [] follow_up_events = [] - with sandbox([]) as proc: - network = Network() - network.set_config(url=proc.url) - client = network.aio_party('TestParty') + network = Network() + network.set_config(url=sandbox) + client = network.aio_new_party() + + async def upload_dars_and_verify(): + # make sure the client is "ready" before uploading DARs, because we are explicitly + # checking to make sure proper reporting of packages that are uploaded after a + # client is running and # operational + await client.ready() + await upload_test_dars(network) - async def upload_dars_and_verify(): - # make sure the client is "ready" before uploading DARs, because we are explicitly - # checking to make sure proper reporting of packages that are uploaded after a - # client is running and # operational - await client.ready() - await upload_test_dars(network) + # give the client some time to pick up the new packages; unfortunately there isn't + # much more to do here except wait + await sleep(10) - # give the client some time to pick up the new packages; unfortunately there isn't - # much more to do here except wait - await sleep(10) + client.add_ledger_packages_added(lambda _: initial_events.append(_), initial=True) + client.add_ledger_packages_added(lambda _: follow_up_events.append(_)) - client.add_ledger_packages_added(lambda _: initial_events.append(_), initial=True) - client.add_ledger_packages_added(lambda _: follow_up_events.append(_)) - network.run_until_complete(upload_dars_and_verify()) + await network.aio_run(upload_dars_and_verify(), keep_open=False) assert len(initial_events) == 2 assert len(follow_up_events) == 1 @@ -58,4 +61,4 @@ async def upload_dars_and_verify(): async def upload_test_dars(network: 'Network'): g = network.aio_global() - await g.ensure_dar(AllKindsOf.read_bytes()) + await g.ensure_dar(UploadTest) diff --git a/python/tests/unit/test_event_order.py b/python/tests/unit/test_event_order.py index d2e7f569..7d82128e 100644 --- a/python/tests/unit/test_event_order.py +++ b/python/tests/unit/test_event_order.py @@ -2,32 +2,28 @@ # SPDX-License-Identifier: Apache-2.0 import asyncio -import logging import random +import uuid +from asyncio import sleep, new_event_loop, set_event_loop -from dazl import create, exercise, sandbox, Network +from dazl import create, exercise, Network, Party from .dars import Simple as SimpleDar NOTIFICATION_COUNT = 20 PARTY_COUNT = 10 -OPERATOR_PARTY = 'Operator' -USER_PARTIES = frozenset(f'Party{i}' for i in range(0, PARTY_COUNT)) -ALL_PARTIES = [OPERATOR_PARTY, *sorted(USER_PARTIES)] - class Simple: OperatorRole = 'Simple.OperatorRole' OperatorNotification = 'Simple.OperatorNotification' -def test_event_order(): +def test_event_order(sandbox): stage1 = Stage1LedgerInit() stage2 = Stage2LedgerVerify() - with sandbox(SimpleDar) as proc: - stage1.run(proc.url) - stage2.run(proc.url) + stage1.run(sandbox) + stage2.run(sandbox, stage1.operator_party) for event, cid, _ in stage2.events: print(event, cid) @@ -35,25 +31,49 @@ def test_event_order(): class Stage1LedgerInit: + def __init__(self): + self.network = Network() + self.operator_party = None + self.user_parties = None + self.done = None + self.evt_count = 0 + def run(self, url): - network = Network() - network.set_config(url=url) + set_event_loop(new_event_loop()) + self.done = asyncio.Event() + + self.network.set_config(url=url) - operator = network.aio_party(OPERATOR_PARTY) - operator.add_ledger_ready(lambda _: create(Simple.OperatorRole, {'operator': OPERATOR_PARTY})) + # TODO: These should be party allocations instead of just random strings. + self.user_parties = frozenset(Party(str(uuid.uuid4())) for _ in range(0, PARTY_COUNT)) + + operator = self.network.aio_new_party() + operator.add_ledger_ready(self.on_ready) operator.add_ledger_created(Simple.OperatorRole, self.on_operator) operator.add_ledger_created(Simple.OperatorNotification, self.on_notification) - network.run_until_complete(asyncio.sleep(15)) + + self.operator_party = operator.party + self.network.run_until_complete(self.done.wait()) + + async def on_ready(self, event): + await self.network.aio_global().ensure_dar(SimpleDar) + while not event.package_store.resolve_template(Simple.OperatorRole): + await sleep(1) + + return create(Simple.OperatorRole, {'operator': event.party}) @staticmethod def on_operator(event): return [exercise(event.cid, 'Publish', {"text": n}) for n in range(0, NOTIFICATION_COUNT)] - @staticmethod - def on_notification(event): - missing_parties = USER_PARTIES.difference(event.cdata['theObservers']) - if missing_parties: - return exercise(event.cid, 'Share', {'sharingParty': random.choice(list(missing_parties))}) + def on_notification(self, event): + if self.evt_count == 25: + self.done.set() + else: + self.evt_count += 1 + missing_parties = self.user_parties.difference(event.cdata['theObservers']) + if missing_parties: + return exercise(event.cid, 'Share', {'sharingParty': random.choice(list(missing_parties))}) class Stage2LedgerVerify: @@ -62,11 +82,13 @@ def __init__(self): self.store = {} self.events = [] - def run(self, url): + def run(self, url, operator_party): + set_event_loop(new_event_loop()) + network = Network() network.set_config(url=url) - operator = network.aio_party(OPERATOR_PARTY) + operator = network.aio_party(operator_party) operator.add_ledger_ready(self.on_ready) operator.add_ledger_created(Simple.OperatorNotification, self.on_notification_created) operator.add_ledger_archived(Simple.OperatorNotification, self.on_notification_archived) @@ -83,9 +105,3 @@ def on_notification_created(self, event): def on_notification_archived(self, event): self.events.append(('archived', event.cid, ())) del self.store[event.cid] - - -if __name__ == '__main__': - from dazl import setup_default_logger - setup_default_logger(logging.INFO) - test_event_order() diff --git a/python/tests/unit/test_package_loading.py b/python/tests/unit/test_package_loading.py index 64056f78..a74e4cd7 100644 --- a/python/tests/unit/test_package_loading.py +++ b/python/tests/unit/test_package_loading.py @@ -6,25 +6,27 @@ """ from operator import setitem -from dazl import sandbox, Network +import pytest + +from dazl import async_network from dazl.model.types_store import PackageStore from dazl.util.dar import DarFile from .dars import AllKindsOf -def test_package_loading(): +@pytest.mark.asyncio +async def test_package_loading(sandbox): d = {} with DarFile(AllKindsOf) as dar: expected_package_ids = dar.get_package_provider().get_package_ids() - with sandbox(AllKindsOf) as proc: - network = Network() - network.set_config(url=proc.url) - client = network.aio_party('TestParty') + async with async_network(url=sandbox, dars=AllKindsOf) as network: + client = network.aio_new_party() client.add_ledger_ready(lambda event: setitem(d, 'metadata', event.package_store)) - network.run_until_complete() + + network.start() store: PackageStore = d['metadata'] actual_package_ids = store.package_ids() - assert set(expected_package_ids) == set(actual_package_ids) + assert set(expected_package_ids).issubset(set(actual_package_ids)) diff --git a/python/tests/unit/test_sandbox_wrapper.py b/python/tests/unit/test_sandbox_wrapper.py deleted file mode 100644 index ba24f92c..00000000 --- a/python/tests/unit/test_sandbox_wrapper.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -import logging - -from dazl import sandbox, create, Network -from dazl.model.core import ProcessDiedException -from .dars import Simple - -PARTY = 'Operator' -OperatorRole = 'Simple.OperatorRole' - - -def test_nice_sandbox_allows_test(): - _sandbox_test() - - -def test_dead_sandbox_aborts_test(): - try: - _sandbox_test(extra_args=['--please-crash']) - except ProcessDiedException: - return - - assert False, "Should have raised an exception" - - -def _sandbox_test(extra_args=None): - cids = [] - with sandbox(Simple, extra_args=extra_args) as proc: - network = Network() - network.set_config(url=proc.url) - - party_client = network.aio_party(PARTY) - party_client.add_ledger_ready(lambda _: create(OperatorRole, {'operator': PARTY})) - party_client.add_ledger_created(OperatorRole, lambda e: cids.append(e.cid)) - network.run_until_complete() - - logging.info('got to the end with contracts: %s', cids) - assert len(cids) == 1 diff --git a/python/tests/unit/test_simple_client_api.py b/python/tests/unit/test_simple_client_api.py index 7cb3d8bc..3305875a 100644 --- a/python/tests/unit/test_simple_client_api.py +++ b/python/tests/unit/test_simple_client_api.py @@ -1,23 +1,24 @@ -# Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -from dazl import sandbox, simple_client, LOG +import logging +from dazl import simple_client +from .blocking_setup import blocking_setup from .dars import PostOffice -def test_simple_client_api(): - party = 'abc' +def test_simple_client_api(sandbox): + party = blocking_setup(sandbox, PostOffice) - LOG.info('Creating sandbox...') - with sandbox(dar_path=PostOffice) as proc: - LOG.info('Creating client...') - with simple_client(url=proc.url, party=party) as client: - client.ready() - LOG.info('Submitting...') - client.submit_create('Main.PostmanRole', {'postman': party}) - LOG.info('getting contracts') - contracts = client.find_active('*') - LOG.info('got the contracts') + logging.info('Creating client...') + with simple_client(url=sandbox, party=party) as client: + client.ready() + logging.info('Submitting...') + client.submit_create('Main.PostmanRole', {'postman': party}) + logging.info('getting contracts') + contracts = client.find_active('*') + logging.info('got the contracts') assert 1 == len(contracts) + diff --git a/python/tests/unit/test_ssl.py b/python/tests/unit/test_ssl.py deleted file mode 100644 index 09eeb4ba..00000000 --- a/python/tests/unit/test_ssl.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -import pytest - -from subprocess import run -from pathlib import Path -from typing import Tuple - -from dazl import create, sandbox, Network, LOG -from dazl.model.network import SSLSettings -from .dars import PostOffice - - -_DAZL_ROOT = Path(__file__).parent.parent.parent -# TODO: This utility should be fetched in a less ugly way -TLS_KEYGEN_SCRIPT = _DAZL_ROOT.parent.parent.parent / 'pipeline' / 'scripts' / 'TLS' / 'generate-certificates.sh' - - -def test_ssl_not_defined(): - assert not SSLSettings(None, None, None) - - -@pytest.mark.skip('Still needs work') -def test_ssl_connectivity(): - client_ssl_settings, server_ssl_settings = create_ssl_test_package() - messages_received = [] - with sandbox(PostOffice, ssl_settings=server_ssl_settings) as proc: - network = Network() - network.set_config(url=proc.url, - ca_file=client_ssl_settings.ca_file, - cert_file=client_ssl_settings.cert_file, - cert_key_file=client_ssl_settings.cert_key_file) - - client = network.aio_party('SOME_PARTY') - client.add_ledger_ready(lambda e: create('Main.PostmanRole', dict(party='SOME_PARTY'))) - client.add_ledger_created('Main.PostmanRole', lambda cid, cdata: messages_received.append(cid)) - network.run_until_complete() - - assert len(messages_received) == 1 - - -def create_ssl_test_package(force=False) -> Tuple[SSLSettings, SSLSettings]: - """ - Generate certs and scripts for running a Sandbox. - - The contents of the directory will be reused for no longer than 24 hours. If the test package is - older than that or not present at all, it will be (re-)created. - - :return: A tuple of client/server SSL settings. - """ - import time - pkg_dir = Path(_DAZL_ROOT).resolve() / '.tmp' / 'ssl' - pkg_dir.mkdir(parents=True, exist_ok=True) - sandbox_script = pkg_dir / 'sandbox.sh' - - client_ssl_settings = SSLSettings(ca_file=str(pkg_dir / 'ca.crt'), - cert_file=str(pkg_dir / 'server.crt'), - cert_key_file=str(pkg_dir / 'server.key')) - server_ssl_settings = SSLSettings(ca_file=str(pkg_dir / 'ca.crt'), - cert_file=str(pkg_dir / 'server.crt'), - cert_key_file=str(pkg_dir / 'server.pem')) - if not force: - if sandbox_script.exists(): - if (time.time() - sandbox_script.stat().st_mtime) < 86400: - LOG.info('Skipping package creation because we already did it') - return client_ssl_settings, server_ssl_settings - - # wipe out the package and start over - for p in pkg_dir.glob('**'): - if not p.is_dir(): - p.unlink() - - # create the certs - run(args=[str(TLS_KEYGEN_SCRIPT), '--destination', str(pkg_dir), '--common-name', 'digitalasset.com.test'], check=True) - return client_ssl_settings, server_ssl_settings diff --git a/python/tests/unit/test_static_time.py b/python/tests/unit/test_static_time.py deleted file mode 100644 index 377bddf4..00000000 --- a/python/tests/unit/test_static_time.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -""" -Test of two clients operating simultaneously. -""" - -import logging - -from asyncio import gather, get_event_loop, ensure_future -from datetime import datetime - -import pytest - -from dazl import create, exercise, sandbox, Network, setup_default_logger - -from .dars import PostOffice - -LOG = logging.getLogger('test_static_time') - -PARTY = 'POSTMAN' - -setup_default_logger(logging.DEBUG) - - -@pytest.mark.skip( - "This test cannot coexist with other tests on the same sandbox because it manipulates static time.") -def test_set_static_time(): - """ - Run a simple test involving manipulation of static time: - * Send a command at ledger startup. - * Upon receipt of a corresponding event, advance the time and submit a new command - * Observe a corresponding event has been received. - """ - with sandbox(PostOffice) as damli_proc: - network = Network() - network.set_config(url=damli_proc.url) - - async def _handle_postman_role(event): - try: - LOG.info('Received the postman role contract.') - await network.aio_global().set_time(datetime(1980, 1, 1)) - return exercise(event.cid, 'InviteParticipant', dict(party=PARTY, address='something')) - except BaseException as ex: - LOG.exception(ex) - - def _invitation_seen(event): - LOG.info('Invitation seen, so the app will be terminating now.') - network.shutdown() - - LOG.info('Adding event listeners to the application...') - client = network.aio_party(PARTY) - client.add_ledger_ready(lambda _: create('Main.PostmanRole', dict(postman=PARTY))) - client.add_ledger_created('Main.PostmanRole', _handle_postman_role) - - # run the manager "forever"; we'll stop ourselves manually when we see an author role - # because that means our call to invite an author was successful - client.add_ledger_created('Main.InviteAuthorRole', _invitation_seen) - network.run_forever() - - LOG.info('Application finished.') - - -@pytest.mark.skip( - "This test cannot coexist with other tests on the same sandbox because it manipulates static time.") -def test_set_static_time_two_clients(): - """ - Run a slightly complicated test involving manipulation of static time and multiple clients: - * Client 1 sends a command at ledger startup. - * Client 2 also listens for the command. - * When both Client 1 and Client 2 have heard the original command, Client 1 advances the - time and shuts down WITHOUT producing a new command. - * After Client 1 is shut down, Client 2 manually syncs its local time and submits a command - to the Sandbox. - * Observe Client 2 receives its corresponding event. - """ - event_loop = get_event_loop() - with sandbox(PostOffice) as damli_proc: - test = _TestSetStaticTimeTwoClients(damli_proc.url) - event_loop.run_until_complete(test.main()) - - -class _TestSetStaticTimeTwoClients: - - def __init__(self, url): - self.manager1 = Network() - self.manager1.set_config(url=url) - - self.manager2 = Network() - self.manager2.set_config(url=url) - - self.manager1.aio_party(PARTY).add_ledger_ready(self._handle_ready_1) - self.manager2.aio_party(PARTY).add_ledger_ready(self._handle_ready_2) - self.manager1.aio_party(PARTY).add_ledger_created('Main.PostmanRole', self._handle_postman_role_1) - self.manager2.aio_party(PARTY).add_ledger_created('Main.PostmanRole', self._handle_postman_role_2) - - async def main(self): - # begin both clients - m1 = ensure_future(self.manager1.aio_run()) - m2 = ensure_future(self.manager2.aio_run()) - - # wait for the first manager to shut itself down; this will have happened in - # the _handle_postman_role_1 callback - await gather(m1, self.manager2.aio_party(PARTY).ready()) - - # now ensure that the second client's time is in sync with the Sandbox - await self.manager2.aio_global().get_time() - # TODO: Come up with a better signal to be ABSOLUTELY sure that the second client is - # "caught up" with the current time - from asyncio import sleep - await sleep(1.0) - - # this call can only succeed if the second client knows of the time as it was set by the - # first party - await self.manager2.aio_party(PARTY).submit( - exercise(self.postman_cid, 'InviteParticipant', dict(party=PARTY, address='something'))) - - # shut down the second client - self.manager2.shutdown() - - # wait for it to stop cleanly - await m2 - - def _handle_ready_1(self, _): - LOG.info('Client 1 is now ready: sending the initial postman role contract...') - return create('Main.PostmanRole', dict(postman=PARTY)) - - def _handle_ready_2(self, _): - LOG.info('Client 2 is now ready.') - - async def _handle_postman_role_1(self, _): - LOG.info('Received the postman role contract; advancing time and shutting down.') - await self.manager1.aio_global().set_time(datetime(1980, 1, 1)) - self.manager1.shutdown() - - async def _handle_postman_role_2(self, event): - LOG.info('Received the postman role contract; ') - self.postman_cid = event.cid diff --git a/python/tests/unit/test_threadsafe_methods.py b/python/tests/unit/test_threadsafe_methods.py index 513db5f4..50c5397a 100644 --- a/python/tests/unit/test_threadsafe_methods.py +++ b/python/tests/unit/test_threadsafe_methods.py @@ -1,27 +1,27 @@ # Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -import uuid -from dazl import exercise, Party, simple_client +from dazl import exercise, simple_client +from .blocking_setup import blocking_setup from .dars import Simple -PARTY = Party(f'Operator-{uuid.uuid4()}') OperatorRole = 'Simple.OperatorRole' OperatorNotification = 'Simple.OperatorNotification' def test_threadsafe_methods(sandbox): - with simple_client(sandbox, PARTY) as client: - client.ensure_dar(Simple) + party = blocking_setup(sandbox, Simple) + + with simple_client(url=sandbox, party=party) as client: client.ready() - client.submit_create(OperatorRole, {'operator': PARTY}) + client.submit_create(OperatorRole, {'operator': party}) operator_cid, _ = client.find_one(OperatorRole) client.submit_exercise(operator_cid, 'PublishMany', dict(count=5)) - notifications = client.find_nonempty(OperatorNotification, {'operator': PARTY}, min_count=5) + notifications = client.find_nonempty(OperatorNotification, {'operator': party}, min_count=5) contracts_to_delete = [] for cid, cdata in notifications.items(): if int(cdata['text']) <= 3: