From faaca44bea60bd6c75077fcd2c17f13a2b9eb7f0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 3 May 2024 14:50:05 +0200 Subject: [PATCH] hosting asset validation for Cloud-Server to Webspace --- .../asset/HsHostingAssetController.java | 15 +- .../HsHostingAssetPropertyValidator.java | 95 ----------- .../asset/HsHostingAssetValidator.java | 51 ------ .../HsHostingAssetPropertyValidator.java | 148 ++++++++++++++++++ .../validator/HsHostingAssetValidator.java | 75 +++++++++ .../hs/hosting/asset/validator/lombok.config | 3 + .../hsadminng/arch/ArchitectureTest.java | 1 + ...sHostingAssetControllerAcceptanceTest.java | 37 ++++- .../HsHostingAssetValidatorUnitTest.java | 97 ++++++++++++ 9 files changed, 369 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropertyValidator.java delete mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidator.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidatorUnitTest.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java index b3411576..384fc2e3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetController.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset; +import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetsApi; import net.hostsharing.hsadminng.context.Context; @@ -15,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; +import jakarta.validation.ValidationException; import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; @@ -71,11 +73,6 @@ public class HsHostingAssetController implements HsHostingAssetsApi { return ResponseEntity.created(uri).body(mapped); } - private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { - HsHostingAssetValidator.forType(entityToSave.getType()).validate(entityToSave); - return entityToSave; - } - @Override @Transactional(readOnly = true) public ResponseEntity getAssetByUuid( @@ -125,6 +122,14 @@ public class HsHostingAssetController implements HsHostingAssetsApi { return ResponseEntity.ok(mapped); } + private HsHostingAssetEntity valid(final HsHostingAssetEntity entityToSave) { + final var violations = HsHostingAssetValidator.forType(entityToSave.getType()).validate(entityToSave); + if (!violations.isEmpty()) { + throw new ValidationException(violations.toString()); + } + return entityToSave; + } + @SuppressWarnings("unchecked") final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { entity.putConfig(KeyValueMap.from(resource.getConfig())); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropertyValidator.java deleted file mode 100644 index 7dd4154d..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetPropertyValidator.java +++ /dev/null @@ -1,95 +0,0 @@ -package net.hostsharing.hsadminng.hs.hosting.asset; - -import lombok.With; -import lombok.experimental.SuperBuilder; - -import java.util.Map; - -class Validator { -} - -@SuperBuilder -class PropertyValidator extends Validator { - String propertyName; -} - -@SuperBuilder -class NumericPropertyValidator extends PropertyValidator { - - private String unit; - private Integer min; - private Integer max; - private Integer step; - - static NumericPropertyValidatorBuilder numericProperty(final String propertyName) { - return NumericPropertyValidator.builder().propertyName(propertyName); - } -} - -@With -@SuperBuilder -class EnumPropertyValidator extends PropertyValidator { - - private String[] values; - - static EnumPropertyValidatorBuilderExtension enumProperty(final String propertyName) { - return new EnumPropertyValidatorBuilderExtension(propertyName); - } - static class EnumPropertyValidatorBuilderExtension extends ValidatorBuilder { - - private final String propertyName; - private String[] values; - - EnumPropertyValidatorBuilderExtension(final String propertyName) { - this.propertyName = propertyName; - } - - - public EnumPropertyValidatorBuilderExtension values(final String... values) { - this.values = values; - return this; - } - - @Override - protected EnumPropertyValidatorBuilderExtension self() { - return this; - } - - @Override - public EnumPropertyValidator build() { - return EnumPropertyValidator.builder().propertyName(propertyName).values(values).build(); - } - } -} - -@SuperBuilder -class BooleanPropertyValidator extends PropertyValidator { - - private Map.Entry falseIf; - static BooleanPropertyValidatorBuilderExtension booleanProperty(final String propertyName) { - return new BooleanPropertyValidatorBuilderExtension(propertyName); - } - - static class BooleanPropertyValidatorBuilderExtension extends PropertyValidatorBuilder { - - - BooleanPropertyValidatorBuilderExtension(final String propertyName) { - super.propertyName(propertyName); - } - - - BooleanPropertyValidatorBuilderExtension falseIf(final String propertyName, final String propertyValue) { - return this; - } - - @Override - protected BooleanPropertyValidatorBuilderExtension self() { - return this; - } - - @Override - public BooleanPropertyValidator build() { - return new BooleanPropertyValidator() - } - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidator.java deleted file mode 100644 index 95f94124..00000000 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidator.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.hostsharing.hsadminng.hs.hosting.asset; - -import java.util.Map; - -import static java.util.Map.entry; -import static net.hostsharing.hsadminng.hs.hosting.asset.BooleanPropertyValidator.booleanProperty; -import static net.hostsharing.hsadminng.hs.hosting.asset.EnumPropertyValidator.enumProperty; -import static net.hostsharing.hsadminng.hs.hosting.asset.NumericPropertyValidator.numericProperty; - -public class HsHostingAssetValidator { - - private static final Map validators = Map.ofEntries( - entry(HsHostingAssetType.CLOUD_SERVER, new HsHostingAssetValidator( - numericProperty("CPUs").min(1).max(32).build(), - numericProperty("RAM").unit("GB").min(1).max(128).build(), - numericProperty("SSD").unit("GB").min(25).max(1000).step(25).build(), - numericProperty("HDD").unit("GB").min(0).max(4000).step(250).build(), - numericProperty("Traffic").unit("GB").min(250).max(10000).step(250).build(), - enumProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").build())), - entry(HsHostingAssetType.MANAGED_SERVER, new HsHostingAssetValidator( - numericProperty("CPUs").min(1).max(32).build(), - numericProperty("RAM").unit("GB").min(1).max(128).build(), - numericProperty("SSD").unit("GB").min(25).max(1000).step(25).build(), - numericProperty("HDD").unit("GB").min(0).max(4000).step(250).build(), - numericProperty("Traffic").unit("GB").min(250).max(10000).step(250).build(), - enumProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").build(), - booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").build(), - booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").build(), - booleanProperty("SLA-PgSQL").falseIf("SLA-Platform", "BASIC").build(), - booleanProperty("SLA-Office").falseIf("SLA-Platform", "BASIC").build(), - booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").build())), - entry(HsHostingAssetType.MANAGED_WEBSPACE, new HsHostingAssetValidator( - numericProperty("SSD").unit("GB").min(1).max(100).step(1).build(), - numericProperty("HDD").unit("GB").min(0).max(250).step(10).build(), - numericProperty("Traffic").unit("GB").min(10).max(1000).step(10).build(), - enumProperty("SLA-Platform").values("BASIC", "EXT24H").build(), - numericProperty("Daemons").min(0).max(10).build(), - booleanProperty("Online Office Server").build()) - )); - - public static HsHostingAssetValidator forType(final HsHostingAssetType type) { - return validators.get(type); - } - - HsHostingAssetValidator(final Validator... validators) { - - } - - public void validate(final HsHostingAssetEntity entityToSave) { - } -} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java new file mode 100644 index 00000000..71a1ec13 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetPropertyValidator.java @@ -0,0 +1,148 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validator; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@RequiredArgsConstructor +public abstract class HsHostingAssetPropertyValidator { + + final Class type; + final String propertyName; + private Boolean required; + + public static Map.Entry defType(K k, V v) { + return new SimpleImmutableEntry<>(k, v); + } + + public HsHostingAssetPropertyValidator required() { + required = Boolean.TRUE; + return this; + } + + public HsHostingAssetPropertyValidator optional() { + required = Boolean.FALSE; + return this; + } + + public final List validate(final Map props) { + final var result = new ArrayList(); + final var propValue = props.get(propertyName); + if (propValue == null) { + if (required) { + result.add("'" + propertyName + "' is required but missing"); + } + } + if (propValue != null){ + if ( type.isInstance(propValue)) { + //noinspection unchecked + validate(result, (T) propValue, props); + } else { + result.add("'" + propertyName + "' is expected to be of type " + type + ", " + + "but is of type '" + propValue.getClass().getSimpleName() + "'"); + } + } + return result; + } + + protected abstract void validate(final ArrayList result, final T propValue, final Map props); + + public void verifyConsistency(final Map.Entry typeDef) { + if (required == null ) { + throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); + } + } +} + +@Setter +class IntegerPropertyValidator extends HsHostingAssetPropertyValidator{ + + private String unit; + private Integer min; + private Integer max; + private Integer step; + + public static IntegerPropertyValidator integerProperty(final String propertyName) { + return new IntegerPropertyValidator(propertyName); + } + + private IntegerPropertyValidator(final String propertyName) { + super(Integer.class, propertyName); + } + + + @Override + protected void validate(final ArrayList result, final Integer propValue, final Map props) { + if (min != null && propValue < min) { + result.add("'" + propertyName + "' is expected to be >= " + min + " but is " + propValue); + } + if (max != null && propValue > max) { + result.add("'" + propertyName + "' is expected to be <= " + max + " but is " + propValue); + } + if (step != null && propValue % step != 0) { + result.add("'" + propertyName + "' is expected to be multiple of " + step + " but is " + propValue); + } + } +} + +@Setter +class EnumPropertyValidator extends HsHostingAssetPropertyValidator { + + private String[] values; + + private EnumPropertyValidator(final String propertyName) { + super(String.class, propertyName); + } + + public static EnumPropertyValidator enumProperty(final String propertyName) { + return new EnumPropertyValidator(propertyName); + } + + public HsHostingAssetPropertyValidator values(final String... values) { + this.values = values; + return this; + } + + @Override + protected void validate(final ArrayList result, final String propValue, final Map props) { + if (Arrays.stream(values).noneMatch(v -> v.equals(propValue))) { + result.add("'" + propertyName + "' is expected to be one of " + Arrays.toString(values) + " but is '" + propValue + "'"); + } + } +} + +@Setter +class BooleanPropertyValidator extends HsHostingAssetPropertyValidator { + + private Map.Entry falseIf; + + private BooleanPropertyValidator(final String propertyName) { + super(Boolean.class, propertyName); + } + + public static BooleanPropertyValidator booleanProperty(final String propertyName) { + return new BooleanPropertyValidator(propertyName); + } + + HsHostingAssetPropertyValidator falseIf(final String refPropertyName, final String refPropertyValue) { + this.falseIf = new SimpleImmutableEntry<>(refPropertyName, refPropertyValue); + return this; + } + + @Override + protected void validate(final ArrayList result, final Boolean propValue, final Map props) { + if (falseIf != null && !Objects.equals(props.get(falseIf.getKey()), falseIf.getValue())) { + if (propValue) { + result.add("'" + propertyName + "' is expected to be false because " + + falseIf.getKey()+ "=" + falseIf.getValue() + " but is " + propValue); + } + } + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java new file mode 100644 index 00000000..26d4c5e4 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/HsHostingAssetValidator.java @@ -0,0 +1,75 @@ +package net.hostsharing.hsadminng.hs.hosting.asset.validator; + +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; +import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.stream; +import static net.hostsharing.hsadminng.hs.hosting.asset.validator.EnumPropertyValidator.enumProperty; +import static net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetPropertyValidator.defType; +import static net.hostsharing.hsadminng.hs.hosting.asset.validator.BooleanPropertyValidator.booleanProperty; +import static net.hostsharing.hsadminng.hs.hosting.asset.validator.IntegerPropertyValidator.integerProperty; + +public class HsHostingAssetValidator { + + private static final Map validators = Map.ofEntries( + defType(HsHostingAssetType.CLOUD_SERVER, new HsHostingAssetValidator( + integerProperty("CPUs").min(1).max(32).required(), + integerProperty("RAM").unit("GB").min(1).max(128).required(), + integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), + integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), + integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), + enumProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional())), + defType(HsHostingAssetType.MANAGED_SERVER, new HsHostingAssetValidator( + integerProperty("CPUs").min(1).max(32).required(), + integerProperty("RAM").unit("GB").min(1).max(128).required(), + integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required(), + integerProperty("HDD").unit("GB").min(0).max(4000).step(250).optional(), + integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required(), + enumProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional(), + booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").optional(), + booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(), + booleanProperty("SLA-PgSQL").falseIf("SLA-Platform", "BASIC").optional(), + booleanProperty("SLA-Office").falseIf("SLA-Platform", "BASIC").optional(), + booleanProperty("SLA-Web").falseIf("SLA-Platform", "BASIC").optional())), + defType(HsHostingAssetType.MANAGED_WEBSPACE, new HsHostingAssetValidator( + 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(), + enumProperty("SLA-Platform").values("BASIC", "EXT24H").optional(), + integerProperty("Daemons").min(0).max(10).optional(), + booleanProperty("Online Office Server").optional()) + )); + static { + validators.entrySet().forEach(typeDef -> { + stream(typeDef.getValue().propertyValidators).forEach( entry -> { + entry.verifyConsistency(typeDef); + }); + }); + } + private final HsHostingAssetPropertyValidator[] propertyValidators; + + public static HsHostingAssetValidator forType(final HsHostingAssetType type) { + return validators.get(type); + } + + HsHostingAssetValidator(final HsHostingAssetPropertyValidator... validators) { + propertyValidators = validators; + } + + public List validate(final HsHostingAssetEntity assetEntity) { + final var result = new ArrayList(); + assetEntity.getConfig().keySet().forEach( givenPropName -> { + if (stream(propertyValidators).map(pv -> pv.propertyName).noneMatch(propName -> propName.equals(givenPropName))) { + result.add("'" + givenPropName + "' is not expected but is '" +assetEntity.getConfig().get(givenPropName) + "'"); + } + }); + stream(propertyValidators).forEach(pv -> { + result.addAll(pv.validate(assetEntity.getConfig())); + }); + return result; + } +} diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config new file mode 100644 index 00000000..18183936 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/asset/validator/lombok.config @@ -0,0 +1,3 @@ +lombok.addLombokGeneratedAnnotation = true +lombok.accessors.chain = true +lombok.accessors.fluent = true diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java index 15f9c152..0cb1a086 100644 --- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java @@ -52,6 +52,7 @@ public class ArchitectureTest { "..hs.office.sepamandate", "..hs.booking.item", "..hs.hosting.asset", + "..hs.hosting.asset.validator", "..errors", "..mapper", "..ping", diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java index 26d1b763..0cde4075 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerAcceptanceTest.java @@ -174,7 +174,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup "type": "MANAGED_SERVER", "identifier": "vm1400", "caption": "some new CloudServer", - "config": { "CPU": 3, "extra": 42 } + "config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 } } """.formatted(givenBookingItem.getUuid())) .port(port) @@ -188,7 +188,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup "type": "MANAGED_SERVER", "identifier": "vm1400", "caption": "some new CloudServer", - "config": { "CPU": 3, "extra": 42 } + "config": { "CPUs": 2, "RAM": 100, "SSD": 300, "Traffic": 250 } } """)) .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*")) @@ -199,6 +199,39 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup location.substring(location.lastIndexOf('/') + 1)); assertThat(newUserUuid).isNotNull(); } + + @Test + void additionalValidationsArePerformend_whenAddingAsset() { + + context.define("superuser-alex@hostsharing.net"); + final var givenBookingItem = givenBookingItem("First", "some PrivateCloud"); + + final var location = RestAssured // @formatter:off + .given() + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(ContentType.JSON) + .body(""" + { + "bookingItemUuid": "%s", + "type": "MANAGED_SERVER", + "identifier": "vm1400", + "caption": "some new CloudServer", + "config": { "CPUs": 0, "extra": 42 } + } + """.formatted(givenBookingItem.getUuid())) + .port(port) + .when() + .post("http://localhost/api/hs/hosting/assets") + .then().log().all().assertThat() + .statusCode(400) + .contentType(ContentType.JSON) + .body("", lenientlyEquals(""" + { + "statusPhrase": "Bad Request", + "message": "['extra' is not expected but is '42', 'CPUs' is expected to be >= 1 but is 0, 'RAM' is required but missing, 'SSD' is required but missing, 'Traffic' is required but missing]" + } + """)); // @formatter:on + } } @Nested diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidatorUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidatorUnitTest.java new file mode 100644 index 00000000..d7f21222 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetValidatorUnitTest.java @@ -0,0 +1,97 @@ +package net.hostsharing.hsadminng.hs.hosting.asset; + +import net.hostsharing.hsadminng.hs.hosting.asset.validator.HsHostingAssetValidator; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static java.util.Collections.emptyMap; +import static java.util.Map.entry; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; +import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; +import static org.assertj.core.api.Assertions.assertThat; + +class HsHostingAssetValidatorUnitTest { + + @Test + void validatesMissingProperties() { + // given + final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() + .type(MANAGED_WEBSPACE) + .config(emptyMap()) + .build(); + + // when + final var result = validator.validate(mangedWebspaceHostingAssetEntity); + + // then + assertThat(result).containsExactlyInAnyOrder( + "'SSD' is required but missing", + "'Traffic' is required but missing" + ); + } + + @Test + void validatesUnknownProperties() { + // given + final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() + .type(MANAGED_WEBSPACE) + .config(Map.ofEntries( + entry("HDD", 0), + entry("SSD", 1), + entry("Traffic", 10), + entry("unknown", "some value") + )) + .build(); + + // when + final var result = validator.validate(mangedWebspaceHostingAssetEntity); + + // then + assertThat(result).containsExactly("'unknown' is not expected but is 'some value'"); + } + + @Test + void validatesDependentProperties() { + // given + final var validator = HsHostingAssetValidator.forType(MANAGED_SERVER); + final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() + .type(MANAGED_SERVER) + .config(Map.ofEntries( + entry("CPUs", 2), + entry("RAM", 25), + entry("SSD", 25), + entry("Traffic", 250), + entry("SLA-EMail", true) + )) + .build(); + + // when + final var result = validator.validate(mangedWebspaceHostingAssetEntity); + + // then + assertThat(result).containsExactly("'SLA-EMail' is expected to be false because SLA-Platform=BASIC but is true"); + } + + @Test + void validatesValidProperties() { + // given + final var validator = HsHostingAssetValidator.forType(MANAGED_WEBSPACE); + final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder() + .type(MANAGED_WEBSPACE) + .config(Map.ofEntries( + entry("HDD", 200), + entry("SSD", 25), + entry("Traffic", 250) + )) + .build(); + + // when + final var result = validator.validate(mangedWebspaceHostingAssetEntity); + + // then + assertThat(result).isEmpty(); + } +}