add advanced scenario-tests for coop-assets #123

Merged
hsh-michaelhoennig merged 24 commits from feature/add-advanced-scenario-tests-for-coop-assets into master 2024-11-25 16:06:26 +01:00
5 changed files with 215 additions and 48 deletions
Showing only changes of commit 071c61a568 - Show all commits

View File

@ -26,6 +26,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.CLEARING; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.CLEARING;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DEPOSIT; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DEPOSIT;
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DISBURSAL; import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DISBURSAL;
@ -147,23 +148,37 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
} }
final BiConsumer<HsOfficeCoopAssetsTransactionEntity, HsOfficeCoopAssetsTransactionResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { final BiConsumer<HsOfficeCoopAssetsTransactionEntity, HsOfficeCoopAssetsTransactionResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setMembershipUuid(entity.getMembership().getUuid());
resource.setMembershipMemberNumber(entity.getMembership().getTaggedMemberNumber());
// FIXME: extract to one method to be used in all 4 cases
if (entity.getReversalAssetTx() != null) { if (entity.getReversalAssetTx() != null) {
resource.getReversalAssetTx().setRevertedAssetTxUuid(entity.getUuid()); resource.getReversalAssetTx().setRevertedAssetTxUuid(entity.getUuid());
resource.getReversalAssetTx().setMembershipUuid(entity.getRevertedAssetTx().getMembership().getUuid());
resource.getReversalAssetTx().setMembershipMemberNumber(entity.getRevertedAssetTx().getTaggedMemberNumber());
} }
if (entity.getRevertedAssetTx() != null) { if (entity.getRevertedAssetTx() != null) {
resource.getRevertedAssetTx().setReversalAssetTxUuid(entity.getUuid()); resource.getRevertedAssetTx().setReversalAssetTxUuid(entity.getUuid());
resource.getRevertedAssetTx().setMembershipUuid(entity.getReversalAssetTx().getMembership().getUuid());
resource.getRevertedAssetTx().setMembershipMemberNumber(entity.getReversalAssetTx().getTaggedMemberNumber());
} }
if (entity.getAdoptionAssetTx() != null) { if (entity.getAdoptionAssetTx() != null) {
resource.getAdoptionAssetTx().setTransferAssetTxUuid(entity.getUuid()); resource.getAdoptionAssetTx().setTransferAssetTxUuid(entity.getUuid());
resource.getAdoptionAssetTx().setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
resource.getAdoptionAssetTx().setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
} }
if (entity.getTransferAssetTx() != null) { if (entity.getTransferAssetTx() != null) {
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid()); resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
resource.getTransferAssetTx().setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
} }
}; };
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
if (resource.getMembershipUuid() != null) { if (resource.getMembershipUuid() != null) {
entity.setMembership(emw.getReference(HsOfficeMembershipEntity.class, resource.getMembershipUuid())); final HsOfficeMembershipEntity membership = ofNullable(emw.find(HsOfficeMembershipEntity.class, resource.getMembershipUuid()))
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] membership.uuid %s not found".formatted(
resource.getMembershipUuid())));
entity.setMembership(membership);
} }
if (resource.getRevertedAssetTxUuid() != null) { if (resource.getRevertedAssetTxUuid() != null) {
entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid()) entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())

View File

@ -20,6 +20,15 @@ components:
uuid: uuid:
type: string type: string
format: uuid format: uuid
membership.uuid:
type: string
format: uuid
nullable: false
membership.memberNumber:
type: string
minLength: 9
maxLength: 9
pattern: 'M-[0-9]{7}'
transactionType: transactionType:
$ref: '#/components/schemas/HsOfficeCoopAssetsTransactionType' $ref: '#/components/schemas/HsOfficeCoopAssetsTransactionType'
assetValue: assetValue:
@ -53,6 +62,15 @@ components:
uuid: uuid:
type: string type: string
format: uuid format: uuid
membership.uuid:
type: string
format: uuid
nullable: false
membership.memberNumber:
type: string
minLength: 9
maxLength: 9
pattern: 'M-[0-9]{7}'
transactionType: transactionType:
$ref: '#/components/schemas/HsOfficeCoopAssetsTransactionType' $ref: '#/components/schemas/HsOfficeCoopAssetsTransactionType'
assetValue: assetValue:

View File

