WIP: HostingAsset validation beyond property validators

This commit is contained in:
Michael Hoennig 2024-06-21 17:31:36 +02:00
parent 9418303b7c
commit 641f7b6ea3
15 changed files with 341 additions and 34 deletions

View File

@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.persistence.Version;
import jakarta.validation.constraints.NotNull;
import java.io.IOException;
import java.time.LocalDate;
import java.util.HashMap;
@ -92,6 +93,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
@JoinColumn(name = "parentitemuuid")
private HsBookingItemEntity parentItem;
@NotNull
@Column(name = "type")
@Enumerated(EnumType.STRING)
private HsBookingItemType type;

View File

@ -0,0 +1,23 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import java.util.regex.Pattern;
class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator {
HsCloudServerHostingAssetValidator() {
super(
BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER),
ParentAsset.mustBeNull(),
AssignedToAsset.mustBeNull(),
AlarmContact.isOptional(),
NO_EXTRA_PROPERTIES);
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
}
}

View File

@ -1,35 +1,80 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static java.util.Arrays.stream;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
public HsHostingAssetEntityValidator(final ValidatableProperty<?>... properties) {
static final ValidatableProperty<?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?>[0];
private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation;
private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation;
private final HsHostingAssetEntityValidator.AssignedToAsset assignedToAssetValidation;
private final HsHostingAssetEntityValidator.AlarmContact alarmContactValidation;
HsHostingAssetEntityValidator(
@NotNull final BookingItem bookingItemValidation,
@NotNull final ParentAsset parentAssetValidation,
@NotNull final AssignedToAsset assignedToAssetValidation,
@NotNull final AlarmContact alarmContactValidation,
final ValidatableProperty<?>... properties) {
super(properties);
this.bookingItemValidation = bookingItemValidation;
this.parentAssetValidation = parentAssetValidation;
this.assignedToAssetValidation = assignedToAssetValidation;
this.alarmContactValidation = alarmContactValidation;
}
@Override
public List<String> validate(final HsHostingAssetEntity assetEntity) {
return sequentiallyValidate(
() -> validateProperties(assetEntity),
() -> validateEntityReferences(assetEntity),
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
() -> optionallyValidate(assetEntity.getBookingItem()),
() -> optionallyValidate(assetEntity.getParentAsset()),
() -> validateAgainstSubEntities(assetEntity)
);
}
private List<String> validateEntityReferences(final HsHostingAssetEntity assetEntity) {
return Stream.of(
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetValidation::validate),
validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate),
validateProperties(assetEntity))
.filter(Objects::nonNull)
.flatMap(List::stream)
.filter(Objects::nonNull)
.toList();
}
private List<String> validateReferencedEntity(
final HsHostingAssetEntity assetEntity,
final String referenceFieldName,
final BiFunction<HsHostingAssetEntity, String, List<String>> validator) {
return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName));
}
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
}
@ -73,4 +118,120 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
propName, maxValue, propUnit, propName, totalValue, propUnit)
: null;
}
private List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
final var expectedIdentifierPattern = identifierPattern(assetEntity);
if (!expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) {
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
}
return Collections.emptyList();
}
protected abstract Pattern identifierPattern(HsHostingAssetEntity assetEntity);
static abstract class ReferenceValidator<S, T> {
private final Policy policy;
private final T subEntityType;
private final Function<HsHostingAssetEntity, S> subEntityGetter;
private final Function<S,T> subEntityTypeGetter;
public ReferenceValidator(
final Policy policy,
final T subEntityType,
final Function<HsHostingAssetEntity, S> subEntityGetter,
final Function<S, T> subEntityTypeGetter) {
this.policy = policy;
this.subEntityType = subEntityType;
this.subEntityGetter = subEntityGetter;
this.subEntityTypeGetter = subEntityTypeGetter;
}
public ReferenceValidator(
final Policy policy,
final Function<HsHostingAssetEntity, S> subEntityGetter) {
this.policy = policy;
this.subEntityType = null;
this.subEntityGetter = subEntityGetter;
this.subEntityTypeGetter = e -> null;
}
enum Policy {
OPTIONAL, FORBIDDEN, REQUIRED
}
List<String> validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) {
final var subEntity = subEntityGetter.apply(assetEntity);
if (policy == Policy.REQUIRED && subEntity == null) {
return List.of(referenceFieldName + "' must not be null but is null");
}
if (policy == Policy.FORBIDDEN && subEntity != null) {
return List.of(referenceFieldName + "' must not be null but is set to "+ assetEntity.getBookingItem().toShortString());
}
final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null;
if (subEntityType != null && subItemType != subEntityType) {
return List.of(referenceFieldName + "' must be of type " + subEntityType + " but is of type " + subItemType);
}
return emptyList();
}
}
static class BookingItem extends ReferenceValidator<HsBookingItemEntity, HsBookingItemType> {
BookingItem(final Policy policy, final HsBookingItemType bookingItemType) {
super(policy, bookingItemType, HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType);
}
static BookingItem mustBeNull() {
return new BookingItem(Policy.FORBIDDEN, null);
}
static BookingItem mustBeOfType(final HsBookingItemType hsBookingItemType) {
return new BookingItem(Policy.REQUIRED, hsBookingItemType);
}
}
static class ParentAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
ParentAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType parentAssetType) {
super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType);
}
static ParentAsset mustBeNull() {
return new ParentAsset(Policy.FORBIDDEN, null);
}
static ParentAsset mustBeOfType(final HsHostingAssetType hostingAssetType) {
return new ParentAsset(Policy.REQUIRED, hostingAssetType);
}
static ParentAsset mustBeNullOrOfType(final HsHostingAssetType hostingAssetType) {
return new ParentAsset(Policy.OPTIONAL, hostingAssetType);
}
}
static class AssignedToAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
AssignedToAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType assignedToAssetType) {
super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType);
}
static AssignedToAsset mustBeNull() {
return new AssignedToAsset(Policy.FORBIDDEN, null);
}
}
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
AlarmContact(final ReferenceValidator.Policy policy) {
super(policy, HsHostingAssetEntity::getAlarmContact);
}
static AlarmContact isOptional() {
return new AlarmContact(Policy.OPTIONAL);
}
}
}

