From 5f28f1267639f1dd1f629794cd0575c15f827d01 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 12 Jun 2024 09:55:45 +0200 Subject: [PATCH] dbuser+db test data for hierarchical multi-options validations --- .../HsBookingItemEntityValidator.java | 18 ++-- ...HsManagedWebspaceBookingItemValidator.java | 12 ++- .../hs/validation/ValidatableProperty.java | 21 ++++- ...gedServerBookingItemValidatorUnitTest.java | 83 +++++++++++++++++++ 4 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java index 28cecedf..a2067e9b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsBookingItemEntityValidator.java @@ -6,11 +6,13 @@ import net.hostsharing.hsadminng.hs.validation.HsEntityValidator; import net.hostsharing.hsadminng.hs.validation.ValidatableProperty; import jakarta.validation.ValidationException; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; @@ -77,14 +79,18 @@ public class HsBookingItemEntityValidator extends HsEntityValidator validateSubEntities(final HsBookingItemEntity bookingItem) { - return stream(propertyValidators) - .filter(ValidatableProperty::isTotalsValidator) - .map(prop -> validateMaxTotalValue(bookingItem, prop)) - .filter(Objects::nonNull) - .toList(); + return Stream.concat( + stream(propertyValidators) + .map(propDef -> propDef.validateTotals(bookingItem)) + .flatMap(Collection::stream), + stream(propertyValidators) + .filter(ValidatableProperty::isTotalsValidator) + .map(prop -> validateMaxTotalValue(bookingItem, prop)) + ).filter(Objects::nonNull).toList(); } - private String validateMaxTotalValue( + // FIXME: convert into generic shape like multi-options validator + private static String validateMaxTotalValue( final HsBookingItemEntity bookingItem, final ValidatableProperty propDef) { final var propName = propDef.propertyName(); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java index e1a00bbf..0e94dc31 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedWebspaceBookingItemValidator.java @@ -25,21 +25,22 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(), integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(), integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(), - integerProperty("MultiOptions").min(1).max(100).step(1).required() + integerProperty("MultiOptions").min(1).max(100).step(1).withDefault(1) .eachComprising( 25, unixUsers()) .eachComprising( 5, databaseUsers()) .eachComprising( 5, databases()) .eachComprising(250, eMailAddresses()), - integerProperty("Daemons").min(0).max(10).optional(), + integerProperty("Daemons").min(0).max(10).withDefault(0), booleanProperty("Online Office Server").optional(), - enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").optional() + enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").withDefault("BASIC") ); } private static TriFunction> unixUsers() { return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = entity.getSubHostingAssets().stream() - .filter(bi -> bi.getType() == UNIX_USER) + .flatMap(ha -> ha.getSubHostingAssets().stream()) + .filter(ha -> ha.getType() == UNIX_USER) .count(); final long limitingValue = prop.getValue(entity.getResources()); if (unixUserCount > factor*limitingValue) { @@ -52,6 +53,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator private static TriFunction> databaseUsers() { return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = entity.getSubHostingAssets().stream() + .flatMap(ha -> ha.getSubHostingAssets().stream()) .filter(bi -> bi.getType() == PGSQL_USER || bi.getType() == MARIADB_USER ) .count(); final long limitingValue = prop.getValue(entity.getResources()); @@ -65,6 +67,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator private static TriFunction> databases() { return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = entity.getSubHostingAssets().stream() + .flatMap(ha -> ha.getSubHostingAssets().stream()) .filter(bi -> bi.getType()==PGSQL_USER || bi.getType()==MARIADB_USER ) .flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream() .filter(ha -> ha.getType()==PGSQL_DATABASE || ha.getType()==MARIADB_DATABASE)) @@ -80,6 +83,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator private static TriFunction> eMailAddresses() { return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> { final var unixUserCount = entity.getSubHostingAssets().stream() + .flatMap(ha -> ha.getSubHostingAssets().stream()) .filter(bi -> bi.getType() == DOMAIN_EMAIL_SETUP) .flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream() .filter(ha -> ha.getType()==EMAIL_ADDRESS)) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java index 6e35780f..062ec4f7 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/validation/ValidatableProperty.java @@ -8,14 +8,17 @@ import net.hostsharing.hsadminng.mapper.Array; import org.apache.commons.lang3.function.TriFunction; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import static java.util.Collections.emptyList; @RequiredArgsConstructor public abstract class ValidatableProperty { @@ -64,7 +67,7 @@ public abstract class ValidatableProperty { } public boolean isTotalsValidator() { - return isTotalsValidator; + return isTotalsValidator || asTotalLimitValidators != null; } public Integer thresholdPercentage() { @@ -155,4 +158,20 @@ public abstract class ValidatableProperty { } return value; } + + public List validateTotals(final HsBookingItemEntity bookingItem) { + if (asTotalLimitValidators==null) { + return emptyList(); + } + return asTotalLimitValidators.stream() + .map(v -> v.apply(bookingItem)) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(v -> wrap(v)) + .toList(); + } + + private String wrap(final String v) { + return v; + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java index 987da16a..415ef895 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/validators/HsManagedServerBookingItemValidatorUnitTest.java @@ -3,15 +3,23 @@ package net.hostsharing.hsadminng.hs.booking.item.validators; import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import org.junit.jupiter.api.Test; +import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static java.util.Arrays.stream; import static java.util.List.of; import static java.util.Map.entry; import static java.util.Map.ofEntries; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE; import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; import static net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidator.forType; import static org.assertj.core.api.Assertions.assertThat; @@ -119,4 +127,79 @@ class HsManagedServerBookingItemValidatorUnitTest { "D-12345:Test-Project:null.parentItem.total Traffic is 5500 GB exceeds max total Traffic 5000 GB" ); } + + @Test + void validatesExceedingTotals() { + // given + final var managedWebspaceBookingItem = HsBookingItemEntity.builder() + .type(MANAGED_WEBSPACE) + .caption("test Managed-Webspace") + .resources(ofEntries( + entry("SSD", 100), + entry("Traffic", 1000), + entry("MultiOptions", 1) + )) + .subHostingAssets(of( + HsHostingAssetEntity.builder() + .type(HsHostingAssetType.MANAGED_WEBSPACE) + .identifier("abc00") + .subHostingAssets(concat( + generate(26, HsHostingAssetType.UNIX_USER, "xyz00-%c%c"), + generateDbUsersWithDatabases(3, HsHostingAssetType.PGSQL_USER, + "xyz00_%c%c", + 1, HsHostingAssetType.PGSQL_DATABASE + ), + generateDbUsersWithDatabases(3, HsHostingAssetType.MARIADB_USER, + "xyz00_%c%c", + 1, HsHostingAssetType.MARIADB_DATABASE + ) + )) + .build() + )) + .build(); + + // when + final var result = HsBookingItemEntityValidator.doValidate(managedWebspaceBookingItem); + + // then + assertThat(result).containsExactlyInAnyOrder( + "MultiOptions=1 allows at maximum 25 unix users, but 26 found", + "MultiOptions=1 allows at maximum 5 database users, but 6 found", + "MultiOptions=1 allows at maximum 5 databases, but 6 found" + ); + } + + @SafeVarargs + private List concat(final List... hostingAssets) { + return stream(hostingAssets) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private List generate(final int count, final HsHostingAssetType hostingAssetType, + final String identifierPattern) { + return IntStream.range(0, count) + .mapToObj(number -> HsHostingAssetEntity.builder() + .type(hostingAssetType) + .identifier(identifierPattern.formatted((number/'a')+'a', (number%'a')+'a')) + .build()) + .toList(); + } + + private List generateDbUsersWithDatabases( + final int userCount, + final HsHostingAssetType directAssetType, + final String directAssetIdentifierFormat, final int dbCount, + final HsHostingAssetType subAssetType) { + return IntStream.range(0, userCount) + .mapToObj(n -> HsHostingAssetEntity.builder() + .type(directAssetType) + .identifier(directAssetIdentifierFormat.formatted((n/'a')+'a', (n%'a')+'a')) + .subHostingAssets( + generate(dbCount, subAssetType, "xyz00_%c%c%%c%%c".formatted((n/'a')+'a', (n%'a')+'a')) + ) + .build()) + .toList(); + } + }