diff --git a/datasette/views/table.py b/datasette/views/table.py index 717341ae..d190b6af 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -1083,7 +1083,7 @@ class BlobView(BaseView): "Content-Disposition": 'attachment; filename="{}"'.format(filename), } return Response( - body=rows[0][column], + body=rows[0][column] or b"", status=200, headers=headers, content_type="application/binary", diff --git a/tests/fixtures.py b/tests/fixtures.py index 7786ca8c..31638fc8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -667,6 +667,7 @@ CREATE VIEW searchable_view_configured_by_metadata AS TABLE_PARAMETERIZED_SQL = [ ("insert into binary_data (data) values (?);", [b"\x15\x1c\x02\xc7\xad\x05\xfe"]), ("insert into binary_data (data) values (?);", [b"\x15\x1c\x03\xc7\xad\x05\xfe"]), + ("insert into binary_data (data) values (null);", []), ] EXTRA_DATABASE_SQL = """ diff --git a/tests/test_api.py b/tests/test_api.py index 53f33a9c..5e9c1a0a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -106,7 +106,7 @@ def test_database_page(app_client): "name": "binary_data", "columns": ["data"], "primary_keys": [], - "count": 2, + "count": 3, "hidden": False, "fts_table": None, "foreign_keys": {"incoming": [], "outgoing": []}, @@ -1812,6 +1812,7 @@ def test_inspect_file_used_for_count(app_client_immutable_and_inspect_file): [ {"rowid": 1, "data": {"$base64": True, "encoded": "FRwCx60F/g=="}}, {"rowid": 2, "data": {"$base64": True, "encoded": "FRwDx60F/g=="}}, + {"rowid": 3, "data": None}, ], None, ), @@ -1820,7 +1821,8 @@ def test_inspect_file_used_for_count(app_client_immutable_and_inspect_file): None, ( '{"rowid": 1, "data": {"$base64": true, "encoded": "FRwCx60F/g=="}}\n' - '{"rowid": 2, "data": {"$base64": true, "encoded": "FRwDx60F/g=="}}' + '{"rowid": 2, "data": {"$base64": true, "encoded": "FRwDx60F/g=="}}\n' + '{"rowid": 3, "data": null}' ), ), ], diff --git a/tests/test_html.py b/tests/test_html.py index 06b11de5..3c1101d2 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -1238,21 +1238,36 @@ def test_binary_data_display(app_client): '2', '<Binary:\xa07\xa0bytes>', ], + [ + '3', + '3', + '\xa0', + ], ] assert expected_tds == [ [str(td) for td in tr.select("td")] for tr in table.select("tbody tr") ] -def test_blob_download(app_client): - response = app_client.get("/fixtures/binary_data/-/blob/1/data.blob") +@pytest.mark.parametrize( + "path,expected_body,expected_filename", + [ + ( + "/fixtures/binary_data/-/blob/1/data.blob", + b"\x15\x1c\x02\xc7\xad\x05\xfe", + "binary_data-1-data.blob", + ), + ("/fixtures/binary_data/-/blob/3/data.blob", b"", "binary_data-3-data.blob"), + ], +) +def test_blob_download(app_client, path, expected_body, expected_filename): + response = app_client.get(path) assert response.status == 200 - assert response.body == b"\x15\x1c\x02\xc7\xad\x05\xfe" + assert response.body == expected_body assert response.headers["x-content-type-options"] == "nosniff" - assert ( - response.headers["content-disposition"] - == 'attachment; filename="binary_data-1-data.blob"' - ) + assert response.headers[ + "content-disposition" + ] == 'attachment; filename="{}"'.format(expected_filename) assert response.headers["content-type"] == "application/binary"