implements optimistic locking for PackageEntity
This commit is contained in:
parent
5ea8069608
commit
a04929453c
@ -19,6 +19,9 @@ public class PackageEntity {
|
||||
|
||||
private @Id UUID uuid;
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "customeruuid")
|
||||
private CustomerEntity customer;
|
||||
|
@ -7,6 +7,7 @@
|
||||
create table if not exists package
|
||||
(
|
||||
uuid uuid unique references RbacObject (uuid),
|
||||
version int not null default 0,
|
||||
customerUuid uuid references customer (uuid),
|
||||
name varchar(5),
|
||||
description varchar(96)
|
||||
|
@ -2,11 +2,13 @@ package net.hostsharing.hsadminng.hs.hspackage;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.hscustomer.CustomerRepository;
|
||||
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.ObjectOptimisticLockingFailureException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -18,7 +20,7 @@ import static net.hostsharing.test.JpaAttempt.attempt;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DataJpaTest
|
||||
@ComponentScan(basePackageClasses = { Context.class, CustomerRepository.class })
|
||||
@ComponentScan(basePackageClasses = { Context.class, CustomerRepository.class, JpaAttempt.class })
|
||||
@DirtiesContext
|
||||
class PackageRepositoryIntegrationTest {
|
||||
|
||||
@ -28,7 +30,11 @@ class PackageRepositoryIntegrationTest {
|
||||
@Autowired
|
||||
PackageRepository packageRepository;
|
||||
|
||||
@Autowired EntityManager em;
|
||||
@Autowired
|
||||
EntityManager em;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@Nested
|
||||
class FindAllByOptionalNameLike {
|
||||
@ -88,13 +94,13 @@ class PackageRepositoryIntegrationTest {
|
||||
|
||||
// when
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
// then
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
|
||||
JpaSystemException.class,
|
||||
"[403] user admin@aaa.example.com", "has no permission to assume role package#aab00#admin");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -102,12 +108,12 @@ class PackageRepositoryIntegrationTest {
|
||||
currentUser("unknown@example.org");
|
||||
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -117,16 +123,59 @@ class PackageRepositoryIntegrationTest {
|
||||
assumedRoles("customer#aaa.admin");
|
||||
|
||||
final var result = attempt(
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
em,
|
||||
() -> packageRepository.findAllByOptionalNameLike(null));
|
||||
|
||||
result.assertExceptionWithRootCauseMessage(
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
JpaSystemException.class,
|
||||
"hsadminng.currentUser defined as unknown@example.org, but does not exists");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class OptimisticLocking {
|
||||
|
||||
@Test
|
||||
public void supportsOptimisticLocking() throws InterruptedException {
|
||||
// given
|
||||
hostsharingAdminWithAssumedRole("package#aaa00.admin");
|
||||
final var pac = packageRepository.findAllByOptionalNameLike("%").get(0);
|
||||
|
||||
// when
|
||||
final var result1 = jpaAttempt.transacted(() -> {
|
||||
hostsharingAdminWithAssumedRole("package#aaa00.admin");
|
||||
pac.setDescription("description set by thread 1");
|
||||
packageRepository.save(pac);
|
||||
});
|
||||
final var result2 = jpaAttempt.transacted(() -> {
|
||||
hostsharingAdminWithAssumedRole("package#aaa00.admin");
|
||||
pac.setDescription("description set by thread 2");
|
||||
packageRepository.save(pac);
|
||||
sleep(1500);
|
||||
});
|
||||
|
||||
// then
|
||||
em.refresh(pac);
|
||||
assertThat(pac.getDescription()).isEqualTo("description set by thread 1");
|
||||
assertThat(result1.caughtException()).isNull();
|
||||
assertThat(result2.caughtException()).isInstanceOf(ObjectOptimisticLockingFailureException.class);
|
||||
}
|
||||
|
||||
private void sleep(final int millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hostsharingAdminWithAssumedRole(final String assumedRoles) {
|
||||
currentUser("mike@hostsharing.net");
|
||||
assumedRoles(assumedRoles);
|
||||
}
|
||||
|
||||
void currentUser(final String currentUser) {
|
||||
context.setCurrentUser(currentUser);
|
||||
assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser);
|
||||
@ -139,14 +188,14 @@ class PackageRepositoryIntegrationTest {
|
||||
|
||||
void noPackagesAreReturned(final List<PackageEntity> actualResult) {
|
||||
assertThat(actualResult)
|
||||
.extracting(PackageEntity::getName)
|
||||
.isEmpty();
|
||||
.extracting(PackageEntity::getName)
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
void exactlyThesePackagesAreReturned(final List<PackageEntity> actualResult, final String... packageNames) {
|
||||
assertThat(actualResult)
|
||||
.extracting(PackageEntity::getName)
|
||||
.containsExactlyInAnyOrder(packageNames);
|
||||
.extracting(PackageEntity::getName)
|
||||
.containsExactlyInAnyOrder(packageNames);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ public class TestPackage {
|
||||
public static final PackageEntity xxx02 = hsPackage(TestCustomer.xxx, "xxx02");
|
||||
|
||||
public static PackageEntity hsPackage(final CustomerEntity customer, final String name) {
|
||||
return new PackageEntity(randomUUID(), customer, name, "initial description of package " + name);
|
||||
return new PackageEntity(randomUUID(), 0, customer, name, "initial description of package " + name);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user