Skip to content

Commit 141c73a

Browse files
committed
cleaned up nicely
1 parent 3ea3158 commit 141c73a

File tree

1 file changed

+89
-19
lines changed

1 file changed

+89
-19
lines changed

tests/unit/test_mock_s3.py

+89-19
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
11
import os
22
import s3fs
33
import pathlib
4-
import boto3
4+
import json
55
import moto
66
import pyfive
77
import pytest
88
import h5netcdf
99

1010
from tempfile import NamedTemporaryFile
11-
from test_s3fs import s3 as tests3
1211
from moto.moto_server.threaded_moto_server import ThreadedMotoServer
1312

13+
1414
# some spoofy server parameters
1515
port = 5555
1616
endpoint_uri = "http://127.0.0.1:%s/" % port
17+
test_bucket_name = "test"
18+
versioned_bucket_name = "test-versioned"
19+
secure_bucket_name = "test-secure"
20+
21+
def get_boto3_client():
22+
from botocore.session import Session
23+
24+
# NB: we use the sync botocore client for setup
25+
session = Session()
26+
return session.create_client("s3", endpoint_url=endpoint_uri)
1727

1828
@pytest.fixture(scope="module")
1929
def s3_base():
2030
# writable local S3 system
2131

2232
# This fixture is module-scoped, meaning that we can re-use the MotoServer across all tests
33+
#####
34+
# lifted from https://github.com/fsspec/s3fs/blob/main/s3fs/tests/test_s3fs.py
35+
#####
2336
server = ThreadedMotoServer(ip_address="127.0.0.1", port=port)
2437
server.start()
2538
if "AWS_SECRET_ACCESS_KEY" not in os.environ:
@@ -34,7 +47,58 @@ def s3_base():
3447
server.stop()
3548

3649

37-
def spoof_s3(bucket, file_name, file_path):
50+
@pytest.fixture()
51+
def s3fs_s3(s3_base):
52+
"""
53+
Create a fully functional "virtual" S3 FileSystem compatible with fsspec/s3fs.
54+
Method inspired by https://github.com/fsspec/s3fs/blob/main/s3fs/tests/test_s3fs.py
55+
"""
56+
client = get_boto3_client()
57+
client.create_bucket(Bucket=test_bucket_name, ACL="public-read")
58+
59+
client.create_bucket(Bucket=versioned_bucket_name, ACL="public-read")
60+
client.put_bucket_versioning(
61+
Bucket=versioned_bucket_name, VersioningConfiguration={"Status": "Enabled"}
62+
)
63+
64+
# initialize secure bucket
65+
client.create_bucket(Bucket=secure_bucket_name, ACL="public-read")
66+
policy = json.dumps(
67+
{
68+
"Version": "2012-10-17",
69+
"Id": "PutObjPolicy",
70+
"Statement": [
71+
{
72+
"Sid": "DenyUnEncryptedObjectUploads",
73+
"Effect": "Deny",
74+
"Principal": "*",
75+
"Action": "s3:PutObject",
76+
"Resource": "arn:aws:s3:::{bucket_name}/*".format(
77+
bucket_name=secure_bucket_name
78+
),
79+
"Condition": {
80+
"StringNotEquals": {
81+
"s3:x-amz-server-side-encryption": "aws:kms"
82+
}
83+
},
84+
}
85+
],
86+
}
87+
)
88+
89+
client.put_bucket_policy(Bucket=secure_bucket_name, Policy=policy)
90+
s3fs.S3FileSystem.clear_instance_cache()
91+
s3 = s3fs.S3FileSystem(anon=False, client_kwargs={"endpoint_url": endpoint_uri})
92+
s3.invalidate_cache()
93+
94+
yield s3
95+
96+
97+
def spoof_boto3_s3(bucket, file_name, file_path):
98+
# this is a pure boto3 implementation
99+
# I am leaving it here just in case we'll ever need it in the future
100+
# NOTE: we are NOT including boto3 as dependency yet, until we ever need it
101+
38102
# "put" file
39103
if os.path.exists(file_path):
40104
with open(file_path, "rb") as file_contents:
@@ -61,17 +125,18 @@ def spoof_s3(bucket, file_name, file_path):
61125
object.download_fileobj(ncdata)
62126
with open('testobj.nc', 'rb') as ncdata:
63127
ncfile = h5netcdf.File(ncdata, 'r', invalid_netcdf=True)
64-
print(ncfile) # it works but...
65-
# correct coupling between boto3 and s3fs requires yielding
66-
# an s3fs Filesystem,
67-
# see https://stackoverflow.com/questions/75902766/how-to-access-my-own-fake-bucket-with-s3filesystem-pytest-and-moto
128+
print(ncfile)
68129

