1
0

add DomainSetup-HostingAssets for new BookingItem via created-event (#111)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/111
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-10-08 11:48:31 +02:00
parent cc2b04472f
commit 60341bf644
77 changed files with 1558 additions and 273 deletions

View File

@@ -43,6 +43,7 @@ public class ArchitectureTest {
"..test.dom",
"..context",
"..hash",
"..lambda",
"..generated..",
"..persistence..",
"..system..",
@@ -64,6 +65,7 @@ public class ArchitectureTest {
"..hs.booking.item.validators",
"..hs.hosting.asset",
"..hs.hosting.asset.validators",
"..hs.hosting.asset.factories",
"..errors",
"..mapper",
"..ping",

View File

@@ -5,12 +5,15 @@ import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealRepository;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
@@ -31,6 +34,8 @@ import java.util.UUID;
import static java.util.Map.entry;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.matchesRegex;
@@ -58,6 +63,9 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
@Autowired
HsHostingAssetRealRepository realHostingAssetRepo;
@Autowired
BookingItemCreatedEventRepository bookingItemCreationEventRepo;
@Autowired
JpaAttempt jpaAttempt;
@@ -70,11 +78,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
// given
context("superuser-alex@hostsharing.net");
final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream()
.map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid()))
.flatMap(List::stream)
.findFirst()
.orElseThrow();
final var givenProject = findDefaultProjectOfDebitorNumber(1000111);
RestAssured // @formatter:off
.given()
@@ -138,11 +142,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
void globalAdmin_canAddBookingItem() {
context.define("superuser-alex@hostsharing.net");
final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream()
.map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid()))
.flatMap(List::stream)
.findFirst()
.orElseThrow();
final var givenProject = findDefaultProjectOfDebitorNumber(1000111);
final var location = RestAssured // @formatter:off
.given()
@@ -186,37 +186,121 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
}
@Test
void projectAgent_canAddBookingItemWithHostingAsset() {
void projectAgent_canAddManagedWebspaceBookingItemWithHostingAsset() {
context.define("superuser-alex@hostsharing.net", "hs_booking.project#D-1000111-D-1000111defaultproject:AGENT");
final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream()
.map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid()))
.flatMap(List::stream)
.findFirst()
.orElseThrow();
Dns.fakeResultForDomain("example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code"));
final var givenProject = findDefaultProjectOfDebitorNumber(1000111);
final var givenManagedServer = realHostingAssetRepo.findByTypeAndIdentifier(MANAGED_SERVER, "vm1011").stream()
.map(HsHostingAsset::getBookingItem)
.findFirst().orElseThrow();
final var location = RestAssured // @formatter:off
.given()
.header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"projectUuid": "{projectUuid}",
"type": "DOMAIN_SETUP",
"caption": "some new domain-setup booking",
"resources": {
"domainName": "example.org",
"targetUnixUser": "fir01-web",
"verificationCode": "just-a-fake-verification-code"
{
"projectUuid": "{projectUuid}",
"parentItemUuid": "{managedServerUuid}",
"type": "MANAGED_WEBSPACE",
"caption": "some managed webspace",
"resources": {
"SSD": 25,
"Traffic": 250
},
"hostingAsset": {
"type": "MANAGED_WEBSPACE",
"identifier": "fir00"
}
}
}
"""
"""
.replace("{projectUuid}", givenProject.getUuid().toString())
.replace("{managedServerUuid}", givenManagedServer.getUuid().toString())
)
.port(port)
.when()
.post("http://localhost/api/hs/booking/items")
.then().log().all().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("", lenientlyEquals("""
{
"type": "MANAGED_WEBSPACE",
"caption": "some managed webspace",
"validFrom": "{today}",
"validTo": null
}
"""
.replace("{today}", LocalDate.now().toString())
.replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString()))
)
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/booking/items/[^/]*"))
.extract().header("Location"); // @formatter:on
// then, the new BookingItem can be accessed under the generated UUID
final var newBookingItem = fetchRealBookingItemFromURI(location);
assertThat(newBookingItem)
.extracting(HsBookingItem::getCaption)
.isEqualTo("some managed webspace");
// and the related HostingAssets are also got created
final var domainSetupHostingAsset = realHostingAssetRepo.findByIdentifier("fir00");
assertThat(domainSetupHostingAsset).isNotEmpty()
.map(HsHostingAsset::getBookingItem)
.contains(newBookingItem);
final var event = bookingItemCreationEventRepo.findByBookingItem(newBookingItem);
assertThat(event).isNull();
}
@Test
void projectAgent_canAddDomainSetupBookingItemWithHostingAsset() {
context.define("superuser-alex@hostsharing.net", "hs_booking.project#D-1000111-D-1000111defaultproject:AGENT");
final var givenProject = findDefaultProjectOfDebitorNumber(1000111);
// TODO.impl: "sec01-web" should not work, but does
final var givenUnixUser = realHostingAssetRepo.findByTypeAndIdentifier(UNIX_USER, "fir01-web").stream()
.findFirst().orElseThrow();
Dns.fakeResultForDomain("example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code"));
final var location = RestAssured // @formatter:off
.given()
.header("current-subject", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
{
"projectUuid": "{projectUuid}",
"type": "DOMAIN_SETUP",
"caption": "Domain-Setup for example.org",
"resources": {
"domainName": "example.org",
"verificationCode": "just-a-fake-verification-code"
},
"hostingAsset": {
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserUuid}"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
}
"""
.replace("{projectUuid}", givenProject.getUuid().toString())
.replace("{unixUserUuid}", givenUnixUser.getUuid().toString())
)
.port(port)
.when()
.post("http://localhost/api/hs/booking/items")
.then().log().all().assertThat()
@@ -225,10 +309,9 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
.body("", lenientlyEquals("""
{
"type": "DOMAIN_SETUP",
"caption": "some new domain-setup booking",
"caption": "Domain-Setup for example.org",
"validFrom": "{today}",
"validTo": null,
"resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" }
"validTo": null
}
"""
.replace("{today}", LocalDate.now().toString())
@@ -240,24 +323,34 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
// then, the new BookingItem can be accessed under the generated UUID
final var newBookingItem = fetchRealBookingItemFromURI(location);
assertThat(newBookingItem)
.extracting(bi -> bi.getDirectValue("domainName", String.class))
.isEqualTo("example.org");
.extracting(HsBookingItem::getCaption)
.isEqualTo("Domain-Setup for example.org");
// and the related HostingAsset also got created
assertThat(realHostingAssetRepo.findByIdentifier("example.org")).isNotEmpty()
// and the related HostingAssets are also got created
final var domainSetupHostingAsset = realHostingAssetRepo.findByIdentifier("example.org");
assertThat(domainSetupHostingAsset).isNotEmpty()
.map(HsHostingAsset::getBookingItem)
.contains(newBookingItem);
// TODO.legacy: add check for example.org|DNS
assertThat(realHostingAssetRepo.findByIdentifier("example.org|HTTP")).isNotEmpty()
.map(HsHostingAsset::getParentAsset)
.isEqualTo(domainSetupHostingAsset);
assertThat(realHostingAssetRepo.findByIdentifier("example.org|MBOX")).isNotEmpty()
.map(HsHostingAsset::getParentAsset)
.isEqualTo(domainSetupHostingAsset);
assertThat(realHostingAssetRepo.findByIdentifier("example.org|SMTP")).isNotEmpty()
.map(HsHostingAsset::getParentAsset)
.isEqualTo(domainSetupHostingAsset);
final var event = bookingItemCreationEventRepo.findByBookingItem(newBookingItem);
assertThat(event).isNull();
}
@Test
void projectAgent_canAddBookingItemEvenIfHostingAssetCreationFails() {
context.define("superuser-alex@hostsharing.net", "hs_booking.project#D-1000111-D-1000111defaultproject:AGENT");
final var givenProject = debitorRepo.findByDebitorNumber(1000111).stream()
.map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid()))
.flatMap(List::stream)
.findFirst()
.orElseThrow();
final var givenProject = findDefaultProjectOfDebitorNumber(1000111);
final var givenUnixUser = realHostingAssetRepo.findByIdentifier("fir01-web").stream().findFirst().orElseThrow();
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords()); // without valid verificationCode
@@ -272,12 +365,30 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
"caption": "some new domain-setup booking",
"resources": {
"domainName": "example.org",
"targetUnixUser": "fir01-web",
"verificationCode": "just-a-fake-verification-code"
},
"hostingAsset": {
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserUuid}"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
}
"""
.replace("{projectUuid}", givenProject.getUuid().toString())
.replace("{unixUserUuid}", givenUnixUser.getUuid().toString())
)
.port(port)
.when()
@@ -291,7 +402,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
"caption": "some new domain-setup booking",
"validFrom": "{today}",
"validTo": null,
"resources": { "domainName": "example.org", "targetUnixUser": "fir01-web" }
"resources": { "domainName": "example.org" }
}
"""
.replace("{today}", LocalDate.now().toString())
@@ -305,8 +416,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
assertThat(newBookingItem)
.extracting(bi -> bi.getDirectValue("domainName", String.class))
.isEqualTo("example.org");
assertThat(newBookingItem)
.extracting(bi -> bi.getDirectValue("status", String.class))
final var event = bookingItemCreationEventRepo.findByBookingItem(newBookingItem);
assertThat(event.getStatusMessage())
.isEqualTo("[[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=just-a-fake-verification-code' found for domain name 'example.org' (nor in its super-domain)]");
// but the related HostingAsset did not get created
@@ -314,6 +425,14 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
}
}
private @NotNull HsBookingProjectRealEntity findDefaultProjectOfDebitorNumber(final int debitorNumber) {
return debitorRepo.findByDebitorNumber(debitorNumber).stream()
.map(d -> realProjectRepo.findAllByDebitorUuid(d.getUuid()))
.flatMap(List::stream)
.findFirst()
.orElseThrow();
}
@Nested
@Order(1)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@@ -534,6 +653,13 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
}).assertSuccessful().returnedValue();
}
@AfterEach
void cleanupEventEntities() {
jpaAttempt.transacted(() -> {
em.createQuery("delete from BookingItemCreatedEventEntity").executeUpdate();
}).assertSuccessful();
}
private Map.Entry<String, Object> resource(final String key, final Object value) {
return entry(key, value);
}

View File

@@ -36,8 +36,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", "example.org"),
entry("targetUnixUser", "xyz00")
entry("domainName", "example.org")
))
.build();
@@ -59,7 +58,6 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", "example.org"),
entry("targetUnixUser", "xyz00"),
entry("verificationCode", "1234-5678-9100")
))
.build();
@@ -80,8 +78,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 253)),
entry("targetUnixUser", "xyz00")
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 253))
))
.build();
@@ -99,8 +96,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 254)),
entry("targetUnixUser", "xyz00")
entry("domainName", right(TOO_LONG_DOMAIN_NAME, 254))
))
.build();
@@ -118,8 +114,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", "example.com"),
entry("targetUnixUser", "xyz00-test")
entry("domainName", "example.com")
))
.build();
@@ -130,25 +125,6 @@ class HsDomainSetupBookingItemValidatorUnitTest {
assertThat(result).isEmpty();
}
@Test
void rejectsInvalidUnixUser() {
final var domainSetupBookingItemEntity = HsBookingItemRealEntity.builder()
.type(DOMAIN_SETUP)
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", "example.com"),
entry("targetUnixUser", "xyz00test")
))
.build();
// when
final var result = HsBookingItemEntityValidatorRegistry.doValidate(em, domainSetupBookingItemEntity);
// then
assertThat(result).contains("'D-12345:Test-Project:Test-Domain.resources.targetUnixUser' = 'xyz00test' is not a valid unix-user name");
}
@ParameterizedTest
@ValueSource(strings = {
"de", "com", "net", "org", "actually-any-top-level-domain",
@@ -196,8 +172,7 @@ class HsDomainSetupBookingItemValidatorUnitTest {
.project(project)
.caption("Test-Domain")
.resources(Map.ofEntries(
entry("domainName", secondLevelRegistrarDomain),
entry("targetUnixUser", "xyz00")
entry("domainName", secondLevelRegistrarDomain)
))
.build();
@@ -219,7 +194,6 @@ class HsDomainSetupBookingItemValidatorUnitTest {
// then
assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder(
"{type=string, propertyName=domainName, matchesRegEx=[^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,12}], matchesRegExDescription=is not a (non-top-level) fully qualified domain name, notMatchesRegEx=[[^.]+, (co|org|gov|ac|sch)\\.uk, (com|net|org|edu|gov|asn|id)\\.au, (co|ne|or|ac|go)\\.jp, (com|net|org|gov|edu|ac)\\.cn, (com|net|org|gov|edu|mil|art)\\.br, (co|net|org|gen|firm|ind)\\.in, (com|net|org|gob|edu)\\.mx, (gov|edu)\\.it, (co|net|org|govt|ac|school|geek|kiwi)\\.nz, (co|ne|or|go|re|pe)\\.kr], notMatchesRegExDescription=is a forbidden registrar-level domain name, maxLength=253, required=true, writeOnce=true}",
"{type=string, propertyName=targetUnixUser, matchesRegEx=[^[a-z][a-z0-9]{2}[0-9]{2}$|^[a-z][a-z0-9]{2}[0-9]{2}-[a-z0-9\\._-]+$], matchesRegExDescription=is not a valid unix-user name, maxLength=253, required=true, writeOnce=true}",
"{type=string, propertyName=verificationCode, minLength=12, maxLength=64, computed=IN_INIT}");
}
}

View File

@@ -157,6 +157,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
)
);
final var givenParentAsset = givenParentAsset(MANAGED_SERVER, "vm1011");
final var expectedUnixUserId = nextUnixUserId();
final var location = RestAssured // @formatter:off
.given()
@@ -184,10 +185,12 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
"identifier": "fir10",
"caption": "some separate ManagedWebspace HA",
"config": {
"groupid": 1000000
"groupid": {lastUnixUserId}
}
}
"""))
"""
.replace("{lastUnixUserId}", expectedUnixUserId.toString())
))
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*"))
.extract().header("Location"); // @formatter:on
@@ -205,9 +208,11 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
.isEqualTo("""
HsHostingAsset(UNIX_USER, fir10, fir10 webspace user, MANAGED_WEBSPACE:fir10, {
"password" : null,
"userid" : 1000000
"userid" : {lastUnixUserId}
})
""".trim());
"""
.replace("{lastUnixUserId}", expectedUnixUserId.toString())
.trim());
}
@Test
@@ -777,4 +782,11 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
}).returnedValue();
}
private Integer nextUnixUserId() {
final Object result = em.createNativeQuery("SELECT nextval('hs_hosting.asset_unixuser_system_id_seq')", Integer.class)
.getSingleResult();
return (Integer) result + 1;
}
}

