From 4f2b84f28ff97365cffde161147d6a72fe2e8116 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 09:31:13 +0100 Subject: [PATCH 01/18] Simplifying patch. --- .../stac_fastapi/core/base_database_logic.py | 2 + .../elasticsearch/database_logic.py | 8 +- .../stac_fastapi/opensearch/database_logic.py | 8 +- .../sfeos_helpers/database/utils.py | 64 ++++++--- .../sfeos_helpers/models/patch.py | 121 +++++++----------- 5 files changed, 102 insertions(+), 101 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/base_database_logic.py b/stac_fastapi/core/stac_fastapi/core/base_database_logic.py index e3c4d64e..494b0dd7 100644 --- a/stac_fastapi/core/stac_fastapi/core/base_database_logic.py +++ b/stac_fastapi/core/stac_fastapi/core/base_database_logic.py @@ -48,6 +48,7 @@ async def json_patch_item( item_id: str, operations: List, base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Dict: """Patch a item in the database follows RF6902.""" @@ -94,6 +95,7 @@ async def json_patch_collection( collection_id: str, operations: List, base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Dict: """Patch a collection in the database follows RF6902.""" diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py index 5f100980..3e07d251 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py @@ -886,6 +886,7 @@ async def merge_patch_item( item_id=item_id, operations=operations, base_url=base_url, + create_nest=True, refresh=refresh, ) @@ -895,6 +896,7 @@ async def json_patch_item( item_id: str, operations: List[PatchOperation], base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Item: """Database logic for json patching an item following RF6902. @@ -929,7 +931,7 @@ async def json_patch_item( else: script_operations.append(operation) - script = operations_to_script(script_operations) + script = operations_to_script(script_operations, create_nest=create_nest) try: search_response = await self.client.search( @@ -1265,6 +1267,7 @@ async def merge_patch_collection( collection_id=collection_id, operations=operations, base_url=base_url, + create_nest=True, refresh=refresh, ) @@ -1273,6 +1276,7 @@ async def json_patch_collection( collection_id: str, operations: List[PatchOperation], base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Collection: """Database logic for json patching a collection following RF6902. @@ -1300,7 +1304,7 @@ async def json_patch_collection( else: script_operations.append(operation) - script = operations_to_script(script_operations) + script = operations_to_script(script_operations, create_nest=create_nest) try: await self.client.update( diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py index 4ff44ca0..0e6b7b6d 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py @@ -869,6 +869,7 @@ async def merge_patch_item( item_id=item_id, operations=operations, base_url=base_url, + create_nest=True, refresh=refresh, ) @@ -878,6 +879,7 @@ async def json_patch_item( item_id: str, operations: List[PatchOperation], base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Item: """Database logic for json patching an item following RF6902. @@ -912,7 +914,7 @@ async def json_patch_item( else: script_operations.append(operation) - script = operations_to_script(script_operations) + script = operations_to_script(script_operations, create_nest=create_nest) try: search_response = await self.client.search( @@ -1220,6 +1222,7 @@ async def merge_patch_collection( collection_id=collection_id, operations=operations, base_url=base_url, + create_nest=True, refresh=refresh, ) @@ -1228,6 +1231,7 @@ async def json_patch_collection( collection_id: str, operations: List[PatchOperation], base_url: str, + create_nest: bool = False, refresh: bool = True, ) -> Collection: """Database logic for json patching a collection following RF6902. @@ -1255,7 +1259,7 @@ async def json_patch_collection( else: script_operations.append(operation) - script = operations_to_script(script_operations) + script = operations_to_script(script_operations, create_nest=create_nest) try: await self.client.update( diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index 169298a8..c00fd422 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -90,6 +90,7 @@ def check_commands( op: str, path: ElasticPath, from_path: bool = False, + create_nest: bool = False, ) -> None: """Add Elasticsearch checks to operation. @@ -101,24 +102,43 @@ def check_commands( """ if path.nest: - commands.add( - f"if (!ctx._source.containsKey('{path.nest}'))" - f"{{Debug.explain('{path.nest} does not exist');}}" - ) + part_nest = "" + + for index, path_part in enumerate(path.parts): + + # Create nested dictionaries if not present for merge operations + if create_nest and not from_path: + value = "[:]" + for sub_part in reversed(path.parts[index + 1 :]): + value = f"['{sub_part}': {value}]" + + commands.add( + f"if (!ctx._source{part_nest}.containsKey('{path_part}'))" + f"{{ctx._source{part_nest}['{path_part}'] = {value};}}" + f"{'' if index == len(path.parts) - 1 else' else '}" + ) - if path.index or op in ["remove", "replace", "test"] or from_path: + else: + commands.add( + f"if (!ctx._source{part_nest}.containsKey('{path_part}'))" + f"{{Debug.explain('{path_part} in {path.nest} does not exist');}}" + ) + + part_nest += f"['{path_part}']" + + if path.index or from_path or op in ["remove", "replace", "test"]: commands.add( f"if (!ctx._source{path.es_nest}.containsKey('{path.key}'))" - f"{{Debug.explain('{path.key} does not exist in {path.nest}');}}" + f"{{Debug.explain('{path.key} does not exist in {path.nest}');}}" ) if from_path and path.index is not None: commands.add( f"if ((ctx._source{path.es_location} instanceof ArrayList" - f" && ctx._source{path.es_location}.size() < {path.index})" + f" && ctx._source{path.es_location}.size() < {abs(path.index)})" f" || (!(ctx._source{path.es_location} instanceof ArrayList)" f" && !ctx._source{path.es_location}.containsKey('{path.index}')))" - f"{{Debug.explain('{path.path} does not exist');}}" + f"{{Debug.explain('{path.es_location} does not exist');}}" ) @@ -132,7 +152,7 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None: """ if path.index is not None: commands.add( - f"def {path.variable_name} = ctx._source{path.es_location}.remove({path.index});" + f"def {path.variable_name} = ctx._source{path.es_location}.remove({path.es_index});" ) else: @@ -160,7 +180,7 @@ def add_commands( value = ( from_path.variable_name if operation.op == "move" - else f"ctx._source.{from_path.es_path}" + else f"ctx._source{from_path.es_location}" ) else: value = f"params.{path.param_key}" @@ -169,12 +189,12 @@ def add_commands( if path.index is not None: commands.add( f"if (ctx._source{path.es_location} instanceof ArrayList)" - f"{{ctx._source{path.es_location}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.index}, {value})}}" - f"else{{ctx._source.{path.es_path} = {value}}}" + f"{{ctx._source{path.es_location}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.es_index}, {value})}}" + f"else{{ctx._source{path.es_location}['{path.index}'] = {value}}}" ) else: - commands.add(f"ctx._source.{path.es_path} = {value};") + commands.add(f"ctx._source{path.es_location} = {value};") def test_commands( @@ -191,13 +211,13 @@ def test_commands( params[path.param_key] = operation.value commands.add( - f"if (ctx._source.{path.es_path} != {value})" - f"{{Debug.explain('Test failed `{path.path}` | " - f"{operation.json_value} != ' + ctx._source.{path.es_path});}}" + f"if (ctx._source{path.es_location} != {value})" + f"{{Debug.explain('Test failed `{path.location}` | " + f"{operation.json_value} != ' + ctx._source{path.es_location});}}" ) -def operations_to_script(operations: List) -> Dict: +def operations_to_script(operations: List, create_nest: bool = False) -> Dict: """Convert list of operation to painless script. Args: @@ -215,10 +235,16 @@ def operations_to_script(operations: List) -> Dict: ElasticPath(path=operation.from_) if hasattr(operation, "from_") else None ) - check_commands(commands=commands, op=operation.op, path=path) + check_commands( + commands=commands, op=operation.op, path=path, create_nest=create_nest + ) if from_path is not None: check_commands( - commands=commands, op=operation.op, path=from_path, from_path=True + commands=commands, + op=operation.op, + path=from_path, + from_path=True, + create_nest=create_nest, ) if operation.op in ["remove", "move"]: diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index ce49bdb2..b170cf9d 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -1,9 +1,9 @@ """patch helpers.""" import re -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional -from pydantic import BaseModel, computed_field, model_validator +from pydantic import BaseModel, model_validator regex = re.compile(r"([^.' ]*:[^.'[ ]*)\.?") replacements = str.maketrans({"/": "", ".": "", ":": "", "[": "", "]": ""}) @@ -71,16 +71,25 @@ class ElasticPath(BaseModel): """ - path: str - nest: Optional[str] = None - partition: Optional[str] = None + parts: list[str] = [] + key: Optional[str] = None + nest: Optional[str] = None + location: Optional[str] = None + index: Optional[int] = None - es_path: Optional[str] = None - es_nest: Optional[str] = None es_key: Optional[str] = None + es_nest: Optional[str] = None + es_location: Optional[str] = None + es_index: Optional[str] = None + + variable_name: Optional[str] = None + param_key: Optional[str] = None + + class Config: + """Class config.""" - index_: Optional[int] = None + frozen = True @model_validator(mode="before") @classmethod @@ -90,77 +99,33 @@ def validate_model(cls, data: Any): Args: data (Any): input data """ - data["path"] = data["path"].lstrip("/").replace("/", ".") - data["nest"], data["partition"], data["key"] = data["path"].rpartition(".") + data["parts"] = data["path"].lstrip("/").split("/") + data["key"] = data["parts"].pop(-1) if data["key"].lstrip("-").isdigit() or data["key"] == "-": - data["index_"] = -1 if data["key"] == "-" else int(data["key"]) - data["path"] = f"{data['nest']}[{data['index_']}]" - data["nest"], data["partition"], data["key"] = data["nest"].rpartition(".") - - data["es_path"] = to_es(data["path"]) - data["es_nest"] = f".{to_es(data['nest'])}" if data["nest"] else "" - data["es_key"] = to_es(data["key"]) + data["index"] = -1 if data["key"] == "-" else int(data["key"]) + data["key"] = data["parts"].pop(-1) + + data["nest"] = ".".join(data["parts"]) + data["location"] = data["nest"] + "." + data["key"] + + data["es_key"] = f"['{data['key']}']" + data["es_nest"] = "".join([f"['{part}']" for part in data["parts"]]) + data["es_location"] = data["es_nest"] + data["es_key"] + data[ + "variable_name" + ] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" + data["param_key"] = data["location"].translate(replacements) + + if "index" in data: + data["es_index"] = ( + f"ctx._source{data['es_location']}.size() - {-data['index']}" + if data["index"] < 0 + else data["index"] + ) + + data[ + "variable_name" + ] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" return data - - @computed_field # type: ignore[misc] - @property - def index(self) -> Union[int, str, None]: - """Compute location of path. - - Returns: - str: path index - """ - if self.index_ and self.index_ < 0: - - return f"ctx._source.{self.location}.size() - {-self.index_}" - - return self.index_ - - @computed_field # type: ignore[misc] - @property - def location(self) -> str: - """Compute location of path. - - Returns: - str: path location - """ - return self.nest + self.partition + self.key - - @computed_field # type: ignore[misc] - @property - def es_location(self) -> str: - """Compute location of path. - - Returns: - str: path location - """ - if self.es_key and ":" in self.es_key: - return self.es_nest + self.es_key - return self.es_nest + self.partition + self.es_key - - @computed_field # type: ignore[misc] - @property - def variable_name(self) -> str: - """Variable name for scripting. - - Returns: - str: variable name - """ - if self.index is not None: - return f"{self.location.replace('.','_').replace(':','_')}_{self.index}" - - return ( - f"{self.nest.replace('.','_').replace(':','_')}_{self.key.replace(':','_')}" - ) - - @computed_field # type: ignore[misc] - @property - def param_key(self) -> str: - """Param key for scripting. - - Returns: - str: param key - """ - return self.path.translate(replacements) From 23fef9b820467551dd14ad3d46e70615ffc9757e Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 09:44:04 +0100 Subject: [PATCH 02/18] int to str for es_index. --- .../sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index b170cf9d..01c13813 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -121,7 +121,7 @@ def validate_model(cls, data: Any): data["es_index"] = ( f"ctx._source{data['es_location']}.size() - {-data['index']}" if data["index"] < 0 - else data["index"] + else str(data["index"]) ) data[ From 46c4ea226d9543f6fe6c068af18f1a59d74a1c20 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 09:50:27 +0100 Subject: [PATCH 03/18] use '/' seperator in merge to operations. --- .../sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index c00fd422..ecab2f65 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -76,7 +76,7 @@ def merge_to_operations(data: Dict) -> List: nested_operations = merge_to_operations(value) for nested_operation in nested_operations: - nested_operation.path = f"{key}.{nested_operation.path}" + nested_operation.path = f"{key}/{nested_operation.path}" operations.append(nested_operation) else: From 77b849d85893f5fe5a5e53ea8e46b4b46d18a1f1 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 12:57:13 +0100 Subject: [PATCH 04/18] Update test command output. --- .../stac_fastapi/sfeos_helpers/database/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index ecab2f65..e3bdea58 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -212,8 +212,8 @@ def test_commands( commands.add( f"if (ctx._source{path.es_location} != {value})" - f"{{Debug.explain('Test failed `{path.location}` | " - f"{operation.json_value} != ' + ctx._source{path.es_location});}}" + f"{{Debug.explain('Test failed `{path.location}`" + f" != ' + ctx._source{path.es_location});}}" ) From 93a34b975ab847e152d38340b5f3e7ad7802a6c7 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 14:21:58 +0100 Subject: [PATCH 05/18] Update es_location for indexed operation. --- .../stac_fastapi/sfeos_helpers/models/patch.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index 01c13813..ed10479f 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -112,9 +112,7 @@ def validate_model(cls, data: Any): data["es_key"] = f"['{data['key']}']" data["es_nest"] = "".join([f"['{part}']" for part in data["parts"]]) data["es_location"] = data["es_nest"] + data["es_key"] - data[ - "variable_name" - ] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" + data["variable_name"] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" data["param_key"] = data["location"].translate(replacements) if "index" in data: @@ -124,8 +122,8 @@ def validate_model(cls, data: Any): else str(data["index"]) ) - data[ - "variable_name" - ] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" + data["es_location"] = data["es_location"] + f"[{data['es_index']}]" + + data["variable_name"] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" return data From 8af3c8660451cb11002ff722f8db1e0d63e90bc0 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 28 Aug 2025 14:22:29 +0100 Subject: [PATCH 06/18] Pre-commit. --- .../stac_fastapi/sfeos_helpers/models/patch.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index ed10479f..a5ab3e82 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -112,7 +112,9 @@ def validate_model(cls, data: Any): data["es_key"] = f"['{data['key']}']" data["es_nest"] = "".join([f"['{part}']" for part in data["parts"]]) data["es_location"] = data["es_nest"] + data["es_key"] - data["variable_name"] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" + data[ + "variable_name" + ] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" data["param_key"] = data["location"].translate(replacements) if "index" in data: @@ -124,6 +126,8 @@ def validate_model(cls, data: Any): data["es_location"] = data["es_location"] + f"[{data['es_index']}]" - data["variable_name"] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" + data[ + "variable_name" + ] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" return data From ef840f488991441e0450a1a5e74d50f44f87e15d Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 08:49:20 +0100 Subject: [PATCH 07/18] Remove index from patch class. --- .../sfeos_helpers/database/utils.py | 69 +++++++++++-------- .../sfeos_helpers/models/patch.py | 46 +++++-------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index e3bdea58..3adc4cf5 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -103,7 +103,6 @@ def check_commands( """ if path.nest: part_nest = "" - for index, path_part in enumerate(path.parts): # Create nested dictionaries if not present for merge operations @@ -126,20 +125,21 @@ def check_commands( part_nest += f"['{path_part}']" - if path.index or from_path or op in ["remove", "replace", "test"]: - commands.add( - f"if (!ctx._source{path.es_nest}.containsKey('{path.key}'))" - f"{{Debug.explain('{path.key} does not exist in {path.nest}');}}" - ) + if from_path or op in ["remove", "replace", "test"]: - if from_path and path.index is not None: - commands.add( - f"if ((ctx._source{path.es_location} instanceof ArrayList" - f" && ctx._source{path.es_location}.size() < {abs(path.index)})" - f" || (!(ctx._source{path.es_location} instanceof ArrayList)" - f" && !ctx._source{path.es_location}.containsKey('{path.index}')))" - f"{{Debug.explain('{path.es_location} does not exist');}}" - ) + if isinstance(path.key, int): + commands.add( + f"if ((ctx._source{path.es_nest} instanceof ArrayList" + f" && ctx._source{path.es_nest}.size() < {abs(path.key)})" + f" || (!(ctx._source{path.es_nest} instanceof ArrayList)" + f" && !ctx._source{path.es_nest}.containsKey('{path.key}')))" + f"{{Debug.explain('{path.key} does not exist in {path.nest}');}}" + ) + else: + commands.add( + f"if (!ctx._source{path.es_nest}.containsKey('{path.key}'))" + f"{{Debug.explain('{path.key} does not exist in {path.nest}');}}" + ) def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None: @@ -150,15 +150,15 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None: path (ElasticPath): Path to value to be removed """ - if path.index is not None: + if isinstance(path.key, int): commands.add( - f"def {path.variable_name} = ctx._source{path.es_location}.remove({path.es_index});" + f"if ((ctx._source{path.es_nest} instanceof ArrayList" + f"{{def {path.variable_name} = ctx._source{path.es_nest}.remove({path.es_key});}} else " ) - else: - commands.add( - f"def {path.variable_name} = ctx._source{path.es_nest}.remove('{path.key}');" - ) + commands.add( + f"def {path.variable_name} = ctx._source{path.es_nest}.remove('{path.key}');" + ) def add_commands( @@ -180,21 +180,21 @@ def add_commands( value = ( from_path.variable_name if operation.op == "move" - else f"ctx._source{from_path.es_location}" + else f"ctx._source{from_path.es_path}" ) + else: value = f"params.{path.param_key}" params[path.param_key] = operation.value - if path.index is not None: + if isinstance(path.key, int): commands.add( - f"if (ctx._source{path.es_location} instanceof ArrayList)" - f"{{ctx._source{path.es_location}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.es_index}, {value})}}" - f"else{{ctx._source{path.es_location}['{path.index}'] = {value}}}" + f"if (ctx._source{path.es_nest} instanceof ArrayList)" + f"{{ctx._source{path.es_nest}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.es_key}, {value})}}" + f" else " ) - else: - commands.add(f"ctx._source{path.es_location} = {value};") + commands.add(f"ctx._source{path.es_location} = {value};") def test_commands( @@ -210,10 +210,19 @@ def test_commands( value = f"params.{path.param_key}" params[path.param_key] = operation.value + if isinstance(path.key, int): + commands.add( + f"if (ctx._source{path.es_nest} instanceof ArrayList)" + f"{{if (ctx._source{path.es_nest}[{path.es_key}] != {value})" + f"{{Debug.explain('Test failed `{path.es_path}`" + f" != ' + ctx._source{path.es_path});}}" + f"}} else " + ) + commands.add( - f"if (ctx._source{path.es_location} != {value})" - f"{{Debug.explain('Test failed `{path.location}`" - f" != ' + ctx._source{path.es_location});}}" + f"if (ctx._source{path.es_path} != {value})" + f"{{Debug.explain('Test failed `{path.es_path}`" + f" != ' + ctx._source{path.es_path});}}" ) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index a5ab3e82..91bf243c 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -73,15 +73,13 @@ class ElasticPath(BaseModel): parts: list[str] = [] + path: Optional[str] = None key: Optional[str] = None nest: Optional[str] = None - location: Optional[str] = None - index: Optional[int] = None + es_path: Optional[str] = None es_key: Optional[str] = None es_nest: Optional[str] = None - es_location: Optional[str] = None - es_index: Optional[str] = None variable_name: Optional[str] = None param_key: Optional[str] = None @@ -100,34 +98,28 @@ def validate_model(cls, data: Any): data (Any): input data """ data["parts"] = data["path"].lstrip("/").split("/") + data["key"] = data["parts"].pop(-1) + data["nest"] = "/".join(data["parts"]) + data["path"] = data["nest"] + "/" + data["key"] - if data["key"].lstrip("-").isdigit() or data["key"] == "-": - data["index"] = -1 if data["key"] == "-" else int(data["key"]) - data["key"] = data["parts"].pop(-1) + data["es_key"] = data["key"] + data["es_nest"] = "".join([f"['{part}']" for part in data["parts"]]) + data["es_path"] = data["es_nest"] + f"['{data['es_key']}']" - data["nest"] = ".".join(data["parts"]) - data["location"] = data["nest"] + "." + data["key"] + if data["key"].lstrip("-").isdigit() or data["key"] == "-": + data["key"] = -1 if data["key"] == "-" else int(data["key"]) + data["es_key"] = ( + f"ctx._source{data['es_nest']}.size() - {-data['key']}" + if data["key"] < 0 + else str(data["key"]) + ) + # data["es_key"] = f"[{data['key']}]" + data["es_path"] = data["es_nest"] + f"[{data['es_key']}]" - data["es_key"] = f"['{data['key']}']" - data["es_nest"] = "".join([f"['{part}']" for part in data["parts"]]) - data["es_location"] = data["es_nest"] + data["es_key"] data[ "variable_name" - ] = f"{data['nest'].replace('.','_').replace(':','_')}_{data['key'].replace(':','_')}" - data["param_key"] = data["location"].translate(replacements) - - if "index" in data: - data["es_index"] = ( - f"ctx._source{data['es_location']}.size() - {-data['index']}" - if data["index"] < 0 - else str(data["index"]) - ) - - data["es_location"] = data["es_location"] + f"[{data['es_index']}]" - - data[ - "variable_name" - ] = f"{data['location'].replace('.','_').replace(':','_')}_{data['index']}" + ] = f"{data['nest'].replace('.','_').replace(':','_')}_{str(data['key']).replace(':','_')}" + data["param_key"] = data["path"].translate(replacements) return data From 2b705fff00ca24d1f129c85ebcbac89d8ff49310 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 08:55:03 +0100 Subject: [PATCH 08/18] es_location to es_path. --- .../sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index 3adc4cf5..9eb2bc4c 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -194,7 +194,7 @@ def add_commands( f" else " ) - commands.add(f"ctx._source{path.es_location} = {value};") + commands.add(f"ctx._source{path.es_path} = {value};") def test_commands( From b426609650cd116cd92d9791aa60706409c62787 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 09:10:31 +0100 Subject: [PATCH 09/18] Fix test command message. --- .../stac_fastapi/sfeos_helpers/database/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index 9eb2bc4c..5473b97c 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -120,7 +120,7 @@ def check_commands( else: commands.add( f"if (!ctx._source{part_nest}.containsKey('{path_part}'))" - f"{{Debug.explain('{path_part} in {path.nest} does not exist');}}" + f"{{Debug.explain('{path_part} in {path.path} does not exist');}}" ) part_nest += f"['{path_part}']" @@ -221,7 +221,7 @@ def test_commands( commands.add( f"if (ctx._source{path.es_path} != {value})" - f"{{Debug.explain('Test failed `{path.es_path}`" + f"{{Debug.explain('Test failed `{path.path}`" f" != ' + ctx._source{path.es_path});}}" ) From 96e1ecb1944adecf73035322d69f66fa841afab6 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 09:18:18 +0100 Subject: [PATCH 10/18] Update path key type. --- .../stac_fastapi/sfeos_helpers/database/utils.py | 2 +- .../sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index 5473b97c..02edb9b6 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -214,7 +214,7 @@ def test_commands( commands.add( f"if (ctx._source{path.es_nest} instanceof ArrayList)" f"{{if (ctx._source{path.es_nest}[{path.es_key}] != {value})" - f"{{Debug.explain('Test failed `{path.es_path}`" + f"{{Debug.explain('Test failed `{path.path}`" f" != ' + ctx._source{path.es_path});}}" f"}} else " ) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index 91bf243c..a856cb90 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -1,7 +1,7 @@ """patch helpers.""" import re -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Union from pydantic import BaseModel, model_validator @@ -74,7 +74,7 @@ class ElasticPath(BaseModel): parts: list[str] = [] path: Optional[str] = None - key: Optional[str] = None + key: Optional[Union[str, int]] = None nest: Optional[str] = None es_path: Optional[str] = None @@ -114,7 +114,6 @@ def validate_model(cls, data: Any): if data["key"] < 0 else str(data["key"]) ) - # data["es_key"] = f"[{data['key']}]" data["es_path"] = data["es_nest"] + f"[{data['es_key']}]" data[ From 3b630f27f6c81db5009311adc31a0b9c1ef4c178 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 09:51:29 +0100 Subject: [PATCH 11/18] Update varaible name replace. --- .../sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py index a856cb90..f1430270 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/models/patch.py @@ -118,7 +118,7 @@ def validate_model(cls, data: Any): data[ "variable_name" - ] = f"{data['nest'].replace('.','_').replace(':','_')}_{str(data['key']).replace(':','_')}" + ] = f"{data['nest'].replace('/','_').replace(':','_')}_{str(data['key']).replace(':','_')}" data["param_key"] = data["path"].translate(replacements) return data From 7fd1b2a1b98b8b73c76cc41bbb3cd7d6f2b87cb0 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 10:05:44 +0100 Subject: [PATCH 12/18] Fix remove if statement. --- .../sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index 02edb9b6..c0d85ccb 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -152,7 +152,7 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None: """ if isinstance(path.key, int): commands.add( - f"if ((ctx._source{path.es_nest} instanceof ArrayList" + f"if (ctx._source{path.es_nest} instanceof ArrayList)" f"{{def {path.variable_name} = ctx._source{path.es_nest}.remove({path.es_key});}} else " ) From b9a37175c74993cef96c77c9b3db79a44820d001 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 10:28:26 +0100 Subject: [PATCH 13/18] Move variable definition outside if. --- .../stac_fastapi/sfeos_helpers/database/utils.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py index c0d85ccb..2a9dc20e 100644 --- a/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py +++ b/stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database/utils.py @@ -150,14 +150,15 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None: path (ElasticPath): Path to value to be removed """ + commands.add(f"def {path.variable_name};") if isinstance(path.key, int): commands.add( f"if (ctx._source{path.es_nest} instanceof ArrayList)" - f"{{def {path.variable_name} = ctx._source{path.es_nest}.remove({path.es_key});}} else " + f"{{{path.variable_name} = ctx._source{path.es_nest}.remove({path.es_key});}} else " ) commands.add( - f"def {path.variable_name} = ctx._source{path.es_nest}.remove('{path.key}');" + f"{path.variable_name} = ctx._source{path.es_nest}.remove('{path.key}');" ) @@ -190,11 +191,12 @@ def add_commands( if isinstance(path.key, int): commands.add( f"if (ctx._source{path.es_nest} instanceof ArrayList)" - f"{{ctx._source{path.es_nest}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.es_key}, {value})}}" - f" else " + f"{{ctx._source{path.es_nest}.{'add' if operation.op in ['add', 'move'] else 'set'}({path.es_key}, {value});}}" + f" else ctx._source{path.es_nest}['{path.es_key}'] = {value};" ) - commands.add(f"ctx._source{path.es_path} = {value};") + else: + commands.add(f"ctx._source{path.es_path} = {value};") def test_commands( From cc5ed6154bad7537964dfed6d767f5112758cc2e Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 29 Aug 2025 11:23:39 +0100 Subject: [PATCH 14/18] Update CHANGELOG. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a7bc3a2..7d5fc867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Changed + +- Simplified Patch class and updated patch script creation including adding nest creation for merge patch [#420](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/420) ## [v6.2.0] - 2025-08-27 From 3113a074c755da2ec6e0048aef45d41ae70a4de8 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Tue, 2 Sep 2025 08:49:26 +0100 Subject: [PATCH 15/18] Adding nest creation tests. --- stac_fastapi/tests/clients/test_es_os.py | 349 ++++++++--------------- 1 file changed, 115 insertions(+), 234 deletions(-) diff --git a/stac_fastapi/tests/clients/test_es_os.py b/stac_fastapi/tests/clients/test_es_os.py index df6dae36..0238dce9 100644 --- a/stac_fastapi/tests/clients/test_es_os.py +++ b/stac_fastapi/tests/clients/test_es_os.py @@ -48,9 +48,7 @@ async def test_update_collection( collection_data = load_test_data("test_collection.json") item_data = load_test_data("test_item.json") - await txn_client.create_collection( - api.Collection(**collection_data), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -59,9 +57,7 @@ async def test_update_collection( ) collection_data["keywords"].append("new keyword") - await txn_client.update_collection( - collection_data["id"], api.Collection(**collection_data), request=MockRequest - ) + await txn_client.update_collection(collection_data["id"], api.Collection(**collection_data), request=MockRequest) coll = await core_client.get_collection(collection_data["id"], request=MockRequest) assert "new keyword" in coll["keywords"] @@ -88,9 +84,7 @@ async def test_update_collection_id( item_data = load_test_data("test_item.json") new_collection_id = "new-test-collection" - await txn_client.create_collection( - api.Collection(**collection_data), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -202,14 +196,10 @@ async def test_get_collection_items(app_client, ctx, core_client, txn_client): @pytest.mark.asyncio async def test_create_item(ctx, core_client, txn_client): - resp = await core_client.get_item( - ctx.item["id"], ctx.item["collection"], request=MockRequest - ) - assert Item(**ctx.item).model_dump( - exclude={"links": ..., "properties": {"created", "updated"}} - ) == Item(**resp).model_dump( - exclude={"links": ..., "properties": {"created", "updated"}} - ) + resp = await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) + assert Item(**ctx.item).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) == Item( + **resp + ).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) @pytest.mark.asyncio @@ -236,9 +226,7 @@ async def test_update_item(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" @@ -254,9 +242,7 @@ async def test_merge_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" @@ -273,25 +259,37 @@ async def test_merge_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] +@pytest.mark.asyncio +async def test_merge_patch_create_nest(ctx, core_client, txn_client): + item = ctx.item + collection_id = item["collection"] + item_id = item["id"] + await txn_client.patch_item( + collection_id=collection_id, + item_id=item_id, + patch={"properties": {"new": {"nest": "foo"}}}, + request=MockRequest(headers={"content-type": "application/merge-patch+json"}), + ) + + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + assert "new" in updated_item["properties"] + assert "nest" in updated_item["properties"]["new"] + assert updated_item["properties"]["new"]["nest"] == "foo" + + @pytest.mark.asyncio async def test_json_patch_item_add(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "add", "path": "/properties/foo", "value": "bar"} - ), - PatchAddReplaceTest.model_validate( - {"op": "add", "path": "/properties/ext:hello", "value": "world"} - ), + PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/foo", "value": "bar"}), + PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/ext:hello", "value": "world"}), PatchAddReplaceTest.model_validate( { "op": "add", @@ -314,16 +312,11 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" - assert ( - len(updated_item["properties"]["eo:bands"]) - == len(ctx.item["properties"]["eo:bands"]) + 1 - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + 1 assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -339,12 +332,8 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/gsd", "value": 100} - ), - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/proj:epsg", "value": 12345} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/gsd", "value": 100}), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/proj:epsg", "value": 12345}), PatchAddReplaceTest.model_validate( { "op": "replace", @@ -367,15 +356,11 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["gsd"] == 100 assert updated_item["properties"]["proj:epsg"] == 12345 - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -391,12 +376,8 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/gsd", "value": 15} - ), - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/proj:epsg", "value": 32756} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/gsd", "value": 15}), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/proj:epsg", "value": 32756}), PatchAddReplaceTest.model_validate( { "op": "test", @@ -413,15 +394,11 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["gsd"] == 15 assert updated_item["properties"]["proj:epsg"] == 32756 - assert ( - updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] - ) + assert updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -430,12 +407,8 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/foo", "from": "/properties/gsd"} - ), - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/foo", "from": "/properties/gsd"}), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"}), PatchMoveCopy.model_validate( { "op": "move", @@ -452,25 +425,15 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == 15 assert "gsd" not in updated_item["properties"] assert updated_item["properties"]["bar"] == 32756 assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) - assert ( - updated_item["properties"]["eo:bands"][0] - == ctx.item["properties"]["eo:bands"][1] - ) - assert ( - updated_item["properties"]["eo:bands"][1] - != ctx.item["properties"]["eo:bands"][1] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] + assert updated_item["properties"]["eo:bands"][1] != ctx.item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -479,12 +442,8 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"} - ), - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"}), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"}), PatchMoveCopy.model_validate( { "op": "copy", @@ -501,19 +460,12 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == updated_item["properties"]["gsd"] assert updated_item["properties"]["bar"] == updated_item["properties"]["proj:epsg"] - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) - assert ( - updated_item["properties"]["eo:bands"][0] - == ctx.item["properties"]["eo:bands"][1] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -534,32 +486,43 @@ async def test_json_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] - assert ( - len(updated_item["properties"]["eo:bands"]) - == len(ctx.item["properties"]["eo:bands"]) - 1 - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - 1 assert ( updated_item["properties"]["eo:bands"] - == ctx.item["properties"]["eo:bands"][:1] - + ctx.item["properties"]["eo:bands"][2:] + == ctx.item["properties"]["eo:bands"][:1] + ctx.item["properties"]["eo:bands"][2:] ) +@pytest.mark.asyncio +async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): + item = ctx.item + collection_id = item["collection"] + item_id = item["id"] + operations = [ + PatchRemove.model_validate({"op": "add", "path": "/properties/bad/nest", "value": "foo"}), + ] + + with pytest.raises(HTTPException): + + await txn_client.patch_item( + collection_id=collection_id, + item_id=item_id, + patch=operations, + request=MockRequest(headers={"content-type": "application/json-patch+json"}), + ) + + @pytest.mark.asyncio async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/platform", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/platform", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -568,23 +531,17 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_replace_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/foo", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/foo", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -593,16 +550,12 @@ async def test_json_patch_item_replace_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_remove_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] @@ -616,23 +569,17 @@ async def test_json_patch_item_remove_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_move_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/bar", "from": "/properties/foo"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/foo"}), ] with pytest.raises(HTTPException): @@ -641,23 +588,17 @@ async def test_json_patch_item_move_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_copy_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/bar", "from": "/properties/foo"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/foo"}), ] with pytest.raises(HTTPException): @@ -666,9 +607,7 @@ async def test_json_patch_item_copy_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @@ -694,9 +633,7 @@ async def test_update_geometry(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["geometry"]["coordinates"] == new_coordinates @@ -705,18 +642,14 @@ async def test_delete_item(ctx, core_client, txn_client): await txn_client.delete_item(ctx.item["id"], ctx.item["collection"]) with pytest.raises(NotFoundError): - await core_client.get_item( - ctx.item["id"], ctx.item["collection"], request=MockRequest - ) + await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) @pytest.mark.asyncio async def test_landing_page_no_collection_title(ctx, core_client, txn_client, app): ctx.collection["id"] = "new_id" del ctx.collection["title"] - await txn_client.create_collection( - api.Collection(**ctx.collection), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**ctx.collection), request=MockRequest) landing_page = await core_client.landing_page(request=MockRequest(app=app)) for link in landing_page["links"]: @@ -735,9 +668,7 @@ async def test_merge_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["hello"] == "world" @@ -752,9 +683,7 @@ async def test_merge_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert "gsd" not in updated_collection["summaries"] @@ -777,9 +706,7 @@ async def test_json_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["gsd"] == [30, 100] @@ -790,9 +717,7 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/summaries/gsd", "value": [100]} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/gsd", "value": [100]}), ] await txn_client.patch_collection( @@ -801,9 +726,7 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["gsd"] == [100] @@ -813,9 +736,7 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/summaries/gsd", "value": [30]} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/gsd", "value": [30]}), ] await txn_client.patch_collection( @@ -824,9 +745,7 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["gsd"] == [30] @@ -836,9 +755,7 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"}), ] await txn_client.patch_collection( @@ -847,9 +764,7 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["bar"] == [30] assert "gsd" not in updated_collection["summaries"] @@ -860,9 +775,7 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"}), ] await txn_client.patch_collection( @@ -871,13 +784,9 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) - assert ( - updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] - ) + assert updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] @pytest.mark.asyncio @@ -894,9 +803,7 @@ async def test_json_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert "gsd" not in updated_collection["summaries"] @@ -906,9 +813,7 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/summaries/platform", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/platform", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -916,22 +821,16 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_replace_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/summaries/foo", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/foo", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -939,16 +838,12 @@ async def test_json_patch_collection_replace_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_remove_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ @@ -960,22 +855,16 @@ async def test_json_patch_collection_remove_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_move_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_move_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"}), ] with pytest.raises(HTTPException): @@ -983,22 +872,16 @@ async def test_json_patch_collection_move_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_copy_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"}), ] with pytest.raises(HTTPException): @@ -1006,7 +889,5 @@ async def test_json_patch_collection_copy_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) From cdbb4e6c75bf382116fa95e5e46fc251398aa67e Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Tue, 2 Sep 2025 08:49:53 +0100 Subject: [PATCH 16/18] pre-commit. --- stac_fastapi/tests/clients/test_es_os.py | 324 +++++++++++++++++------ 1 file changed, 243 insertions(+), 81 deletions(-) diff --git a/stac_fastapi/tests/clients/test_es_os.py b/stac_fastapi/tests/clients/test_es_os.py index 0238dce9..197a725e 100644 --- a/stac_fastapi/tests/clients/test_es_os.py +++ b/stac_fastapi/tests/clients/test_es_os.py @@ -48,7 +48,9 @@ async def test_update_collection( collection_data = load_test_data("test_collection.json") item_data = load_test_data("test_item.json") - await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) + await txn_client.create_collection( + api.Collection(**collection_data), request=MockRequest + ) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -57,7 +59,9 @@ async def test_update_collection( ) collection_data["keywords"].append("new keyword") - await txn_client.update_collection(collection_data["id"], api.Collection(**collection_data), request=MockRequest) + await txn_client.update_collection( + collection_data["id"], api.Collection(**collection_data), request=MockRequest + ) coll = await core_client.get_collection(collection_data["id"], request=MockRequest) assert "new keyword" in coll["keywords"] @@ -84,7 +88,9 @@ async def test_update_collection_id( item_data = load_test_data("test_item.json") new_collection_id = "new-test-collection" - await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) + await txn_client.create_collection( + api.Collection(**collection_data), request=MockRequest + ) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -196,10 +202,14 @@ async def test_get_collection_items(app_client, ctx, core_client, txn_client): @pytest.mark.asyncio async def test_create_item(ctx, core_client, txn_client): - resp = await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) - assert Item(**ctx.item).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) == Item( - **resp - ).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) + resp = await core_client.get_item( + ctx.item["id"], ctx.item["collection"], request=MockRequest + ) + assert Item(**ctx.item).model_dump( + exclude={"links": ..., "properties": {"created", "updated"}} + ) == Item(**resp).model_dump( + exclude={"links": ..., "properties": {"created", "updated"}} + ) @pytest.mark.asyncio @@ -226,7 +236,9 @@ async def test_update_item(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" @@ -242,7 +254,9 @@ async def test_merge_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" @@ -259,7 +273,9 @@ async def test_merge_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] @@ -276,7 +292,9 @@ async def test_merge_patch_create_nest(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "new" in updated_item["properties"] assert "nest" in updated_item["properties"]["new"] assert updated_item["properties"]["new"]["nest"] == "foo" @@ -288,8 +306,12 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/foo", "value": "bar"}), - PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/ext:hello", "value": "world"}), + PatchAddReplaceTest.model_validate( + {"op": "add", "path": "/properties/foo", "value": "bar"} + ), + PatchAddReplaceTest.model_validate( + {"op": "add", "path": "/properties/ext:hello", "value": "world"} + ), PatchAddReplaceTest.model_validate( { "op": "add", @@ -312,11 +334,16 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + 1 + assert ( + len(updated_item["properties"]["eo:bands"]) + == len(ctx.item["properties"]["eo:bands"]) + 1 + ) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -332,8 +359,12 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/gsd", "value": 100}), - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/proj:epsg", "value": 12345}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/gsd", "value": 100} + ), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/proj:epsg", "value": 12345} + ), PatchAddReplaceTest.model_validate( { "op": "replace", @@ -356,11 +387,15 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["gsd"] == 100 assert updated_item["properties"]["proj:epsg"] == 12345 - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -376,8 +411,12 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/gsd", "value": 15}), - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/proj:epsg", "value": 32756}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/gsd", "value": 15} + ), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/proj:epsg", "value": 32756} + ), PatchAddReplaceTest.model_validate( { "op": "test", @@ -394,11 +433,15 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["gsd"] == 15 assert updated_item["properties"]["proj:epsg"] == 32756 - assert updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] + assert ( + updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -407,8 +450,12 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/foo", "from": "/properties/gsd"}), - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/foo", "from": "/properties/gsd"} + ), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"} + ), PatchMoveCopy.model_validate( { "op": "move", @@ -425,15 +472,25 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == 15 assert "gsd" not in updated_item["properties"] assert updated_item["properties"]["bar"] == 32756 assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] - assert updated_item["properties"]["eo:bands"][1] != ctx.item["properties"]["eo:bands"][1] + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) + assert ( + updated_item["properties"]["eo:bands"][0] + == ctx.item["properties"]["eo:bands"][1] + ) + assert ( + updated_item["properties"]["eo:bands"][1] + != ctx.item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -442,8 +499,12 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"}), - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"} + ), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"} + ), PatchMoveCopy.model_validate( { "op": "copy", @@ -460,12 +521,19 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == updated_item["properties"]["gsd"] assert updated_item["properties"]["bar"] == updated_item["properties"]["proj:epsg"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) + assert ( + updated_item["properties"]["eo:bands"][0] + == ctx.item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -486,14 +554,20 @@ async def test_json_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - 1 + assert ( + len(updated_item["properties"]["eo:bands"]) + == len(ctx.item["properties"]["eo:bands"]) - 1 + ) assert ( updated_item["properties"]["eo:bands"] - == ctx.item["properties"]["eo:bands"][:1] + ctx.item["properties"]["eo:bands"][2:] + == ctx.item["properties"]["eo:bands"][:1] + + ctx.item["properties"]["eo:bands"][2:] ) @@ -503,7 +577,9 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchRemove.model_validate({"op": "add", "path": "/properties/bad/nest", "value": "foo"}), + PatchRemove.model_validate( + {"op": "add", "path": "/properties/bad/nest", "value": "foo"} + ), ] with pytest.raises(HTTPException): @@ -512,7 +588,9 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @@ -522,7 +600,9 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/platform", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/platform", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -531,17 +611,23 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_replace_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/foo", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/foo", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -550,12 +636,16 @@ async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_remove_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] @@ -569,17 +659,23 @@ async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_move_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/foo"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/bar", "from": "/properties/foo"} + ), ] with pytest.raises(HTTPException): @@ -588,17 +684,23 @@ async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, t collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_copy_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/foo"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/bar", "from": "/properties/foo"} + ), ] with pytest.raises(HTTPException): @@ -607,7 +709,9 @@ async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, t collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @@ -633,7 +737,9 @@ async def test_update_geometry(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["geometry"]["coordinates"] == new_coordinates @@ -642,14 +748,18 @@ async def test_delete_item(ctx, core_client, txn_client): await txn_client.delete_item(ctx.item["id"], ctx.item["collection"]) with pytest.raises(NotFoundError): - await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) + await core_client.get_item( + ctx.item["id"], ctx.item["collection"], request=MockRequest + ) @pytest.mark.asyncio async def test_landing_page_no_collection_title(ctx, core_client, txn_client, app): ctx.collection["id"] = "new_id" del ctx.collection["title"] - await txn_client.create_collection(api.Collection(**ctx.collection), request=MockRequest) + await txn_client.create_collection( + api.Collection(**ctx.collection), request=MockRequest + ) landing_page = await core_client.landing_page(request=MockRequest(app=app)) for link in landing_page["links"]: @@ -668,7 +778,9 @@ async def test_merge_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["hello"] == "world" @@ -683,7 +795,9 @@ async def test_merge_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert "gsd" not in updated_collection["summaries"] @@ -706,7 +820,9 @@ async def test_json_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["gsd"] == [30, 100] @@ -717,7 +833,9 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/gsd", "value": [100]}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/summaries/gsd", "value": [100]} + ), ] await txn_client.patch_collection( @@ -726,7 +844,9 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["gsd"] == [100] @@ -736,7 +856,9 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/gsd", "value": [30]}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/summaries/gsd", "value": [30]} + ), ] await txn_client.patch_collection( @@ -745,7 +867,9 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["gsd"] == [30] @@ -755,7 +879,9 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"} + ), ] await txn_client.patch_collection( @@ -764,7 +890,9 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["bar"] == [30] assert "gsd" not in updated_collection["summaries"] @@ -775,7 +903,9 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"} + ), ] await txn_client.patch_collection( @@ -784,9 +914,13 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) - assert updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] + assert ( + updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] + ) @pytest.mark.asyncio @@ -803,7 +937,9 @@ async def test_json_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert "gsd" not in updated_collection["summaries"] @@ -813,7 +949,9 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/platform", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/summaries/platform", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -821,16 +959,22 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_replace_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/foo", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/summaries/foo", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -838,12 +982,16 @@ async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_ await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_remove_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ @@ -855,16 +1003,22 @@ async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_c await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_move_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_move_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"} + ), ] with pytest.raises(HTTPException): @@ -872,16 +1026,22 @@ async def test_json_patch_collection_move_property_does_not_exists(ctx, core_cli await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_copy_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"} + ), ] with pytest.raises(HTTPException): @@ -889,5 +1049,7 @@ async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_cli await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) From b9866de9fbe3dcecca8407f8730ac7d61f74b213 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Tue, 2 Sep 2025 08:53:25 +0100 Subject: [PATCH 17/18] Correct operation class. --- stac_fastapi/tests/clients/test_es_os.py | 324 ++++++----------------- 1 file changed, 81 insertions(+), 243 deletions(-) diff --git a/stac_fastapi/tests/clients/test_es_os.py b/stac_fastapi/tests/clients/test_es_os.py index 197a725e..d590d24d 100644 --- a/stac_fastapi/tests/clients/test_es_os.py +++ b/stac_fastapi/tests/clients/test_es_os.py @@ -48,9 +48,7 @@ async def test_update_collection( collection_data = load_test_data("test_collection.json") item_data = load_test_data("test_item.json") - await txn_client.create_collection( - api.Collection(**collection_data), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -59,9 +57,7 @@ async def test_update_collection( ) collection_data["keywords"].append("new keyword") - await txn_client.update_collection( - collection_data["id"], api.Collection(**collection_data), request=MockRequest - ) + await txn_client.update_collection(collection_data["id"], api.Collection(**collection_data), request=MockRequest) coll = await core_client.get_collection(collection_data["id"], request=MockRequest) assert "new keyword" in coll["keywords"] @@ -88,9 +84,7 @@ async def test_update_collection_id( item_data = load_test_data("test_item.json") new_collection_id = "new-test-collection" - await txn_client.create_collection( - api.Collection(**collection_data), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -202,14 +196,10 @@ async def test_get_collection_items(app_client, ctx, core_client, txn_client): @pytest.mark.asyncio async def test_create_item(ctx, core_client, txn_client): - resp = await core_client.get_item( - ctx.item["id"], ctx.item["collection"], request=MockRequest - ) - assert Item(**ctx.item).model_dump( - exclude={"links": ..., "properties": {"created", "updated"}} - ) == Item(**resp).model_dump( - exclude={"links": ..., "properties": {"created", "updated"}} - ) + resp = await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) + assert Item(**ctx.item).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) == Item( + **resp + ).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) @pytest.mark.asyncio @@ -236,9 +226,7 @@ async def test_update_item(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" @@ -254,9 +242,7 @@ async def test_merge_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" @@ -273,9 +259,7 @@ async def test_merge_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] @@ -292,9 +276,7 @@ async def test_merge_patch_create_nest(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert "new" in updated_item["properties"] assert "nest" in updated_item["properties"]["new"] assert updated_item["properties"]["new"]["nest"] == "foo" @@ -306,12 +288,8 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "add", "path": "/properties/foo", "value": "bar"} - ), - PatchAddReplaceTest.model_validate( - {"op": "add", "path": "/properties/ext:hello", "value": "world"} - ), + PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/foo", "value": "bar"}), + PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/ext:hello", "value": "world"}), PatchAddReplaceTest.model_validate( { "op": "add", @@ -334,16 +312,11 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" - assert ( - len(updated_item["properties"]["eo:bands"]) - == len(ctx.item["properties"]["eo:bands"]) + 1 - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + 1 assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -359,12 +332,8 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/gsd", "value": 100} - ), - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/proj:epsg", "value": 12345} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/gsd", "value": 100}), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/proj:epsg", "value": 12345}), PatchAddReplaceTest.model_validate( { "op": "replace", @@ -387,15 +356,11 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["gsd"] == 100 assert updated_item["properties"]["proj:epsg"] == 12345 - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -411,12 +376,8 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/gsd", "value": 15} - ), - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/proj:epsg", "value": 32756} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/gsd", "value": 15}), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/proj:epsg", "value": 32756}), PatchAddReplaceTest.model_validate( { "op": "test", @@ -433,15 +394,11 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["gsd"] == 15 assert updated_item["properties"]["proj:epsg"] == 32756 - assert ( - updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] - ) + assert updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -450,12 +407,8 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/foo", "from": "/properties/gsd"} - ), - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/foo", "from": "/properties/gsd"}), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"}), PatchMoveCopy.model_validate( { "op": "move", @@ -472,25 +425,15 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == 15 assert "gsd" not in updated_item["properties"] assert updated_item["properties"]["bar"] == 32756 assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) - assert ( - updated_item["properties"]["eo:bands"][0] - == ctx.item["properties"]["eo:bands"][1] - ) - assert ( - updated_item["properties"]["eo:bands"][1] - != ctx.item["properties"]["eo:bands"][1] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] + assert updated_item["properties"]["eo:bands"][1] != ctx.item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -499,12 +442,8 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"} - ), - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"}), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"}), PatchMoveCopy.model_validate( { "op": "copy", @@ -521,19 +460,12 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["properties"]["foo"] == updated_item["properties"]["gsd"] assert updated_item["properties"]["bar"] == updated_item["properties"]["proj:epsg"] - assert len(updated_item["properties"]["eo:bands"]) == len( - ctx.item["properties"]["eo:bands"] - ) - assert ( - updated_item["properties"]["eo:bands"][0] - == ctx.item["properties"]["eo:bands"][1] - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] @pytest.mark.asyncio @@ -554,20 +486,14 @@ async def test_json_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] - assert ( - len(updated_item["properties"]["eo:bands"]) - == len(ctx.item["properties"]["eo:bands"]) - 1 - ) + assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - 1 assert ( updated_item["properties"]["eo:bands"] - == ctx.item["properties"]["eo:bands"][:1] - + ctx.item["properties"]["eo:bands"][2:] + == ctx.item["properties"]["eo:bands"][:1] + ctx.item["properties"]["eo:bands"][2:] ) @@ -577,9 +503,7 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchRemove.model_validate( - {"op": "add", "path": "/properties/bad/nest", "value": "foo"} - ), + PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/bad/nest", "value": "foo"}), ] with pytest.raises(HTTPException): @@ -588,9 +512,7 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @@ -600,9 +522,7 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/properties/platform", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/platform", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -611,23 +531,17 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_replace_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/properties/foo", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/foo", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -636,16 +550,12 @@ async def test_json_patch_item_replace_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_remove_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] @@ -659,23 +569,17 @@ async def test_json_patch_item_remove_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_move_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/properties/bar", "from": "/properties/foo"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/foo"}), ] with pytest.raises(HTTPException): @@ -684,23 +588,17 @@ async def test_json_patch_item_move_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_item_copy_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, txn_client): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/properties/bar", "from": "/properties/foo"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/foo"}), ] with pytest.raises(HTTPException): @@ -709,9 +607,7 @@ async def test_json_patch_item_copy_property_does_not_exists( collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @@ -737,9 +633,7 @@ async def test_update_geometry(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item( - item_id, collection_id, request=MockRequest - ) + updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) assert updated_item["geometry"]["coordinates"] == new_coordinates @@ -748,18 +642,14 @@ async def test_delete_item(ctx, core_client, txn_client): await txn_client.delete_item(ctx.item["id"], ctx.item["collection"]) with pytest.raises(NotFoundError): - await core_client.get_item( - ctx.item["id"], ctx.item["collection"], request=MockRequest - ) + await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) @pytest.mark.asyncio async def test_landing_page_no_collection_title(ctx, core_client, txn_client, app): ctx.collection["id"] = "new_id" del ctx.collection["title"] - await txn_client.create_collection( - api.Collection(**ctx.collection), request=MockRequest - ) + await txn_client.create_collection(api.Collection(**ctx.collection), request=MockRequest) landing_page = await core_client.landing_page(request=MockRequest(app=app)) for link in landing_page["links"]: @@ -778,9 +668,7 @@ async def test_merge_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["hello"] == "world" @@ -795,9 +683,7 @@ async def test_merge_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert "gsd" not in updated_collection["summaries"] @@ -820,9 +706,7 @@ async def test_json_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["gsd"] == [30, 100] @@ -833,9 +717,7 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/summaries/gsd", "value": [100]} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/gsd", "value": [100]}), ] await txn_client.patch_collection( @@ -844,9 +726,7 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["gsd"] == [100] @@ -856,9 +736,7 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/summaries/gsd", "value": [30]} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/gsd", "value": [30]}), ] await txn_client.patch_collection( @@ -867,9 +745,7 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["gsd"] == [30] @@ -879,9 +755,7 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"}), ] await txn_client.patch_collection( @@ -890,9 +764,7 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert updated_collection["summaries"]["bar"] == [30] assert "gsd" not in updated_collection["summaries"] @@ -903,9 +775,7 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"}), ] await txn_client.patch_collection( @@ -914,13 +784,9 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) - assert ( - updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] - ) + assert updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] @pytest.mark.asyncio @@ -937,9 +803,7 @@ async def test_json_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection( - collection_id, request=MockRequest - ) + updated_collection = await core_client.get_collection(collection_id, request=MockRequest) assert "gsd" not in updated_collection["summaries"] @@ -949,9 +813,7 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "test", "path": "/summaries/platform", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/platform", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -959,22 +821,16 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_replace_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate( - {"op": "replace", "path": "/summaries/foo", "value": "landsat-9"} - ), + PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/foo", "value": "landsat-9"}), ] with pytest.raises(HTTPException): @@ -982,16 +838,12 @@ async def test_json_patch_collection_replace_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_remove_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ @@ -1003,22 +855,16 @@ async def test_json_patch_collection_remove_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_move_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_move_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"} - ), + PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"}), ] with pytest.raises(HTTPException): @@ -1026,22 +872,16 @@ async def test_json_patch_collection_move_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) @pytest.mark.asyncio -async def test_json_patch_collection_copy_property_does_not_exists( - ctx, core_client, txn_client -): +async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate( - {"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"} - ), + PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"}), ] with pytest.raises(HTTPException): @@ -1049,7 +889,5 @@ async def test_json_patch_collection_copy_property_does_not_exists( await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest( - headers={"content-type": "application/json-patch+json"} - ), + request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) From 6c5cfc09fa472662ebd1d9218d711e93de3dfff5 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Tue, 2 Sep 2025 08:53:34 +0100 Subject: [PATCH 18/18] pre-commit. --- stac_fastapi/tests/clients/test_es_os.py | 324 +++++++++++++++++------ 1 file changed, 243 insertions(+), 81 deletions(-) diff --git a/stac_fastapi/tests/clients/test_es_os.py b/stac_fastapi/tests/clients/test_es_os.py index d590d24d..206c51c0 100644 --- a/stac_fastapi/tests/clients/test_es_os.py +++ b/stac_fastapi/tests/clients/test_es_os.py @@ -48,7 +48,9 @@ async def test_update_collection( collection_data = load_test_data("test_collection.json") item_data = load_test_data("test_item.json") - await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) + await txn_client.create_collection( + api.Collection(**collection_data), request=MockRequest + ) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -57,7 +59,9 @@ async def test_update_collection( ) collection_data["keywords"].append("new keyword") - await txn_client.update_collection(collection_data["id"], api.Collection(**collection_data), request=MockRequest) + await txn_client.update_collection( + collection_data["id"], api.Collection(**collection_data), request=MockRequest + ) coll = await core_client.get_collection(collection_data["id"], request=MockRequest) assert "new keyword" in coll["keywords"] @@ -84,7 +88,9 @@ async def test_update_collection_id( item_data = load_test_data("test_item.json") new_collection_id = "new-test-collection" - await txn_client.create_collection(api.Collection(**collection_data), request=MockRequest) + await txn_client.create_collection( + api.Collection(**collection_data), request=MockRequest + ) await txn_client.create_item( collection_id=collection_data["id"], item=api.Item(**item_data), @@ -196,10 +202,14 @@ async def test_get_collection_items(app_client, ctx, core_client, txn_client): @pytest.mark.asyncio async def test_create_item(ctx, core_client, txn_client): - resp = await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) - assert Item(**ctx.item).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) == Item( - **resp - ).model_dump(exclude={"links": ..., "properties": {"created", "updated"}}) + resp = await core_client.get_item( + ctx.item["id"], ctx.item["collection"], request=MockRequest + ) + assert Item(**ctx.item).model_dump( + exclude={"links": ..., "properties": {"created", "updated"}} + ) == Item(**resp).model_dump( + exclude={"links": ..., "properties": {"created", "updated"}} + ) @pytest.mark.asyncio @@ -226,7 +236,9 @@ async def test_update_item(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" @@ -242,7 +254,9 @@ async def test_merge_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" @@ -259,7 +273,9 @@ async def test_merge_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] @@ -276,7 +292,9 @@ async def test_merge_patch_create_nest(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "new" in updated_item["properties"] assert "nest" in updated_item["properties"]["new"] assert updated_item["properties"]["new"]["nest"] == "foo" @@ -288,8 +306,12 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/foo", "value": "bar"}), - PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/ext:hello", "value": "world"}), + PatchAddReplaceTest.model_validate( + {"op": "add", "path": "/properties/foo", "value": "bar"} + ), + PatchAddReplaceTest.model_validate( + {"op": "add", "path": "/properties/ext:hello", "value": "world"} + ), PatchAddReplaceTest.model_validate( { "op": "add", @@ -312,11 +334,16 @@ async def test_json_patch_item_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == "bar" assert updated_item["properties"]["ext:hello"] == "world" - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + 1 + assert ( + len(updated_item["properties"]["eo:bands"]) + == len(ctx.item["properties"]["eo:bands"]) + 1 + ) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -332,8 +359,12 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/gsd", "value": 100}), - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/proj:epsg", "value": 12345}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/gsd", "value": 100} + ), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/proj:epsg", "value": 12345} + ), PatchAddReplaceTest.model_validate( { "op": "replace", @@ -356,11 +387,15 @@ async def test_json_patch_item_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["gsd"] == 100 assert updated_item["properties"]["proj:epsg"] == 12345 - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) assert updated_item["properties"]["eo:bands"][1] == { "gsd": 10, "name": "FB", @@ -376,8 +411,12 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/gsd", "value": 15}), - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/proj:epsg", "value": 32756}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/gsd", "value": 15} + ), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/proj:epsg", "value": 32756} + ), PatchAddReplaceTest.model_validate( { "op": "test", @@ -394,11 +433,15 @@ async def test_json_patch_item_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["gsd"] == 15 assert updated_item["properties"]["proj:epsg"] == 32756 - assert updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] + assert ( + updated_item["properties"]["eo:bands"][1] == item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -407,8 +450,12 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/foo", "from": "/properties/gsd"}), - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/foo", "from": "/properties/gsd"} + ), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/bar", "from": "/properties/proj:epsg"} + ), PatchMoveCopy.model_validate( { "op": "move", @@ -425,15 +472,25 @@ async def test_json_patch_item_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == 15 assert "gsd" not in updated_item["properties"] assert updated_item["properties"]["bar"] == 32756 assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] - assert updated_item["properties"]["eo:bands"][1] != ctx.item["properties"]["eo:bands"][1] + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) + assert ( + updated_item["properties"]["eo:bands"][0] + == ctx.item["properties"]["eo:bands"][1] + ) + assert ( + updated_item["properties"]["eo:bands"][1] + != ctx.item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -442,8 +499,12 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"}), - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/foo", "from": "/properties/gsd"} + ), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/bar", "from": "/properties/proj:epsg"} + ), PatchMoveCopy.model_validate( { "op": "copy", @@ -460,12 +521,19 @@ async def test_json_patch_item_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["properties"]["foo"] == updated_item["properties"]["gsd"] assert updated_item["properties"]["bar"] == updated_item["properties"]["proj:epsg"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - assert updated_item["properties"]["eo:bands"][0] == ctx.item["properties"]["eo:bands"][1] + assert len(updated_item["properties"]["eo:bands"]) == len( + ctx.item["properties"]["eo:bands"] + ) + assert ( + updated_item["properties"]["eo:bands"][0] + == ctx.item["properties"]["eo:bands"][1] + ) @pytest.mark.asyncio @@ -486,14 +554,20 @@ async def test_json_patch_item_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert "gsd" not in updated_item["properties"] assert "proj:epsg" not in updated_item["properties"] - assert len(updated_item["properties"]["eo:bands"]) == len(ctx.item["properties"]["eo:bands"]) - 1 + assert ( + len(updated_item["properties"]["eo:bands"]) + == len(ctx.item["properties"]["eo:bands"]) - 1 + ) assert ( updated_item["properties"]["eo:bands"] - == ctx.item["properties"]["eo:bands"][:1] + ctx.item["properties"]["eo:bands"][2:] + == ctx.item["properties"]["eo:bands"][:1] + + ctx.item["properties"]["eo:bands"][2:] ) @@ -503,7 +577,9 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "add", "path": "/properties/bad/nest", "value": "foo"}), + PatchAddReplaceTest.model_validate( + {"op": "add", "path": "/properties/bad/nest", "value": "foo"} + ), ] with pytest.raises(HTTPException): @@ -512,7 +588,9 @@ async def test_json_patch_add_with_bad_nest(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @@ -522,7 +600,9 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/properties/platform", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/properties/platform", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -531,17 +611,23 @@ async def test_json_patch_item_test_wrong_value(ctx, core_client, txn_client): collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_replace_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/properties/foo", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/properties/foo", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -550,12 +636,16 @@ async def test_json_patch_item_replace_property_does_not_exists(ctx, core_client collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_remove_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] @@ -569,17 +659,23 @@ async def test_json_patch_item_remove_property_does_not_exists(ctx, core_client, collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_move_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/properties/bar", "from": "/properties/foo"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/properties/bar", "from": "/properties/foo"} + ), ] with pytest.raises(HTTPException): @@ -588,17 +684,23 @@ async def test_json_patch_item_move_property_does_not_exists(ctx, core_client, t collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_item_copy_property_does_not_exists( + ctx, core_client, txn_client +): item = ctx.item collection_id = item["collection"] item_id = item["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/properties/bar", "from": "/properties/foo"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/properties/bar", "from": "/properties/foo"} + ), ] with pytest.raises(HTTPException): @@ -607,7 +709,9 @@ async def test_json_patch_item_copy_property_does_not_exists(ctx, core_client, t collection_id=collection_id, item_id=item_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @@ -633,7 +737,9 @@ async def test_update_geometry(ctx, core_client, txn_client): request=MockRequest, ) - updated_item = await core_client.get_item(item_id, collection_id, request=MockRequest) + updated_item = await core_client.get_item( + item_id, collection_id, request=MockRequest + ) assert updated_item["geometry"]["coordinates"] == new_coordinates @@ -642,14 +748,18 @@ async def test_delete_item(ctx, core_client, txn_client): await txn_client.delete_item(ctx.item["id"], ctx.item["collection"]) with pytest.raises(NotFoundError): - await core_client.get_item(ctx.item["id"], ctx.item["collection"], request=MockRequest) + await core_client.get_item( + ctx.item["id"], ctx.item["collection"], request=MockRequest + ) @pytest.mark.asyncio async def test_landing_page_no_collection_title(ctx, core_client, txn_client, app): ctx.collection["id"] = "new_id" del ctx.collection["title"] - await txn_client.create_collection(api.Collection(**ctx.collection), request=MockRequest) + await txn_client.create_collection( + api.Collection(**ctx.collection), request=MockRequest + ) landing_page = await core_client.landing_page(request=MockRequest(app=app)) for link in landing_page["links"]: @@ -668,7 +778,9 @@ async def test_merge_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["hello"] == "world" @@ -683,7 +795,9 @@ async def test_merge_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/merge-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert "gsd" not in updated_collection["summaries"] @@ -706,7 +820,9 @@ async def test_json_patch_collection_add(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["foo"] == "bar" assert updated_collection["summaries"]["gsd"] == [30, 100] @@ -717,7 +833,9 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/gsd", "value": [100]}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/summaries/gsd", "value": [100]} + ), ] await txn_client.patch_collection( @@ -726,7 +844,9 @@ async def test_json_patch_collection_replace(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["gsd"] == [100] @@ -736,7 +856,9 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/gsd", "value": [30]}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/summaries/gsd", "value": [30]} + ), ] await txn_client.patch_collection( @@ -745,7 +867,9 @@ async def test_json_patch_collection_test(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["gsd"] == [30] @@ -755,7 +879,9 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/summaries/bar", "from": "/summaries/gsd"} + ), ] await txn_client.patch_collection( @@ -764,7 +890,9 @@ async def test_json_patch_collection_move(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert updated_collection["summaries"]["bar"] == [30] assert "gsd" not in updated_collection["summaries"] @@ -775,7 +903,9 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/summaries/foo", "from": "/summaries/gsd"} + ), ] await txn_client.patch_collection( @@ -784,9 +914,13 @@ async def test_json_patch_collection_copy(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) - assert updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] + assert ( + updated_collection["summaries"]["foo"] == updated_collection["summaries"]["gsd"] + ) @pytest.mark.asyncio @@ -803,7 +937,9 @@ async def test_json_patch_collection_remove(ctx, core_client, txn_client): request=MockRequest(headers={"content-type": "application/json-patch+json"}), ) - updated_collection = await core_client.get_collection(collection_id, request=MockRequest) + updated_collection = await core_client.get_collection( + collection_id, request=MockRequest + ) assert "gsd" not in updated_collection["summaries"] @@ -813,7 +949,9 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "test", "path": "/summaries/platform", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "test", "path": "/summaries/platform", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -821,16 +959,22 @@ async def test_json_patch_collection_test_wrong_value(ctx, core_client, txn_clie await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_replace_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchAddReplaceTest.model_validate({"op": "replace", "path": "/summaries/foo", "value": "landsat-9"}), + PatchAddReplaceTest.model_validate( + {"op": "replace", "path": "/summaries/foo", "value": "landsat-9"} + ), ] with pytest.raises(HTTPException): @@ -838,12 +982,16 @@ async def test_json_patch_collection_replace_property_does_not_exists(ctx, core_ await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_remove_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ @@ -855,16 +1003,22 @@ async def test_json_patch_collection_remove_property_does_not_exists(ctx, core_c await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_move_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_move_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"}), + PatchMoveCopy.model_validate( + {"op": "move", "path": "/summaries/bar", "from": "/summaries/foo"} + ), ] with pytest.raises(HTTPException): @@ -872,16 +1026,22 @@ async def test_json_patch_collection_move_property_does_not_exists(ctx, core_cli await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), ) @pytest.mark.asyncio -async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_client, txn_client): +async def test_json_patch_collection_copy_property_does_not_exists( + ctx, core_client, txn_client +): collection = ctx.collection collection_id = collection["id"] operations = [ - PatchMoveCopy.model_validate({"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"}), + PatchMoveCopy.model_validate( + {"op": "copy", "path": "/summaries/bar", "from": "/summaries/foo"} + ), ] with pytest.raises(HTTPException): @@ -889,5 +1049,7 @@ async def test_json_patch_collection_copy_property_does_not_exists(ctx, core_cli await txn_client.patch_collection( collection_id=collection_id, patch=operations, - request=MockRequest(headers={"content-type": "application/json-patch+json"}), + request=MockRequest( + headers={"content-type": "application/json-patch+json"} + ), )