implements create new rbac-user and transacted JpaAttemp
This commit is contained in:
@ -36,19 +36,21 @@ class CustomerRepositoryIntegrationTest {
|
||||
public void hostsharingAdmin_withoutAssumedRole_canCreateNewCustomer() {
|
||||
// given
|
||||
currentUser("mike@hostsharing.net");
|
||||
final var count = customerRepository.count();
|
||||
|
||||
// when
|
||||
|
||||
final var attempt = attempt(em, () -> {
|
||||
final var result = attempt(em, () -> {
|
||||
final var newCustomer = new CustomerEntity(
|
||||
UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com");
|
||||
return customerRepository.save(newCustomer);
|
||||
});
|
||||
|
||||
// then
|
||||
assertThat(attempt.wasSuccessful()).isTrue();
|
||||
assertThat(attempt.returnedResult()).isNotNull().extracting(CustomerEntity::getUuid).isNotNull();
|
||||
assertThatCustomerIsPersisted(attempt.returnedResult());
|
||||
assertThat(result.wasSuccessful()).isTrue();
|
||||
assertThat(result.returnedValue()).isNotNull().extracting(CustomerEntity::getUuid).isNotNull();
|
||||
assertThatCustomerIsPersisted(result.returnedValue());
|
||||
assertThat(customerRepository.count()).isEqualTo(count + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -58,14 +60,14 @@ class CustomerRepositoryIntegrationTest {
|
||||
assumedRoles("customer#aaa.admin");
|
||||
|
||||
// when
|
||||
final var attempt = attempt(em, () -> {
|
||||
final var result = attempt(em, () -> {
|
||||
final var newCustomer = new CustomerEntity(
|
||||
UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com");
|
||||
return customerRepository.save(newCustomer);
|
||||
});
|
||||
|
||||
// then
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
PersistenceException.class,
|
||||
"add-customer not permitted for customer#aaa.admin");
|
||||
}
|
||||
@ -76,14 +78,14 @@ class CustomerRepositoryIntegrationTest {
|
||||
currentUser("admin@aaa.example.com");
|
||||
|
||||
// when
|
||||
final var attempt = attempt(em, () -> {
|
||||
final var result = attempt(em, () -> {
|
||||
final var newCustomer = new CustomerEntity(
|
||||
UUID.randomUUID(), "yyy", 90002, "admin@yyy.example.com");
|
||||
return customerRepository.save(newCustomer);
|
||||
});
|
||||
|
||||
// then
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
PersistenceException.class,
|
||||
"add-customer not permitted for admin@aaa.example.com");
|
||||
|
||||
@ -152,12 +154,12 @@ class CustomerRepositoryIntegrationTest {
|
||||
assumedRoles("package#aab00.admin");
|
||||
|
||||
// when
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> customerRepository.findCustomerByOptionalPrefixLike(null));
|
||||
|
||||
// then
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
|
||||
}
|
||||
@ -166,11 +168,11 @@ class CustomerRepositoryIntegrationTest {
|
||||
void unknownUser_withoutAssumedRole_cannotViewAnyCustomers() {
|
||||
currentUser("unknown@example.org");
|
||||
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> customerRepository.findCustomerByOptionalPrefixLike(null));
|
||||
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
@ -181,11 +183,11 @@ class CustomerRepositoryIntegrationTest {
|
||||
currentUser("unknown@example.org");
|
||||
assumedRoles("customer#aaa.admin");
|
||||
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> customerRepository.findCustomerByOptionalPrefixLike(null));
|
||||
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
|
@ -85,12 +85,12 @@ class PackageRepositoryIntegrationTest {
|
||||
assumedRoles("package#aab00.admin");
|
||||
|
||||
// when
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
// then
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
|
||||
}
|
||||
@ -99,11 +99,11 @@ class PackageRepositoryIntegrationTest {
|
||||
void unknownUser_withoutAssumedRole_cannotViewAnyPackages() {
|
||||
currentUser("unknown@example.org");
|
||||
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
@ -114,11 +114,11 @@ class PackageRepositoryIntegrationTest {
|
||||
currentUser("unknown@example.org");
|
||||
assumedRoles("customer#aaa.admin");
|
||||
|
||||
final var attempt = attempt(
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
attempt.assertExceptionWithRootCauseMessage(
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
@ -141,7 +141,6 @@ class PackageRepositoryIntegrationTest {
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
void exactlyThesePackagesAreReturned(final List<PackageEntity> actualResult, final String... packageNames) {
|
||||
assertThat(actualResult)
|
||||
.extracting(PackageEntity::getName)
|
||||
|
@ -0,0 +1,157 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class
|
||||
)
|
||||
@Transactional
|
||||
class RbacUserControllerAcceptanceTest {
|
||||
|
||||
@LocalServerPort
|
||||
private Integer port;
|
||||
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Autowired
|
||||
Context context;
|
||||
|
||||
@Autowired
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Nested
|
||||
class ApiRbacUsersGet {
|
||||
|
||||
@Test
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewAllUsers() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].name", is("aaa00@aaa.example.com"))
|
||||
.body("[1].name", is("aaa01@aaa.example.com"))
|
||||
.body("[2].name", is("aaa02@aaa.example.com"))
|
||||
.body("size()", is(14));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void hostsharingAdmin_withoutAssumedRole_canViewAllUsersByName() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users?name=aac")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].name", is("aac00@aac.example.com"))
|
||||
.body("[1].name", is("aac01@aac.example.com"))
|
||||
.body("[2].name", is("aac02@aac.example.com"))
|
||||
.body("size()", is(3));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void customerAdmin_withoutAssumedRole_canViewUsersInItsRealm() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "admin@aab.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].name", is("aab00@aab.example.com"))
|
||||
.body("[1].name", is("aab01@aab.example.com"))
|
||||
.body("[2].name", is("aab02@aab.example.com"))
|
||||
.body("[3].name", is("admin@aab.example.com"))
|
||||
.body("size()", is(4));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void packetAdmin_withoutAssumedRole_canViewAllUsersOfItsPackage() {
|
||||
|
||||
// @formatter:off
|
||||
RestAssured
|
||||
.given()
|
||||
.header("current-user", "aaa01@aaa.example.com")
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("[0].name", is("aaa01@aaa.example.com"))
|
||||
.body("size()", is(1));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ApiRbacUsersPost {
|
||||
|
||||
@Test
|
||||
void anybody_canCreateANewUser() {
|
||||
|
||||
// @formatter:off
|
||||
final var location = RestAssured
|
||||
.given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"name": "new-user@example.com"
|
||||
}
|
||||
""")
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/rbac-users")
|
||||
.then().assertThat()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("name", is("new-user@example.com"))
|
||||
.header("Location", startsWith("http://localhost"))
|
||||
.extract().header("Location");
|
||||
// @formatter:on
|
||||
|
||||
// finally, the user can view its own record
|
||||
final var newUserUuid = UUID.fromString(
|
||||
location.substring(location.lastIndexOf('/') + 1));
|
||||
context.setCurrentUser("new-user@example.com");
|
||||
assertThat(rbacUserRepository.findByUuid(newUserUuid))
|
||||
.extracting(RbacUserEntity::getName).isEqualTo("new-user@example.com");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.rbacrole.TestRbacRole.*;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacuser.TestRbacUser.userAaa;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacuser.TestRbacUser.userBbb;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
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;
|
||||
|
||||
@WebMvcTest(RbacUserController.class)
|
||||
class RbacUserControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mockMvc;
|
||||
@MockBean
|
||||
Context contextMock;
|
||||
@MockBean
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Test
|
||||
void willListAllUsers() throws Exception {
|
||||
|
||||
// given
|
||||
when(rbacUserRepository.findByOptionalNameLike(null)).thenReturn(
|
||||
List.of(userAaa, userBbb));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/rbac-users")
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", hasSize(2)))
|
||||
.andExpect(jsonPath("$[0].name", is(userAaa.getName())))
|
||||
.andExpect(jsonPath("$[1].name", is(userBbb.getName())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void willListUsersByName() throws Exception {
|
||||
|
||||
// given
|
||||
when(rbacUserRepository.findByOptionalNameLike("admin@aaa")).thenReturn(
|
||||
List.of(userAaa));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/rbac-users")
|
||||
.param("name", "admin@aaa")
|
||||
.header("current-user", "mike@hostsharing.net")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$", hasSize(1)))
|
||||
.andExpect(jsonPath("$[0].name", is(userAaa.getName())));
|
||||
}
|
||||
}
|
@ -2,21 +2,26 @@ package net.hostsharing.hsadminng.rbac.rbacuser;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.test.Array;
|
||||
import net.hostsharing.test.JpaAttempt;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.test.annotation.Commit;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.test.JpaAttempt.attempt;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DataJpaTest
|
||||
@ComponentScan(basePackageClasses = { Context.class, RbacUserRepository.class })
|
||||
@ComponentScan(basePackageClasses = { RbacUserRepository.class, Context.class, JpaAttempt.class })
|
||||
class RbacUserRepositoryIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
@ -25,8 +30,59 @@ class RbacUserRepositoryIntegrationTest {
|
||||
@Autowired
|
||||
RbacUserRepository rbacUserRepository;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Autowired EntityManager em;
|
||||
|
||||
@Nested
|
||||
class CreateUser {
|
||||
|
||||
@Test
|
||||
public void anyoneCanCreateTheirOwnUser() {
|
||||
// given
|
||||
final var givenNewUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
|
||||
|
||||
// when
|
||||
final var result = rbacUserRepository.create(
|
||||
new RbacUserEntity(null, givenNewUserName));
|
||||
|
||||
// then the persisted user is returned
|
||||
assertThat(result).isNotNull().extracting(RbacUserEntity::getName).isEqualTo(givenNewUserName);
|
||||
|
||||
// and the new user entity can be fetched by the user itself
|
||||
currentUser(givenNewUserName);
|
||||
assertThat(em.find(RbacUserEntity.class, result.getUuid()))
|
||||
.isNotNull().extracting(RbacUserEntity::getName).isEqualTo(givenNewUserName);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Commit
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
void anyoneCanCreateTheirOwnUser_committed() {
|
||||
|
||||
// given:
|
||||
final var givenUuid = UUID.randomUUID();
|
||||
final var newUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
|
||||
|
||||
// when:
|
||||
final var result = jpaAttempt.transacted(() -> {
|
||||
currentUser("admin@aaa.example.com");
|
||||
return rbacUserRepository.create(new RbacUserEntity(givenUuid, newUserName));
|
||||
});
|
||||
|
||||
// then:
|
||||
assertThat(result.wasSuccessful()).isTrue();
|
||||
assertThat(result.returnedValue()).isNotNull()
|
||||
.extracting(RbacUserEntity::getUuid).isEqualTo(givenUuid);
|
||||
jpaAttempt.transacted(() -> {
|
||||
currentUser(newUserName);
|
||||
assertThat(em.find(RbacUserEntity.class, givenUuid))
|
||||
.isNotNull().extracting(RbacUserEntity::getName).isEqualTo(newUserName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindByOptionalNameLike {
|
||||
|
||||
@ -210,7 +266,8 @@ class RbacUserRepositoryIntegrationTest {
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("admin@aaa.example.com");
|
||||
|
||||
// then
|
||||
exactlyTheseRbacPermissionsAreReturned(result,
|
||||
exactlyTheseRbacPermissionsAreReturned(
|
||||
result,
|
||||
// @formatter:off
|
||||
"customer#aaa.admin -> customer#aaa: add-package",
|
||||
"customer#aaa.admin -> customer#aaa: view",
|
||||
@ -256,7 +313,8 @@ class RbacUserRepositoryIntegrationTest {
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("aaa00@aaa.example.com");
|
||||
|
||||
// then
|
||||
exactlyTheseRbacPermissionsAreReturned(result,
|
||||
exactlyTheseRbacPermissionsAreReturned(
|
||||
result,
|
||||
// @formatter:off
|
||||
"customer#aaa.tenant -> customer#aaa: view",
|
||||
// "customer#aaa.admin -> customer#aaa: view" - Not permissions through the customer admin!
|
||||
@ -288,7 +346,8 @@ class RbacUserRepositoryIntegrationTest {
|
||||
final var result = rbacUserRepository.findPermissionsOfUser("aaa00@aaa.example.com");
|
||||
|
||||
// then
|
||||
exactlyTheseRbacPermissionsAreReturned(result,
|
||||
exactlyTheseRbacPermissionsAreReturned(
|
||||
result,
|
||||
// @formatter:off
|
||||
"customer#aaa.tenant -> customer#aaa: view",
|
||||
// "customer#aaa.admin -> customer#aaa: view" - Not permissions through the customer admin!
|
||||
@ -323,7 +382,6 @@ class RbacUserRepositoryIntegrationTest {
|
||||
.containsExactlyInAnyOrder();
|
||||
}
|
||||
|
||||
|
||||
void exactlyTheseRbacPermissionsAreReturned(
|
||||
final List<RbacUserPermission> actualResult,
|
||||
final String... expectedRoleNames) {
|
||||
|
@ -1,7 +1,11 @@
|
||||
package net.hostsharing.test;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.Optional;
|
||||
@ -13,69 +17,98 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Wraps the 'when' part of a DataJpaTest to improve readability of tests.
|
||||
* <p>
|
||||
* It
|
||||
* - makes sure that the SQL code is actually performed (em.flush()),
|
||||
* - if any exception is throw, it's caught and stored,
|
||||
* - makes the result available for assertions,
|
||||
* - cleans the JPA first level cache to force assertions read from the database, not just cache,
|
||||
* - offers some assertions based on the exception.
|
||||
* *
|
||||
*
|
||||
* @param <T> success result type
|
||||
* <li> makes sure that the SQL code is actually performed (em.flush()),
|
||||
* <li> if any exception is throw, it's caught and stored,
|
||||
* <li> makes the result available for assertions,
|
||||
* <li> cleans the JPA first level cache to force assertions read from the database, not just cache,
|
||||
* <li> offers some assertions based on the exception.
|
||||
* </p>
|
||||
* <p>
|
||||
* To run in same transaction as caller, use the static `attempt` method,
|
||||
* to run in a new transaction, inject this class and use the instance `transacted` methods.
|
||||
* </p>
|
||||
*/
|
||||
public class JpaAttempt<T> {
|
||||
@Service
|
||||
public class JpaAttempt {
|
||||
|
||||
private T result = null;
|
||||
private RuntimeException exception = null;
|
||||
@Autowired
|
||||
private final EntityManager em;
|
||||
|
||||
private String firstRootCauseMessageLineOf(final RuntimeException exception) {
|
||||
final var rootCause = NestedExceptionUtils.getRootCause(exception);
|
||||
return Optional.ofNullable(rootCause)
|
||||
.map(Throwable::getMessage)
|
||||
.map(message -> message.split("\\r|\\n|\\r\\n", 0)[0])
|
||||
.orElse(null);
|
||||
public JpaAttempt(final EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
public static <T> JpaAttempt<T> attempt(final EntityManager em, final Supplier<T> code) {
|
||||
return new JpaAttempt<>(em, code);
|
||||
}
|
||||
|
||||
public JpaAttempt(final EntityManager em, final Supplier<T> code) {
|
||||
public static <T> JpaResult<T> attempt(final EntityManager em, final Supplier<T> code) {
|
||||
try {
|
||||
result = code.get();
|
||||
final var result = new JpaResult<T>(code.get(), null);
|
||||
em.flush();
|
||||
em.clear();
|
||||
return result;
|
||||
} catch (RuntimeException exc) {
|
||||
exception = exc;
|
||||
return new JpaResult<T>(null, exc);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wasSuccessful() {
|
||||
return exception == null;
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public <T> JpaResult<T> transacted(final Supplier<T> code) {
|
||||
return attempt(em, code);
|
||||
}
|
||||
|
||||
public T returnedResult() {
|
||||
return result;
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void transacted(final Runnable code) {
|
||||
attempt(em, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public RuntimeException caughtException() {
|
||||
return exception;
|
||||
}
|
||||
public static class JpaResult<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends RuntimeException> E caughtException(final Class<E> expectedExceptionClass) {
|
||||
if (expectedExceptionClass.isAssignableFrom(exception.getClass())) {
|
||||
return (E) exception;
|
||||
final T result;
|
||||
final RuntimeException exception;
|
||||
|
||||
public JpaResult(final T result, final RuntimeException exception) {
|
||||
this.result = result;
|
||||
this.exception = exception;
|
||||
}
|
||||
throw new AssertionFailedError("expected " + expectedExceptionClass + " but got " + exception);
|
||||
}
|
||||
|
||||
public void assertExceptionWithRootCauseMessage(
|
||||
final Class<? extends RuntimeException> expectedExceptionClass,
|
||||
final String... expectedRootCauseMessages) {
|
||||
assertThat(wasSuccessful()).isFalse();
|
||||
final String firstRootCauseMessageLine = firstRootCauseMessageLineOf(caughtException(expectedExceptionClass));
|
||||
for ( String expectedRootCauseMessage: expectedRootCauseMessages ) {
|
||||
assertThat(firstRootCauseMessageLine).contains(expectedRootCauseMessage);
|
||||
public boolean wasSuccessful() {
|
||||
return exception == null;
|
||||
}
|
||||
|
||||
public T returnedValue() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public RuntimeException caughtException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends RuntimeException> E caughtException(final Class<E> expectedExceptionClass) {
|
||||
if (expectedExceptionClass.isAssignableFrom(exception.getClass())) {
|
||||
return (E) exception;
|
||||
}
|
||||
throw new AssertionFailedError("expected " + expectedExceptionClass + " but got " + exception);
|
||||
}
|
||||
|
||||
public void assertExceptionWithRootCauseMessage(
|
||||
final Class<? extends RuntimeException> expectedExceptionClass,
|
||||
final String... expectedRootCauseMessages) {
|
||||
assertThat(wasSuccessful()).isFalse();
|
||||
final String firstRootCauseMessageLine = firstRootCauseMessageLineOf(caughtException(expectedExceptionClass));
|
||||
for (String expectedRootCauseMessage : expectedRootCauseMessages) {
|
||||
assertThat(firstRootCauseMessageLine).contains(expectedRootCauseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private String firstRootCauseMessageLineOf(final RuntimeException exception) {
|
||||
final var rootCause = NestedExceptionUtils.getRootCause(exception);
|
||||
return Optional.ofNullable(rootCause)
|
||||
.map(Throwable::getMessage)
|
||||
.map(message -> message.split("\\r|\\n|\\r\\n", 0)[0])
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ spring:
|
||||
hibernate:
|
||||
default_schema: public
|
||||
dialect: net.hostsharing.hsadminng.config.PostgreSQL95CustomDialect
|
||||
format_sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
show-sql: true
|
||||
@ -29,3 +30,6 @@ spring:
|
||||
logging:
|
||||
level:
|
||||
liquibase: INFO
|
||||
org.hibernate.SQL: DEBUG
|
||||
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
|
||||
X.org.springframework.jdbc.core: TRACE
|
||||
|
Reference in New Issue
Block a user