1
0

hosting-asset-validation-for-cloud-server-to-webspace (#54)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/54
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-05-06 10:50:59 +02:00
parent 6c25dddcda
commit 2e9e5d6ef0
14 changed files with 750 additions and 18 deletions

View File

@@ -52,6 +52,7 @@ public class ArchitectureTest {
"..hs.office.sepamandate",
"..hs.booking.item",
"..hs.hosting.asset",
"..hs.hosting.asset.validator",
"..errors",
"..mapper",
"..ping",

View File

@@ -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

View File

@@ -0,0 +1,156 @@
package net.hostsharing.hsadminng.hs.hosting.asset;
import io.restassured.RestAssured;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { HsadminNgApplication.class, JpaAttempt.class }
)
class HsHostingAssetPropsControllerAcceptanceTest {
@LocalServerPort
private Integer port;
@Test
void anyone_canListAvailableAssetTypes() {
RestAssured // @formatter:off
.given()
.port(port)
.when()
.get("http://localhost/api/hs/hosting/asset-types")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
"MANAGED_SERVER",
"MANAGED_WEBSPACE",
"CLOUD_SERVER"
]
"""));
// @formatter:on
}
@Test
void globalAdmin_canListPropertiesOfGivenAssetType() {
RestAssured // @formatter:off
.given()
.port(port)
.when()
.get("http://localhost/api/hs/hosting/asset-types/" + HsHostingAssetType.MANAGED_SERVER)
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"type": "integer",
"propertyName": "CPUs",
"required": true,
"unit": null,
"min": 1,
"max": 32,
"step": null
},
{
"type": "integer",
"propertyName": "RAM",
"required": true,
"unit": "GB",
"min": 1,
"max": 128,
"step": null
},
{
"type": "integer",
"propertyName": "SSD",
"required": true,
"unit": "GB",
"min": 25,
"max": 1000,
"step": 25
},
{
"type": "integer",
"propertyName": "HDD",
"required": false,
"unit": "GB",
"min": 0,
"max": 4000,
"step": 250
},
{
"type": "integer",
"propertyName": "Traffic",
"required": true,
"unit": "GB",
"min": 250,
"max": 10000,
"step": 250
},
{
"type": "enumeration",
"propertyName": "SLA-Platform",
"required": false,
"values": [
"BASIC",
"EXT8H",
"EXT4H",
"EXT2H"
]
},
{
"type": "boolean",
"propertyName": "SLA-EMail",
"required": false,
"falseIf": {
"SLA-Platform": "BASIC"
}
},
{
"type": "boolean",
"propertyName": "SLA-Maria",
"required": false,
"falseIf": {
"SLA-Platform": "BASIC"
}
},
{
"type": "boolean",
"propertyName": "SLA-PgSQL",
"required": false,
"falseIf": {
"SLA-Platform": "BASIC"
}
},
{
"type": "boolean",
"propertyName": "SLA-Office",
"required": false,
"falseIf": {
"SLA-Platform": "BASIC"
}
},
{
"type": "boolean",
"propertyName": "SLA-Web",
"required": false,
"falseIf": {
"SLA-Platform": "BASIC"
}
}
]
"""));
// @formatter:on
}
}

View File

@@ -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();
}
}