View File

@ -14,10 +14,10 @@ public class HsHostingAssetEntityValidatorRegistry {
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>();
static {
register(CLOUD_SERVER, new HsHostingAssetEntityValidator());
register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator());
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
register(UNIX_USER, new HsHostingAssetEntityValidator());
register(UNIX_USER, new HsUnixUserHostingAssetValidator());
}
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {

View File

@ -1,5 +1,10 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
@ -8,6 +13,11 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
public HsManagedServerHostingAssetValidator() {
super(
BookingItem.mustBeOfType(HsBookingItemType.MANAGED_SERVER),
ParentAsset.mustBeNull(), // until we introduce a hosting asset for 'HOST'
AssignedToAsset.mustBeNull(),
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
// monitoring
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
integerProperty("monit_max_ram_usage").unit("%").min(10).max(100).withDefault(92),
@ -15,7 +25,6 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
// stringProperty("monit_alarm_email").unit("GB").optional() TODO.impl: via Contact?
// other settings
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
@ -45,4 +54,9 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
);
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
}
}

View File

@ -1,29 +1,22 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.Collection;
import java.util.stream.Stream;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.regex.Pattern;
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
public HsManagedWebspaceHostingAssetValidator() {
super(BookingItem.mustBeOfType(HsBookingItemType.MANAGED_WEBSPACE),
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_SERVER), // the (shared or private) ManagedServer
AssignedToAsset.mustBeNull(),
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
NO_EXTRA_PROPERTIES);
}
@Override
public List<String> validate(final HsHostingAssetEntity assetEntity) {
return Stream.of(validateIdentifierPattern(assetEntity), super.validate(assetEntity))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
private static List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
final var expectedIdentifierPattern = "^" + assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() + "[0-9][0-9]$";
if ( !assetEntity.getIdentifier().matches(expectedIdentifierPattern)) {
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
}
return Collections.emptyList();
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^" + assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() + "[0-9][0-9]$");
}
}

View File

@ -0,0 +1,23 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern;
class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
HsUnixUserHostingAssetValidator() {
super(BookingItem.mustBeNull(),
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
AssignedToAsset.mustBeNull(),
AlarmContact.isOptional(), // TODO.spec: for quota notifications
NO_EXTRA_PROPERTIES); // TODO.spec: yet to be specified
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9]+$");
}
}

View File

@ -12,13 +12,21 @@ import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.
@UtilityClass
public class TestHsBookingItem {
public static final HsBookingItemEntity TEST_BOOKING_ITEM = HsBookingItemEntity.builder()
public static final HsBookingItemEntity TEST_MANAGED_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
.project(TEST_PROJECT)
.caption("test booking item")
.type(HsBookingItemType.MANAGED_SERVER)
.caption("test project booking item")
.resources(Map.ofEntries(
entry("someThing", 1),
entry("anotherThing", "blue")
))
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
.build();
public static final HsBookingItemEntity TEST_CLOUD_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
.project(TEST_PROJECT)
.type(HsBookingItemType.CLOUD_SERVER)
.caption("test cloud server booking item")
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
.build();
}

View File

@ -15,7 +15,7 @@ import java.util.Map;
import java.util.UUID;
import java.util.stream.Stream;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
import static net.hostsharing.hsadminng.mapper.PatchMap.patchMap;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
@ -70,7 +70,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
protected HsHostingAssetEntity newInitialEntity() {
final var entity = new HsHostingAssetEntity();
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
entity.setBookingItem(TEST_BOOKING_ITEM);
entity.setBookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM);
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
entity.setCaption(INITIAL_CAPTION);
entity.setAlarmContact(givenInitialContact);