69130
return res
70131

71132

72133
@pytest.fixture(scope='session')
73134
def aws_credentials():
74-
"""Mocked AWS Credentials for moto."""
135+
"""
136+
Mocked AWS Credentials for moto.
137+
NOTE: Used ONLY by the pure boto3 test method spoof_boto3_s3.
138+
"""
139+
# NOTE: Used ONLY by the pure boto3 test method spoof_boto3_s3
75140
os.environ['AWS_ACCESS_KEY_ID'] = 'testing'
76141
os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing'
77142
os.environ['AWS_SECURITY_TOKEN'] = 'testing'
@@ -81,8 +146,8 @@ def aws_credentials():
81146
try:
82147
tmp = NamedTemporaryFile(delete=False)
83148
tmp.write(b"""[wild weasel]
84-
aws_access_key_id = testing
85-
aws_secret_access_key = testing""")
149+
aws_access_key_id = testing
150+
aws_secret_access_key = testing""")
86151
tmp.close()
87152
os.environ['AWS_SHARED_CREDENTIALS_FILE'] = str(tmp.name)
88153
yield
@@ -92,6 +157,8 @@ def aws_credentials():
92157

93158
@pytest.fixture(scope='function')
94159
def empty_bucket(aws_credentials):
160+
"""Create an empty bucket."""
161+
# NOTE: Used ONLY by the pure boto3 test method spoof_boto3_s3
95162
moto_fake = moto.mock_aws()
96163
try:
97164
moto_fake.start()
@@ -101,38 +168,41 @@ def empty_bucket(aws_credentials):
101168
finally:
102169
moto_fake.stop()
103170

104-
@pytest.mark.skip(reason="This test is now obsolete")
105-
def test_s3file_spoofing(empty_bucket):
171+
172+
@pytest.mark.skip(reason="This test uses the pure boto3 implement which we don't need at the moment.")
173+
def test_s3file_with_pure_boto3(empty_bucket):
106174
ncfile = "./tests/test_data/daily_data.nc"
107175
file_path = pathlib.Path(ncfile)
108176
file_name = pathlib.Path(ncfile).name
109177
# partial spoofing with only boto3+moto
110178
result = spoof_s3("MY_BUCKET", file_name, file_path)
111-
112179
with s3.open(os.path.join("MY_BUCKET", file_name), "rb") as f:
113180
ncfile = h5netcdf.File(f, 'r', invalid_netcdf=True)
114181
assert result.get('HTTPStatusCode') == 200
115182

116183

117-
def test_s3file_spoofing_2(tests3):
184+
def test_s3file_with_s3fs(s3fs_s3):
118185
"""
119-
This test spoofs a complete s3fs FileSystem,
186+
This test spoofs a complete s3fs FileSystem via s3fs_s3,
120187
creates a mock bucket inside it, then puts a REAL netCDF4 file in it,
121188
then it loads it as if it was an S3 file. This is proper
122189
Wild Weasel stuff right here.
123190
"""
191+
# set up physical file and Path properties
124192
ncfile = "./tests/test_data/daily_data.nc"
125193
file_path = pathlib.Path(ncfile)
126194
file_name = pathlib.Path(ncfile).name
127195

128-
# use s3fs proper
196+
# use mocked s3fs
129197
bucket = "MY_BUCKET"
130-
tests3.mkdir(bucket)
131-
tests3.put(file_path, bucket)
198+
s3fs_s3.mkdir(bucket)
199+
s3fs_s3.put(file_path, bucket)
132200
s3 = s3fs.S3FileSystem(
133201
anon=False, version_aware=True, client_kwargs={"endpoint_url": endpoint_uri}
134202
)
135203
with s3.open(os.path.join("MY_BUCKET", file_name), "rb") as f:
136204
ncfile = h5netcdf.File(f, 'r', invalid_netcdf=True)
137205
print("File loaded from spoof S3 with h5netcdf:", ncfile)
138-
print(x)
206+
print(ncfile["ta"])
207+
208+
assert "ta" in ncfile

0 commit comments

Comments
 (0)