Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Add test coverage for stac_api_io.py error handling ([#835](https://github.com/stac-utils/pystac-client/pull/835))

### Changed

- Make `get_collection` raise if `collection_id` is empty ([#809](https://github.com/stac-utils/pystac-client/pull/809))
Expand Down
56 changes: 56 additions & 0 deletions tests/test_stac_api_io.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import typing
from pathlib import Path
from typing import Any
from urllib.parse import parse_qs, urlsplit

import pystac
Expand Down Expand Up @@ -289,3 +290,58 @@ def test_stac_io_in_pystac() -> None:
stac_io = root._stac_io
assert isinstance(stac_io, StacApiIO)
assert stac_io.timeout == 42


def test_request_decode_error(requests_mock: Mocker) -> None:
"""Test that decode errors in request() are properly handled."""
url = "https://example.com/bad-encoding"
# Mock a response with invalid UTF-8 content
requests_mock.get(url, status_code=200, content=b"\xff\xfe\x00\x00")

stac_api_io = StacApiIO()

with pytest.raises(APIError) as excinfo:
stac_api_io.request(url)

assert (
"decode" in str(excinfo.value).lower() or "utf-8" in str(excinfo.value).lower()
)


def test_write_text_to_href_url_error() -> None:
"""Test that write_text_to_href raises APIError for URLs."""
stac_api_io = StacApiIO()

with pytest.raises(APIError, match="Transactions not supported"):
stac_api_io.write_text_to_href("https://example.com/write", "content")


def test_stac_object_from_dict_unknown_type(monkeypatch: MonkeyPatch) -> None:
"""Test that unknown STAC object types raise ValueError."""
stac_api_io = StacApiIO()

import json

with open("tests/data/planetary-computer-collection.json") as f:
real_stac_data = json.load(f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just modify the real_stac_data with a bogus type attribute, instead of mocking the functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gadomski Back from travel and picking this up. identify_stac_object_type in pystac will raise a STACTypeError if the object type is not one of catalog, collection, or item (https://pystac.readthedocs.io/en/v1.12.2/_modules/pystac/serialization/identify.html#identify_stac_object). So I don't think the ValueError at line 290 in stac_api_io.py would ever be triggered, since pystac is catching type errors before then. I was mocking to get around that error, but should have dug deeper.

I'd suggest removing my test and marking the ValueError at line 290 with something like # pragma: no cover, since pystac should guarantee that we only get valid types. Alternatively, we could remove both. Do you have a preference?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 I'm good with a pragma: no cover


# Mock identify_stac_object to return an unknown type
class MockInfo:
object_type = "UNKNOWN_TYPE"

def mock_identify_stac_object(d: dict[str, Any]) -> MockInfo:
return MockInfo()

# Mock migrate_to_latest to just return the data unchanged
def mock_migrate_to_latest(d: dict[str, Any], info: MockInfo) -> dict[str, Any]:
return d

monkeypatch.setattr(
"pystac_client.stac_api_io.identify_stac_object", mock_identify_stac_object
)
monkeypatch.setattr(
"pystac_client.stac_api_io.migrate_to_latest", mock_migrate_to_latest
)

with pytest.raises(ValueError, match="Unknown STAC object type"):
stac_api_io.stac_object_from_dict(real_stac_data)