finalize PrivateCloud, Cloud- and ManagedServer and ManagedWebspace Billingtems and HostingAssets #63
@ -16,6 +16,7 @@ import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBui
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
@ -130,9 +131,8 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final BiConsumer<HsBookingItemInsertResource, HsBookingItemEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo()));
|
||||
entity.setValidity(toPostgresDateRange(LocalDate.now(), resource.getValidTo()));
|
||||
entity.putResources(KeyValueMap.from(resource.getResources()));
|
||||
};
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
||||
@Builder.Default
|
||||
@Type(PostgreSQLRangeType.class)
|
||||
@Column(name = "validity", columnDefinition = "daterange")
|
||||
private Range<LocalDate> validity = Range.emptyRange(LocalDate.class);
|
||||
private Range<LocalDate> validity = Range.closedInfinite(LocalDate.now());
|
||||
|
||||
@Column(name = "caption")
|
||||
private String caption;
|
||||
|
@ -62,10 +62,6 @@ components:
|
||||
minLength: 3
|
||||
maxLength: 80
|
||||
nullable: false
|
||||
validFrom:
|
||||
type: string
|
||||
format: date
|
||||
nullable: false
|
||||
validTo:
|
||||
type: string
|
||||
format: date
|
||||
|
@ -144,13 +144,16 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"projectUuid": "%s",
|
||||
"projectUuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 },
|
||||
"validFrom": "2022-10-13"
|
||||
"validTo": "{validTo}",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
""".formatted(givenProject.getUuid()))
|
||||
"""
|
||||
.replace("{projectUuid}", givenProject.getUuid().toString())
|
||||
.replace("{validTo}", LocalDate.now().plusMonths(1).toString())
|
||||
)
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/booking/items")
|
||||
@ -161,11 +164,14 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
{
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "2022-10-13",
|
||||
"validTo": null,
|
||||
"validFrom": "{today}",
|
||||
"validTo": "{todayPlus1Month}",
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""))
|
||||
"""
|
||||
.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
|
||||
|
||||
@ -236,7 +242,6 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
// TODO.impl: For unknown reason this test fails in about 50%, not finding the uuid (404).
|
||||
void projectAdmin_canGetRelatedBookingItem() {
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenBookingItem = bookingItemRepo.findByCaption("separate ManagedServer").stream()
|
||||
|
@ -0,0 +1,171 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.item;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRepository;
|
||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
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;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
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.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||
import static org.hamcrest.Matchers.matchesRegex;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsBookingItemController.class)
|
||||
@Import(Mapper.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
class HsBookingItemControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
Context contextMock;
|
||||
|
||||
@Mock
|
||||
EntityManager em;
|
||||
|
||||
@MockBean
|
||||
EntityManagerFactory emf;
|
||||
|
||||
@MockBean
|
||||
HsBookingProjectRepository bookingProjectRepo;
|
||||
|
||||
@MockBean
|
||||
HsBookingItemRepository bookingItemRepo;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AddBookingItem {
|
||||
|
||||
@Test
|
||||
void globalAdmin_canAddValidBookingItem() throws Exception {
|
||||
|
||||
final var givenProjectUuid = UUID.randomUUID();
|
||||
|
||||
// given
|
||||
when(em.find(HsBookingProjectEntity.class, givenProjectUuid)).thenAnswer(invocation ->
|
||||
HsBookingProjectEntity.builder()
|
||||
.uuid(invocation.getArgument(1))
|
||||
.build()
|
||||
);
|
||||
when(bookingItemRepo.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/hs/booking/items")
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validTo": "{validTo}",
|
||||
"garbage": "should not be accepted",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{projectUuid}", givenProjectUuid.toString())
|
||||
.replace("{validTo}", LocalDate.now().plusMonths(1).toString())
|
||||
)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$", lenientlyEquals("""
|
||||
{
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{today}",
|
||||
"validTo": "{todayPlus1Month}",
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{today}", LocalDate.now().toString())
|
||||
.replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString()))
|
||||
))
|
||||
.andExpect(header().string("Location", matchesRegex("http://localhost/api/hs/booking/items/[^/]*")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_canNotAddInvalidBookingItem() throws Exception {
|
||||
|
||||
final var givenProjectUuid = UUID.randomUUID();
|
||||
|
||||
// given
|
||||
when(em.find(HsBookingProjectEntity.class, givenProjectUuid)).thenAnswer(invocation ->
|
||||
HsBookingProjectEntity.builder()
|
||||
.uuid(invocation.getArgument(1))
|
||||
.build()
|
||||
);
|
||||
when(bookingItemRepo.save(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/hs/booking/items")
|
||||
.header("current-user", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{validFrom}",
|
||||
"resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{projectUuid}", givenProjectUuid.toString())
|
||||
.replace("{validFrom}", LocalDate.now().plusMonths(1).toString())
|
||||
)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
// TODO.test: MockMvc does not seem to validate additionalProperties=false
|
||||
// .andExpect(status().is4xxClientError())
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$", lenientlyEquals("""
|
||||
{
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{today}",
|
||||
"validTo": null,
|
||||
"resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 }
|
||||
}
|
||||
"""
|
||||
.replace("{today}", LocalDate.now().toString())
|
||||
.replace("{todayPlus1Month}", LocalDate.now().plusMonths(1).toString()))
|
||||
))
|
||||
.andExpect(header().string("Location", matchesRegex("http://localhost/api/hs/booking/items/[^/]*")));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.item;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
@ -14,6 +18,8 @@ class HsBookingItemEntityUnitTest {
|
||||
public static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-01-01");
|
||||
public static final LocalDate GIVEN_VALID_TO = LocalDate.parse("2030-12-31");
|
||||
|
||||
private MockedStatic<LocalDate> localDateMockedStatic = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS);
|
||||
|
||||
final HsBookingItemEntity givenBookingItem = HsBookingItemEntity.builder()
|
||||
.project(TEST_PROJECT)
|
||||
.type(HsBookingItemType.CLOUD_SERVER)
|
||||
@ -25,6 +31,24 @@ class HsBookingItemEntityUnitTest {
|
||||
.validity(toPostgresDateRange(GIVEN_VALID_FROM, GIVEN_VALID_TO))
|
||||
.build();
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
localDateMockedStatic.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void validityStartsToday() {
|
||||
// given
|
||||
final var fakedToday = LocalDate.of(2024, Month.MAY, 1);
|
||||
localDateMockedStatic.when(LocalDate::now).thenReturn(fakedToday);
|
||||
|
||||
// when
|
||||
final var newBookingItem = HsBookingItemEntity.builder().build();
|
||||
|
||||
// then
|
||||
assertThat(newBookingItem.getValidity().toString()).isEqualTo("Range{lower=2024-05-01, upper=null, mask=82, clazz=class java.time.LocalDate}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringContainsAllPropertiesAndResourcesSortedByKey() {
|
||||
final var result = givenBookingItem.toString();
|
||||
|
Loading…
Reference in New Issue
Block a user