View File

@@ -0,0 +1,259 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedEventEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import java.util.UUID;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
import static net.hostsharing.hsadminng.mapper.PatchableMapWrapper.entry;
import static org.assertj.core.api.Assertions.assertThat;
// Tests the DomainSetupHostingAssetFactory through a HsBookingItemCreatedListener instance.
@ExtendWith(MockitoExtension.class)
class DomainSetupHostingAssetFactoryUnitTest {
private final HsHostingAssetRealEntity managedWebspaceHostingAsset = HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.type(MANAGED_WEBSPACE)
.identifier("one00")
.build();
private final HsHostingAssetRealEntity unixUserHostingAsset = HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.type(UNIX_USER)
.identifier("one00-web")
.parentAsset(managedWebspaceHostingAsset)
.build();
private final HsHostingAssetRealEntity anotherManagedWebspaceHostingAsset = HsHostingAssetRealEntity.builder()
.uuid(UUID.randomUUID())
.type(MANAGED_WEBSPACE)
.identifier("two00")
.build();
private EntityManagerWrapperFake emwFake = new EntityManagerWrapperFake();
@Spy
private EntityManagerWrapper emw = emwFake;
@Spy
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy
private StandardMapper standardMapper = new StandardMapper(emw);
@InjectMocks
private HsBookingItemCreatedListener listener;
@BeforeEach
void initMocks() {
emwFake.persist(managedWebspaceHostingAsset);
emwFake.persist(unixUserHostingAsset);
}
@Test
void doesNotPersistEventEntityWithoutValidationErrors() {
// given
final var givenBookingItem = createBookingItemFromResources(
entry("domainName", "example.org"),
entry("verificationCode", "just-a-fake-verification-code")
);
final var givenAssetJson = """
{
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
},
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
"""
.replace("{unixUserHostingAssetUuid}", unixUserHostingAsset.getUuid().toString());
Dns.fakeResultForDomain("example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code"));
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertThat(emwFake.stream(BookingItemCreatedEventEntity.class))
.as("the event should not have been persisted, but got persisted")
.isEmpty();
}
@Test
void persistsEventEntityIfDomainSetupVerificationFails() {
// given
final var givenBookingItem = createBookingItemFromResources(
entry("domainName", "example.org")
);
final var givenAssetJson = """
{
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
},
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
"""
.replace("{unixUserHostingAssetUuid}", unixUserHostingAsset.getUuid().toString());
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords()); // without valid verificationCode
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertEventStatus(givenBookingItem, givenAssetJson,
"[[DNS] no TXT record 'Hostsharing-domain-setup-verification-code=null' found for domain name 'example.org' (nor in its super-domain)]");
}
@Test
void persistsEventEntityIfDomainDnsSetupIsSupplied() {
// given
final var givenBookingItem = createBookingItemFromResources(
entry("domainName", "example.org"),
entry("verificationCode", "just-a-fake-verification-code")
);
final var givenAssetJson = """
{
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
},
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
"""
.replace("{unixUserHostingAssetUuid}", unixUserHostingAsset.getUuid().toString())
.replace("{managedWebspaceHostingAssetUuid}", managedWebspaceHostingAsset.getUuid().toString());
Dns.fakeResultForDomain("example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code"));
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertEventStatus(givenBookingItem, givenAssetJson,
"domain DNS setup not allowed for legacy compatibility");
}
@Test
void persistsEventEntityIfSuppliedDomainUnixUserAndSmtpSetupWebspaceDontMatch() {
// given
final var givenBookingItem = createBookingItemFromResources(
entry("domainName", "example.org"),
entry("verificationCode", "just-a-fake-verification-code")
);
final var givenAssetJson = """
{
"identifier": "example.org", // also as default for all subAssets
"subHostingAssets": [
{
"type": "DOMAIN_HTTP_SETUP",
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
},
{
"type": "DOMAIN_DNS_SETUP"
},
{
"type": "DOMAIN_MBOX_SETUP"
},
{
"type": "DOMAIN_SMTP_SETUP"
}
]
}
"""
.replace("{unixUserHostingAssetUuid}", unixUserHostingAsset.getUuid().toString())
.replace("{managedWebspaceHostingAssetUuid}", anotherManagedWebspaceHostingAsset.getUuid().toString());
Dns.fakeResultForDomain("example.org",
Dns.Result.fromRecords("Hostsharing-domain-setup-verification-code=just-a-fake-verification-code"));
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertEventStatus(givenBookingItem, givenAssetJson,
"domain DNS setup not allowed for legacy compatibility");
}
@SafeVarargs
private static HsBookingItemRealEntity createBookingItemFromResources(final Map.Entry<String, String>... givenResources) {
return HsBookingItemRealEntity.builder()
.type(HsBookingItemType.DOMAIN_SETUP)
.resources(Map.ofEntries(givenResources))
.build();
}
private void assertEventStatus(
final HsBookingItemRealEntity givenBookingItem,
final String givenAssetJson,
final String expectedErrorMessage) {
emwFake.stream(BookingItemCreatedEventEntity.class)
.reduce(Reducer::toSingleElement)
.map(eventEntity -> {
assertThat(eventEntity.getBookingItem()).isSameAs(givenBookingItem);
assertThat(eventEntity.getAssetJson()).isEqualTo(givenAssetJson);
assertThat(eventEntity.getStatusMessage()).isEqualTo(expectedErrorMessage);
return true;
});
}
}

