From 18ec9e939a1d668946f733ecdfe9055d53ea6e10 Mon Sep 17 00:00:00 2001 From: David Lougheed Date: Tue, 16 Apr 2024 13:05:40 -0400 Subject: [PATCH] test: object deletion; use real local backend for tests --- chord_drs/backends/base.py | 14 +--------- tests/conftest.py | 19 +++++++++----- tests/test_routes.py | 52 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/chord_drs/backends/base.py b/chord_drs/backends/base.py index 31ee67b..61a2af4 100644 --- a/chord_drs/backends/base.py +++ b/chord_drs/backends/base.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod -__all__ = ["Backend", "FakeBackend"] +__all__ = ["Backend"] class Backend(ABC): @@ -12,15 +12,3 @@ def save(self, current_location: str, filename: str) -> str: # pragma: no cover @abstractmethod def delete(self, location: str) -> None: # pragma: no cover pass - - -class FakeBackend(Backend): - """ - For the tests - """ - - def save(self, current_location: str, filename: str) -> str: - return current_location - - def delete(self, location: str): - return None diff --git a/tests/conftest.py b/tests/conftest.py index c53f60c..3b95de6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,13 +2,14 @@ import os import pathlib import pytest +import shutil from flask import g +from flask.testing import FlaskClient from moto import mock_s3 from pytest_lazyfixture import lazy_fixture # Must only be imports that don't import authz/app/config/db -from chord_drs.backends.base import FakeBackend from chord_drs.backends.minio import MinioBackend from chord_drs.data_sources import DATA_SOURCE_LOCAL, DATA_SOURCE_MINIO @@ -47,7 +48,7 @@ def empty_file_path(): # Function rather than constant so we can set environ fi @pytest.fixture -def client_minio(): +def client_minio() -> FlaskClient: os.environ["BENTO_AUTHZ_SERVICE_URL"] = AUTHZ_URL from chord_drs.app import application, db @@ -72,9 +73,12 @@ def client_minio(): @pytest.fixture -def client_local(): +def client_local() -> FlaskClient: + local_test_volume = (pathlib.Path(__file__).parent / "data").absolute() + local_test_volume.mkdir(parents=True, exist_ok=True) + os.environ["BENTO_AUTHZ_SERVICE_URL"] = AUTHZ_URL - os.environ["DATA"] = str((pathlib.Path(__file__).parent / "data").absolute()) + os.environ["DATA"] = str(local_test_volume) from chord_drs.app import application, db @@ -82,8 +86,6 @@ def client_local(): application.config["SERVICE_DATA_SOURCE"] = DATA_SOURCE_LOCAL with application.app_context(): - g.backend = FakeBackend() - db.create_all() yield application.test_client() @@ -91,9 +93,12 @@ def client_local(): db.session.remove() db.drop_all() + # clear test volume + shutil.rmtree(local_test_volume) + @pytest.fixture(params=[lazy_fixture("client_minio"), lazy_fixture("client_local")]) -def client(request): +def client(request) -> FlaskClient: return request.param diff --git a/tests/test_routes.py b/tests/test_routes.py index 6328f33..c999c9f 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -284,6 +284,58 @@ def test_object_delete(client): assert res.status_code == 404 +@responses.activate +def test_object_multi_delete(client): + from chord_drs.models import DrsBlob + + authz_everything_true() + + contents = str(uuid.uuid4()) + + # first, ingest two new objects with the same contents + with tempfile.NamedTemporaryFile(mode="w") as tf: + tf.write(contents) # random content, so checksum is unique + tf.flush() + + # two different projects to ensure we have two objects pointing to the same resource: + res1 = client.post("/ingest", data={"path": tf.name, "project_id": "project1"}) + assert res1.status_code == 201 + res2 = client.post("/ingest", data={"path": tf.name, "project_id": "project2"}) + assert res2.status_code == 201 + + i1 = res1.get_json() + i2 = res2.get_json() + + assert i1["id"] != i2["id"] + + b1 = DrsBlob.query.filter_by(id=i1["id"]).first() + b2 = DrsBlob.query.filter_by(id=i2["id"]).first() + + assert b1.location == b2.location + + # make sure we can get the bytes of i2 + assert client.get(f"/objects/{i2['id']}/download").status_code == 200 + + # delete i2 + rd2 = client.delete(f"/objects/{i2['id']}") + assert rd2.status_code == 204 + + # make sure we can still get the bytes of i1 + assert client.get(f"/objects/{i1['id']}/download").status_code == 200 + + # check file exists if local + if b1.location.startswith("/"): + assert os.path.exists(b1.location) + + # delete i1 + rd1 = client.delete(f"/objects/{i1['id']}") + assert rd1.status_code == 204 + + # check file doesn't exist if local + if b1.location.startswith("/"): + assert not os.path.exists(b1.location) + + @responses.activate def test_bundle_and_download(client, drs_bundle): authz_everything_true()