|
17 | 17 |
|
18 | 18 | import static java.math.BigInteger.ONE; |
19 | 19 |
|
| 20 | +import static org.apache.commons.lang3.StringUtils.SPACE; |
20 | 21 | import static org.assertj.core.api.Assertions.assertThat; |
21 | 22 |
|
22 | 23 | import java.math.BigInteger; |
23 | 24 | import java.util.List; |
24 | 25 |
|
25 | 26 | import org.testng.annotations.Test; |
26 | 27 |
|
| 28 | +import com.expediagroup.beans.sample.FromFooContainingList; |
27 | 29 | import com.expediagroup.beans.sample.FromFooSimple; |
28 | 30 | import com.expediagroup.beans.sample.FromFooSimpleNested; |
| 31 | +import com.expediagroup.beans.sample.FromFooWithFullName; |
29 | 32 | import com.expediagroup.beans.sample.FromFooWithPrimitiveAndNestedObject; |
30 | 33 | import com.expediagroup.beans.sample.ToFooNestedWithDiffFieldName; |
31 | 34 | import com.expediagroup.beans.sample.ToFooWithNestedFieldMapping; |
| 35 | +import com.expediagroup.beans.sample.immutable.ImmutableToFooContainingList; |
| 36 | +import com.expediagroup.beans.sample.immutable.ImmutableToFooWithSplitName; |
32 | 37 | import com.expediagroup.beans.sample.mixed.MixedToFoo; |
33 | 38 | import com.expediagroup.beans.sample.mixed.MixedToFooDiffFields; |
34 | 39 | import com.expediagroup.beans.sample.mixed.MixedToFooMissingAllArgsConstructor; |
@@ -385,4 +390,65 @@ public void testMultipleRootFieldsMappedIntoSameNestedObject() { |
385 | 390 | assertThat(actual.getNestedObject().getName()).isEqualTo("Lucas"); |
386 | 391 | underTest.reset(); |
387 | 392 | } |
| 393 | + |
| 394 | + /** |
| 395 | + * Test that a field transformer applied with flat field name transformation correctly uses |
| 396 | + * the collection element as the source, not the root object. |
| 397 | + * Regression test: in 3.0.1, resolveEffectiveSource matched the flat mapping ("name" -> "fullName") |
| 398 | + * when the breadcrumb was null (collection element path), redirecting the source lookup to the |
| 399 | + * root object which had no "fullName" field. The silenced MissingFieldException left the value |
| 400 | + * as null, causing an NPE in the transformer function. |
| 401 | + */ |
| 402 | + @Test |
| 403 | + public void testFieldTransformerOnCollectionElementsUsesElementNotRootAsSource() { |
| 404 | + // GIVEN |
| 405 | + FromFooContainingList source = new FromFooContainingList("root-id", |
| 406 | + List.of(new FromFooWithFullName("Donald Duck"), new FromFooWithFullName("Daisy Duck"))); |
| 407 | + |
| 408 | + // WHEN |
| 409 | + ImmutableToFooContainingList actual = underTest |
| 410 | + .setFlatFieldNameTransformation(true) |
| 411 | + .withFieldMapping(new FieldMapping<>("fullName", "name")) |
| 412 | + .withFieldMapping(new FieldMapping<>("fullName", "surname")) |
| 413 | + .withFieldTransformer(new FieldTransformer<String, String>("name", fullName -> fullName.split(SPACE)[0])) |
| 414 | + .withFieldTransformer(new FieldTransformer<String, String>("surname", fullName -> fullName.split(SPACE)[1])) |
| 415 | + .transform(source, ImmutableToFooContainingList.class); |
| 416 | + |
| 417 | + // THEN |
| 418 | + assertThat(actual.getId()).isEqualTo("root-id"); |
| 419 | + assertThat(actual.getItems()).hasSize(2); |
| 420 | + assertThat(actual.getItems().get(0).getName()).isEqualTo("Donald"); |
| 421 | + assertThat(actual.getItems().get(0).getSurname()).isEqualTo("Duck"); |
| 422 | + assertThat(actual.getItems().get(1).getName()).isEqualTo("Daisy"); |
| 423 | + assertThat(actual.getItems().get(1).getSurname()).isEqualTo("Duck"); |
| 424 | + underTest.reset(); |
| 425 | + } |
| 426 | + |
| 427 | + /** |
| 428 | + * Test that multiple items in a collection are all correctly transformed using their own |
| 429 | + * element as the source, not the root. Verifies no cross-contamination between elements. |
| 430 | + */ |
| 431 | + @Test |
| 432 | + public void testAllCollectionElementsAreIndependentlyTransformedFromTheirOwnSource() { |
| 433 | + // GIVEN |
| 434 | + FromFooContainingList source = new FromFooContainingList("root-id", |
| 435 | + List.of(new FromFooWithFullName("Tony Stark"), new FromFooWithFullName("Bruce Banner"), |
| 436 | + new FromFooWithFullName("Steve Rogers"))); |
| 437 | + |
| 438 | + // WHEN |
| 439 | + ImmutableToFooContainingList actual = underTest |
| 440 | + .setFlatFieldNameTransformation(true) |
| 441 | + .withFieldMapping(new FieldMapping<>("fullName", "name")) |
| 442 | + .withFieldMapping(new FieldMapping<>("fullName", "surname")) |
| 443 | + .withFieldTransformer(new FieldTransformer<String, String>("name", fullName -> fullName.split(SPACE)[0])) |
| 444 | + .withFieldTransformer(new FieldTransformer<String, String>("surname", fullName -> fullName.split(SPACE)[1])) |
| 445 | + .transform(source, ImmutableToFooContainingList.class); |
| 446 | + |
| 447 | + // THEN |
| 448 | + assertThat(actual.getItems()).extracting(ImmutableToFooWithSplitName::getName) |
| 449 | + .containsExactly("Tony", "Bruce", "Steve"); |
| 450 | + assertThat(actual.getItems()).extracting(ImmutableToFooWithSplitName::getSurname) |
| 451 | + .containsExactly("Stark", "Banner", "Rogers"); |
| 452 | + underTest.reset(); |
| 453 | + } |
388 | 454 | } |
0 commit comments