View File

@@ -0,0 +1,98 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedEventEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.DOMAIN_SETUP;
import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_WEBSPACE;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class)
class HsBookingItemCreatedListenerUnitTest {
final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder()
.debitorNumber(12345)
.defaultPrefix("xyz")
.build();
private EntityManagerWrapperFake emwFake = new EntityManagerWrapperFake();
@Spy
private EntityManagerWrapper emw = emwFake;
@Spy
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy
private StandardMapper standardMapper = new StandardMapper(emw);
@InjectMocks
private HsBookingItemCreatedListener listener;
@ParameterizedTest
@MethodSource("bookingItemTypesWithoutAutomaticAssetCreation")
void persistsEventEntityIfBookingItemTypeDoesNotSupportAutomaticHostingAssetCreation(final HsBookingItemType bookingItemType) {
// given
final var givenBookingItem = createBookingItemFromResources(bookingItemType);
final var givenAssetJson = """
{
// anything should be rejected
}
""";
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertEventStatus(givenBookingItem, givenAssetJson,
"waiting for manual setup of hosting asset for booking item of type " + bookingItemType);
}
static List<HsBookingItemType> bookingItemTypesWithoutAutomaticAssetCreation() {
return Arrays.stream(HsBookingItemType.values())
.filter(v -> v != MANAGED_WEBSPACE && v != DOMAIN_SETUP)
.toList();
}
private static HsBookingItemRealEntity createBookingItemFromResources(
final HsBookingItemType bookingItemType
) {
return HsBookingItemRealEntity.builder()
.type(bookingItemType)
.build();
}
private void assertEventStatus(
final HsBookingItemRealEntity givenBookingItem,
final String givenAssetJson,
final String expectedErrorMessage) {
emwFake.stream(BookingItemCreatedEventEntity.class)
.reduce(Reducer::toSingleElement)
.map(eventEntity -> {
assertThat(eventEntity.getBookingItem()).isSameAs(givenBookingItem);
assertThat(eventEntity.getAssetJson()).isEqualTo(givenAssetJson);
assertThat(eventEntity.getStatusMessage()).isEqualTo(expectedErrorMessage);
return true;
});
}
}

