From e69fcd6511455a3ebe1f267a54f87ddab2b0a8a5 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Wed, 21 Aug 2024 15:37:38 -0400 Subject: [PATCH] test(streaming): some http/DRS streaming tests --- tests/conftest.py | 18 ++++++++-- tests/shared_data.py | 37 +++++++++++++++++++++ tests/test_streaming.py | 74 ++++++++++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 14 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 930ac56..8960efa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,9 @@ -import asyncio import asyncpg import pytest import pytest_asyncio from aioresponses import aioresponses +from bento_lib.drs.resolver import DrsResolver from fastapi.testclient import TestClient from typing import AsyncGenerator @@ -14,12 +14,26 @@ os.environ["CORS_ORIGINS"] = "*" os.environ["BENTO_AUTHZ_SERVICE_URL"] = "https://authz.local" -from bento_reference_service.config import get_config +from bento_reference_service.config import Config, get_config from bento_reference_service.db import Database, get_db +from bento_reference_service.drs import get_drs_resolver from bento_reference_service.logger import get_logger from bento_reference_service.main import app +@pytest.fixture() +def config() -> Config: + return get_config() + + +@pytest.fixture() +def drs_resolver(config: Config) -> DrsResolver: + drs = get_drs_resolver(config) + drs._cache_ttl = 0 + drs._drs_record_cache = {} + return drs + + async def get_test_db() -> AsyncGenerator[Database, None]: config = get_config() db_instance = Database(config, get_logger(config)) diff --git a/tests/shared_data.py b/tests/shared_data.py index acc6bea..e48b6f5 100644 --- a/tests/shared_data.py +++ b/tests/shared_data.py @@ -15,6 +15,9 @@ "TEST_GENOME_HG38_CHR1_F100K", "TEST_GENOME_HG38_CHR1_F100K_OBJ", "AUTHORIZATION_HEADER", + "TEST_DRS_ID", + "TEST_DRS_REPLY_NO_ACCESS", + "TEST_DRS_REPLY", ] DATA_DIR = (pathlib.Path(__file__).parent / "data").absolute() @@ -75,3 +78,37 @@ TEST_GENOME_HG38_CHR1_F100K_OBJ = Genome(**TEST_GENOME_HG38_CHR1_F100K) AUTHORIZATION_HEADER = {"Authorization": "Token bearer"} + + +# Test DRS responses + +TEST_DRS_ID = "dd11912c-3433-4a0a-8a01-3c0699288bef" + +TEST_DRS_REPLY_NO_ACCESS = { + "checksums": [ + { + "checksum": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "type": "sha-256", + } + ], + "created_time": "2021-03-17T21:29:15+00:00", + "updated_time": "2021-03-17T21:29:15+00:00", + "id": TEST_DRS_ID, + "mime_type": "text/plain", + "self_uri": f"drs://localhost/{TEST_DRS_ID}", + "size": 4, +} + +TEST_DRS_REPLY = { + **TEST_DRS_REPLY_NO_ACCESS, + "access_methods": [ + { + "type": "file", + "access_url": {"url": "file:///test.txt"}, + }, + { + "type": "https", + "access_url": {"url": "https://example.org/test.txt"}, + }, + ], +} diff --git a/tests/test_streaming.py b/tests/test_streaming.py index c241e45..4137b3c 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py @@ -2,10 +2,13 @@ import pytest from aioresponses import aioresponses +from bento_lib.drs.resolver import DrsResolver from bento_lib.streaming import exceptions as se from fastapi import HTTPException, status -from bento_reference_service import config as c, drs as d, streaming as s +from bento_reference_service import config as c, streaming as s + +from .shared_data import TEST_DRS_REPLY_NO_ACCESS, TEST_DRS_REPLY HTTP_TEST_URI = "https://test.local/file.txt" @@ -13,17 +16,65 @@ @pytest.mark.asyncio() -async def test_uri_streaming_bad_uri(): - config = c.get_config() +async def test_drs_bytes_url_from_uri(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): + aioresponse.get("https://example.org/ga4gh/drs/v1/objects/abc", payload=TEST_DRS_REPLY) + assert ( + await s.drs_bytes_url_from_uri(config, drs_resolver, logger, "drs://example.org/abc") + == TEST_DRS_REPLY["access_methods"][1]["access_url"]["url"] + ) + + +@pytest.mark.asyncio() +async def test_drs_bytes_url_from_uri_not_found(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): + aioresponse.get( + "https://example.org/ga4gh/drs/v1/objects/abc", + status=status.HTTP_404_NOT_FOUND, + payload={"message": "Not Found"}, + ) + + with pytest.raises(HTTPException) as e: + await s.drs_bytes_url_from_uri(config, drs_resolver, logger, "drs://example.org/abc") + + assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert "Not Found error" in e.value.detail + + +@pytest.mark.asyncio() +async def test_drs_bytes_url_from_uri_500(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): + aioresponse.get( + "https://example.org/ga4gh/drs/v1/objects/abc", + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + payload={"message": "Internal Server Error"}, + ) + + with pytest.raises(HTTPException) as e: + await s.drs_bytes_url_from_uri(config, drs_resolver, logger, "drs://example.org/abc") + + assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert "while accessing DRS record" in e.value.detail + + +@pytest.mark.asyncio() +async def test_drs_bytes_url_from_uri_no_access(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): + aioresponse.get("https://example.org/ga4gh/drs/v1/objects/abc", payload=TEST_DRS_REPLY_NO_ACCESS) + + with pytest.raises(HTTPException) as e: + await s.drs_bytes_url_from_uri(config, drs_resolver, logger, "drs://example.org/abc") + + assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert "HTTPS access method" in e.value.detail + + +@pytest.mark.asyncio() +async def test_uri_streaming_bad_uri(config: c.Config, drs_resolver: DrsResolver): with pytest.raises(se.StreamingBadURI): - await s.stream_from_uri(config, d.get_drs_resolver(config), logger, "http://[.com", None, False) + await s.stream_from_uri(config, drs_resolver, logger, "http://[.com", None, False) @pytest.mark.asyncio() -async def test_uri_streaming_bad_scheme(): - config = c.get_config() +async def test_uri_streaming_bad_scheme(config: c.Config, drs_resolver: DrsResolver): with pytest.raises(se.StreamingUnsupportedURIScheme): - await s.stream_from_uri(config, d.get_drs_resolver(config), logger, "asdf://example.org", None, False) + await s.stream_from_uri(config, drs_resolver, logger, "asdf://example.org", None, False) @pytest.mark.asyncio() @@ -75,22 +126,21 @@ async def test_http_streaming_404_1(aioresponse: aioresponses): @pytest.mark.asyncio() -async def test_http_streaming_404_2(aioresponse: aioresponses): +async def test_http_streaming_404_2(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): aioresponse.get(HTTP_TEST_URI, status=status.HTTP_404_NOT_FOUND, body=b"Not Found") with pytest.raises(se.StreamingProxyingError): - config = c.get_config() - _, _, stream = await s.stream_from_uri(config, d.get_drs_resolver(config), logger, HTTP_TEST_URI, None, False) + _, _, stream = await s.stream_from_uri(config, drs_resolver, logger, HTTP_TEST_URI, None, False) await anext(stream) @pytest.mark.asyncio() -async def test_http_streaming_404_3(aioresponse: aioresponses): +async def test_http_streaming_404_3(aioresponse: aioresponses, config: c.Config, drs_resolver: DrsResolver): aioresponse.get(HTTP_TEST_URI, status=status.HTTP_404_NOT_FOUND, body=b"Not Found") with pytest.raises(HTTPException): config = c.get_config() res = await s.generate_uri_streaming_response( config, - d.get_drs_resolver(config), + drs_resolver, logger, HTTP_TEST_URI, None,