diff --git a/lib/pbench/server/api/resources/datasets_inventory.py b/lib/pbench/server/api/resources/datasets_inventory.py index ae7e9d7aee..8062b1e9dd 100644 --- a/lib/pbench/server/api/resources/datasets_inventory.py +++ b/lib/pbench/server/api/resources/datasets_inventory.py @@ -1,4 +1,5 @@ from http import HTTPStatus +from pathlib import Path from urllib.request import Request from flask import current_app, send_file @@ -84,10 +85,9 @@ def _get( # We link a callback to close the file stream and TarFile object on # completion. stream = file_info["stream"] + name = Path(file_info["name"]).name try: - resp = send_file( - stream, as_attachment=target is None, download_name=file_info["name"] - ) + resp = send_file(stream, as_attachment=target is None, download_name=name) except Exception as e: if stream: stream.close() diff --git a/lib/pbench/test/functional/server/test_datasets.py b/lib/pbench/test/functional/server/test_datasets.py index 3f35fe0297..fe37d90d65 100644 --- a/lib/pbench/test/functional/server/test_datasets.py +++ b/lib/pbench/test/functional/server/test_datasets.py @@ -751,6 +751,15 @@ def read_metadata(response: Response) -> JSONOBJECT: API.DATASETS_INVENTORY, {"dataset": dataset.resource_id, "target": "metadata.log"}, ) + + # Note that Werkzeug doesn't understand anything special about the + # ".log" filetype, but will apply a better content-type header for + # file types it understands like ".json". + assert response.headers["content-type"] == "application/octet-stream" + assert ( + response.headers["content-disposition"] + == "inline; filename=metadata.log" + ) assert ( response.ok ), f"INVENTORY {dataset.name} failed {response.status_code}:{response.json()['message']}" diff --git a/lib/pbench/test/unit/server/test_datasets_inventory.py b/lib/pbench/test/unit/server/test_datasets_inventory.py index cab31fbb46..100d9e0e1c 100644 --- a/lib/pbench/test/unit/server/test_datasets_inventory.py +++ b/lib/pbench/test/unit/server/test_datasets_inventory.py @@ -156,3 +156,20 @@ def mock_send_file(path_or_file, *args, **kwargs): query_get_as("fio_2", "f1.json", HTTPStatus.INTERNAL_SERVER_ERROR) assert mock_close + + def test_get_inventory(self, query_get_as, monkeypatch): + exp_stream = io.BytesIO(b"file_as_a_byte_stream") + + def mock_get_inventory(_s, _d: str, _t: str): + return { + "name": "f1.json", + "type": CacheType.FILE, + "stream": Inventory(exp_stream, None), + } + + monkeypatch.setattr(CacheManager, "get_inventory", mock_get_inventory) + response = query_get_as("fio_2", "f1.json", HTTPStatus.OK) + assert response.status_code == HTTPStatus.OK + assert response.text == "file_as_a_byte_stream" + assert response.headers["content-type"] == "application/json" + assert response.headers["content-disposition"] == "inline; filename=f1.json"