View File

@@ -0,0 +1,137 @@
package net.hostsharing.hsadminng.hs.hosting.asset.factories;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.hostsharing.hsadminng.config.JsonObjectMapperConfiguration;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedAppEvent;
import net.hostsharing.hsadminng.hs.booking.item.BookingItemCreatedEventEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealEntity;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapperFake;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Map;
import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PatchableMapWrapper.entry;
import static org.assertj.core.api.Assertions.assertThat;
// Tests the DomainSetupHostingAssetFactory through a HsBookingItemCreatedListener instance.
@ExtendWith(MockitoExtension.class)
class ManagedWebspaceHostingAssetFactoryUnitTest {
final HsBookingDebitorEntity debitor = HsBookingDebitorEntity.builder()
.debitorNumber(12345)
.defaultPrefix("xyz")
.build();
final HsBookingProjectRealEntity project = HsBookingProjectRealEntity.builder()
.debitor(debitor)
.caption("Test-Project")
.build();
final HsOfficeContact alarmContact = HsOfficeContactRealEntity.builder()
.uuid(UUID.randomUUID())
.caption("Alarm Contact xyz")
.build();
private EntityManagerWrapperFake emwFake = new EntityManagerWrapperFake();
@Spy
private EntityManagerWrapper emw = emwFake;
@Spy
private ObjectMapper jsonMapper = new JsonObjectMapperConfiguration().customObjectMapper().build();
@Spy
private StandardMapper standardMapper = new StandardMapper(emw);
@InjectMocks
private HsBookingItemCreatedListener listener;
@BeforeEach
void initMocks() {
emwFake.persist(alarmContact);
}
@Test
void doesNotPersistAnyEntityWithoutHostingAssetWithoutValidationErrors() {
// given
final var givenBookingItem = HsBookingItemRealEntity.builder()
.type(HsBookingItemType.MANAGED_WEBSPACE)
.project(project)
.caption("Test Managed-Webspace")
.resources(Map.ofEntries(
Map.entry("RAM", 25),
Map.entry("Traffic", 250)
))
.build();
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, null)
);
// then
assertThat(emwFake.stream(BookingItemCreatedEventEntity.class).findAny().isEmpty())
.as("the event should not have been persisted, but got persisted").isTrue();
assertThat(emwFake.stream(HsHostingAssetRealEntity.class).findAny().isEmpty())
.as("the hosting asset should not have been persisted, but got persisted").isTrue();
}
@Test
void persistsEventEntityIfDomainSetupVerificationFails() {
// given
final var givenBookingItem = createBookingItemFromResources(
entry("domainName", "example.org")
);
final var givenAssetJson = """
{
"identifier": "xyz00"
}
""";
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords()); // without valid verificationCode
// when
listener.onApplicationEvent(
new BookingItemCreatedAppEvent(this, givenBookingItem, givenAssetJson)
);
// then
assertEventStatus(givenBookingItem, givenAssetJson,
"requires MANAGED_WEBSPACE hosting asset, but got null");
}
@SafeVarargs
private static HsBookingItemRealEntity createBookingItemFromResources(final Map.Entry<String, String>... givenResources) {
return HsBookingItemRealEntity.builder()
.type(HsBookingItemType.MANAGED_WEBSPACE)
.resources(Map.ofEntries(givenResources))
.build();
}
private void assertEventStatus(
final HsBookingItemRealEntity givenBookingItem,
final String givenAssetJson,
final String expectedErrorMessage) {
emwFake.stream(BookingItemCreatedEventEntity.class)
.reduce(Reducer::toSingleElement)
.map(eventEntity -> {
assertThat(eventEntity.getBookingItem()).isSameAs(givenBookingItem);
assertThat(eventEntity.getAssetJson()).isEqualTo(givenAssetJson);
assertThat(eventEntity.getStatusMessage()).isEqualTo(expectedErrorMessage);
return true;
});
}
}

