Skip to content

Commit 453e516

Browse files
authored
Merge pull request #1118 from ethho/dev-tests-plat-165
PLAT-165: Migrate test_s3.py
2 parents 0ed5c3f + 3b5047b commit 453e516

4 files changed

Lines changed: 197 additions & 17 deletions

File tree

tests/conftest.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
schema_simple,
2424
schema_advanced,
2525
schema_adapted,
26+
schema_external,
2627
)
2728

2829

@@ -38,6 +39,20 @@ def monkeymodule():
3839
yield mp
3940

4041

42+
@pytest.fixture
43+
def enable_adapted_types(monkeypatch):
44+
monkeypatch.setenv(ADAPTED_TYPE_SWITCH, "TRUE")
45+
yield
46+
monkeypatch.delenv(ADAPTED_TYPE_SWITCH, raising=True)
47+
48+
49+
@pytest.fixture
50+
def enable_filepath_feature(monkeypatch):
51+
monkeypatch.setenv(FILEPATH_FEATURE_SWITCH, "TRUE")
52+
yield
53+
monkeypatch.delenv(FILEPATH_FEATURE_SWITCH, raising=True)
54+
55+
4156
@pytest.fixture(scope="session")
4257
def connection_root_bare():
4358
connection = dj.Connection(
@@ -161,6 +176,24 @@ def connection_test(connection_root):
161176
connection.close()
162177

163178

179+
@pytest.fixture(scope="session")
180+
def stores_config():
181+
stores_config = {
182+
"raw": dict(protocol="file", location=tempfile.mkdtemp()),
183+
"repo": dict(
184+
stage=tempfile.mkdtemp(), protocol="file", location=tempfile.mkdtemp()
185+
),
186+
"repo-s3": dict(
187+
S3_CONN_INFO, protocol="s3", location="dj/repo", stage=tempfile.mkdtemp()
188+
),
189+
"local": dict(protocol="file", location=tempfile.mkdtemp(), subfolding=(1, 1)),
190+
"share": dict(
191+
S3_CONN_INFO, protocol="s3", location="dj/store/repo", subfolding=(2, 4)
192+
),
193+
}
194+
return stores_config
195+
196+
164197
@pytest.fixture
165198
def schema_any(connection_test):
166199
schema_any = dj.Schema(
@@ -254,6 +287,28 @@ def schema_adv(connection_test):
254287
schema.drop()
255288

256289

290+
@pytest.fixture
291+
def schema_ext(connection_test, stores_config, enable_filepath_feature):
292+
schema = dj.Schema(
293+
PREFIX + "_extern",
294+
context=schema_external.LOCALS_EXTERNAL,
295+
connection=connection_test,
296+
)
297+
dj.config["stores"] = stores_config
298+
dj.config["cache"] = tempfile.mkdtemp()
299+
300+
schema(schema_external.Simple)
301+
schema(schema_external.SimpleRemote)
302+
schema(schema_external.Seed)
303+
schema(schema_external.Dimension)
304+
schema(schema_external.Image)
305+
schema(schema_external.Attach)
306+
schema(schema_external.Filepath)
307+
schema(schema_external.FilepathS3)
308+
yield schema
309+
schema.drop()
310+
311+
257312
@pytest.fixture(scope="session")
258313
def http_client():
259314
# Initialize httpClient with relevant timeout.
@@ -270,6 +325,7 @@ def http_client():
270325

271326
@pytest.fixture(scope="session")
272327
def minio_client_bare(http_client):
328+
"""Initialize MinIO with an endpoint and access/secret keys."""
273329
client = minio.Minio(
274330
S3_CONN_INFO["endpoint"],
275331
access_key=S3_CONN_INFO["access_key"],
@@ -282,8 +338,8 @@ def minio_client_bare(http_client):
282338

283339
@pytest.fixture(scope="session")
284340
def minio_client(minio_client_bare):
285-
"""Initialize MinIO with an endpoint and access/secret keys."""
286-
# Bootstrap MinIO bucket
341+
"""Initialize a MinIO client and create buckets for testing session."""
342+
# Setup MinIO bucket
287343
aws_region = "us-east-1"
288344
try:
289345
minio_client_bare.make_bucket(S3_CONN_INFO["bucket"], location=aws_region)

tests/schema_external.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
A schema for testing external attributes
3+
"""
4+
5+
import tempfile
6+
import inspect
7+
import datajoint as dj
8+
from . import PREFIX, CONN_INFO, S3_CONN_INFO
9+
import numpy as np
10+
11+
12+
class Simple(dj.Manual):
13+
definition = """
14+
simple : int
15+
---
16+
item : blob@local
17+
"""
18+
19+
20+
class SimpleRemote(dj.Manual):
21+
definition = """
22+
simple : int
23+
---
24+
item : blob@share
25+
"""
26+
27+
28+
class Seed(dj.Lookup):
29+
definition = """
30+
seed : int
31+
"""
32+
contents = zip(range(4))
33+
34+
35+
class Dimension(dj.Lookup):
36+
definition = """
37+
dim : int
38+
---
39+
dimensions : blob
40+
"""
41+
contents = ([0, [100, 50]], [1, [3, 4, 8, 6]])
42+
43+
44+
class Image(dj.Computed):
45+
definition = """
46+
# table for storing
47+
-> Seed
48+
-> Dimension
49+
----
50+
img : blob@share # objects are stored as specified by dj.config['stores']['share']
51+
neg : blob@local # objects are stored as specified by dj.config['stores']['local']
52+
"""
53+
54+
def make(self, key):
55+
np.random.seed(key["seed"])
56+
img = np.random.rand(*(Dimension() & key).fetch1("dimensions"))
57+
self.insert1(dict(key, img=img, neg=-img.astype(np.float32)))
58+
59+
60+
class Attach(dj.Manual):
61+
definition = """
62+
# table for storing attachments
63+
attach : int
64+
----
65+
img : attach@share # attachments are stored as specified by: dj.config['stores']['raw']
66+
txt : attach # attachments are stored directly in the database
67+
"""
68+
69+
70+
class Filepath(dj.Manual):
71+
definition = """
72+
# table for file management
73+
fnum : int # test comment containing :
74+
---
75+
img : filepath@repo # managed files
76+
"""
77+
78+
79+
class FilepathS3(dj.Manual):
80+
definition = """
81+
# table for file management
82+
fnum : int
83+
---
84+
img : filepath@repo-s3 # managed files
85+
"""
86+
87+
88+
LOCALS_EXTERNAL = {k: v for k, v in locals().items() if inspect.isclass(v)}
89+
__all__ = list(LOCALS_EXTERNAL)

tests/test_adapted_attributes.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import pytest
33
import tempfile
44
import datajoint as dj
5-
from datajoint.errors import ADAPTED_TYPE_SWITCH, FILEPATH_FEATURE_SWITCH
65
import networkx as nx
76
from itertools import zip_longest
87
from . import schema_adapted
@@ -17,20 +16,6 @@ def adapted_graph_instance():
1716
yield schema_adapted.GraphAdapter()
1817

1918

20-
@pytest.fixture
21-
def enable_adapted_types(monkeypatch):
22-
monkeypatch.setenv(ADAPTED_TYPE_SWITCH, "TRUE")
23-
yield
24-
monkeypatch.delenv(ADAPTED_TYPE_SWITCH, raising=True)
25-
26-
27-
@pytest.fixture
28-
def enable_filepath_feature(monkeypatch):
29-
monkeypatch.setenv(FILEPATH_FEATURE_SWITCH, "TRUE")
30-
yield
31-
monkeypatch.delenv(FILEPATH_FEATURE_SWITCH, raising=True)
32-
33-
3419
@pytest.fixture
3520
def schema_ad(
3621
connection_test,

tests/test_s3.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import pytest
2+
import urllib3
3+
import certifi
4+
from .schema_external import SimpleRemote
5+
from datajoint.errors import DataJointError
6+
from datajoint.hash import uuid_from_buffer
7+
from datajoint.blob import pack
8+
from . import S3_CONN_INFO
9+
from minio import Minio
10+
11+
12+
class TestS3:
13+
def test_connection(self, http_client, minio_client):
14+
assert minio_client.bucket_exists(S3_CONN_INFO["bucket"])
15+
16+
def test_connection_secure(self, minio_client):
17+
assert minio_client.bucket_exists(S3_CONN_INFO["bucket"])
18+
19+
def test_remove_object_exception(self, schema_ext):
20+
# https://github.com/datajoint/datajoint-python/issues/952
21+
22+
# Insert some test data and remove it so that the external table is populated
23+
test = [1, [1, 2, 3]]
24+
SimpleRemote.insert1(test)
25+
SimpleRemote.delete()
26+
27+
# Save the old external table minio client
28+
old_client = schema_ext.external["share"].s3.client
29+
30+
# Apply our new minio client which has a user that does not exist
31+
schema_ext.external["share"].s3.client = Minio(
32+
S3_CONN_INFO["endpoint"],
33+
access_key="jeffjeff",
34+
secret_key="jeffjeff",
35+
secure=False,
36+
)
37+
38+
# This method returns a list of errors
39+
error_list = schema_ext.external["share"].delete(
40+
delete_external_files=True, errors_as_string=False
41+
)
42+
43+
# Teardown
44+
schema_ext.external["share"].s3.client = old_client
45+
schema_ext.external["share"].delete(delete_external_files=True)
46+
47+
with pytest.raises(DataJointError):
48+
# Raise the error we want if the error matches the expected uuid
49+
if str(error_list[0][0]) == str(uuid_from_buffer(pack(test[1]))):
50+
raise error_list[0][2]

0 commit comments

Comments
 (0)