From b55d95645be36dba334bd5ea5bb3e5732d6ae92d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 10 Sep 2024 09:51:34 +0200 Subject: [PATCH] improve business tests by creating a DSL --- .../HsDomainSetupHostingAssetValidator.java | 2 +- ...ainSetupHostingAssetValidatorUnitTest.java | 282 +++++++++--------- 2 files changed, 139 insertions(+), 145 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java index 08ac5179..392eba16 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidator.java @@ -44,7 +44,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator { if (verificationFound.isEmpty()) { violations.add( "[DNS] no TXT record '" + expectedTxtRecordValue + - "' found for domain name '" + domainName + "'"); + "' found for domain name '" + domainName + "' (nor in its super-domain)"); } break; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java index d275e6f6..2baa7e04 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/validators/HsDomainSetupHostingAssetValidatorUnitTest.java @@ -16,7 +16,9 @@ import javax.naming.NameNotFoundException; import javax.naming.NamingException; import javax.naming.ServiceUnavailableException; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; import static java.util.Map.entry; import static java.util.Map.ofEntries; @@ -27,13 +29,18 @@ import static org.assertj.core.api.Assertions.assertThat; class HsDomainSetupHostingAssetValidatorUnitTest { - static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder validEntityBuilder(final String domainName) { - final HsBookingItemRealEntity bookingItem = HsBookingItemRealEntity.builder() - .type(HsBookingItemType.DOMAIN_SETUP) - .resources(new HashMap<>(ofEntries( - entry("domainName", domainName) - ))) - .build(); + public static final Dns.Result DOMAIN_NOT_REGISTERED = Dns.Result.fromException(new NameNotFoundException( + "domain not registered")); + + static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder validEntityBuilder( + final String domainName, + final Function, HsBookingItemRealEntity> buildBookingItem) { + final HsBookingItemRealEntity bookingItem = buildBookingItem.apply( + HsBookingItemRealEntity.builder() + .type(HsBookingItemType.DOMAIN_SETUP) + .resources(new HashMap<>(ofEntries( + entry("domainName", domainName) + )))); HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem); return HsHostingAssetRbacEntity.builder() .type(DOMAIN_SETUP) @@ -41,8 +48,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest { .identifier(domainName); } - static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder validEntityBuilder() { - return validEntityBuilder("example.org"); + static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder validEntityBuilder(final String domainName) { + return validEntityBuilder(domainName, HsBookingItemRealEntity.HsBookingItemRealEntityBuilder::build); } @AfterEach @@ -50,6 +57,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest { Dns.resetFakeResults(); } + //===================================================================================================================== + enum InvalidDomainNameIdentifier { EMPTY(""), TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"), @@ -68,7 +77,12 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @EnumSource(InvalidDomainNameIdentifier.class) void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) { // given - final var givenEntity = validEntityBuilder().identifier(testCase.domainName).build(); + final var givenEntity = validEntityBuilder(testCase.domainName, + bib -> bib.resources(new HashMap<>(ofEntries( + entry("domainName", "example.org") + ))).build() + ).build(); + fakeValidDnsVerification(givenEntity); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when @@ -98,11 +112,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest { void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) { // given final var givenEntity = validEntityBuilder(testCase.domainName).identifier(testCase.domainName).build(); - final var expectedHash = givenEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - Dns.fakeResultForDomain( - testCase.domainName, - Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); + fakeValidDnsVerification(givenEntity); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); // when @@ -112,6 +122,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest { assertThat(result).isEmpty(); } + private static void fakeValidDnsVerification(final HsHostingAssetRbacEntity givenEntity) { + final var expectedHash = givenEntity.getBookingItem().getDirectValue("verificationCode", String.class); + Dns.fakeResultForDomain( + givenEntity.getIdentifier(), + Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); + } + @Test void containsNoProperties() { // when @@ -124,10 +141,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @Test void validatesReferencedEntities() { // given - final var domainSetupHostingAssetEntity = validEntityBuilder() + final var domainSetupHostingAssetEntity = validEntityBuilder("example.org", + bib -> bib.type(HsBookingItemType.CLOUD_SERVER).build()) .parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build()) .assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build()) - .bookingItem(HsBookingItemRealEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build()) .build(); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); @@ -144,9 +161,11 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @Test void rejectsDomainNameNotMatchingBookingItemDomainName() { // given - final var domainSetupHostingAssetEntity = validEntityBuilder() - .identifier("not-matching-booking-item-domain-name.org") - .build(); + final var domainSetupHostingAssetEntity = validEntityBuilder("not-matching-booking-item-domain-name.org", + bib -> bib.resources(new HashMap<>(ofEntries( + entry("domainName", "example.org") + ))).build() + ).build(); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); // when @@ -161,10 +180,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest { @ValueSource(strings = { "not-matching-booking-item-domain-name.org", "indirect.subdomain.example.org" }) void rejectsDomainNameWhichIsNotADirectSubdomainOfParentAsset(final String newDomainName) { // given - final var domainSetupHostingAssetEntity = validEntityBuilder() + final var domainSetupHostingAssetEntity = validEntityBuilder(newDomainName) .bookingItem(null) .parentAsset(createValidParentDomainSetupAsset("example.org")) - .identifier(newDomainName) .build(); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); @@ -178,10 +196,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest { } @Test - void expectsEitherParentAssetOrBookingItem() { + void rejectsIfNeitherBookingItemNorParentAssetAreSet() { // given - final var domainSetupHostingAssetEntity = validEntityBuilder().build(); + final var domainSetupHostingAssetEntity = validEntityBuilder("example.org") + .bookingItem(null) + .parentAsset(null) + .build(); Dns.fakeResultForDomain( domainSetupHostingAssetEntity.getIdentifier(), new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); @@ -191,7 +212,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest { final var result = validator.validateEntity(domainSetupHostingAssetEntity); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactly("'DOMAIN_SETUP:example.org.bookingItem' must be of type DOMAIN_SETUP but is null"); } enum DnsLookupFailureTestCase { @@ -222,7 +243,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest { void handlesDnsLookupFailures(final DnsLookupFailureTestCase testCase) { // given - final var domainSetupHostingAssetEntity = validEntityBuilder().build(); + final var domainSetupHostingAssetEntity = validEntityBuilder("example.org").build(); Dns.fakeResultForDomain( domainSetupHostingAssetEntity.getIdentifier(), Dns.Result.fromException(testCase.givenException)); @@ -239,153 +260,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest { } } + //===================================================================================================================== + @Test void allowSetupOfAvailableRegistrableDomain() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder().build(); - final var domainName = domainSetupHostingAssetEntity.getIdentifier(); - Dns.fakeResultForDomain( - domainName, - Dns.Result.fromException(new NameNotFoundException("domain not registered"))); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).isEmpty(); + domainSetupFor("example.com").notRegistered() + .isAccepted(); } @Test void rejectSetupOfExistingRegistrableDomainWithoutValidDnsVerification() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder().build(); - final var domainName = domainSetupHostingAssetEntity.getIdentifier(); - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - Dns.fakeResultForDomain( - domainName, - Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=SOME-DEFINITELY-WRONG-HASH")); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).contains("[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash - + "' found for domain name 'example.org'"); + domainSetupFor("example.com").registered() + .isRejectedWithCauseMissingVerificationIn("example.com"); } @Test void allowSetupOfExistingRegistrableDomainWithValidDnsVerification() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder().build(); - final var domainName = domainSetupHostingAssetEntity.getIdentifier(); - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - Dns.fakeResultForDomain( - domainName, - Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).isEmpty(); + domainSetupFor("example.org").registeredWithVerification() + .isAccepted(); } @Test void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); - // ... the new subdomain is not yet registered: - Dns.fakeResultForDomain( - "sub.example.org", - Dns.Result.fromException(new NameNotFoundException("domain not registered"))); - // ... and a valid verification-code in the super-domain: - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - Dns.fakeResultForDomain( - "example.org", - Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).isEmpty(); + domainSetupFor("sub.example.org").notRegistered().withVerificationIn("example.org") + .isAccepted(); } @Test - void RejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() { + void rejectSetupOfExistingRegistrableDomainWithInvalidDnsVerification() { + domainSetupFor("example.com").registeredWithInvalidVerification() + .isRejectedWithCauseMissingVerificationIn("example.com"); + } - // given - final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); - // ... the new subdomain is not yet registered: - Dns.fakeResultForDomain( - "sub.example.org", - Dns.Result.fromException(new NameNotFoundException("domain not registered"))); - // ... and a valid verification-code in the super-domain: - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); + @Test + void acceptSetupOfRegisteredSubdomainWithInvalidDnsVerificationButValidDnsVerificationInSuperDomain() { + domainSetupFor("sub.example.com").registeredWithInvalidVerification().withVerificationIn("example.com") + .isAccepted(); + } - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).containsExactly( - "[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash - + "' found for domain name 'example.org'"); + @Test + void rejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() { + domainSetupFor("sub.example.org").notRegistered() + .isRejectedWithCauseMissingVerificationIn("example.org"); } @Test void allowSetupOfExistingSubdomainWithValidDnsVerificationInSuperDomain() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); - // ... the new subdomain is already registered: - Dns.fakeResultForDomain("sub.example.org", Dns.Result.fromRecords()); - // ... and a valid verification-code in the super-domain: - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - Dns.fakeResultForDomain( - "example.org", - Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).isEmpty(); + domainSetupFor("sub.example.org").registered() + .withVerificationIn("example.org") + .isAccepted(); } @Test void rejectSetupOfExistingSubdomainWithoutDnsVerification() { - - // given - final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); - // ... the new subdomain is already registered: - Dns.fakeResultForDomain("sub.example.org", Dns.Result.fromRecords()); - final var expectedHash = domainSetupHostingAssetEntity.getBookingItem() - .getDirectValue("verificationCode", String.class); - final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); - - // when - final var result = validator.validateEntity(domainSetupHostingAssetEntity); - - // then - assertThat(result).contains("[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash - + "' found for domain name 'sub.example.org'"); + domainSetupFor("sub.example.org").registered() + .isRejectedWithCauseMissingVerificationIn("sub.example.org"); } + //==================================================================================================================== + private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) { final var bookingItem = HsBookingItemRealEntity.builder() .type(HsBookingItemType.DOMAIN_SETUP) @@ -399,4 +332,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest { .identifier(parentDomainName).build(); return parentAsset; } + + class DomainSetupBuilder { + + private final HsHostingAssetRbacEntity domainAsset; + private final String expectedHash; + + public DomainSetupBuilder(final String domainName) { + domainAsset = validEntityBuilder(domainName).build(); + expectedHash = domainAsset.getBookingItem().getDirectValue("verificationCode", String.class); + } + + DomainSetupBuilder notRegistered() { + Dns.fakeResultForDomain(domainAsset.getIdentifier(), DOMAIN_NOT_REGISTERED); + return this; + } + + DomainSetupBuilder registered() { + Dns.fakeResultForDomain( + domainAsset.getIdentifier(), + Dns.Result.fromRecords()); + return this; + } + + DomainSetupBuilder registeredWithInvalidVerification() { + Dns.fakeResultForDomain( + domainAsset.getIdentifier(), + Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=SOME-DEFINITELY-WRONG-HASH")); + return this; + } + + DomainSetupBuilder registeredWithVerification() { + withVerificationIn(domainAsset.getIdentifier()); + return this; + } + + DomainSetupBuilder withVerificationIn(final String domainName) { + Dns.fakeResultForDomain( + domainName, + Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash)); + return this; + } + + void isRejectedWithCauseMissingVerificationIn(final String domainName) { + assertThat(validate()).contains( + "[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash + + "' found for domain name '" + domainName + "' (nor in its super-domain)"); + } + + void isAccepted() { + assertThat(validate()).isEmpty(); + } + + private List validate() { + final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SETUP); + return validator.validateEntity(domainAsset); + } + } + + private DomainSetupBuilder domainSetupFor(final String domainName) { + return new DomainSetupBuilder(domainName); + } }