|
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import base64 |
| 8 | +import os |
8 | 9 | import threading |
9 | 10 | import uuid |
10 | 11 | from datetime import datetime |
| 12 | +from urllib.parse import urljoin, urlparse |
11 | 13 |
|
12 | 14 | import awkward |
13 | 15 | import dask.dataframe |
|
17 | 19 | import pyarrow |
18 | 20 | import pytest |
19 | 21 | import sparse |
| 22 | +from minio import Minio |
| 23 | +from minio.error import S3Error |
20 | 24 | from pandas.testing import assert_frame_equal |
21 | 25 | from starlette.status import ( |
22 | 26 | HTTP_404_NOT_FOUND, |
|
37 | 41 | from ..structures.data_source import DataSource |
38 | 42 | from ..structures.sparse import COOStructure |
39 | 43 | from ..structures.table import TableStructure |
40 | | -from ..utils import APACHE_ARROW_FILE_MIME_TYPE, patch_mimetypes |
| 44 | +from ..utils import APACHE_ARROW_FILE_MIME_TYPE, patch_mimetypes, sanitize_uri |
41 | 45 | from ..validation_registration import ValidationRegistry |
42 | 46 | from .utils import fail_with_status_code |
43 | 47 |
|
|
46 | 50 |
|
47 | 51 |
|
48 | 52 | @pytest.fixture |
49 | | -def tree(tmpdir): |
50 | | - return in_memory( |
51 | | - writable_storage=[ |
52 | | - f"file://localhost{str(tmpdir / 'data')}", |
53 | | - f"duckdb:///{tmpdir / 'data.duckdb'}", |
54 | | - ] |
55 | | - ) |
| 53 | +def tmp_minio_bucket(): |
| 54 | + """Create a temporary MinIO bucket and clean it up after tests.""" |
| 55 | + if uri := os.getenv("TILED_TEST_BUCKET"): |
| 56 | + clean_uri, username, password = sanitize_uri(uri) |
| 57 | + minio_client = Minio( |
| 58 | + urlparse(clean_uri).netloc, # e.g. only "localhost:9000" |
| 59 | + access_key=username or "minioadmin", |
| 60 | + secret_key=password or "minioadmin", |
| 61 | + secure=False, |
| 62 | + ) |
| 63 | + |
| 64 | + bucket_name = f"test-{uuid.uuid4().hex}" |
| 65 | + minio_client.make_bucket(bucket_name) |
| 66 | + |
| 67 | + try: |
| 68 | + yield urljoin(uri, "/" + bucket_name) # full URI with credentials |
| 69 | + finally: |
| 70 | + # Cleanup: remove all objects and delete the bucket |
| 71 | + try: |
| 72 | + objects = minio_client.list_objects(bucket_name, recursive=True) |
| 73 | + for obj in objects: |
| 74 | + minio_client.remove_object(bucket_name, obj.object_name) |
| 75 | + minio_client.remove_bucket(bucket_name) |
| 76 | + except S3Error as e: |
| 77 | + print(f"Warning: failed to delete test bucket {bucket_name}: {e}") |
| 78 | + |
| 79 | + else: |
| 80 | + yield None |
| 81 | + |
| 82 | + |
| 83 | +@pytest.fixture |
| 84 | +def tree(tmpdir, tmp_minio_bucket): |
| 85 | + writable_storage = [f"duckdb:///{tmpdir / 'data.duckdb'}"] |
| 86 | + |
| 87 | + if tmp_minio_bucket: |
| 88 | + writable_storage.append( |
| 89 | + { |
| 90 | + "provider": "s3", |
| 91 | + "uri": tmp_minio_bucket, |
| 92 | + "config": { |
| 93 | + "virtual_hosted_style_request": False, |
| 94 | + "client_options": {"allow_http": True}, |
| 95 | + }, |
| 96 | + } |
| 97 | + ) |
| 98 | + |
| 99 | + writable_storage.append(f"file://localhost{str(tmpdir / 'data')}") |
| 100 | + |
| 101 | + return in_memory(writable_storage=writable_storage) |
56 | 102 |
|
57 | 103 |
|
58 | 104 | def test_write_array_full(tree): |
|
0 commit comments