@@ -45,6 +45,13 @@ def test_custom_overwrites_on_key_collision(self):
4545 merge_mappings (base , custom )
4646 assert base ["level1" ]["a" ] == {"type" : "date" }
4747
48+ def test_merge_adds_properties_to_existing_nested_dict (self ):
49+ """Test that merging adds new properties to existing nested dicts."""
50+ base = {"geometry" : {"type" : "geo_shape" }}
51+ custom = {"geometry" : {"ignore_malformed" : True }}
52+ merge_mappings (base , custom )
53+ assert base == {"geometry" : {"type" : "geo_shape" , "ignore_malformed" : True }}
54+
4855 @pytest .mark .parametrize (
4956 "base,custom,expected" ,
5057 [
@@ -111,24 +118,49 @@ def test_no_op_for_empty_input(self, custom_json):
111118 apply_custom_mappings (mappings , custom_json )
112119 assert mappings == original
113120
114- def test_merges_valid_json (self ):
115- """Test that valid JSON custom mappings are merged into properties ."""
121+ def test_merges_at_root_level (self ):
122+ """Test that custom mappings are merged at the root level ."""
116123 mappings = {
124+ "numeric_detection" : False ,
117125 "properties" : {
118- "properties" : {"properties" : {"datetime" : {"type" : "date_nanos" }}}
119- }
126+ "id" : {"type" : "keyword" },
127+ "properties" : {"properties" : {"datetime" : {"type" : "date_nanos" }}},
128+ },
120129 }
121130 custom_json = json .dumps (
122- {"properties" : {"properties" : {"sar:frequency_band" : {"type" : "keyword" }}}}
131+ {
132+ "properties" : {
133+ "properties" : {
134+ "properties" : {"sar:frequency_band" : {"type" : "keyword" }}
135+ },
136+ "bbox" : {"type" : "object" , "enabled" : False },
137+ }
138+ }
123139 )
124140 apply_custom_mappings (mappings , custom_json )
125141
142+ # Existing fields preserved
143+ assert mappings ["properties" ]["id" ] == {"type" : "keyword" }
126144 assert mappings ["properties" ]["properties" ]["properties" ]["datetime" ] == {
127145 "type" : "date_nanos"
128146 }
147+ # New fields added
129148 assert mappings ["properties" ]["properties" ]["properties" ][
130149 "sar:frequency_band"
131150 ] == {"type" : "keyword" }
151+ assert mappings ["properties" ]["bbox" ] == {"type" : "object" , "enabled" : False }
152+
153+ def test_can_override_dynamic_templates (self ):
154+ """Test that dynamic_templates can be overridden via custom mappings."""
155+ mappings = {
156+ "dynamic_templates" : [{"old" : "template" }],
157+ "properties" : {"id" : {"type" : "keyword" }},
158+ }
159+ custom_json = json .dumps ({"dynamic_templates" : [{"new" : "template" }]})
160+ apply_custom_mappings (mappings , custom_json )
161+
162+ assert mappings ["dynamic_templates" ] == [{"new" : "template" }]
163+ assert mappings ["properties" ]["id" ] == {"type" : "keyword" }
132164
133165 def test_invalid_json_logs_error_and_preserves_mappings (self , caplog ):
134166 """Test that invalid JSON logs an error and doesn't modify mappings."""
@@ -159,7 +191,11 @@ def test_dynamic_mapping_values(self, dynamic_mapping, expected):
159191 def test_custom_mappings_merged_preserving_defaults (self ):
160192 """Test that custom mappings are merged while preserving default fields."""
161193 custom = json .dumps (
162- {"properties" : {"properties" : {"custom:field" : {"type" : "keyword" }}}}
194+ {
195+ "properties" : {
196+ "properties" : {"properties" : {"custom:field" : {"type" : "keyword" }}}
197+ }
198+ }
163199 )
164200 mappings = get_items_mappings (custom_mappings = custom )
165201
@@ -177,7 +213,11 @@ def test_custom_mappings_merged_preserving_defaults(self):
177213 def test_custom_can_override_defaults (self ):
178214 """Test that custom mappings can override default field types."""
179215 custom = json .dumps (
180- {"properties" : {"properties" : {"datetime" : {"type" : "date" }}}}
216+ {
217+ "properties" : {
218+ "properties" : {"properties" : {"datetime" : {"type" : "date" }}}
219+ }
220+ }
181221 )
182222 mappings = get_items_mappings (custom_mappings = custom )
183223 assert mappings ["properties" ]["properties" ]["properties" ]["datetime" ] == {
@@ -212,9 +252,11 @@ class TestSTACExtensionUseCases:
212252 {
213253 "properties" : {
214254 "properties" : {
215- "sar:frequency_band" : {"type" : "keyword" },
216- "sar:center_frequency" : {"type" : "float" },
217- "sar:polarizations" : {"type" : "keyword" },
255+ "properties" : {
256+ "sar:frequency_band" : {"type" : "keyword" },
257+ "sar:center_frequency" : {"type" : "float" },
258+ "sar:polarizations" : {"type" : "keyword" },
259+ }
218260 }
219261 }
220262 },
@@ -224,8 +266,10 @@ class TestSTACExtensionUseCases:
224266 {
225267 "properties" : {
226268 "properties" : {
227- "cube:dimensions" : {"type" : "object" , "enabled" : False },
228- "cube:variables" : {"type" : "object" , "enabled" : False },
269+ "properties" : {
270+ "cube:dimensions" : {"type" : "object" , "enabled" : False },
271+ "cube:variables" : {"type" : "object" , "enabled" : False },
272+ }
229273 }
230274 }
231275 },
@@ -238,7 +282,7 @@ def test_add_extension_fields(self, extension_name, custom_fields):
238282 mappings = get_items_mappings (custom_mappings = json .dumps (custom_fields ))
239283
240284 props = mappings ["properties" ]["properties" ]["properties" ]
241- for field_name , field_config in custom_fields ["properties" ][
285+ for field_name , field_config in custom_fields ["properties" ]["properties" ][
242286 "properties"
243287 ].items ():
244288 assert props [field_name ] == field_config
@@ -250,8 +294,10 @@ def test_performance_optimization_with_disabled_dynamic_mapping(self):
250294 query_fields = {
251295 "properties" : {
252296 "properties" : {
253- "platform" : {"type" : "keyword" },
254- "eo:cloud_cover" : {"type" : "float" },
297+ "properties" : {
298+ "platform" : {"type" : "keyword" },
299+ "eo:cloud_cover" : {"type" : "float" },
300+ }
255301 }
256302 }
257303 }
0 commit comments