From 65857a21371b4ff4953157fa4632a1c7039fb2b4 Mon Sep 17 00:00:00 2001 From: teks Date: Wed, 29 Jan 2025 10:35:25 -0500 Subject: [PATCH 1/4] 993 convert test_version.py to pytest --- tests/test_version.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/test_version.py b/tests/test_version.py index cc4d0763e..58b0e6cd2 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,25 +1,31 @@ import os -import unittest +from collections.abc import Generator from unittest.mock import patch +import pytest + import pystac from tests.utils import TestCases -class VersionTest(unittest.TestCase): - def setUp(self) -> None: - pystac.version.STACVersion._override_version = None - - def test_override_stac_version_with_environ(self) -> None: - override_version = "1.0.0-gamma.2" - with patch.dict(os.environ, {"PYSTAC_STAC_VERSION_OVERRIDE": override_version}): - cat = TestCases.case_1() - d = cat.to_dict() - self.assertEqual(d["stac_version"], override_version) - - def test_override_stac_version_with_call(self) -> None: - override_version = "1.0.0-delta.2" - pystac.set_stac_version(override_version) +def test_override_stac_version_with_environ() -> None: + override_version = "1.0.0-gamma.2" + with patch.dict(os.environ, {"PYSTAC_STAC_VERSION_OVERRIDE": override_version}): cat = TestCases.case_1() d = cat.to_dict() - self.assertEqual(d["stac_version"], override_version) + assert d["stac_version"] == override_version + + +@pytest.fixture +def override_pystac_version() -> Generator[str]: + stac_version = pystac.get_stac_version() + override_version = "1.0.0-delta.2" + pystac.set_stac_version(override_version) + yield override_version + pystac.set_stac_version(stac_version) + + +def test_override_stac_version_with_call(override_pystac_version: str) -> None: + cat = TestCases.case_1() + d = cat.to_dict() + assert d["stac_version"] == override_pystac_version From ee62773d957160f6514f26a8d3210bc251360f14 Mon Sep 17 00:00:00 2001 From: teks Date: Fri, 31 Jan 2025 14:13:05 -0500 Subject: [PATCH 2/4] 993 convert test_item.py to pytest --- tests/test_item.py | 149 ++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/tests/test_item.py b/tests/test_item.py index 9932e112f..cc00e7c4a 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -27,86 +27,93 @@ from tests.utils import TestCases, assert_to_from_dict -class ItemTest(unittest.TestCase): - def get_example_item_dict(self) -> dict[str, Any]: - m = TestCases.get_path("data-files/item/sample-item.json") - with open(m) as f: - item_dict: dict[str, Any] = json.load(f) - return item_dict +def get_example_item_dict() -> dict[str, Any]: + m = TestCases.get_path("data-files/item/sample-item.json") + with open(m) as f: + item_dict: dict[str, Any] = json.load(f) + return item_dict - def test_to_from_dict(self) -> None: - self.maxDiff = None - item_dict = self.get_example_item_dict() - param_dict = deepcopy(item_dict) +def test_to_from_dict() -> None: + item_dict = get_example_item_dict() + param_dict = deepcopy(item_dict) - assert_to_from_dict(Item, param_dict) - item = Item.from_dict(param_dict) - self.assertEqual(item.id, "CS3-20160503_132131_05") + assert_to_from_dict(Item, param_dict) + item = Item.from_dict(param_dict) + assert item.id == "CS3-20160503_132131_05" - # test asset creation additional field(s) - self.assertEqual( - item.assets["analytic"].extra_fields["product"], - "http://cool-sat.com/catalog/products/analytic.json", - ) - self.assertEqual(len(item.assets["thumbnail"].extra_fields), 0) + # test asset creation additional field(s) + assert ( + item.assets["analytic"].extra_fields["product"] + == "http://cool-sat.com/catalog/products/analytic.json" + ) + assert len(item.assets["thumbnail"].extra_fields) == 0 + + # test that the parameter is preserved + assert param_dict == item_dict + + # assert that the parameter is preserved regardless of preserve_dict + Item.from_dict(param_dict, preserve_dict=False) + assert param_dict == item_dict + + +def test_from_dict_set_root() -> None: + item_dict = get_example_item_dict() + catalog = pystac.Catalog(id="test", description="test desc") + item = Item.from_dict(item_dict, root=catalog) + assert item.get_root() is catalog + + +def test_set_self_href_does_not_break_asset_hrefs() -> None: + cat = TestCases.case_2() + for item in cat.get_items(recursive=True): + for asset in item.assets.values(): + if is_absolute_href(asset.href): + asset.href = f"./{os.path.basename(asset.href)}" + item.set_self_href("http://example.com/item.json") + for asset in item.assets.values(): + assert is_absolute_href(asset.href) + + +def test_set_self_href_none_ignores_relative_asset_hrefs() -> None: + cat = TestCases.case_2() + for item in cat.get_items(recursive=True): + for asset in item.assets.values(): + if is_absolute_href(asset.href): + asset.href = f"./{os.path.basename(asset.href)}" + item.set_self_href(None) + for asset in item.assets.values(): + assert not is_absolute_href(asset.href) + + +def test_asset_absolute_href() -> None: + item_path = TestCases.get_path("data-files/item/sample-item.json") + item_dict = get_example_item_dict() + item = Item.from_dict(item_dict) + item.set_self_href(item_path) + rel_asset = Asset("./data.geojson") + rel_asset.set_owner(item) + expected_href = make_posix_style( + os.path.abspath(os.path.join(os.path.dirname(item_path), "./data.geojson")) + ) + actual_href = rel_asset.get_absolute_href() + assert expected_href == actual_href - # test that the parameter is preserved - self.assertEqual(param_dict, item_dict) - # assert that the parameter is preserved regardless of - # preserve_dict - _ = Item.from_dict(param_dict, preserve_dict=False) - self.assertEqual(param_dict, item_dict) +def test_asset_absolute_href_no_item_self() -> None: + item_dict = get_example_item_dict() + item = Item.from_dict(item_dict) + assert item.get_self_href() is None - def test_from_dict_set_root(self) -> None: - item_dict = self.get_example_item_dict() - catalog = pystac.Catalog(id="test", description="test desc") - item = Item.from_dict(item_dict, root=catalog) - self.assertIs(item.get_root(), catalog) + rel_asset = Asset("./data.geojson") + rel_asset.set_owner(item) + actual_href = rel_asset.get_absolute_href() + assert actual_href is None - def test_set_self_href_does_not_break_asset_hrefs(self) -> None: - cat = TestCases.case_2() - for item in cat.get_items(recursive=True): - for asset in item.assets.values(): - if is_absolute_href(asset.href): - asset.href = f"./{os.path.basename(asset.href)}" - item.set_self_href("http://example.com/item.json") - for asset in item.assets.values(): - self.assertTrue(is_absolute_href(asset.href)) - - def test_set_self_href_none_ignores_relative_asset_hrefs(self) -> None: - cat = TestCases.case_2() - for item in cat.get_items(recursive=True): - for asset in item.assets.values(): - if is_absolute_href(asset.href): - asset.href = f"./{os.path.basename(asset.href)}" - item.set_self_href(None) - for asset in item.assets.values(): - self.assertFalse(is_absolute_href(asset.href)) - - def test_asset_absolute_href(self) -> None: - item_path = TestCases.get_path("data-files/item/sample-item.json") - item_dict = self.get_example_item_dict() - item = Item.from_dict(item_dict) - item.set_self_href(item_path) - rel_asset = Asset("./data.geojson") - rel_asset.set_owner(item) - expected_href = make_posix_style( - os.path.abspath(os.path.join(os.path.dirname(item_path), "./data.geojson")) - ) - actual_href = rel_asset.get_absolute_href() - self.assertEqual(expected_href, actual_href) - def test_asset_absolute_href_no_item_self(self) -> None: - item_dict = self.get_example_item_dict() - item = Item.from_dict(item_dict) - assert item.get_self_href() is None - - rel_asset = Asset("./data.geojson") - rel_asset.set_owner(item) - actual_href = rel_asset.get_absolute_href() - self.assertEqual(None, actual_href) +class ItemTest(unittest.TestCase): + def get_example_item_dict(self) -> dict[str, Any]: + return get_example_item_dict() def test_item_field_order(self) -> None: item = pystac.Item.from_file( From 0fcff09b5f201865a601edebb650510d51d16fdb Mon Sep 17 00:00:00 2001 From: teks Date: Fri, 31 Jan 2025 19:47:59 -0500 Subject: [PATCH 3/4] 993 apply pre-commit fixes --- ..._geometry.yaml => test_null_geometry.yaml} | 98 ++-- tests/test_item.py | 515 ++++++++---------- 2 files changed, 287 insertions(+), 326 deletions(-) rename tests/cassettes/test_item/{ItemTest.test_null_geometry.yaml => test_null_geometry.yaml} (91%) diff --git a/tests/cassettes/test_item/ItemTest.test_null_geometry.yaml b/tests/cassettes/test_item/test_null_geometry.yaml similarity index 91% rename from tests/cassettes/test_item/ItemTest.test_null_geometry.yaml rename to tests/cassettes/test_item/test_null_geometry.yaml index c39119219..61bcb8d14 100644 --- a/tests/cassettes/test_item/ItemTest.test_null_geometry.yaml +++ b/tests/cassettes/test_item/test_null_geometry.yaml @@ -7,7 +7,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/item.json response: @@ -99,7 +99,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:39 GMT ETag: - '"66e1651c-147c"' Last-Modified: @@ -111,19 +111,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - eee882ae55a7f40729d77f3d500a84bed4c637d0 + - 9a54b2c2e8d8dfda667d69d0dd28acd3c223b4c7 X-GitHub-Request-Id: - - 7F28:34A10D:FF6968:11D3377:67925A75 + - 6B5F:16F8:3EC0E8:470F4B:679E5948 X-Served-By: - - cache-den-kden1300051-DEN + - cache-bos4633-BOS X-Timer: - - S1737644661.145511,VS0,VE59 + - S1738454440.623801,VS0,VE20 expires: - - Thu, 23 Jan 2025 15:14:21 GMT + - Sat, 01 Feb 2025 17:36:33 GMT x-proxy-cache: - MISS status: @@ -137,7 +137,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/basics.json response: @@ -166,7 +166,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:39 GMT ETag: - '"66e1651c-21c"' Last-Modified: @@ -178,19 +178,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - 24282bdf85a8fde571d18d0a3007d5cf6bf30fcb + - 8f6c3b198c4c026542ea5cc5b46485cfce14313c X-GitHub-Request-Id: - - 5ED4:12DF28:F44C6C:1121586:67925A75 + - 7D47:4AAAC:67C965:716773:679E5948 X-Served-By: - - cache-den-kden1300073-DEN + - cache-bos4643-BOS X-Timer: - - S1737644661.228637,VS0,VE68 + - S1738454440.712740,VS0,VE30 expires: - - Thu, 23 Jan 2025 15:14:21 GMT + - Sat, 01 Feb 2025 17:36:33 GMT x-origin-cache: - HIT x-proxy-cache: @@ -206,7 +206,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/datetime.json response: @@ -265,7 +265,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:39 GMT ETag: - '"66e1651c-a82"' Last-Modified: @@ -277,19 +277,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - a29e86e905de4ad9406e730b8f75d8d9fc0e363d + - 530a4436422927e3d78506b58f78887510928f7f X-GitHub-Request-Id: - - 5488:230B9F:B893E:CFA99:67925A75 + - 97C0:A1EF3:5E972D:68288C:679E5949 X-Served-By: - - cache-den-kden1300042-DEN + - cache-bos4655-BOS X-Timer: - - S1737644661.327324,VS0,VE57 + - S1738454440.811790,VS0,VE30 expires: - - Thu, 23 Jan 2025 15:14:21 GMT + - Sat, 01 Feb 2025 17:36:33 GMT x-proxy-cache: - MISS status: @@ -303,7 +303,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/instrument.json response: @@ -334,7 +334,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:39 GMT ETag: - '"66e1651c-2a2"' Last-Modified: @@ -346,21 +346,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - dd7f2955fbdd7a5b0b24cac94bac85eadaea1608 + - efcbb9e72539f153ef7cf2b7377c273d12c96c9e X-GitHub-Request-Id: - - 802D:338B06:10BB8A9:1298392:67925A75 + - FA0A:15A6D:5C46DD:65DCD4:679E5947 X-Served-By: - - cache-den-kden1300065-DEN + - cache-bos4692-BOS X-Timer: - - S1737644661.410596,VS0,VE58 + - S1738454440.905646,VS0,VE41 expires: - - Thu, 23 Jan 2025 15:14:21 GMT - x-origin-cache: - - HIT + - Sat, 01 Feb 2025 17:36:33 GMT x-proxy-cache: - MISS status: @@ -374,7 +372,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/licensing.json response: @@ -400,7 +398,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:40 GMT ETag: - '"66e1651c-135"' Last-Modified: @@ -412,19 +410,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - 2b8fd8778031ec89e594e7017d5111a507d66dfc + - 3d703898d330148032f27df7d9d46c65c819285e X-GitHub-Request-Id: - - D377:3405BB:10C72BA:12A3CD2:67925A75 + - 085E:1C811A:56FD1C:609622:679E5949 X-Served-By: - - cache-den-kden1300032-DEN + - cache-bos4624-BOS X-Timer: - - S1737644661.495593,VS0,VE54 + - S1738454440.007807,VS0,VE27 expires: - - Thu, 23 Jan 2025 15:14:21 GMT + - Sat, 01 Feb 2025 17:36:33 GMT x-origin-cache: - HIT x-proxy-cache: @@ -440,7 +438,7 @@ interactions: Host: - schemas.stacspec.org User-Agent: - - Python-urllib/3.12 + - Python-urllib/3.13 method: GET uri: https://schemas.stacspec.org/v1.0.0-beta.2/item-spec/json-schema/provider.json response: @@ -476,7 +474,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 23 Jan 2025 15:04:21 GMT + - Sun, 02 Feb 2025 00:00:40 GMT ETag: - '"66e1651c-40e"' Last-Modified: @@ -488,19 +486,19 @@ interactions: Via: - 1.1 varnish X-Cache: - - MISS + - HIT X-Cache-Hits: - '0' X-Fastly-Request-ID: - - 84d8905f4abd1401e3d76535680a6bda88fa0568 + - 01a1b60753cc5e73cb47bd5d87093ead8c9e73d8 X-GitHub-Request-Id: - - 538F:1011A9:1037FFE:1214BBF:67925A75 + - 6370:26E82C:5B4B43:64E477:679E5947 X-Served-By: - - cache-den-kden1300062-DEN + - cache-bos4647-BOS X-Timer: - - S1737644662.570305,VS0,VE54 + - S1738454440.107510,VS0,VE29 expires: - - Thu, 23 Jan 2025 15:14:21 GMT + - Sat, 01 Feb 2025 17:36:33 GMT x-origin-cache: - HIT x-proxy-cache: diff --git a/tests/test_item.py b/tests/test_item.py index cc00e7c4a..2f0955a97 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -5,10 +5,9 @@ import os import pickle import tempfile -import unittest from copy import deepcopy from pathlib import Path -from typing import Any +from typing import Any, cast import dateutil.relativedelta import pytest @@ -111,266 +110,251 @@ def test_asset_absolute_href_no_item_self() -> None: assert actual_href is None -class ItemTest(unittest.TestCase): - def get_example_item_dict(self) -> dict[str, Any]: - return get_example_item_dict() +def test_item_field_order() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) + item_dict = item.to_dict(include_self_link=False) + expected_order = [ + "type", + "stac_version", + "stac_extensions", + "id", + "geometry", + "bbox", + "properties", + "links", + "assets", + "collection", + ] + actual_order = list(item_dict.keys()) + assert actual_order == expected_order - def test_item_field_order(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item.json") - ) - item_dict = item.to_dict(include_self_link=False) - expected_order = [ - "type", - "stac_version", - "stac_extensions", - "id", - "geometry", - "bbox", - "properties", - "links", - "assets", - "collection", - ] - actual_order = list(item_dict.keys()) - self.assertEqual( - actual_order, - expected_order, - f"Order was {actual_order}, expected {expected_order}", - ) - def test_extra_fields(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item.json") - ) +def test_extra_fields() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) - item.extra_fields["test"] = "extra" - - with tempfile.TemporaryDirectory() as tmp_dir: - p = os.path.join(tmp_dir, "item.json") - item.save_object(include_self_link=False, dest_href=p) - with open(p) as f: - item_json = json.load(f) - self.assertTrue("test" in item_json) - self.assertEqual(item_json["test"], "extra") - - read_item = pystac.Item.from_file(p) - self.assertTrue("test" in read_item.extra_fields) - self.assertEqual(read_item.extra_fields["test"], "extra") - - def test_clearing_collection(self) -> None: - collection = TestCases.case_4().get_child("acc") - assert isinstance(collection, pystac.Collection) - item = next(collection.get_items(recursive=True)) - self.assertEqual(item.collection_id, collection.id) - item.set_collection(None) - self.assertIsNone(item.collection_id) - self.assertIsNone(item.get_collection()) - item.set_collection(collection) - self.assertEqual(item.collection_id, collection.id) - self.assertIs(item.get_collection(), collection) - - def test_datetime_ISO8601_format(self) -> None: - item_dict = self.get_example_item_dict() - - item = Item.from_dict(item_dict) - - formatted_time = item.to_dict()["properties"]["datetime"] - - self.assertEqual("2016-05-03T13:22:30.040000Z", formatted_time) - - @pytest.mark.vcr() - def test_null_datetime(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item.json") - ) + item.extra_fields["test"] = "extra" + + with tempfile.TemporaryDirectory() as tmp_dir: + p = os.path.join(tmp_dir, "item.json") + item.save_object(include_self_link=False, dest_href=p) + with open(p) as f: + item_json = json.load(f) + assert "test" in item_json + assert item_json["test"] == "extra" + + read_item = pystac.Item.from_file(p) + assert "test" in read_item.extra_fields + assert read_item.extra_fields["test"] == "extra" - with self.assertRaises(pystac.STACError): - Item( - "test", - geometry=item.geometry, - bbox=item.bbox, - datetime=None, - properties={}, - ) - null_dt_item = Item( +def test_clearing_collection() -> None: + collection = TestCases.case_4().get_child("acc") + assert isinstance(collection, pystac.Collection) + item = next(collection.get_items(recursive=True)) + assert item.collection_id == collection.id + item.set_collection(None) + assert item.collection_id is None + assert item.get_collection() is None + item.set_collection(collection) + assert item.collection_id == collection.id + assert item.get_collection() is collection + + +def test_datetime_ISO8601_format() -> None: + item_dict = get_example_item_dict() + item = Item.from_dict(item_dict) + formatted_time = item.to_dict()["properties"]["datetime"] + assert "2016-05-03T13:22:30.040000Z" == formatted_time + + +@pytest.mark.vcr() +def test_null_datetime() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) + + with pytest.raises(pystac.STACError): + Item( "test", geometry=item.geometry, bbox=item.bbox, datetime=None, - properties={ - "start_datetime": datetime_to_str(get_opt(item.datetime)), - "end_datetime": datetime_to_str(get_opt(item.datetime)), - }, + properties={}, ) - null_dt_item.validate() + null_dt_item = Item( + "test", + geometry=item.geometry, + bbox=item.bbox, + datetime=None, + properties={ + "start_datetime": datetime_to_str(get_opt(item.datetime)), + "end_datetime": datetime_to_str(get_opt(item.datetime)), + }, + ) - def test_get_assets(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item.json") - ) + null_dt_item.validate() - media_type_filter = item.get_assets(media_type=pystac.MediaType.COG) - self.assertCountEqual(media_type_filter.keys(), ["analytic"]) - role_filter = item.get_assets(role="data") - self.assertCountEqual(role_filter.keys(), ["analytic"]) - multi_filter = item.get_assets( - media_type=pystac.MediaType.PNG, role="thumbnail" - ) - self.assertCountEqual(multi_filter.keys(), ["thumbnail"]) - multi_filter["thumbnail"].description = "foo" - assert item.assets["thumbnail"].description != "foo" - - no_filter = item.get_assets() - self.assertCountEqual(no_filter.keys(), ["analytic", "thumbnail"]) - no_assets = item.get_assets(media_type=pystac.MediaType.HDF) - self.assertEqual(no_assets, {}) - - @pytest.mark.vcr() - def test_null_datetime_constructor(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item.json") + +def test_get_assets() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) + + media_type_filter = item.get_assets(media_type=pystac.MediaType.COG) + assert set(media_type_filter.keys()) == {"analytic"} + role_filter = item.get_assets(role="data") + assert set(role_filter.keys()) == {"analytic"} + multi_filter = item.get_assets(media_type=pystac.MediaType.PNG, role="thumbnail") + assert set(multi_filter.keys()) == {"thumbnail"} + multi_filter["thumbnail"].description = "foo" + assert item.assets["thumbnail"].description != "foo" + + no_filter = item.get_assets() + assert set(no_filter.keys()) == {"analytic", "thumbnail"} + no_assets = item.get_assets(media_type=pystac.MediaType.HDF) + assert no_assets == {} + + +@pytest.mark.vcr() +def test_null_datetime_constructor() -> None: + item = pystac.Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) + with pytest.raises(pystac.STACError): + Item( + "test", + geometry=item.geometry, + bbox=item.bbox, + datetime=None, + end_datetime=item.datetime, + properties={}, ) - with self.assertRaises(pystac.STACError): - Item( - "test", - geometry=item.geometry, - bbox=item.bbox, - datetime=None, - end_datetime=item.datetime, - properties={}, - ) - with self.assertRaises(pystac.STACError): - Item( - "test", - geometry=item.geometry, - bbox=item.bbox, - datetime=None, - start_datetime=item.datetime, - properties={}, - ) - assert item.datetime - null_dt_item = Item( + with pytest.raises(pystac.STACError): + Item( "test", geometry=item.geometry, bbox=item.bbox, datetime=None, start_datetime=item.datetime, - end_datetime=item.datetime + dateutil.relativedelta.relativedelta(days=1), properties={}, ) - null_dt_item.validate() + assert item.datetime + null_dt_item = Item( + "test", + geometry=item.geometry, + bbox=item.bbox, + datetime=None, + start_datetime=item.datetime, + end_datetime=item.datetime + dateutil.relativedelta.relativedelta(days=1), + properties={}, + ) + null_dt_item.validate() - def test_get_set_asset_datetime(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path("data-files/item/sample-item-asset-properties.json") - ) - item_datetime = item.datetime - # No property on asset - self.assertEqual(item.get_datetime(item.assets["thumbnail"]), item.datetime) +def test_get_set_asset_datetime() -> None: + item = pystac.Item.from_file( + TestCases.get_path("data-files/item/sample-item-asset-properties.json") + ) + item_datetime = item.datetime - # Property on asset - self.assertNotEqual(item.get_datetime(item.assets["analytic"]), item.datetime) - self.assertEqual( - item.get_datetime(item.assets["analytic"]), - str_to_datetime("2017-05-03T13:22:30.040Z"), - ) + # No property on asset + assert item.get_datetime(item.assets["thumbnail"]) == item.datetime - item.set_datetime( - str_to_datetime("2018-05-03T13:22:30.040Z"), item.assets["thumbnail"] - ) - self.assertEqual(item.get_datetime(), item_datetime) - self.assertEqual( - item.get_datetime(item.assets["thumbnail"]), - str_to_datetime("2018-05-03T13:22:30.040Z"), - ) + # Property on asset + assert item.get_datetime(item.assets["analytic"]) != item.datetime + assert item.get_datetime(item.assets["analytic"]) == str_to_datetime( + "2017-05-03T13:22:30.040Z" + ) - def test_read_eo_item_owns_asset(self) -> None: - item = next(TestCases.case_1().get_items(recursive=True)) - assert len(item.assets) > 0 - for asset_key in item.assets: - self.assertEqual(item.assets[asset_key].owner, item) + item.set_datetime( + str_to_datetime("2018-05-03T13:22:30.040Z"), item.assets["thumbnail"] + ) + assert item.get_datetime() == item_datetime + assert item.get_datetime(item.assets["thumbnail"]) == str_to_datetime( + "2018-05-03T13:22:30.040Z" + ) - @pytest.mark.vcr() - def test_null_geometry(self) -> None: - m = TestCases.get_path( - "data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json" - ) - with open(m) as f: - item_dict = json.load(f) - validate_dict(item_dict, pystac.STACObjectType.ITEM) +def test_read_eo_item_owns_asset() -> None: + item = next(TestCases.case_1().get_items(recursive=True)) + assert len(item.assets) > 0 + for asset_key in item.assets: + assert item.assets[asset_key].owner == item - item = Item.from_dict(item_dict) - self.assertIsInstance(item, Item) - item.validate() - item_dict = item.to_dict() - self.assertIsNone(item_dict["geometry"]) - self.assertNotIn("bbox", item_dict) +@pytest.mark.vcr() +def test_null_geometry() -> None: + m = TestCases.get_path( + "data-files/examples/1.0.0-beta.2/item-spec/examples/null-geom-item.json" + ) + with open(m) as f: + item_dict = json.load(f) - def test_0_9_item_with_no_extensions_does_not_read_collection_data(self) -> None: - item_json = pystac.StacIO.default().read_json( - TestCases.get_path("data-files/examples/hand-0.9.0/010100/010100.json") - ) - assert item_json.get("stac_extensions") is None - assert item_json.get("stac_version") == "0.9.0" + validate_dict(item_dict, pystac.STACObjectType.ITEM) - did_merge = pystac.serialization.common_properties.merge_common_properties( - item_json - ) - self.assertFalse(did_merge) - - def test_clone_preserves_assets(self) -> None: - cat = TestCases.case_2() - original_item = next(cat.get_items(recursive=True)) - assert len(original_item.assets) > 0 - assert all( - asset.owner is original_item for asset in original_item.assets.values() - ) + item = Item.from_dict(item_dict) + assert isinstance(item, Item) + item.validate() - cloned_item = original_item.clone() + item_dict = item.to_dict() + assert item_dict["geometry"] is None + assert "bbox" not in item_dict - for key in original_item.assets: - with self.subTest(f"Preserves {key} asset"): - self.assertIn(key, cloned_item.assets) - cloned_asset = cloned_item.assets.get(key) - if cloned_asset is not None: - with self.subTest(f"Sets owner for {key}"): - self.assertIs(cloned_asset.owner, cloned_item) - def test_make_asset_href_relative_is_noop_on_relative_hrefs(self) -> None: - cat = TestCases.case_2() - item = next(cat.get_items(recursive=True)) - asset = list(item.assets.values())[0] - assert not is_absolute_href(asset.href) - original_href = asset.get_absolute_href() +def test_0_9_item_with_no_extensions_does_not_read_collection_data() -> None: + item_json = pystac.StacIO.default().read_json( + TestCases.get_path("data-files/examples/hand-0.9.0/010100/010100.json") + ) + assert item_json.get("stac_extensions") is None + assert item_json.get("stac_version") == "0.9.0" - item.make_asset_hrefs_relative() - self.assertEqual(asset.get_absolute_href(), original_href) + did_merge = pystac.serialization.common_properties.merge_common_properties( + item_json + ) + assert not did_merge - def test_from_invalid_dict_raises_exception(self) -> None: - stac_io = pystac.StacIO.default() - catalog_dict = stac_io.read_json( - TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") - ) - with self.assertRaises(pystac.STACTypeError): - _ = pystac.Item.from_dict(catalog_dict) - - @pytest.mark.vcr() - def test_relative_extension_path(self) -> None: - item = pystac.Item.from_file( - TestCases.get_path( - "data-files/item/sample-item-with-relative-extension-path.json" - ) + +def test_clone_preserves_assets() -> None: + cat = TestCases.case_2() + original_item = next(cat.get_items(recursive=True)) + assert len(original_item.assets) > 0 + assert all(asset.owner is original_item for asset in original_item.assets.values()) + + cloned_item = original_item.clone() + + for key in original_item.assets: + assert key in cloned_item.assets, f"Failed to preserve asset {key}" + cloned_asset = cloned_item.assets.get(key) + if cloned_asset is not None: + assert cloned_asset.owner is cloned_item, f"Failed set owner for {key}" + + +def test_make_asset_href_relative_is_noop_on_relative_hrefs() -> None: + cat = TestCases.case_2() + item = next(cat.get_items(recursive=True)) + asset = list(item.assets.values())[0] + assert not is_absolute_href(asset.href) + original_href = asset.get_absolute_href() + + item.make_asset_hrefs_relative() + assert asset.get_absolute_href() == original_href + + +def test_from_invalid_dict_raises_exception() -> None: + stac_io = pystac.StacIO.default() + catalog_dict = stac_io.read_json( + TestCases.get_path("data-files/catalogs/test-case-1/catalog.json") + ) + with pytest.raises(pystac.STACTypeError): + _ = pystac.Item.from_dict(catalog_dict) + + +@pytest.mark.vcr() +def test_relative_extension_path() -> None: + item = pystac.Item.from_file( + TestCases.get_path( + "data-files/item/sample-item-with-relative-extension-path.json" ) - item.validate() + ) + item.validate() -class ItemSubClassTest(unittest.TestCase): +class TestItemSubClass: """This tests cases related to creating classes inheriting from pystac.Catalog to ensure that inheritance, class methods, etc. function as expected.""" @@ -379,87 +363,66 @@ class ItemSubClassTest(unittest.TestCase): class BasicCustomItem(pystac.Item): pass - def setUp(self) -> None: - self.stac_io = pystac.StacIO.default() - def test_from_dict_returns_subclass(self) -> None: - item_dict = self.stac_io.read_json(self.SAMPLE_ITEM) + stac_io = pystac.StacIO.default() + item_dict = stac_io.read_json(self.SAMPLE_ITEM) custom_item = self.BasicCustomItem.from_dict(item_dict) - self.assertIsInstance(custom_item, self.BasicCustomItem) + assert isinstance(custom_item, self.BasicCustomItem) def test_from_file_returns_subclass(self) -> None: custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM) - self.assertIsInstance(custom_item, self.BasicCustomItem) + assert isinstance(custom_item, self.BasicCustomItem) def test_clone(self) -> None: custom_item = self.BasicCustomItem.from_file(self.SAMPLE_ITEM) cloned_item = custom_item.clone() - self.assertIsInstance(cloned_item, self.BasicCustomItem) - - -class AssetTest(unittest.TestCase): - def setUp(self) -> None: - self.maxDiff = None - with open(TestCases.get_path("data-files/item/sample-item.json")) as src: - item_dict = json.load(src) - - self.asset_dict = item_dict["assets"]["analytic"] - - def example_asset(self) -> Asset: - return Asset.from_dict(self.asset_dict) - - def test_clone(self) -> None: - original_asset = self.example_asset() - cloned_asset = original_asset.clone() - - self.assertDictEqual(original_asset.to_dict(), self.asset_dict) - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) - - # Changes to original asset should not affect cloned Asset - original_asset.description = "Some new description" - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) + assert isinstance(cloned_item, self.BasicCustomItem) - original_asset.href = "/path/to/new/href" - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) - original_asset.title = "New Title" - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) +def test_asset_clone() -> None: + with open(TestCases.get_path("data-files/item/sample-item.json")) as src: + item_dict = json.load(src) + asset_dict = item_dict["assets"]["analytic"] + original_asset = Asset.from_dict(asset_dict) - original_asset.roles = ["new role"] - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) + cloned_asset = original_asset.clone() - original_asset.roles.append("new role") - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) + assert original_asset.to_dict() == asset_dict + assert cloned_asset.to_dict() == asset_dict - original_asset.extra_fields["new_field"] = "new_value" - self.assertDictEqual(cloned_asset.to_dict(), self.asset_dict) + # Changes to original asset should not affect cloned Asset + original_asset.description = "Some new description" + original_asset.href = "/path/to/new/href" + original_asset.title = "New Title" + original_asset.roles = ["new role"] + original_asset.roles.append("new role") + original_asset.extra_fields["new_field"] = "new_value" + assert cloned_asset.to_dict() == asset_dict -class AssetSubClassTest(unittest.TestCase): +class TestAssetSubClass: class CustomAsset(Asset): pass - def setUp(self) -> None: - self.maxDiff = None + AssetDict = dict[str, str | list[str]] + + @pytest.fixture + def asset_dict(self) -> AssetDict: with open(TestCases.get_path("data-files/item/sample-item.json")) as src: item_dict = json.load(src) + return cast(TestAssetSubClass.AssetDict, item_dict["assets"]["analytic"]) - self.asset_dict = item_dict["assets"]["analytic"] - - def test_from_dict(self) -> None: - asset = self.CustomAsset.from_dict(self.asset_dict) - - self.assertIsInstance(asset, self.CustomAsset) + def test_from_dict(self, asset_dict: AssetDict) -> None: + asset = self.CustomAsset.from_dict(asset_dict) + assert isinstance(asset, self.CustomAsset) - def test_clone(self) -> None: - asset = self.CustomAsset.from_dict(self.asset_dict) + def test_clone(self, asset_dict: AssetDict) -> None: + asset = self.CustomAsset.from_dict(asset_dict) cloned_asset = asset.clone() - - self.assertIsInstance(cloned_asset, self.CustomAsset) - self.assertIsInstance(cloned_asset, self.CustomAsset) + assert isinstance(cloned_asset, self.CustomAsset) def test_custom_item_from_dict(item: Item) -> None: From 6bea3e7a4e86d0b38491c9e2cf18e46e25cf5159 Mon Sep 17 00:00:00 2001 From: teks Date: Mon, 3 Feb 2025 09:08:02 -0500 Subject: [PATCH 4/4] 993 replace function with fixture --- tests/conftest.py | 10 ++++++++++ tests/test_item.py | 40 +++++++++++++--------------------------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index afdee39b0..10a8b606e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,11 @@ # TODO move all test case code to this file +import json import shutil import uuid from datetime import datetime from pathlib import Path +from typing import Any import pytest @@ -61,6 +63,14 @@ def get_data_file(rel_path: str) -> str: return str(here / "data-files" / rel_path) +@pytest.fixture +def sample_item_dict() -> dict[str, Any]: + m = TestCases.get_path("data-files/item/sample-item.json") + with open(m) as f: + item_dict: dict[str, Any] = json.load(f) + return item_dict + + @pytest.fixture def sample_item() -> Item: return Item.from_file(TestCases.get_path("data-files/item/sample-item.json")) diff --git a/tests/test_item.py b/tests/test_item.py index 2f0955a97..87b8dea32 100644 --- a/tests/test_item.py +++ b/tests/test_item.py @@ -26,16 +26,8 @@ from tests.utils import TestCases, assert_to_from_dict -def get_example_item_dict() -> dict[str, Any]: - m = TestCases.get_path("data-files/item/sample-item.json") - with open(m) as f: - item_dict: dict[str, Any] = json.load(f) - return item_dict - - -def test_to_from_dict() -> None: - item_dict = get_example_item_dict() - param_dict = deepcopy(item_dict) +def test_to_from_dict(sample_item_dict: dict[str, Any]) -> None: + param_dict = deepcopy(sample_item_dict) assert_to_from_dict(Item, param_dict) item = Item.from_dict(param_dict) @@ -49,17 +41,16 @@ def test_to_from_dict() -> None: assert len(item.assets["thumbnail"].extra_fields) == 0 # test that the parameter is preserved - assert param_dict == item_dict + assert param_dict == sample_item_dict # assert that the parameter is preserved regardless of preserve_dict Item.from_dict(param_dict, preserve_dict=False) - assert param_dict == item_dict + assert param_dict == sample_item_dict -def test_from_dict_set_root() -> None: - item_dict = get_example_item_dict() +def test_from_dict_set_root(sample_item_dict: dict[str, Any]) -> None: catalog = pystac.Catalog(id="test", description="test desc") - item = Item.from_dict(item_dict, root=catalog) + item = Item.from_dict(sample_item_dict, root=catalog) assert item.get_root() is catalog @@ -85,13 +76,11 @@ def test_set_self_href_none_ignores_relative_asset_hrefs() -> None: assert not is_absolute_href(asset.href) -def test_asset_absolute_href() -> None: +def test_asset_absolute_href(sample_item: Item) -> None: item_path = TestCases.get_path("data-files/item/sample-item.json") - item_dict = get_example_item_dict() - item = Item.from_dict(item_dict) - item.set_self_href(item_path) + sample_item.set_self_href(item_path) rel_asset = Asset("./data.geojson") - rel_asset.set_owner(item) + rel_asset.set_owner(sample_item) expected_href = make_posix_style( os.path.abspath(os.path.join(os.path.dirname(item_path), "./data.geojson")) ) @@ -99,9 +88,8 @@ def test_asset_absolute_href() -> None: assert expected_href == actual_href -def test_asset_absolute_href_no_item_self() -> None: - item_dict = get_example_item_dict() - item = Item.from_dict(item_dict) +def test_asset_absolute_href_no_item_self(sample_item_dict: dict[str, Any]) -> None: + item = Item.from_dict(sample_item_dict) assert item.get_self_href() is None rel_asset = Asset("./data.geojson") @@ -160,10 +148,8 @@ def test_clearing_collection() -> None: assert item.get_collection() is collection -def test_datetime_ISO8601_format() -> None: - item_dict = get_example_item_dict() - item = Item.from_dict(item_dict) - formatted_time = item.to_dict()["properties"]["datetime"] +def test_datetime_ISO8601_format(sample_item: Item) -> None: + formatted_time = sample_item.to_dict()["properties"]["datetime"] assert "2016-05-03T13:22:30.040000Z" == formatted_time