improve business tests by creating a DSL

This commit is contained in:
Michael Hoennig 2024-09-10 09:51:34 +02:00
parent 17a5aa2ff4
commit b55d95645b
2 changed files with 139 additions and 145 deletions

View File

@ -44,7 +44,7 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
if (verificationFound.isEmpty()) { if (verificationFound.isEmpty()) {
violations.add( violations.add(
"[DNS] no TXT record '" + expectedTxtRecordValue + "[DNS] no TXT record '" + expectedTxtRecordValue +
"' found for domain name '" + domainName + "'"); "' found for domain name '" + domainName + "' (nor in its super-domain)");
} }
break; break;
} }

View File

@ -16,7 +16,9 @@ import javax.naming.NameNotFoundException;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.ServiceUnavailableException; import javax.naming.ServiceUnavailableException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import static java.util.Map.entry; import static java.util.Map.entry;
import static java.util.Map.ofEntries; import static java.util.Map.ofEntries;
@ -27,13 +29,18 @@ import static org.assertj.core.api.Assertions.assertThat;
class HsDomainSetupHostingAssetValidatorUnitTest { class HsDomainSetupHostingAssetValidatorUnitTest {
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(final String domainName) { public static final Dns.Result DOMAIN_NOT_REGISTERED = Dns.Result.fromException(new NameNotFoundException(
final HsBookingItemRealEntity bookingItem = HsBookingItemRealEntity.builder() "domain not registered"));
.type(HsBookingItemType.DOMAIN_SETUP)
.resources(new HashMap<>(ofEntries( static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(
entry("domainName", domainName) final String domainName,
))) final Function<HsBookingItemRealEntity.HsBookingItemRealEntityBuilder<?, ?>, HsBookingItemRealEntity> buildBookingItem) {
.build(); 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); HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem);
return HsHostingAssetRbacEntity.builder() return HsHostingAssetRbacEntity.builder()
.type(DOMAIN_SETUP) .type(DOMAIN_SETUP)
@ -41,8 +48,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
.identifier(domainName); .identifier(domainName);
} }
static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder() { static HsHostingAssetRbacEntity.HsHostingAssetRbacEntityBuilder<?, ?> validEntityBuilder(final String domainName) {
return validEntityBuilder("example.org"); return validEntityBuilder(domainName, HsBookingItemRealEntity.HsBookingItemRealEntityBuilder::build);
} }
@AfterEach @AfterEach
@ -50,6 +57,8 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
Dns.resetFakeResults(); Dns.resetFakeResults();
} }
//=====================================================================================================================
enum InvalidDomainNameIdentifier { enum InvalidDomainNameIdentifier {
EMPTY(""), EMPTY(""),
TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"), TOO_LONG("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456890123456789.de"),
@ -68,7 +77,12 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@EnumSource(InvalidDomainNameIdentifier.class) @EnumSource(InvalidDomainNameIdentifier.class)
void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) { void rejectsInvalidIdentifier(final InvalidDomainNameIdentifier testCase) {
// given // 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()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
// when // when
@ -98,11 +112,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) { void acceptsValidIdentifier(final ValidDomainNameIdentifier testCase) {
// given // given
final var givenEntity = validEntityBuilder(testCase.domainName).identifier(testCase.domainName).build(); final var givenEntity = validEntityBuilder(testCase.domainName).identifier(testCase.domainName).build();
final var expectedHash = givenEntity.getBookingItem() fakeValidDnsVerification(givenEntity);
.getDirectValue("verificationCode", String.class);
Dns.fakeResultForDomain(
testCase.domainName,
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=" + expectedHash));
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
// when // when
@ -112,6 +122,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
assertThat(result).isEmpty(); 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 @Test
void containsNoProperties() { void containsNoProperties() {
// when // when
@ -124,10 +141,10 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@Test @Test
void validatesReferencedEntities() { void validatesReferencedEntities() {
// given // 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()) .parentAsset(HsHostingAssetRealEntity.builder().type(CLOUD_SERVER).build())
.assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build()) .assignedToAsset(HsHostingAssetRealEntity.builder().type(MANAGED_SERVER).build())
.bookingItem(HsBookingItemRealEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
.build(); .build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
@ -144,9 +161,11 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@Test @Test
void rejectsDomainNameNotMatchingBookingItemDomainName() { void rejectsDomainNameNotMatchingBookingItemDomainName() {
// given // given
final var domainSetupHostingAssetEntity = validEntityBuilder() final var domainSetupHostingAssetEntity = validEntityBuilder("not-matching-booking-item-domain-name.org",
.identifier("not-matching-booking-item-domain-name.org") bib -> bib.resources(new HashMap<>(ofEntries(
.build(); entry("domainName", "example.org")
))).build()
).build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
// when // when
@ -161,10 +180,9 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
@ValueSource(strings = { "not-matching-booking-item-domain-name.org", "indirect.subdomain.example.org" }) @ValueSource(strings = { "not-matching-booking-item-domain-name.org", "indirect.subdomain.example.org" })
void rejectsDomainNameWhichIsNotADirectSubdomainOfParentAsset(final String newDomainName) { void rejectsDomainNameWhichIsNotADirectSubdomainOfParentAsset(final String newDomainName) {
// given // given
final var domainSetupHostingAssetEntity = validEntityBuilder() final var domainSetupHostingAssetEntity = validEntityBuilder(newDomainName)
.bookingItem(null) .bookingItem(null)
.parentAsset(createValidParentDomainSetupAsset("example.org")) .parentAsset(createValidParentDomainSetupAsset("example.org"))
.identifier(newDomainName)
.build(); .build();
final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(domainSetupHostingAssetEntity.getType());
@ -178,10 +196,13 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
} }
@Test @Test
void expectsEitherParentAssetOrBookingItem() { void rejectsIfNeitherBookingItemNorParentAssetAreSet() {
// given // given
final var domainSetupHostingAssetEntity = validEntityBuilder().build(); final var domainSetupHostingAssetEntity = validEntityBuilder("example.org")
.bookingItem(null)
.parentAsset(null)
.build();
Dns.fakeResultForDomain( Dns.fakeResultForDomain(
domainSetupHostingAssetEntity.getIdentifier(), domainSetupHostingAssetEntity.getIdentifier(),
new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null)); new Dns.Result(Dns.Status.NAME_NOT_FOUND, null, null));
@ -191,7 +212,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
final var result = validator.validateEntity(domainSetupHostingAssetEntity); final var result = validator.validateEntity(domainSetupHostingAssetEntity);
// then // then
assertThat(result).isEmpty(); assertThat(result).containsExactly("'DOMAIN_SETUP:example.org.bookingItem' must be of type DOMAIN_SETUP but is null");
} }
enum DnsLookupFailureTestCase { enum DnsLookupFailureTestCase {
@ -222,7 +243,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
void handlesDnsLookupFailures(final DnsLookupFailureTestCase testCase) { void handlesDnsLookupFailures(final DnsLookupFailureTestCase testCase) {
// given // given
final var domainSetupHostingAssetEntity = validEntityBuilder().build(); final var domainSetupHostingAssetEntity = validEntityBuilder("example.org").build();
Dns.fakeResultForDomain( Dns.fakeResultForDomain(
domainSetupHostingAssetEntity.getIdentifier(), domainSetupHostingAssetEntity.getIdentifier(),
Dns.Result.fromException(testCase.givenException)); Dns.Result.fromException(testCase.givenException));
@ -239,153 +260,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
} }
} }
//=====================================================================================================================
@Test @Test
void allowSetupOfAvailableRegistrableDomain() { void allowSetupOfAvailableRegistrableDomain() {
domainSetupFor("example.com").notRegistered()
// given .isAccepted();
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();
} }
@Test @Test
void rejectSetupOfExistingRegistrableDomainWithoutValidDnsVerification() { void rejectSetupOfExistingRegistrableDomainWithoutValidDnsVerification() {
domainSetupFor("example.com").registered()
// given .isRejectedWithCauseMissingVerificationIn("example.com");
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'");
} }
@Test @Test
void allowSetupOfExistingRegistrableDomainWithValidDnsVerification() { void allowSetupOfExistingRegistrableDomainWithValidDnsVerification() {
domainSetupFor("example.org").registeredWithVerification()
// given .isAccepted();
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();
} }
@Test @Test
void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() { void allowSetupOfUnregisteredSubdomainWithValidDnsVerificationInSuperDomain() {
domainSetupFor("sub.example.org").notRegistered().withVerificationIn("example.org")
// given .isAccepted();
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();
} }
@Test @Test
void RejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() { void rejectSetupOfExistingRegistrableDomainWithInvalidDnsVerification() {
domainSetupFor("example.com").registeredWithInvalidVerification()
.isRejectedWithCauseMissingVerificationIn("example.com");
}
// given @Test
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); void acceptSetupOfRegisteredSubdomainWithInvalidDnsVerificationButValidDnsVerificationInSuperDomain() {
// ... the new subdomain is not yet registered: domainSetupFor("sub.example.com").registeredWithInvalidVerification().withVerificationIn("example.com")
Dns.fakeResultForDomain( .isAccepted();
"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());
// when @Test
final var result = validator.validateEntity(domainSetupHostingAssetEntity); void rejectSetupOfUnregisteredSubdomainWithoutDnsVerificationInSuperDomain() {
domainSetupFor("sub.example.org").notRegistered()
// then .isRejectedWithCauseMissingVerificationIn("example.org");
assertThat(result).containsExactly(
"[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=" + expectedHash
+ "' found for domain name 'example.org'");
} }
@Test @Test
void allowSetupOfExistingSubdomainWithValidDnsVerificationInSuperDomain() { void allowSetupOfExistingSubdomainWithValidDnsVerificationInSuperDomain() {
domainSetupFor("sub.example.org").registered()
// given .withVerificationIn("example.org")
final var domainSetupHostingAssetEntity = validEntityBuilder("sub.example.org").build(); .isAccepted();
// ... 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();
} }
@Test @Test
void rejectSetupOfExistingSubdomainWithoutDnsVerification() { void rejectSetupOfExistingSubdomainWithoutDnsVerification() {
domainSetupFor("sub.example.org").registered()
// given .isRejectedWithCauseMissingVerificationIn("sub.example.org");
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'");
} }
//====================================================================================================================
private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) { private static HsHostingAssetRealEntity createValidParentDomainSetupAsset(final String parentDomainName) {
final var bookingItem = HsBookingItemRealEntity.builder() final var bookingItem = HsBookingItemRealEntity.builder()
.type(HsBookingItemType.DOMAIN_SETUP) .type(HsBookingItemType.DOMAIN_SETUP)
@ -399,4 +332,65 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
.identifier(parentDomainName).build(); .identifier(parentDomainName).build();
return parentAsset; 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<String> validate() {
final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SETUP);
return validator.validateEntity(domainAsset);
}
}
private DomainSetupBuilder domainSetupFor(final String domainName) {
return new DomainSetupBuilder(domainName);
}
} }