View File

@@ -42,8 +42,7 @@ class HsDomainSetupHostingAssetValidatorUnitTest {
.project(project)
.type(HsBookingItemType.DOMAIN_SETUP)
.resources(new HashMap<>(ofEntries(
entry("domainName", domainName),
entry("targetUnixUser", "xyz00")
entry("domainName", domainName)
))));
HsBookingItemEntityValidatorRegistry.forType(HsBookingItemType.DOMAIN_SETUP).prepareProperties(null, bookingItem);
return HsHostingAssetRbacEntity.builder()

View File

@@ -6,7 +6,7 @@ import com.opencsv.CSVReaderBuilder;
import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

View File

@@ -1680,18 +1680,13 @@ public class ImportHostingAssets extends BaseOfficeDataImport {
final var relatedProject = domainSetup.getSubHostingAssets().stream()
.map(ha -> ha.getAssignedToAsset() != null ? ha.getAssignedToAsset().getRelatedProject() : null)
.findAny().orElseThrow();
final var targetUnixUser = domainSetup.getSubHostingAssets().stream()
.filter(subAsset -> subAsset.getType() == DOMAIN_HTTP_SETUP)
.map(domainHttpSetup -> domainHttpSetup.getAssignedToAsset().getIdentifier())
.findAny().orElse(null);
final var bookingItem = HsBookingItemRealEntity.builder()
.type(HsBookingItemType.DOMAIN_SETUP)
.caption("BI " + domainSetup.getIdentifier())
.project((HsBookingProjectRealEntity) relatedProject)
//.validity(toPostgresDateRange(created, cancelled))
.resources(Map.ofEntries(
entry("domainName", domainSetup.getIdentifier()),
entry("targetUnixUser", targetUnixUser)
entry("domainName", domainSetup.getIdentifier())
))
.build();
domainSetup.setBookingItem(bookingItem);

View File

@@ -4,12 +4,11 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import org.junit.jupiter.api.BeforeEach;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -18,15 +17,10 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.SynchronizationType;
import java.util.Map;
import java.util.UUID;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -47,19 +41,8 @@ public class HsOfficeMembershipControllerRestTest {
@MockBean
HsOfficeMembershipRepository membershipRepo;
@Mock
EntityManager em;
@MockBean
EntityManagerFactory emf;
@BeforeEach
void init() {
when(emf.createEntityManager()).thenReturn(em);
when(emf.createEntityManager(any(Map.class))).thenReturn(em);
when(emf.createEntityManager(any(SynchronizationType.class))).thenReturn(em);
when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em);
}
EntityManagerWrapper em;
@Nested
class AddMembership {

View File

@@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
@@ -12,7 +13,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import jakarta.persistence.EntityManager;
import java.time.LocalDate;
import java.util.UUID;
import java.util.stream.Stream;
@@ -38,9 +38,9 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
private static final Boolean PATCHED_MEMBERSHIP_FEE_BILLABLE = false;
@Mock
private EntityManager em;
private EntityManagerWrapper em;
private StandardMapper mapper = new StandardMapper();
private StandardMapper mapper = new StandardMapper(em);
@BeforeEach
void initMocks() {

View File

@@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -18,7 +19,6 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.SynchronizationType;
@@ -57,7 +57,7 @@ class HsOfficePartnerControllerRestTest {
HsOfficeRelationRealRepository relationRepo;
@MockBean
EntityManager em;
EntityManagerWrapper em;
@MockBean
EntityManagerFactory emf;

View File

@@ -0,0 +1,94 @@
package net.hostsharing.hsadminng.persistence;
import lombok.SneakyThrows;
import jakarta.persistence.Id;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
public class EntityManagerWrapperFake extends EntityManagerWrapper {
private Map<Class<?>, Map<Object, Object>> entityClasses = new HashMap<>();
@Override
public boolean contains(final Object entity) {
final var id = getEntityId(entity);
return find(entity.getClass(), id) != null;
}
@Override
public <T> T getReference(final Class<T> entityClass, final Object primaryKey) {
return find(entityClass, primaryKey);
}
@Override
public <T> T find(final Class<T> entityClass, final Object primaryKey) {
if (entityClasses.containsKey(entityClass)) {
final var entities = entityClasses.get(entityClass);
//noinspection unchecked
return entities.keySet().stream()
.filter(key -> key.equals(primaryKey))
.map(key -> (T) entities.get(key))
.findAny()
.orElse(null);
}
return null;
}
@Override
public void persist(final Object entity) {
if (!entityClasses.containsKey(entity.getClass())) {
entityClasses.put(entity.getClass(), new HashMap<>());
}
final var id = getEntityId(entity).orElseGet(() -> setEntityId(entity, UUID.randomUUID()));
entityClasses.get(entity.getClass()).put(id, entity);
}
@Override
public void flush() {
}
public Stream<Object> stream() {
return entityClasses.values().stream().flatMap(entitiesPerClass -> entitiesPerClass.values().stream());
}
public <T> Stream<T> stream(final Class<T> entityClass) {
if (entityClasses.containsKey(entityClass)) {
//noinspection unchecked
return (Stream<T>) entityClasses.get(entityClass).values().stream();
}
return Stream.empty();
}
@SneakyThrows
private static Optional<Object> getEntityId(final Object entity) {
for (Class<?> currentClass = entity.getClass(); currentClass != null; currentClass = currentClass.getSuperclass()) {
for (Field field : currentClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)) {
field.setAccessible(true);
return Optional.ofNullable(field.get(entity));
}
}
}
throw new IllegalArgumentException("No @Id field found in entity class: " + entity.getClass().getName());
}
@SneakyThrows
private static Object setEntityId(final Object entity, final Object id) {
for (Class<?> currentClass = entity.getClass(); currentClass != null; currentClass = currentClass.getSuperclass()) {
for (Field field : currentClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)) {
field.setAccessible(true);
field.set(entity, id);
return id;
}
}
}
throw new IllegalArgumentException("No @Id field found in entity class: " + entity.getClass().getName());
}
}

