Files
ThothII/harness/tests/test_s3_evidence_source.py
T

90 lines
3.3 KiB
Python

from datetime import UTC, datetime
import pytest
from tht.ports.evidence import EvidenceSourceError
class Body:
def __init__(self, data): self.data, self.closed = data, False
def read(self, amount): return self.data[:amount]
def close(self): self.closed = True
class Client:
def __init__(self): self.body = Body(b"hello")
def get_paginator(self, name): return self
def paginate(self, **kwargs):
yield {"Contents": [{"Key": "clinical/a.md", "ETag": '"abc"', "VersionId": "v1",
"Size": 5, "LastModified": datetime(2026, 1, 1, tzinfo=UTC)}]}
def get_object(self, **kwargs):
assert kwargs == {"Bucket": "evidence", "Key": "clinical/a.md", "VersionId": "v1"}
return {"Body": self.body, "ContentLength": 5, "ContentType": "text/markdown",
"ETag": '"abc"', "VersionId": "v1"}
def test_s3_canonical_uri_version_fingerprint_and_closed_body():
from tht.adapters.evidence.s3 import S3EvidenceSource
client = Client()
source = S3EvidenceSource(bucket="evidence", prefix="clinical/", client=client)
item = next(iter(source.discover()))
assert item.uri == "s3://evidence/clinical/a.md"
assert item.fingerprint == "s3-version:v1"
assert source.acquire(item).content == b"hello"
assert client.body.closed
def test_s3_etag_fallback_and_bounds():
from tht.adapters.evidence.s3 import S3EvidenceSource
client = Client()
with pytest.raises(ValueError):
S3EvidenceSource(bucket="evidence", client=client, max_objects=0)
def test_s3_rejects_private_or_insecure_endpoint_without_explicit_opt_in():
from tht.adapters.evidence.s3 import S3EvidenceSource
with pytest.raises(ValueError, match="private"):
S3EvidenceSource(bucket="evidence", endpoint_url="https://127.0.0.1:9000", client=Client())
with pytest.raises(ValueError, match="HTTPS"):
S3EvidenceSource(bucket="evidence", endpoint_url="http://s3.example.test", client=Client())
def test_s3_size_limit_closes_body():
from tht.adapters.evidence.s3 import S3EvidenceSource
client = Client()
source = S3EvidenceSource(bucket="evidence", client=client, max_bytes=4)
item = next(iter(source.discover()))
with pytest.raises(EvidenceSourceError):
source.acquire(item)
assert client.body.closed
def test_s3_config_serialization_masks_credentials():
from tht.config import S3EvidenceSourceConfig
config = S3EvidenceSourceConfig(type="s3", bucket="evidence",
access_key="access-secret", secret_key="write-secret")
assert "access-secret" not in repr(config)
assert "write-secret" not in repr(config)
def test_s3_config_loads_credentials_from_secret_files(tmp_path):
from tht.config import load_config
access, secret = tmp_path / "access", tmp_path / "secret"
access.write_text("access-value")
secret.write_text("secret-value")
workspace = tmp_path / "workspace.yaml"
workspace.write_text(f"""
dwh:
type: postgres_direct
connection: {{database: d, schema: public, user: u, password: p}}
evidence:
sources:
- type: s3
bucket: evidence
access_key_file: {access}
secret_key_file: {secret}
""")
source = load_config(workspace).evidence.sources[0]
assert source.access_key.get_secret_value() == "access-value"
assert source.secret_key.get_secret_value() == "secret-value"