diff --git a/tests/conftest.py b/tests/conftest.py index db04c8d..8b6337d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,50 +16,33 @@ """Shared fixtures""" -import asyncio - import pytest -from hexkit.providers.akafka.testutils import KafkaFixture, get_kafka_fixture +from hexkit.providers.akafka.testutils import get_kafka_fixture from hexkit.providers.mongodb.testutils import MongoDbFixture, get_mongodb_fixture -from tests.fixtures.datasets import DATASET_UPSERTION_EVENT from wps.config import Config -from wps.inject import Consumer + +from .fixtures.datasets import DATASET kafka_fixture = get_kafka_fixture("session") mongodb_fixture = get_mongodb_fixture("session") -@pytest.fixture(autouse=True, scope="function") -def reset_db(mongodb_fixture: MongoDbFixture): - """Clear the database before tests.""" +@pytest.fixture(name="empty_mongodb") +def empty_mongodb_fixture(mongodb_fixture: MongoDbFixture) -> MongoDbFixture: + """MongoDB Fixture with empty database.""" mongodb_fixture.empty_collections() - - -async def publish_and_process_dataset_upsertion_event( - config: Config, kafka_fixture: KafkaFixture, consumer: Consumer -): - """Publish and process a dataset upsertion event""" - await kafka_fixture.publish_event( - payload=DATASET_UPSERTION_EVENT.model_dump(), - topic=config.dataset_change_event_topic, - type_=config.dataset_upsertion_event_type, - key="test-key-fixture", - ) - await asyncio.wait_for(consumer.event_subscriber.run(forever=False), timeout=10) - await asyncio.sleep(0.125) - - -@pytest.fixture(scope="function") -def populate_db( - reset_db, config: Config, kafka_fixture: KafkaFixture, consumer: Consumer -): - """Populate the database - - Required for tests using the consumer or client fixtures. - """ - asyncio.get_event_loop().run_until_complete( - publish_and_process_dataset_upsertion_event( - config=config, kafka_fixture=kafka_fixture, consumer=consumer - ) - ) + return mongodb_fixture + + +@pytest.fixture(name="populated_mongodb") +def populated_mongodb_fixture( + empty_mongodb: MongoDbFixture, config: Config +) -> MongoDbFixture: + """MongoDB Fixture with a database populated with one dataset.""" + database = empty_mongodb.client.get_database(config.db_name) + collection = database.get_collection(config.datasets_collection) + dataset = DATASET.model_dump() + dataset["_id"] = dataset.pop("id") + collection.insert_one(dataset) + return empty_mongodb diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 38ce083..d0e61a0 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -18,6 +18,8 @@ from collections.abc import AsyncGenerator from datetime import timedelta +import pytest +import pytest_asyncio from ghga_service_commons.api.testing import AsyncTestClient from ghga_service_commons.auth.ghga import AuthContext from ghga_service_commons.utils.jwt_helpers import ( @@ -27,13 +29,11 @@ from ghga_service_commons.utils.utc_dates import now_as_utc from hexkit.providers.akafka.testutils import KafkaFixture from hexkit.providers.mongodb.testutils import MongoDbFixture -from pytest import fixture -from pytest_asyncio import fixture as async_fixture from wps.adapters.outbound.dao import DatasetDaoConstructor, WorkPackageDaoConstructor from wps.config import Config from wps.core.repository import WorkPackageRepository -from wps.inject import Consumer, prepare_consumer, prepare_rest_app +from wps.inject import Consumer, prepare_rest_app from .access import AccessCheckMock @@ -70,14 +70,14 @@ def headers_for_token(token: str) -> dict[str, str]: return {"Authorization": f"Bearer {token}"} -@fixture(name="auth_headers") +@pytest.fixture(name="auth_headers") def fixture_auth_headers() -> dict[str, str]: """Get auth headers for testing""" token = sign_and_serialize_token(AUTH_CLAIMS, AUTH_KEY_PAIR) return headers_for_token(token) -@fixture(name="bad_auth_headers") +@pytest.fixture(name="bad_auth_headers") def fixture_bad_auth_headers() -> dict[str, str]: """Get a invalid auth headers for testing""" claims = AUTH_CLAIMS.copy() @@ -86,18 +86,19 @@ def fixture_bad_auth_headers() -> dict[str, str]: return headers_for_token(token) -@fixture(name="auth_context") +@pytest.fixture(name="auth_context") def fixture_auth_context() -> AuthContext: """Fixture for getting an auth context""" iat = now_as_utc() - timedelta(1) # validity is actually assumed by the repository return AuthContext(**AUTH_CLAIMS, iat=iat, exp=iat) # pyright: ignore -@async_fixture(name="config", scope="session") +@pytest_asyncio.fixture(name="config", scope="session") async def fixture_config( kafka_fixture: KafkaFixture, mongodb_fixture: MongoDbFixture ) -> AsyncGenerator[Config, None]: """Fixture for creating a test configuration.""" + open("/tmp/out.txt", "a").write("Config fixture\n") return Config( auth_key=AUTH_KEY_PAIR.export_public(), # pyright: ignore download_access_url="http://access", @@ -107,7 +108,7 @@ async def fixture_config( ) -@async_fixture(name="repository", scope="session") +@pytest_asyncio.fixture(name="repository", scope="session") async def fixture_repository( config: Config, mongodb_fixture: MongoDbFixture ) -> WorkPackageRepository: @@ -129,16 +130,7 @@ async def fixture_repository( ) -@async_fixture(name="consumer", scope="session") -async def fixture_consumer(config: Config) -> AsyncGenerator[Consumer, None]: - """Get test event subscriber for the work package service.""" - async with prepare_consumer(config=config) as consumer: - # wait for event to be submitted and processed, - # so that the database is populated with the published datasets - yield consumer - - -@async_fixture(name="client", scope="session") +@pytest_asyncio.fixture(name="client", scope="session") async def fixture_client(config: Config) -> AsyncGenerator[AsyncTestClient, None]: """Get test client for the work package service.""" async with prepare_rest_app(config=config) as app: @@ -146,7 +138,7 @@ async def fixture_client(config: Config) -> AsyncGenerator[AsyncTestClient, None yield client -@fixture +@pytest.fixture def non_mocked_hosts() -> list[str]: """Get hosts that are not mocked by pytest-httpx.""" return ["test", "localhost"] diff --git a/tests/test_access.py b/tests/test_access.py index a21499b..9e3e683 100644 --- a/tests/test_access.py +++ b/tests/test_access.py @@ -18,16 +18,18 @@ from collections.abc import AsyncGenerator -from pytest import mark -from pytest_asyncio import fixture as async_fixture +import pytest +import pytest_asyncio from pytest_httpx import HTTPXMock from wps.adapters.outbound.http import AccessCheckAdapter, AccessCheckConfig +pytestmark = pytest.mark.asyncio() + DOWNLOAD_ACCESS_URL = "http://test-access:1234" -@async_fixture(name="access_check", scope="function") +@pytest_asyncio.fixture(name="access_check", scope="function") async def fixture_access_check() -> AsyncGenerator[AccessCheckAdapter, None]: """Get configured access test adapter.""" config = AccessCheckConfig(download_access_url=DOWNLOAD_ACCESS_URL) @@ -35,7 +37,6 @@ async def fixture_access_check() -> AsyncGenerator[AccessCheckAdapter, None]: yield adapter -@mark.asyncio async def test_check_download_access( access_check: AccessCheckAdapter, httpx_mock: HTTPXMock ): @@ -61,7 +62,6 @@ async def test_check_download_access( assert await check_access("some-user-id", "no-data-id") is False -@mark.asyncio async def test_get_download_datasets( access_check: AccessCheckAdapter, httpx_mock: HTTPXMock ): diff --git a/tests/test_api.py b/tests/test_api.py index c3d01b7..3b24a7f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -16,10 +16,10 @@ """Test the API of the work package service.""" +import pytest from fastapi import status from ghga_service_commons.api.testing import AsyncTestClient from ghga_service_commons.utils.jwt_helpers import decode_and_validate_token -from pytest import mark from pytest_httpx import HTTPXMock from .fixtures import ( # noqa: F401 @@ -28,7 +28,6 @@ fixture_bad_auth_headers, fixture_client, fixture_config, - fixture_consumer, fixture_repository, headers_for_token, non_mocked_hosts, @@ -36,6 +35,9 @@ from .fixtures.crypt import decrypt, user_public_crypt4gh_key from .fixtures.datasets import DATASET +pytestmark = pytest.mark.asyncio(scope="session") + + CREATION_DATA = { "dataset_id": "some-dataset-id", "type": "download", @@ -46,7 +48,6 @@ TIMEOUT = 5 -@mark.asyncio(scope="session") async def test_health_check(client: AsyncTestClient): """Test that the health check endpoint works.""" response = await client.get("/health", timeout=TIMEOUT) @@ -55,7 +56,6 @@ async def test_health_check(client: AsyncTestClient): assert response.json() == {"status": "OK"} -@mark.asyncio(scope="session") async def test_create_work_package_unauthorized( client: AsyncTestClient, bad_auth_headers: dict[str, str] ): @@ -68,19 +68,17 @@ async def test_create_work_package_unauthorized( assert response.status_code == status.HTTP_401_UNAUTHORIZED -@mark.asyncio(scope="session") async def test_get_work_package_unauthorized(client: AsyncTestClient): """Test that getting a work package needs authorization.""" response = await client.get("/work-packages/some-work-package-id") assert response.status_code == status.HTTP_403_FORBIDDEN -@mark.asyncio(scope="session") async def test_create_work_order_token( client: AsyncTestClient, auth_headers: dict[str, str], httpx_mock: HTTPXMock, - populate_db, + populated_mongodb, ): """Test that work order tokens can be properly created.""" # mock the access check for the test dataset @@ -219,14 +217,12 @@ async def test_create_work_order_token( } -@mark.asyncio(scope="session") async def test_get_datasets_unauthenticated(client: AsyncTestClient): """Test that the list of accessible datasets cannot be fetched unauthenticated.""" response = await client.get("/users/john-doe@ghga.de/datasets") assert response.status_code == status.HTTP_403_FORBIDDEN -@mark.asyncio(scope="session") async def test_get_datasets_for_another_user( client: AsyncTestClient, auth_headers: dict[str, str] ): @@ -237,12 +233,11 @@ async def test_get_datasets_for_another_user( assert response.status_code == status.HTTP_403_FORBIDDEN -@mark.asyncio(scope="session") async def test_get_datasets_when_none_authorized( client: AsyncTestClient, auth_headers: dict[str, str], httpx_mock: HTTPXMock, - populate_db, + populated_mongodb, ): """Test that no datasets are fetched when none are accessible.""" # mock the access check for the test dataset @@ -265,12 +260,11 @@ async def test_get_datasets_when_none_authorized( assert response_data == [] -@mark.asyncio(scope="session") async def test_get_datasets( client: AsyncTestClient, auth_headers: dict[str, str], httpx_mock: HTTPXMock, - populate_db, + populated_mongodb, ): """Test that the list of accessible datasets can be fetched.""" # mock the access check for the test dataset diff --git a/tests/test_crypt.py b/tests/test_crypt.py index 343538d..4849aa6 100644 --- a/tests/test_crypt.py +++ b/tests/test_crypt.py @@ -18,7 +18,7 @@ import base64 -from pytest import raises +import pytest from wps.core.crypt import validate_public_key @@ -36,28 +36,28 @@ def test_valid_public_key(): def test_empty_public_key(): """Test that an empty public key does not pass.""" - with raises(ValueError, match="empty"): + with pytest.raises(ValueError, match="empty"): assert validate_public_key(None) # type: ignore - with raises(ValueError, match="empty"): + with pytest.raises(ValueError, match="empty"): assert validate_public_key("") - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): assert validate_public_key("null") def test_invalid_public_key(): """Test that an invalid public key does not pass.""" key = encode(b"foo-bar." * 2) # 16 bytes - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): assert validate_public_key(key) key = encode(b"foo-bar." * 5) # 50 bytes - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): assert validate_public_key(key) def test_private_key_instead_of_public_key(): """Test that passing a private key throws.""" key = encode(b"c4gh-v1" + 46 * b"x") - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): validate_public_key(key) @@ -83,7 +83,7 @@ def test_private_key_wrapped_as_crypt4gh_public_key(): + key + "\n-----END CRYPT4GH PUBLIC KEY-----\n" ) - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): validate_public_key(wrapped_key) @@ -95,7 +95,7 @@ def test_valid_public_key_wrapped_as_non_crypt4gh_public_key(): + key + "\n-----END CRYPT9GH PUBLIC KEY-----\n" ) - with raises(ValueError, match="Invalid"): + with pytest.raises(ValueError, match="Invalid"): validate_public_key(wrapped_key) @@ -107,5 +107,5 @@ def test_valid_public_key_wrapped_as_crypt4gh_private_key(): + key + "\n-----END CRYPT4GH PRIVATE KEY-----\n" ) - with raises(ValueError, match="Do not pass a private key"): + with pytest.raises(ValueError, match="Do not pass a private key"): validate_public_key(wrapped_key) diff --git a/tests/test_events.py b/tests/test_events.py index 4a6b1bc..8710ec1 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -17,119 +17,136 @@ """Test that the work package service consumes and processes events properly.""" import asyncio +from collections.abc import AsyncGenerator +import pytest +import pytest_asyncio from hexkit.providers.akafka.testutils import KafkaFixture -from pytest import mark, raises from wps.config import Config +from wps.inject import Consumer, prepare_consumer from .fixtures import ( # noqa: F401 - Consumer, fixture_config, - fixture_consumer, fixture_repository, ) from .fixtures.datasets import DATASET, DATASET_DELETION_EVENT, DATASET_UPSERTION_EVENT +pytestmark = pytest.mark.asyncio(scope="session") + + TIMEOUT = 10 RETRY_INTERVAL = 0.05 RETRIES = round(TIMEOUT / RETRY_INTERVAL) -@mark.asyncio(scope="session") +@pytest_asyncio.fixture(name="consumer", scope="session") +async def consumer(config: Config) -> AsyncGenerator[Consumer, None]: + """Get a consumer object.""" + async with prepare_consumer(config=config) as consumer: + yield consumer + + async def test_dataset_registration( - populate_db, + config: Config, + kafka_fixture: KafkaFixture, consumer: Consumer, + empty_mongodb, ): """Test the registration of a dataset announced as an event.""" - repository = consumer.work_package_repository - dataset = await repository.get_dataset("some-dataset-id") - - assert dataset == DATASET - - with raises(repository.DatasetNotFoundError): - await repository.get_dataset("another-dataset-id") - - -@mark.asyncio(scope="session") -async def test_dataset_insert_update_delete( - config: Config, kafka_fixture: KafkaFixture, consumer: Consumer -): - """Test the whole lifecycle of a dataset announced as an event.""" repository, subscriber = consumer - run = subscriber.run - accession = "another-dataset-id" - with raises(repository.DatasetNotFoundError): - await repository.get_dataset(accession) - key = "test-key-crud" + # make sure that in the beginning the database is empty + with pytest.raises(repository.DatasetNotFoundError): + await repository.get_dataset("some-dataset-id") - # insert a dataset - - inserted_dataset = DATASET_UPSERTION_EVENT - inserted_dataset = inserted_dataset.model_copy(update={"accession": accession}) + # register a dataset by publishing an event await kafka_fixture.publish_event( - payload=inserted_dataset.model_dump(), + payload=DATASET_UPSERTION_EVENT.model_dump(), topic=config.dataset_change_event_topic, type_=config.dataset_upsertion_event_type, - key=key, + key="test-key", ) - await asyncio.wait_for(run(forever=False), timeout=TIMEOUT) + # wait until the event is processed + await asyncio.wait_for(subscriber.run(forever=False), timeout=TIMEOUT) - # wait until dataset is stored + # now this dataset should be retrievable dataset = None for _ in range(RETRIES): await asyncio.sleep(RETRY_INTERVAL) try: - dataset = await repository.get_dataset(accession) + dataset = await repository.get_dataset("some-dataset-id") except repository.DatasetNotFoundError: pass else: - assert dataset.title == "Test dataset 1" + assert dataset == DATASET break else: - assert False, "dataset not created" + assert False, "dataset cannot be retrieved" + + # but another dataset should not be retrievable + with pytest.raises(repository.DatasetNotFoundError): + await repository.get_dataset("another-dataset-id") + + +async def test_dataset_update( + config: Config, kafka_fixture: KafkaFixture, consumer: Consumer, populated_mongodb +): + """Test updating a dataset via an event.""" + repository, subscriber = consumer + + # make sure that in the beginning the dataset exists + dataset = await repository.get_dataset("some-dataset-id") + assert dataset.id == "some-dataset-id" + assert dataset.title == "Test dataset 1" # update the dataset updated_dataset = DATASET_UPSERTION_EVENT - updated_dataset = inserted_dataset.model_copy( - update={"accession": accession, "title": "Changed dataset 1"} - ) + updated_dataset = updated_dataset.model_copy(update={"title": "Changed dataset 1"}) await kafka_fixture.publish_event( payload=updated_dataset.model_dump(), topic=config.dataset_change_event_topic, type_=config.dataset_upsertion_event_type, - key=key, + key="test-key", ) - await asyncio.wait_for(run(forever=False), timeout=TIMEOUT) + await asyncio.wait_for(subscriber.run(forever=False), timeout=TIMEOUT) # wait until dataset is updated - dataset = None for _ in range(RETRIES): await asyncio.sleep(RETRY_INTERVAL) - dataset = await repository.get_dataset(accession) + dataset = await repository.get_dataset(dataset.id) if dataset.title == "Changed dataset 1": break else: assert False, "dataset title not changed" + +async def test_dataset_deletion( + config: Config, kafka_fixture: KafkaFixture, consumer: Consumer, populated_mongodb +): + """Test deleting a dataset via an event.""" + repository, subscriber = consumer + + # make sure that in the beginning the dataset exists + dataset = await repository.get_dataset("some-dataset-id") + assert dataset.id == "some-dataset-id" + # delete the dataset again deleted_dataset = DATASET_DELETION_EVENT - deleted_dataset = deleted_dataset.model_copy(update={"accession": accession}) await kafka_fixture.publish_event( payload=deleted_dataset.model_dump(), topic=config.dataset_change_event_topic, type_=config.dataset_deletion_event_type, - key=key, + key="test_key", ) - await asyncio.wait_for(run(forever=False), timeout=TIMEOUT) + await asyncio.wait_for(subscriber.run(forever=False), timeout=TIMEOUT) # wait until dataset is deleted for _ in range(RETRIES): await asyncio.sleep(RETRY_INTERVAL) try: - await repository.get_dataset(accession) + await repository.get_dataset(dataset.id) except repository.DatasetNotFoundError: break else: diff --git a/tests/test_models.py b/tests/test_models.py index 2bd3964..3d30249 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -18,8 +18,8 @@ from datetime import datetime, timezone +import pytest from pydantic import ValidationError -from pytest import raises from wps.core.models import ( WorkOrderToken, @@ -76,28 +76,28 @@ def test_good_creation_data(): def test_bad_creation_data(): """Test instantiating invalid work package creation DTO.""" - with raises(ValidationError, match="dataset_id"): + with pytest.raises(ValidationError, match="dataset_id"): WorkPackageCreationData( dataset_id=["foo", "bar"], # type: ignore type=WorkType.DOWNLOAD, file_ids=["some-file-id", "another-file-id"], user_public_crypt4gh_key=user_public_crypt4gh_key, ) - with raises(ValidationError, match="type"): + with pytest.raises(ValidationError, match="type"): WorkPackageCreationData( dataset_id="some-dataset-id", type="UNKNOWN_TYPE", # type: ignore file_ids=["some-file-id", "another-file-id"], user_public_crypt4gh_key=user_public_crypt4gh_key, ) - with raises(ValidationError, match="file_ids"): + with pytest.raises(ValidationError, match="file_ids"): WorkPackageCreationData( dataset_id="some-dataset-id", type=WorkType.DOWNLOAD, file_ids="some-file-id", # type: ignore user_public_crypt4gh_key=user_public_crypt4gh_key, ) - with raises(ValidationError, match="user_public_crypt4gh_key"): + with pytest.raises(ValidationError, match="user_public_crypt4gh_key"): WorkPackageCreationData( dataset_id="some-dataset-id", type=WorkType.DOWNLOAD, diff --git a/tests/test_repository.py b/tests/test_repository.py index 9d1e92e..0faa2ae 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -16,9 +16,9 @@ """Test the Work Package Repository.""" +import pytest from ghga_service_commons.auth.ghga import AuthContext from ghga_service_commons.utils.jwt_helpers import decode_and_validate_token -from pytest import mark, raises from wps.core.models import ( WorkPackage, @@ -38,10 +38,11 @@ from .fixtures.crypt import decrypt, user_public_crypt4gh_key from .fixtures.datasets import DATASET +pytestmark = pytest.mark.asyncio(scope="session") + -@mark.asyncio(scope="session") async def test_work_package_and_token_creation( - repository: WorkPackageRepository, auth_context: AuthContext + repository: WorkPackageRepository, auth_context: AuthContext, empty_mongodb ): """Test creating a work package and a work order token""" # announce dataset @@ -67,12 +68,12 @@ async def test_work_package_and_token_creation( # retrieve work package - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.get( work_package_id, check_valid=True, work_package_access_token="foo" ) - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.get( "invalid-id", check_valid=True, work_package_access_token=wpat ) @@ -100,21 +101,21 @@ async def test_work_package_and_token_creation( # crate work order token - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.work_order_token( work_package_id="invalid-work-package-id", file_id="file-id-1", work_package_access_token=wpat, ) - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.work_order_token( work_package_id=work_package_id, file_id="invalid-file-id", work_package_access_token=wpat, ) - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.work_order_token( work_package_id=work_package_id, file_id="file-id-1", @@ -162,14 +163,14 @@ async def test_work_package_and_token_creation( # crate work order token - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.work_order_token( work_package_id=work_package_id, file_id="non-existing-file", work_package_access_token=wpat, ) - with raises(repository.WorkPackageAccessError): + with pytest.raises(repository.WorkPackageAccessError): await repository.work_order_token( work_package_id=work_package_id, file_id="file-id-2", @@ -198,12 +199,11 @@ async def test_work_package_and_token_creation( } -@mark.asyncio(scope="session") async def test_checking_accessible_datasets( - repository: WorkPackageRepository, auth_context: AuthContext + repository: WorkPackageRepository, auth_context: AuthContext, empty_mongodb ): """Test checking the accessibility of datasets""" - with raises(repository.DatasetNotFoundError): + with pytest.raises(repository.DatasetNotFoundError): await repository.get_dataset("some-dataset_id") assert await repository.get_datasets(auth_context=auth_context) == [] @@ -216,12 +216,9 @@ async def test_checking_accessible_datasets( assert await repository.get_datasets(auth_context=auth_context) == [DATASET] -@mark.asyncio(scope="session") -async def test_deletion_of_datasets( - repository: WorkPackageRepository, auth_context: AuthContext -): +async def test_deletion_of_datasets(repository: WorkPackageRepository, empty_mongodb): """Test deletion of existing datasets""" - with raises(repository.DatasetNotFoundError): + with pytest.raises(repository.DatasetNotFoundError): await repository.delete_dataset(DATASET.id) await repository.register_dataset(DATASET) @@ -229,5 +226,5 @@ async def test_deletion_of_datasets( await repository.delete_dataset(DATASET.id) - with raises(repository.DatasetNotFoundError): + with pytest.raises(repository.DatasetNotFoundError): await repository.delete_dataset(DATASET.id)