View File

@ -5,13 +5,13 @@ import org.junit.jupiter.api.Test;
import java.util.Map;
import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
import static org.assertj.core.api.Assertions.assertThat;
class HsHostingAssetEntityUnitTest {
final HsHostingAssetEntity givenParentAsset = HsHostingAssetEntity.builder()
.bookingItem(TEST_BOOKING_ITEM)
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
.type(HsHostingAssetType.MANAGED_SERVER)
.identifier("vm1234")
.caption("some managed asset")
@ -21,7 +21,7 @@ class HsHostingAssetEntityUnitTest {
entry("HDD-storage", 2048)))
.build();
final HsHostingAssetEntity givenWebspace = HsHostingAssetEntity.builder()
.bookingItem(TEST_BOOKING_ITEM)
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
.type(HsHostingAssetType.MANAGED_WEBSPACE)
.parentAsset(givenParentAsset)
.identifier("xyz00")

View File

@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test;
import java.util.Map;
import static java.util.Map.entry;
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
import static org.assertj.core.api.Assertions.assertThat;
@ -28,7 +29,28 @@ class HsCloudServerHostingAssetValidatorUnitTest {
final var result = validator.validate(cloudServerHostingAssetEntity);
// then
assertThat(result).containsExactly("'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
assertThat(result).containsExactlyInAnyOrder(
"'CLOUD_SERVER:vm1234.bookingItem' must not be null but is null",
"'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
}
@Test
void validatesInvalidIdentifier() {
// given
final var cloudServerHostingAssetEntity = HsHostingAssetEntity.builder()
.type(CLOUD_SERVER)
.identifier("xyz99")
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
.build();
final var validator = HsHostingAssetEntityValidatorRegistry.forType(cloudServerHostingAssetEntity.getType());
// when
final var result = validator.validate(cloudServerHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz99'");
}
@Test

View File

@ -1,6 +1,9 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import org.junit.jupiter.api.Test;
@ -16,12 +19,17 @@ class HsHostingAssetEntityValidatorUnitTest {
final var managedServerHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.identifier("vm1234")
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build())
.build();
// when
final var result = catchThrowable( ()-> HsHostingAssetEntityValidatorRegistry.validated(managedServerHostingAssetEntity));
// then
assertThat(result).isNull(); // all required properties have defaults
assertThat(result.getMessage()).contains(
"'MANAGED_SERVER:vm1234.parentAsset' must not be null but is set to D-???????-?:null",
"'MANAGED_SERVER:vm1234.assignedToAsset' must not be null but is set to D-???????-?:null"
);
}
}

View File

@ -1,5 +1,7 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import org.junit.jupiter.api.Test;
@ -17,6 +19,7 @@ class HsManagedServerHostingAssetValidatorUnitTest {
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.identifier("vm1234")
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
.config(Map.ofEntries(
entry("monit_max_hdd_usage", "90"),
entry("monit_max_cpu_usage", 2),
@ -30,8 +33,28 @@ class HsManagedServerHostingAssetValidatorUnitTest {
// then
assertThat(result).containsExactlyInAnyOrder(
"'MANAGED_SERVER:vm1234.parentAsset' must not be null but is set to D-???????-?:null",
"'MANAGED_SERVER:vm1234.assignedToAsset' must not be null but is set to D-???????-?:null",
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be >= 10 but is 2",
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be <= 100 but is 101",
"'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'");
}
@Test
void validatesInvalidIdentifier() {
// given
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_SERVER)
.identifier("xyz00")
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
.build();
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
// when
final var result = validator.validate(mangedServerHostingAssetEntity);
// then
assertThat(result).containsExactlyInAnyOrder(
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz00'");
}
}

View File

@ -40,11 +40,12 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
.build();
@Test
void validatesIdentifier() {
void validatesIdentifierAndReferencedEntities() {
// given
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
.parentAsset(mangedServerAssetEntity)
.identifier("xyz00")
.build();
@ -62,6 +63,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
.parentAsset(mangedServerAssetEntity)
.identifier("abc00")
.config(Map.ofEntries(
@ -82,6 +84,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
.type(MANAGED_WEBSPACE)
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
.parentAsset(mangedServerAssetEntity)
.identifier("abc00")
.build();

View File

@ -0,0 +1,27 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import org.junit.jupiter.api.Test;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
import static org.assertj.core.api.Assertions.assertThat;
class HsUnixUserHostingAssetValidatorUnitTest {
@Test
void validatesInvalidIdentifier() {
// given
final var unixUserHostingAsset = HsHostingAssetEntity.builder()
.type(UNIX_USER)
.identifier("abc")
.build();
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
// when
final var result = validator.validate(unixUserHostingAsset);
// then
assertThat(result).containsExactly("'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz99'");
}
}