diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/EntityPatch.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/EntityPatch.java new file mode 100644 index 00000000..b9ec6ac5 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/EntityPatch.java @@ -0,0 +1,6 @@ +package net.hostsharing.hsadminng.hs.office.partner; + +public interface EntityPatch { + + void apply(R resource); +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatch.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatch.java index 90053ff5..96aa5137 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatch.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatch.java @@ -9,8 +9,9 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.UUID; import java.util.function.Function; +import java.util.function.Supplier; -class HsOfficePartnerEntityPatch { +class HsOfficePartnerEntityPatch implements EntityPatch { private final HsOfficePartnerEntity entity; private final Function> fetchContact; @@ -25,16 +26,17 @@ class HsOfficePartnerEntityPatch { this.fetchPerson = fetchPerson; } - void apply(final HsOfficePartnerPatchResource resource) { + @Override + public void apply(final HsOfficePartnerPatchResource resource) { OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> { - entity.setContact(fetchContact.apply(newValue).orElseThrow( - () -> new NoSuchElementException("cannot find contact uuid " + newValue) - )); + verifyNotNull(newValue, "contact"); + entity.setContact(fetchContact.apply(newValue) + .orElseThrow(noSuchElementException("contact", newValue))); }); OptionalFromJson.of(resource.getPersonUuid()).ifPresent(newValue -> { - entity.setPerson(fetchPerson.apply(newValue).orElseThrow( - () -> new NoSuchElementException("cannot find person uuid " + newValue) - )); + verifyNotNull(newValue, "person"); + entity.setPerson(fetchPerson.apply(newValue) + .orElseThrow(noSuchElementException("person", newValue))); }); OptionalFromJson.of(resource.getRegistrationOffice()).ifPresent(entity::setRegistrationOffice); OptionalFromJson.of(resource.getRegistrationNumber()).ifPresent(entity::setRegistrationNumber); @@ -42,4 +44,14 @@ class HsOfficePartnerEntityPatch { OptionalFromJson.of(resource.getBirthName()).ifPresent(entity::setBirthName); OptionalFromJson.of(resource.getDateOfDeath()).ifPresent(entity::setDateOfDeath); } + + private Supplier noSuchElementException(final String propertyName, final UUID newValue) { + return () -> new NoSuchElementException("cannot find '" + propertyName + "' uuid " + newValue); + } + + private void verifyNotNull(final UUID newValue, final String propertyName) { + if (newValue == null) { + throw new IllegalArgumentException("property '" + propertyName + "' must not be null"); + } + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatchUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatchUnitTest.java index 7c103dd4..9bba6ea1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatchUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatchUnitTest.java @@ -3,23 +3,20 @@ package net.hostsharing.hsadminng.hs.office.partner; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.openapitools.jackson.nullable.JsonNullable; +import org.junit.jupiter.api.TestInstance; import java.time.LocalDate; -import java.util.NoSuchElementException; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; -// TODO: there must be an easier way to test such patch classes -class HsOfficePartnerEntityPatchUnitTest { +@TestInstance(PER_CLASS) +class HsOfficePartnerEntityPatchUnitTest extends PatchUnitTestBase< + HsOfficePartnerPatchResource, + HsOfficePartnerEntity + > { private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); @@ -31,198 +28,84 @@ class HsOfficePartnerEntityPatchUnitTest { private static final LocalDate PATCHED_BIRTHDAY = LocalDate.parse("1990-12-31"); private static final LocalDate INITIAL_DAY_OF_DEATH = LocalDate.parse("2000-01-01"); - private static final LocalDate PATCHED_DAY_OF_DEATH = LocalDate.parse("2022-08-31"); + private static final LocalDate PATCHED_DATE_OF_DEATH = LocalDate.parse("2022-08-31"); - final HsOfficePartnerEntity givenPartner = new HsOfficePartnerEntity(); - private final HsOfficePersonEntity givenInitialPerson = new HsOfficePersonEntity(); - private final HsOfficeContactEntity givenInitialContact = new HsOfficeContactEntity(); + private final HsOfficePersonEntity givenInitialPerson = HsOfficePersonEntity.builder() + .uuid(INITIAL_PERSON_UUID) + .build(); + private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder() + .uuid(INITIAL_CONTACT_UUID) + .build(); - final HsOfficePartnerPatchResource patchResource = new HsOfficePartnerPatchResource(); - - private final HsOfficePartnerEntityPatch hsOfficePartnerEntityPatch = new HsOfficePartnerEntityPatch( - givenPartner, - uuid -> uuid == PATCHED_CONTACT_UUID - ? Optional.of(newContact(uuid)) - : Optional.empty(), - uuid -> uuid == PATCHED_PERSON_UUID - ? Optional.of(newPerson(uuid)) - : Optional.empty()); - - { - givenInitialPerson.setUuid(INITIAL_PERSON_UUID); - givenInitialContact.setUuid(INITIAL_CONTACT_UUID); - - givenPartner.setUuid(INITIAL_PARTNER_UUID); - givenPartner.setPerson(givenInitialPerson); - givenPartner.setContact(givenInitialContact); - givenPartner.setRegistrationOffice("initial Reg-Office"); - givenPartner.setRegistrationNumber("initial Reg-Number"); - givenPartner.setBirthday(INITIAL_BIRTHDAY); - givenPartner.setBirthName("initial birth name"); - givenPartner.setDateOfDeath(INITIAL_DAY_OF_DEATH); + @Override + HsOfficePartnerEntity newInitialEntity() { + final var p = new HsOfficePartnerEntity(); + p.setUuid(INITIAL_PARTNER_UUID); + p.setPerson(givenInitialPerson); + p.setContact(givenInitialContact); + p.setRegistrationOffice("initial Reg-Office"); + p.setRegistrationNumber("initial Reg-Number"); + p.setBirthday(INITIAL_BIRTHDAY); + p.setBirthName("initial birth name"); + p.setDateOfDeath(INITIAL_DAY_OF_DEATH); + return p; } - @Test - void willPatchAllProperties() { - // given - patchResource.setContactUuid(JsonNullable.of(PATCHED_CONTACT_UUID)); - patchResource.setPersonUuid(JsonNullable.of(PATCHED_PERSON_UUID)); - patchResource.setRegistrationNumber(JsonNullable.of("patched Reg-Number")); - patchResource.setRegistrationOffice(JsonNullable.of("patched Reg-Office")); - patchResource.setBirthday(JsonNullable.of(PATCHED_BIRTHDAY)); - patchResource.setBirthName(JsonNullable.of("patched birth name")); - patchResource.setDateOfDeath(JsonNullable.of(PATCHED_DAY_OF_DEATH)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedContactUuid(PATCHED_CONTACT_UUID) - .withPatchedPersonUuid(PATCHED_PERSON_UUID) - .withPatchedRegistrationOffice("patched Reg-Office") - .withPatchedRegistrationNumber("patched Reg-Number") - .withPatchedBirthday(PATCHED_BIRTHDAY) - .withPatchedBirthName("patched birth name") - .withPatchedDateOfDeath(PATCHED_DAY_OF_DEATH) - .matches(givenPartner); + @Override + HsOfficePartnerPatchResource newPatchResource() { + return new HsOfficePartnerPatchResource(); } - @Test - void willThrowIfNoContactFound() { - // given - patchResource.setContactUuid(JsonNullable.of(null)); - - // when - final var exception = catchThrowableOfType(() -> { - hsOfficePartnerEntityPatch.apply(patchResource); - }, NoSuchElementException.class); - - // then - assertThat(exception.getMessage()).isEqualTo("cannot find contact uuid null"); + @Override + HsOfficePartnerEntityPatch createPatcher(final HsOfficePartnerEntity partner) { + return new HsOfficePartnerEntityPatch( + partner, + uuid -> uuid == PATCHED_CONTACT_UUID + ? Optional.of(newContact(uuid)) + : Optional.empty(), + uuid -> uuid == PATCHED_PERSON_UUID + ? Optional.of(newPerson(uuid)) + : Optional.empty()); } - @Test - void willThrowIfNoPersonFound() { - // given - patchResource.setPersonUuid(JsonNullable.of(null)); - - // when - final var exception = catchThrowableOfType(() -> { - hsOfficePartnerEntityPatch.apply(patchResource); - }, NoSuchElementException.class); - - // then - assertThat(exception.getMessage()).isEqualTo("cannot find person uuid null"); + @Override + Stream testCases() { + return Stream.of( + new TestCase( + "contact", + HsOfficePartnerPatchResource::setContactUuid, + PATCHED_CONTACT_UUID, + HsOfficePartnerEntity::setContact, + newContact(PATCHED_CONTACT_UUID)) + .notNullable() + .resolvesUuid(), + new TestCase( + "person", + HsOfficePartnerPatchResource::setPersonUuid, + PATCHED_PERSON_UUID, + HsOfficePartnerEntity::setPerson, + newPerson(PATCHED_PERSON_UUID)) + .notNullable() + .resolvesUuid(), + new TestCase( + "registrationOffice", + HsOfficePartnerPatchResource::setRegistrationOffice, + "patched Reg-Office", + HsOfficePartnerEntity::setRegistrationOffice), + new TestCase( + "birthday", + HsOfficePartnerPatchResource::setBirthday, + PATCHED_BIRTHDAY, + HsOfficePartnerEntity::setBirthday), + new TestCase( + "dayOfDeath", + HsOfficePartnerPatchResource::setDateOfDeath, + PATCHED_DATE_OF_DEATH, + HsOfficePartnerEntity::setDateOfDeath) + ); } - @Test - void willPatchOnlyContactProperty() { - // given - patchResource.setContactUuid(JsonNullable.of(PATCHED_CONTACT_UUID)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedContactUuid(PATCHED_CONTACT_UUID) - .matches(givenPartner); - } - - @Test - void willPatchOnlyPersonProperty() { - // given - patchResource.setPersonUuid(JsonNullable.of(PATCHED_PERSON_UUID)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedPersonUuid(PATCHED_PERSON_UUID) - .matches(givenPartner); - } - - @ParameterizedTest - @ValueSource(strings = { "patched Reg-Office" }) - @NullSource - void willPatchOnlyRegOfficeProperty(final String patchedValue) { - // given - patchResource.setRegistrationOffice(JsonNullable.of(patchedValue)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedRegistrationOffice(patchedValue) - .matches(givenPartner); - } - - @ParameterizedTest - @ValueSource(strings = { "patched birth name" }) - @NullSource - void willPatchOnlyRegNumberProperty(final String patchedValue) { - // given - patchResource.setRegistrationNumber(JsonNullable.of(patchedValue)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedRegistrationNumber(patchedValue) - .matches(givenPartner); - } - - @ParameterizedTest - @EnumSource(LocalDatePatches.class) - void willPatchOnlyBirthdayProperty(final LocalDatePatches patch) { - // given - patchResource.setBirthday(JsonNullable.of(patch.value)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedBirthday(patch.value) - .matches(givenPartner); - } - - @ParameterizedTest - @ValueSource(strings = { "patched birth name" }) - @NullSource - void willPatchOnlyBirthNameProperty(final String patchedValue) { - // given - patchResource.setBirthName(JsonNullable.of(patchedValue)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedBirthName(patchedValue) - .matches(givenPartner); - } - - @ParameterizedTest - @EnumSource(LocalDatePatches.class) - void willPatchOnlyDateOfDeathProperty(final LocalDatePatches patch) { - // given - patchResource.setDateOfDeath(JsonNullable.of(patch.value)); - - // when - hsOfficePartnerEntityPatch.apply(patchResource); - - // then - new HsOfficePartnerEntityMatcher() - .withPatchedDateOfDeath(patch.value) - .matches(givenPartner); - } - - private HsOfficeContactEntity newContact(final UUID uuid) { + private static HsOfficeContactEntity newContact(final UUID uuid) { final var newContact = new HsOfficeContactEntity(); newContact.setUuid(uuid); return newContact; @@ -233,75 +116,4 @@ class HsOfficePartnerEntityPatchUnitTest { newPerson.setUuid(uuid); return newPerson; } - - private static class HsOfficePartnerEntityMatcher { - - private UUID expectedContactUuid = INITIAL_CONTACT_UUID; - private UUID expectedPersonUuid = INITIAL_PERSON_UUID; - private String expectedRegOffice = "initial Reg-Office"; - private String expectedRegNumber = "initial Reg-Number"; - private LocalDate expectedBirthday = INITIAL_BIRTHDAY; - private String expectedBirthName = "initial birth name"; - private LocalDate expectedDateOfDeath = INITIAL_DAY_OF_DEATH; - - HsOfficePartnerEntityMatcher withPatchedContactUuid(final UUID patchedContactUuid) { - expectedContactUuid = patchedContactUuid; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedPersonUuid(final UUID patchedPersonUuid) { - expectedPersonUuid = patchedPersonUuid; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedRegistrationOffice(final String patchedRegOffice) { - expectedRegOffice = patchedRegOffice; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedRegistrationNumber(final String patchedRegNumber) { - expectedRegNumber = patchedRegNumber; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedBirthday(final LocalDate patchedBirthday) { - expectedBirthday = patchedBirthday; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedBirthName(final String patchedBirthName) { - expectedBirthName = patchedBirthName; - return this; - } - - HsOfficePartnerEntityMatcher withPatchedDateOfDeath(final LocalDate patchedDayOfDeath) { - expectedDateOfDeath = patchedDayOfDeath; - return this; - } - - void matches(final HsOfficePartnerEntity givenPartner) { - - assertThat(givenPartner.getContact().getUuid()).isEqualTo(expectedContactUuid); - assertThat(givenPartner.getPerson().getUuid()).isEqualTo(expectedPersonUuid); - assertThat(givenPartner.getRegistrationOffice()).isEqualTo(expectedRegOffice); - assertThat(givenPartner.getRegistrationNumber()).isEqualTo(expectedRegNumber); - assertThat(givenPartner.getBirthday()).isEqualTo(expectedBirthday); - assertThat(givenPartner.getBirthName()).isEqualTo(expectedBirthName); - assertThat(givenPartner.getDateOfDeath()).isEqualTo(expectedDateOfDeath); - - } - - } - - enum LocalDatePatches { - REAL_VALUE(LocalDate.now()), - NULL_VALUE(null); - - final LocalDate value; - - LocalDatePatches(final LocalDate patchedBirthday) { - value = patchedBirthday; - } - } - } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/PatchUnitTestBase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/PatchUnitTestBase.java new file mode 100644 index 00000000..2ec6d0f9 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/PatchUnitTestBase.java @@ -0,0 +1,210 @@ +package net.hostsharing.hsadminng.hs.office.partner; + +import org.junit.jupiter.api.Named; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.openapitools.jackson.nullable.JsonNullable; + +import java.util.NoSuchElementException; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static org.assertj.core.api.Assumptions.assumeThat; + +public abstract class PatchUnitTestBase { + + @Test + void willPatchNoProperty() { + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + + // when + createPatcher(givenEntity).apply(patchResource); + + // then + final var expectedEntity = newInitialEntity(); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(expectedEntity); + } + + @Test + void willPatchAllProperties() { + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + testCases().forEach(testCase -> + testCase.resourceSetter.accept(patchResource, JsonNullable.of(testCase.givenPatchedValue)) + ); + + // when + createPatcher(givenEntity).apply(patchResource); + + // then + final var expectedEntity = newInitialEntity(); + testCases().forEach(testCase -> + testCase.entitySetter.accept(expectedEntity, testCase.expectedPatchValue) + ); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(expectedEntity); + } + + @ParameterizedTest + @MethodSource("testCaseArguments") + void willPatchOnlyGivenProperty(final TestCase testCase) { + + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + testCase.resourceSetter.accept(patchResource, JsonNullable.of(testCase.givenPatchedValue)); + + // when + createPatcher(givenEntity).apply(patchResource); + + // then + final var expectedEntity = newInitialEntity(); + testCase.entitySetter.accept(expectedEntity, testCase.expectedPatchValue); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(expectedEntity); + } + + @ParameterizedTest + @MethodSource("testCaseArguments") + void willThrowIfUUidCannotBeResolved(final TestCase testCase) { + assumeThat(testCase.resolvesUuid).isTrue(); + + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + final var givenPatchValue = UUID.fromString("11111111-1111-1111-1111-111111111111"); + testCase.resourceSetter.accept(patchResource, JsonNullable.of(givenPatchValue)); + + // when + final var exception = catchThrowableOfType(() -> { + createPatcher(givenEntity).apply(patchResource); + }, NoSuchElementException.class); + + // then + assertThat(exception).isInstanceOf(NoSuchElementException.class) + .hasMessage("cannot find '" + testCase.name + "' uuid " + givenPatchValue); + } + + @ParameterizedTest + @MethodSource("testCaseArguments") + void willPatchOnlyGivenPropertyToNull(final TestCase testCase) { + assumeThat(testCase.nullable).isTrue(); + + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + testCase.resourceSetter.accept(patchResource, JsonNullable.of(null)); + + // when + createPatcher(givenEntity).apply(patchResource); + + // then + final var expectedEntity = newInitialEntity(); + testCase.entitySetter.accept(expectedEntity, null); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(expectedEntity); + } + + @ParameterizedTest + @MethodSource("testCaseArguments") + void willThrowExceptionIfResourcePropertyIsNull(final TestCase testCase) { + assumeThat(testCase.nullable).isFalse(); + + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + testCase.resourceSetter.accept(patchResource, JsonNullable.of(null)); + + // when + final var actualException = catchThrowableOfType( + () -> createPatcher(givenEntity).apply(patchResource), + IllegalArgumentException.class); + + // then + assertThat(actualException).hasMessage("property '" + testCase.name + "' must not be null"); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(newInitialEntity()); + } + + @ParameterizedTest + @MethodSource("testCaseArguments") + void willNotPatchIfGivenPropertyNotGiven(final TestCase testCase) { + + // given + final var givenEntity = newInitialEntity(); + final var patchResource = newPatchResource(); + testCase.resourceSetter.accept(patchResource, null); + + // when + createPatcher(givenEntity).apply(patchResource); + + // then + final var expectedEntity = newInitialEntity(); + assertThat(givenEntity).usingRecursiveComparison().isEqualTo(expectedEntity); + } + + abstract E newInitialEntity(); + + abstract R newPatchResource(); + + abstract EntityPatch createPatcher(final E entity); + + abstract Stream testCases(); + + Stream testCaseArguments() { + return testCases().map(tc -> Arguments.of(Named.of(tc.name, tc))); + } + + class TestCase { + + private final String name; + public final Object givenPatchedValue; + private final BiConsumer> resourceSetter; + private final BiConsumer entitySetter; + private final Object expectedPatchValue; + + private boolean nullable = true; + private boolean resolvesUuid = false; + + TestCase( + final String name, + final BiConsumer> resourceSetter, + final V givenPatchValue, + final BiConsumer entitySetter + ) { + this.name = name; + this.resourceSetter = (BiConsumer>) (BiConsumer) resourceSetter; + this.givenPatchedValue = givenPatchValue; + this.entitySetter = (BiConsumer) entitySetter; + this.expectedPatchValue = givenPatchValue; + } + + TestCase( + final String name, + final BiConsumer> resourceSetter, + final V givenPatchValue, + final BiConsumer entitySetter, + final S expectedPatchValue + ) { + this.name = name; + this.resourceSetter = (BiConsumer>) (BiConsumer) resourceSetter; + this.givenPatchedValue = givenPatchValue; + this.entitySetter = (BiConsumer) entitySetter; + this.expectedPatchValue = expectedPatchValue; + } + + TestCase notNullable() { + nullable = false; + return this; + } + + TestCase resolvesUuid() { + resolvesUuid = true; + return this; + } + } +}