@ -16,6 +16,10 @@ components:
uuid: uuid:
type: string type: string
format: uuid format: uuid
membership.uuid:
type: string
format: uuid
nullable: false
transactionType: transactionType:
$ref: '#/components/schemas/HsOfficeCoopSharesTransactionType' $ref: '#/components/schemas/HsOfficeCoopSharesTransactionType'
shareCount: shareCount:
@ -41,6 +45,10 @@ components:
uuid: uuid:
type: string type: string
format: uuid format: uuid
membership.uuid:
type: string
format: uuid
nullable: false
transactionType: transactionType:
$ref: '#/components/schemas/HsOfficeCoopSharesTransactionType' $ref: '#/components/schemas/HsOfficeCoopSharesTransactionType'
shareCount: shareCount:

View File

@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.JsonBuilder; import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
import net.hostsharing.hsadminng.test.TestUuidGenerator;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
@ -25,10 +26,12 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionControllerRestTest.SuccessfullyCreatedTestCases.ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE;
import static net.hostsharing.hsadminng.rbac.test.JsonBuilder.jsonObject; import static net.hostsharing.hsadminng.rbac.test.JsonBuilder.jsonObject;
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; 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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -38,17 +41,30 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
class HsOfficeCoopAssetsTransactionControllerRestTest { class HsOfficeCoopAssetsTransactionControllerRestTest {
private static final UUID AVAILABLE_MEMBERSHIP_UUID = UUID.randomUUID(); private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.get(0);
public static final HsOfficeMembershipEntity AVAILABLE_MEMBER_ENTITY = HsOfficeMembershipEntity.builder()
.uuid(AVAILABLE_MEMBERSHIP_UUID)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
.memberNumberSuffix("00")
.build();
private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = UUID.randomUUID();
private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699"; private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699";
private static final String AVAILABLE_MEMBER_NUMBER = "M-1234600";
private static final UUID ORIGIN_MEMBERSHIP_UUID = TestUuidGenerator.get(1);
private static final String ORIGIN_MEMBER_NUMBER = "M-1111100";
public final HsOfficeMembershipEntity ORIGIN_TARGET_MEMBER_ENTITY = HsOfficeMembershipEntity.builder()
.uuid(ORIGIN_MEMBERSHIP_UUID)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumberOf(ORIGIN_MEMBER_NUMBER))
.build())
.memberNumberSuffix(suffixOf(ORIGIN_MEMBER_NUMBER))
.build();
private static final UUID AVAILABLE_TARGET_MEMBERSHIP_UUID = TestUuidGenerator.get(2);
private static final String AVAILABLE_TARGET_MEMBER_NUMBER = "M-1234500";
public final HsOfficeMembershipEntity AVAILABLE_MEMBER_ENTITY = HsOfficeMembershipEntity.builder()
.uuid(AVAILABLE_TARGET_MEMBERSHIP_UUID)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumberOf(AVAILABLE_TARGET_MEMBER_NUMBER))
.build())
.memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER))
.build();
private static final UUID NEW_EXPLICITLY_CREATED_ASSET_TX_UUID = TestUuidGenerator.get(3);
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@ -61,8 +77,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
StrictMapper mapper; StrictMapper mapper;
@MockBean @MockBean
@SuppressWarnings("unused") // not used in test, but in base-class of StrictMapper EntityManagerWrapper emw; // even if not used in test anymore, it's needed by base-class of StrictMapper
EntityManagerWrapper em;
@MockBean @MockBean
HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo; HsOfficeCoopAssetsTransactionRepository coopAssetsTransactionRepo;
@ -70,7 +85,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
@MockBean @MockBean
HsOfficeMembershipRepository membershipRepo; HsOfficeMembershipRepository membershipRepo;
static final String VALID_INSERT_REQUEST_BODY = """ static final String INSERT_REQUEST_BODY_TEMPLATE = """
{ {
"membership.uuid": "%s", "membership.uuid": "%s",
"transactionType": "DEPOSIT", "transactionType": "DEPOSIT",
@ -81,7 +96,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
"adoptingMembership.uuid": null, "adoptingMembership.uuid": null,
"adoptingMembership.memberNumber": null "adoptingMembership.memberNumber": null
} }
""".formatted(UUID.randomUUID()); """.formatted(ORIGIN_MEMBERSHIP_UUID);
enum BadRequestTestCases { enum BadRequestTestCases {
MEMBERSHIP_UUID_MISSING( MEMBERSHIP_UUID_MISSING(
@ -153,14 +168,16 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
} }
String givenRequestBody() { String givenRequestBody() {
return givenBodyTransformation.apply(jsonObject(VALID_INSERT_REQUEST_BODY)).toString(); return givenBodyTransformation.apply(jsonObject(INSERT_REQUEST_BODY_TEMPLATE)).toString();
} }
} }
@ParameterizedTest @ParameterizedTest
@EnumSource(BadRequestTestCases.class) @EnumSource(BadRequestTestCases.class)
void respondWithBadRequest(final BadRequestTestCases testCase) throws Exception { void respondWithBadRequest(final BadRequestTestCases testCase) throws Exception {
// assumeThat(testCase == ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue(); // HOWTO: run just a single test-case in a data-driven test-method
// org.assertj.core.api.Assumptions.assumeThat(
// testCase == ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -183,7 +200,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
requestBody -> requestBody requestBody -> requestBody
.with("transactionType", "TRANSFER") .with("transactionType", "TRANSFER")
.with("assetValue", -64.00) .with("assetValue", -64.00)
.with("adoptingMembership.memberNumber", AVAILABLE_MEMBER_NUMBER), .with("adoptingMembership.memberNumber", AVAILABLE_TARGET_MEMBER_NUMBER),
""" """
{ {
"transactionType": "TRANSFER", "transactionType": "TRANSFER",
@ -202,14 +219,51 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
requestBody -> requestBody requestBody -> requestBody
.with("transactionType", "TRANSFER") .with("transactionType", "TRANSFER")
.with("assetValue", -64.00) .with("assetValue", -64.00)
.with("adoptingMembership.uuid", AVAILABLE_MEMBERSHIP_UUID.toString()), .with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString())
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()),
// """
// {
// "uuid": "%{NEW_EXPLICITLY_CREATED_ASSET_TX_UUID}",
// "membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
// "transactionType": "TRANSFER",
// "assetValue": -64.00,
// "adoptionAssetTx": {
// "membership.uuid": "%{AVAILABLE_MEMBERSHIP_UUID}",
// "transactionType": "ADOPTION",
// "assetValue": 64.00,
// "transferAssetTx.uuid": "%{NEW_EXPLICITLY_CREATED_ASSET_TX_UUID}"
// },
// "transferAssetTx": null,
// "revertedAssetTx": null,
// "reversalAssetTx": null
// }
// """
// .replace("%{NEW_EXPLICITLY_CREATED_ASSET_TX_UUID}", NEW_EXPLICITLY_CREATED_ASSET_TX_UUID.toString())
// .replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
// .replace("%{AVAILABLE_MEMBERSHIP_UUID}", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()));
""" """
{ {
"uuid": "55555555-5555-5555-5555-555555555555",
"membership.uuid": "11111111-1111-1111-1111-111111111111",
"membership.memberNumber": "M-1111100",
"transactionType": "TRANSFER", "transactionType": "TRANSFER",
"assetValue": -64.00, "assetValue": -64,
"valueDate": "2022-10-13",
"reference": "valid reference",
"comment": "valid comment",
"adoptionAssetTx": { "adoptionAssetTx": {
"uuid": "44444444-4444-4444-4444-444444444444",
"membership.uuid": "22222222-2222-2222-2222-222222222222",
"membership.memberNumber": "M-1234500",
"transactionType": "ADOPTION", "transactionType": "ADOPTION",
"assetValue": 64.00 "assetValue": 64,
"valueDate": "2022-10-13",
"reference": "valid reference",
"comment": "valid comment",
"adoptionAssetTx.uuid": null,
"transferAssetTx.uuid": "55555555-5555-5555-5555-555555555555",
"revertedAssetTx.uuid": null,
"reversalAssetTx.uuid": null
}, },
"transferAssetTx": null, "transferAssetTx": null,
"revertedAssetTx": null, "revertedAssetTx": null,
@ -228,13 +282,16 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
} }
String givenRequestBody() { String givenRequestBody() {
return givenBodyTransformation.apply(jsonObject(VALID_INSERT_REQUEST_BODY)).toString(); return givenBodyTransformation.apply(jsonObject(INSERT_REQUEST_BODY_TEMPLATE)).toString();
} }
} }
@ParameterizedTest @ParameterizedTest
@EnumSource(SuccessfullyCreatedTestCases.class) @EnumSource(SuccessfullyCreatedTestCases.class)
void respondWithSuccessfullyCreated(final SuccessfullyCreatedTestCases testCase) throws Exception { void respondWithSuccessfullyCreated(final SuccessfullyCreatedTestCases testCase) throws Exception {
// uncomment, if you need to run just a single test-case in this data-driven test-method
org.assertj.core.api.Assumptions.assumeThat(
testCase == ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -251,17 +308,33 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
@BeforeEach @BeforeEach
void initMocks() { void initMocks() {
final var availableMemberNumber = Integer.valueOf(AVAILABLE_MEMBER_NUMBER.substring("M-".length())); TestUuidGenerator.start(4);
when(membershipRepo.findMembershipByMemberNumber(availableMemberNumber)).thenReturn(AVAILABLE_MEMBER_ENTITY);
when(membershipRepo.findByUuid(AVAILABLE_MEMBERSHIP_UUID)).thenReturn(Optional.of(AVAILABLE_MEMBER_ENTITY)); when(emw.find(eq(HsOfficeMembershipEntity.class), eq(ORIGIN_MEMBERSHIP_UUID))).thenReturn(ORIGIN_TARGET_MEMBER_ENTITY);
when(emw.find(eq(HsOfficeMembershipEntity.class), eq(AVAILABLE_TARGET_MEMBERSHIP_UUID))).thenReturn(AVAILABLE_MEMBER_ENTITY);
final var availableMemberNumber = Integer.valueOf(AVAILABLE_TARGET_MEMBER_NUMBER.substring("M-".length()));
when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(AVAILABLE_MEMBER_ENTITY);
when(membershipRepo.findByUuid(eq(ORIGIN_MEMBERSHIP_UUID))).thenReturn(Optional.of(ORIGIN_TARGET_MEMBER_ENTITY));
when(membershipRepo.findByUuid(eq(AVAILABLE_TARGET_MEMBERSHIP_UUID))).thenReturn(Optional.of(AVAILABLE_MEMBER_ENTITY));
when(coopAssetsTransactionRepo.save(any(HsOfficeCoopAssetsTransactionEntity.class))) when(coopAssetsTransactionRepo.save(any(HsOfficeCoopAssetsTransactionEntity.class)))
.thenAnswer(invocation -> { .thenAnswer(invocation -> {
final var entity = (HsOfficeCoopAssetsTransactionEntity) invocation.getArgument(0); final var entity = (HsOfficeCoopAssetsTransactionEntity) invocation.getArgument(0);
if (entity.getUuid() == null) { if (entity.getUuid() == null) {
entity.setUuid(UUID.randomUUID()); entity.setUuid(TestUuidGenerator.next());
} }
return entity; return entity;
} }
); );
} }
private int partnerNumberOf(final String memberNumber) {
return Integer.parseInt(memberNumber.substring("M-".length(), memberNumber.length()-2));
}
private String suffixOf(final String memberNumber) {
return memberNumber.substring("M-".length()+5);
}
} }

View File

@ -0,0 +1,53 @@
package net.hostsharing.hsadminng.test;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
@UtilityClass
public class TestUuidGenerator {
private static final UUID ZEROES_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
private static final List<UUID> GIVEN_UUIDS = List.of(
ZEROES_UUID,
uuidWithDigit(1),
uuidWithDigit(2),
uuidWithDigit(3),
uuidWithDigit(4),
uuidWithDigit(5),
uuidWithDigit(6),
uuidWithDigit(7),
uuidWithDigit(8),
uuidWithDigit(9)
);
private Queue<UUID> availableUuids = null;
public static void start(final int firstIndex) {
availableUuids = new LinkedList<>(GIVEN_UUIDS.subList(firstIndex, GIVEN_UUIDS.size()));
}
public static UUID next() {
if (availableUuids == null) {
throw new IllegalStateException("UUID generator not started yet, call start() in @BeforeEach.");
}
if (availableUuids.isEmpty()) {
throw new IllegalStateException("No UUIDs available anymore.");
}
return availableUuids.poll();
}
public static UUID get(final int index) {
return GIVEN_UUIDS.get(index);
}
private static @NotNull UUID uuidWithDigit(final int digit) {
return UUID.fromString(ZEROES_UUID.toString().replace('0', Character.forDigit(digit, 10)));
}
}