View File

@@ -1,6 +1,5 @@
package net.hostsharing.hsadminng.persistence;
import net.hostsharing.hsadminng.mapper.Array;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -53,9 +52,9 @@ class EntityManagerWrapperUnitTest {
if (type == double.class) return 0.0;
if (type == char.class) return '\0';
if (type == String.class) return "dummy";
if (type == String[].class) return Array.of("dummy");
if (type == String[].class) return new String[]{"dummy"};
if (type == Class.class) return String.class;
if (type == Class[].class) return Array.of(String.class);
if (type == Class[].class) return new Class[0];
return mock(type);
}
}

View File

@@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.rbac.context;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.mapper.Array;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +18,7 @@ import jakarta.servlet.http.HttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, StandardMapper.class })
@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, EntityManagerWrapper.class, StandardMapper.class })
@DirtiesContext
class ContextIntegrationTests {

View File

@@ -2,10 +2,10 @@ package net.hostsharing.hsadminng.rbac.role;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -15,7 +15,6 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.SynchronizationType;
import java.util.Map;
@@ -43,8 +42,8 @@ class RbacRoleControllerRestTest {
@MockBean
RbacRoleRepository rbacRoleRepository;
@Mock
EntityManager em;
@MockBean
EntityManagerWrapper em;
@MockBean
EntityManagerFactory emf;

View File

@@ -2,10 +2,9 @@ package net.hostsharing.hsadminng.rbac.subject;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import org.junit.jupiter.api.BeforeEach;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -15,18 +14,12 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.SynchronizationType;
import java.util.Map;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.test.IsValidUuidMatcher.isUuidValid;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -44,19 +37,9 @@ class RbacSubjectControllerRestTest {
@MockBean
RbacSubjectRepository rbacSubjectRepository;
@Mock
EntityManager em;
@MockBean
EntityManagerFactory emf;
EntityManagerWrapper em;
@BeforeEach
void init() {
when(emf.createEntityManager()).thenReturn(em);
when(emf.createEntityManager(any(Map.class))).thenReturn(em);
when(emf.createEntityManager(any(SynchronizationType.class))).thenReturn(em);
when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em);
}
@Test
void createSubjectUsesGivenUuid() throws Exception {

View File

@@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.rbac.test;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.rbac.grant.RbacGrantEntity;
import net.hostsharing.hsadminng.rbac.grant.RbacGrantRepository;
import net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService;

View File

@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.test;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import java.util.List;

View File

@@ -3,13 +3,13 @@ package net.hostsharing.hsadminng.rbac.test;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.mapper.StandardMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import jakarta.persistence.EntityManager;
import jakarta.persistence.ManyToOne;
import jakarta.validation.ValidationException;
import java.util.List;
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.when;
class MapperUnitTest {
@Mock
EntityManager em;
EntityManagerWrapper em;
@InjectMocks
StandardMapper mapper;

View File

@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.test;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.persistence.BaseEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;