@@ -265,6 +265,131 @@ void testMergeList_EliminateDuplicates_Map() {
265265 map ("k1" , list (map ("p2" , "v2" ), map ("p3" , "v3" )))));
266266 }
267267
268+ @ Test
269+ @ SuppressWarnings ("unchecked" )
270+ void testMergeList_KeyValueObjects_Override () {
271+ // Test case for key-value objects: objects with same "key" should merge
272+ Map <String , Object > baseConfig = map (
273+ "key" , "magnolia.bootstrap.dir" ,
274+ "value" , "WEB-INF/bootstrap/author WEB-INF/bootstrap/common" ,
275+ "comment" , "the directories in which the bootstrap files are searched"
276+ );
277+
278+ Map <String , Object > variantConfig = map (
279+ "key" , "magnolia.bootstrap.dir" ,
280+ "value" , "C:/user/test/dir"
281+ );
282+
283+ // When merging with _merge_ token, variant values override base values, but base fields are preserved
284+ Map <String , Object > result = merge (
285+ map ("proj.magnolia.cms.properties" , list (LIST_MERGE_ENTRY , variantConfig )),
286+ map ("proj.magnolia.cms.properties" , list (baseConfig ))
287+ );
288+
289+ // Should have only one entry with merged values: value from variant, comment from base
290+ List <Object > resultList = (List <Object >)result .get ("proj.magnolia.cms.properties" );
291+ assertEquals (1 , resultList .size ());
292+
293+ Map <String , Object > expectedMerged = map (
294+ "key" , "magnolia.bootstrap.dir" ,
295+ "value" , "C:/user/test/dir" ,
296+ "comment" , "the directories in which the bootstrap files are searched"
297+ );
298+ assertEquals (expectedMerged , resultList .get (0 ));
299+ }
300+
301+ @ Test
302+ @ SuppressWarnings ("unchecked" )
303+ void testMergeList_KeyValueObjects_OverrideComment () {
304+ // Test that comment can be explicitly overridden if provided in variant
305+ Map <String , Object > baseConfig = map (
306+ "key" , "magnolia.bootstrap.dir" ,
307+ "value" , "WEB-INF/bootstrap/author WEB-INF/bootstrap/common" ,
308+ "comment" , "old comment"
309+ );
310+
311+ Map <String , Object > variantConfig = map (
312+ "key" , "magnolia.bootstrap.dir" ,
313+ "value" , "woanders" ,
314+ "comment" , "new comment"
315+ );
316+
317+ Map <String , Object > result = merge (
318+ map ("props" , list (LIST_MERGE_ENTRY , variantConfig )),
319+ map ("props" , list (baseConfig ))
320+ );
321+
322+ // Comment should be overridden because it was explicitly provided in variant
323+ List <Object > resultList = (List <Object >)result .get ("props" );
324+ assertEquals (1 , resultList .size ());
325+ assertEquals (variantConfig , resultList .get (0 ));
326+ }
327+
328+ @ Test
329+ @ SuppressWarnings ("unchecked" )
330+ void testMergeList_KeyValueObjects_MultipleKeys () {
331+ // Test with multiple different keys - they should all be preserved
332+ Map <String , Object > config1 = map (
333+ "key" , "prop1" ,
334+ "value" , "value1"
335+ );
336+
337+ Map <String , Object > config2 = map (
338+ "key" , "prop2" ,
339+ "value" , "value2"
340+ );
341+
342+ Map <String , Object > config3 = map (
343+ "key" , "prop3" ,
344+ "value" , "value3"
345+ );
346+
347+ Map <String , Object > result = merge (
348+ map ("props" , list (LIST_MERGE_ENTRY , config3 )),
349+ map ("props" , list (config1 , config2 ))
350+ );
351+
352+ // Should have all three entries
353+ // When LIST_MERGE_ENTRY is at start of l1, l2 elements are inserted at the beginning
354+ // Result: [config1, config2, config3]
355+ List <Object > resultList = (List <Object >)result .get ("props" );
356+ assertEquals (3 , resultList .size ());
357+ assertEquals (config1 , resultList .get (0 ));
358+ assertEquals (config2 , resultList .get (1 ));
359+ assertEquals (config3 , resultList .get (2 ));
360+ }
361+
362+ @ Test
363+ @ SuppressWarnings ("unchecked" )
364+ void testMergeList_KeyValueObjects_MixedOverrideAndNew () {
365+ // Test with mix of overriding and new keys
366+ Map <String , Object > config1 = map ("key" , "prop1" , "value" , "value1" , "comment" , "comment1" );
367+ Map <String , Object > config2 = map ("key" , "prop2" , "value" , "value2" , "comment" , "comment2" );
368+ Map <String , Object > config2Override = map ("key" , "prop2" , "value" , "value2-override" );
369+ Map <String , Object > config3 = map ("key" , "prop3" , "value" , "value3" );
370+
371+ Map <String , Object > result = merge (
372+ map ("props" , list (LIST_MERGE_ENTRY , config2Override , config3 )),
373+ map ("props" , list (config1 , config2 ))
374+ );
375+
376+ // l1 = [LIST_MERGE_ENTRY, config2Override, config3] -> MergingList = [config2Override, config3] with mergePos=0
377+ // l2 = [config1, config2]
378+ // When adding l2:
379+ // - config1 is inserted at position 0 -> [config1, config2Override, config3]
380+ // - config2 is merged into config2Override, preserving comment from config2
381+ // Result: [config1, merged(config2, config2Override), config3]
382+ List <Object > resultList = (List <Object >)result .get ("props" );
383+ assertEquals (3 , resultList .size ());
384+ assertEquals (config1 , resultList .get (0 ));
385+
386+ // config2Override should have merged with config2, keeping comment from config2
387+ Map <String , Object > expectedConfig2Merged = map ("key" , "prop2" , "value" , "value2-override" , "comment" , "comment2" );
388+ assertEquals (expectedConfig2Merged , resultList .get (1 ));
389+
390+ assertEquals (config3 , resultList .get (2 ));
391+ }
392+
268393 private static List <Object > list (Object ... items ) {
269394 return Arrays .asList (items );
270395 }
0 commit comments