mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
HistoricEpcS3Repository reached into utils/s3.py (read_csv_gz_from_s3 + parse_s3_uri), the legacy utility that self-constructs boto3 inside free functions. The other S3 repositories deliberately depend on the infrastructure/s3 layer instead (UnstandardisedAddressListCsvS3Repository injects a CsvS3Client). Bring historic EPC into line. - Add GzipCsvS3Client(S3Client) in infrastructure/s3: read_csv_gz(key) -> DataFrame (get_object + gzip decode). - Inject it into HistoricEpcS3Repository; the bucket lives in the client and the repo only builds the per-postcode key + maps rows (no S3/HTTP code). Add with_default_s3_client(s3_root) for composition roots. - Update main.py and the match_addresses_for_postcode seam to the factory. - Repo tests inject a real GzipCsvS3Client over a controlled boto stub (exact key assertions + AccessDenied); add a moto-based client test and a factory test covering s3_root -> bucket+key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MQE5TsSuQTeNSCSz9A9GQf
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
import gzip
|
|
from collections.abc import Iterator
|
|
|
|
import pytest
|
|
from botocore.exceptions import ClientError
|
|
from moto import mock_aws
|
|
|
|
from infrastructure.s3.gzip_csv_s3_client import GzipCsvS3Client
|
|
from tests.infrastructure import make_boto_client
|
|
|
|
BUCKET = "gzip-csv-bucket"
|
|
|
|
|
|
@pytest.fixture
|
|
def gzip_client() -> Iterator[GzipCsvS3Client]:
|
|
with mock_aws():
|
|
boto_client = make_boto_client("s3")
|
|
boto_client.create_bucket(Bucket=BUCKET)
|
|
yield GzipCsvS3Client(boto_client, BUCKET)
|
|
|
|
|
|
def test_read_csv_gz_decodes_gzipped_csv_into_dataframe(
|
|
gzip_client: GzipCsvS3Client,
|
|
) -> None:
|
|
# arrange
|
|
csv = "ADDRESS,UPRN\n47 GORDON ROAD,100\n48 GORDON ROAD,200\n"
|
|
gzip_client.put_object("historical_epc/AB338AL/data.csv.gz", gzip.compress(csv.encode()))
|
|
|
|
# act
|
|
df = gzip_client.read_csv_gz("historical_epc/AB338AL/data.csv.gz")
|
|
|
|
# assert
|
|
assert list(df.columns) == ["ADDRESS", "UPRN"]
|
|
assert df.shape == (2, 2)
|
|
assert df["ADDRESS"].tolist() == ["47 GORDON ROAD", "48 GORDON ROAD"]
|
|
|
|
|
|
def test_read_csv_gz_raises_no_such_key_when_object_missing(
|
|
gzip_client: GzipCsvS3Client,
|
|
) -> None:
|
|
# act / assert — a missing object surfaces as a ClientError; translating that
|
|
# into a domain miss is the Repo's job, not the client's.
|
|
with pytest.raises(ClientError) as exc_info:
|
|
gzip_client.read_csv_gz("historical_epc/ZZ999ZZ/data.csv.gz")
|
|
assert exc_info.value.response["Error"]["Code"] == "NoSuchKey"
|