implement coop-asset-TRANSFER-transaction reversal #125

Merged
hsh-michaelhoennig merged 7 commits from feature/implement-coop-asset-transaction-reversal into master 2024-11-28 07:10:36 +01:00
3 changed files with 112 additions and 21 deletions
Showing only changes of commit e3de6bd60b - Show all commits

View File

@ -227,7 +227,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
throw new ValidationException("REVERSAL asset transaction requires revertedAssetTx.uuid"); throw new ValidationException("REVERSAL asset transaction requires revertedAssetTx.uuid");
} }
final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid()) final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedEntityUuid %s not found".formatted( .orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedAssetTx.uuid %s not found".formatted(
resource.getRevertedAssetTxUuid()))); resource.getRevertedAssetTxUuid())));
revertedAssetTx.setReversalAssetTx(entity); revertedAssetTx.setReversalAssetTx(entity);
entity.setRevertedAssetTx(revertedAssetTx); entity.setRevertedAssetTx(revertedAssetTx);

View File

@ -30,6 +30,7 @@ import java.util.function.Function;
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.assertj.core.api.Assumptions.assumeThat;
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.ArgumentMatchers.eq;
@ -42,6 +43,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
class HsOfficeCoopAssetsTransactionControllerRestTest { class HsOfficeCoopAssetsTransactionControllerRestTest {
// If you need to run just a single test-case in this data-driven test-method,
// set SINGLE_TEST_CASE_EXECUTION to true and make sure you do NOT EVER commit it to git!
private static final boolean SINGLE_TEST_CASE_EXECUTION = false;
private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.use(0); private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.use(0);
private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699"; private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699";
@ -68,8 +73,8 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
// The following refs depend on the implementation of the respective implementation and might change if it changes. // The following refs depend on the implementation of the respective implementation and might change if it changes.
// The same TestUuidGenerator.ref(#) does NOT mean the UUIDs refer to the same entity, // The same TestUuidGenerator.ref(#) does NOT mean the UUIDs refer to the same entity,
// its rather coincidence because different test-cases have different execution paths in the production code. // its rather coincidence because different test-cases have different execution paths in the production code.
private static final UUID NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.ref(4); private static final UUID NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.ref(6);
private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(4); private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(6);
private static final UUID SOME_EXISTING_LOSS_ASSET_TX_UUID = TestUuidGenerator.use(3); private static final UUID SOME_EXISTING_LOSS_ASSET_TX_UUID = TestUuidGenerator.use(3);
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_LOSS_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder() public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_LOSS_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
@ -82,6 +87,33 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
.valueDate(LocalDate.parse("2024-10-15")) .valueDate(LocalDate.parse("2024-10-15"))
.build(); .build();
private static final UUID SOME_EXISTING_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.use(4);
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
.uuid(SOME_EXISTING_TRANSFER_ASSET_TX_UUID)
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
.transactionType(HsOfficeCoopAssetsTransactionType.TRANSFER)
.assetValue(BigDecimal.valueOf(-256))
.reference("some transfer asset tx ref")
.comment("some transfer asset tx comment")
.valueDate(LocalDate.parse("2024-10-15"))
.build();
private static final UUID SOME_EXISTING_ADOPTION_ASSET_TX_UUID = TestUuidGenerator.use(5);
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
.uuid(SOME_EXISTING_ADOPTION_ASSET_TX_UUID)
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
.transactionType(HsOfficeCoopAssetsTransactionType.TRANSFER)
.assetValue(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY.getAssetValue().negate())
.reference("some adoption asset tx ref")
.comment("some adoption asset tx comment")
.valueDate(LocalDate.parse("2024-10-15"))
.transferAssetTx(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY)
.build();
{
SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY.setAdoptionAssetTx(SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY);
}
@Autowired @Autowired
MockMvc mockMvc; MockMvc mockMvc;
@ -192,8 +224,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
@EnumSource(BadRequestTestCases.class) @EnumSource(BadRequestTestCases.class)
void respondWithBadRequest(final BadRequestTestCases testCase) throws Exception { void respondWithBadRequest(final BadRequestTestCases testCase) throws Exception {
// HOWTO: run just a single test-case in a data-driven test-method // HOWTO: run just a single test-case in a data-driven test-method
// org.assertj.core.api.Assumptions.assumeThat( // - set SINGLE_TEST_CASE_EXECUTION to true - see above
// testCase == ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue(); // - select the test case enum value you want to run
assumeThat(!SINGLE_TEST_CASE_EXECUTION ||
testCase == BadRequestTestCases.ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -217,10 +251,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
.with("transactionType", "REVERSAL") .with("transactionType", "REVERSAL")
.with("assetValue", "64.00") .with("assetValue", "64.00")
.with("valueDate", "2024-10-15") .with("valueDate", "2024-10-15")
.with("reference", "reversal ref") .with("reference", "reversal of loss ref")
.with("comment", "reversal comment") .with("comment", "reversal of loss asset tx comment")
.with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString()), .with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString()),
Expected.REVERT_RESPONSE), Expected.REVERT_LOSS_RESPONSE),
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER( TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER(
requestBody -> requestBody requestBody -> requestBody
@ -235,7 +269,17 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
.with("assetValue", -64.00) .with("assetValue", -64.00)
.with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString()) .with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString())
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()), .with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()),
Expected.TRANSFER_RESPONSE); Expected.TRANSFER_RESPONSE),
REVERTING_TRANSFER_ASSET_TRANSACTION_IMPLICITLY_REVERTS_ADOPTING_ASSET_TRANSACTION(
requestBody -> requestBody
.with("transactionType", "REVERSAL")
.with("assetValue", "256.00")
.with("valueDate", "2024-10-15")
.with("reference", "reversal of transfer ref")
.with("comment", "reversal of transfer asset tx comment")
.with("revertedAssetTx.uuid", SOME_EXISTING_TRANSFER_ASSET_TX_UUID.toString()),
Expected.REVERT_TRANSFER_RESPONSE);
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation; private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
private final String expectedResponseBody; private final String expectedResponseBody;
@ -253,7 +297,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
private static class Expected { private static class Expected {
public static final String REVERT_RESPONSE = """ public static final String REVERT_LOSS_RESPONSE = """
{ {
"uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}", "uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}", "membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
@ -261,8 +305,8 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
"transactionType": "REVERSAL", "transactionType": "REVERSAL",
"assetValue": 64.00, "assetValue": 64.00,
"valueDate": "2024-10-15", "valueDate": "2024-10-15",
"reference": "reversal ref", "reference": "reversal of loss ref",
"comment": "reversal comment", "comment": "reversal of loss asset tx comment",
"adoptionAssetTx": null, "adoptionAssetTx": null,
"transferAssetTx": null, "transferAssetTx": null,
"revertedAssetTx": { "revertedAssetTx": {
@ -281,7 +325,9 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
} }
} }
""" """
.replace("%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}", NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString()) .replace(
"%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString())
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString()) .replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER) .replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
.replace("%{SOME_EXISTING_LOSS_ASSET_TX_UUID}", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString()); .replace("%{SOME_EXISTING_LOSS_ASSET_TX_UUID}", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString());
@ -305,20 +351,57 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
"reversalAssetTx": null "reversalAssetTx": null
} }
""" """
.replace("%{NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID}", NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID.toString()) .replace(
"%{NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID}",
NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID.toString())
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString()) .replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER) .replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
.replace("%{AVAILABLE_MEMBERSHIP_UUID}", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()) .replace("%{AVAILABLE_MEMBERSHIP_UUID}", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString())
.replace("%{AVAILABLE_TARGET_MEMBER_NUMBER}", AVAILABLE_TARGET_MEMBER_NUMBER); .replace("%{AVAILABLE_TARGET_MEMBER_NUMBER}", AVAILABLE_TARGET_MEMBER_NUMBER);
public static final String REVERT_TRANSFER_RESPONSE = """
{
"uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
"membership.memberNumber": "%{ORIGIN_MEMBER_NUMBER}",
"transactionType": "REVERSAL",
"assetValue": 256.00,
"valueDate": "2024-10-15",
"reference": "reversal of transfer ref",
"comment": "reversal of transfer asset tx comment",
"adoptionAssetTx": null,
"transferAssetTx": null,
"revertedAssetTx": {
hsh-michaelhoennig marked this conversation as resolved Outdated

reversalAssetTx: null fehlt

reversalAssetTx: null fehlt
"uuid": "%{SOME_EXISTING_TRANSFER_ASSET_TX_UUID}",
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
"membership.memberNumber": "%{ORIGIN_MEMBER_NUMBER}",
"transactionType": "TRANSFER",
"assetValue": -256.00,
"valueDate": "2024-10-15",
"reference": "some transfer asset tx ref",
"comment": "some transfer asset tx comment",
"adoptionAssetTx.uuid": "%{SOME_EXISTING_ADOPTION_ASSET_TX_UUID}",
"transferAssetTx.uuid": null,
"revertedAssetTx.uuid": null,
"reversalAssetTx.uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}"
}
}
"""
.replace(
"%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString())
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
.replace("%{SOME_EXISTING_TRANSFER_ASSET_TX_UUID}", SOME_EXISTING_TRANSFER_ASSET_TX_UUID.toString())
.replace("%{SOME_EXISTING_ADOPTION_ASSET_TX_UUID}", SOME_EXISTING_ADOPTION_ASSET_TX_UUID.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 assumeThat(!SINGLE_TEST_CASE_EXECUTION ||
// org.assertj.core.api.Assumptions.assumeThat( testCase == SuccessfullyCreatedTestCases.REVERTING_TRANSFER_ASSET_TRANSACTION_IMPLICITLY_REVERTS_ADOPTING_ASSET_TRANSACTION).isTrue();
// testCase == ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
// when // when
mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
@ -335,10 +418,11 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
@BeforeEach @BeforeEach
void initMocks() { void initMocks() {
TestUuidGenerator.start(4); TestUuidGenerator.start(6);
when(emw.find(eq(HsOfficeMembershipEntity.class), eq(ORIGIN_MEMBERSHIP_UUID))).thenReturn(ORIGIN_TARGET_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); 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())); final var availableMemberNumber = Integer.valueOf(AVAILABLE_TARGET_MEMBER_NUMBER.substring("M-".length()));
when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(AVAILABLE_MEMBER_ENTITY); when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(AVAILABLE_MEMBER_ENTITY);
@ -348,6 +432,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_LOSS_ASSET_TX_UUID)) when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_LOSS_ASSET_TX_UUID))
.thenReturn(Optional.of(SOME_EXISTING_LOSS_ASSET_TX_ENTITY)); .thenReturn(Optional.of(SOME_EXISTING_LOSS_ASSET_TX_ENTITY));
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_TRANSFER_ASSET_TX_UUID))
.thenReturn(Optional.of(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY));
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_ADOPTION_ASSET_TX_UUID))
.thenReturn(Optional.of(SOME_EXISTING_ADOPTION_ASSET_TX_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);
@ -360,10 +448,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
} }
private int partnerNumberOf(final String memberNumber) { private int partnerNumberOf(final String memberNumber) {
return Integer.parseInt(memberNumber.substring("M-".length(), memberNumber.length()-2)); return Integer.parseInt(memberNumber.substring("M-".length(), memberNumber.length() - 2));
} }
private String suffixOf(final String memberNumber) { private String suffixOf(final String memberNumber) {
return memberNumber.substring("M-".length()+5); return memberNumber.substring("M-".length() + 5);
} }
} }

View File

@ -63,6 +63,9 @@ public class TestUuidGenerator {
* @return a constant UUID related to the given index * @return a constant UUID related to the given index
*/ */
public static UUID use(final int index) { public static UUID use(final int index) {
if (staticallyUsedIndexes.contains(index)) {
throw new IllegalArgumentException("index " + index + " already used statically");
}
staticallyUsedIndexes.add(index); staticallyUsedIndexes.add(index);
return GIVEN_UUIDS.get(index); return GIVEN_UUIDS.get(index);
} }