diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java index 450e8cd1..8288d7c1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java @@ -240,7 +240,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse final var adoptionAssetTx = revertedAssetTx.getAdoptionAssetTx(); final var adoptionReversalAssetTx = HsOfficeCoopAssetsTransactionEntity.builder() .transactionType(REVERSAL) - .membership(adoptionAssetTx.getMembership()) // FIXME: check + .membership(adoptionAssetTx.getMembership()) .revertedAssetTx(adoptionAssetTx) .assetValue(adoptionAssetTx.getAssetValue().negate()) .comment(resource.getComment()) @@ -254,6 +254,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse if (resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER) { final var adoptingMembership = determineAdoptingMembership(resource); + if ( entity.getMembership() == adoptingMembership) { + throw new ValidationException("transferring and adopting membership must be different, but both are " + + adoptingMembership.getTaggedMemberNumber()); + } final var adoptingAssetTx = createAdoptingAssetTx(entity, adoptingMembership); entity.setAdoptionAssetTx(adoptingAssetTx); } @@ -289,13 +293,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse + "' not found or not accessible"); } - if (resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER) { - throw new ValidationException( - "either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType=" - + HsOfficeCoopAssetsTransactionTypeResource.TRANSFER); - } - - return null; + throw new ValidationException( + "either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType=" + + HsOfficeCoopAssetsTransactionTypeResource.TRANSFER); } private HsOfficeCoopAssetsTransactionEntity createAdoptingAssetTx( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index c0ebfd00..4e6d5d51 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -31,11 +31,13 @@ import java.util.UUID; import java.util.function.Function; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.ADOPTION; +import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DEPOSIT; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DISBURSAL; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.REVERSAL; import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.TRANSFER; import static net.hostsharing.hsadminng.rbac.test.JsonBuilder.jsonObject; import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; @@ -49,13 +51,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) 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! + // If you need to run just a single test-case in this data-driven test-method, set SINGLE_TEST_CASE_EXECUTION to true! + // There is a test which fails if single test-case execution active to avoid merging this to master. private static final boolean SINGLE_TEST_CASE_EXECUTION = false; private static final int DYNAMIC_UUID_START_INDEX = 13; - private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.use(0); + private static final UUID UNAVAILABLE_UUID = TestUuidGenerator.use(0); private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699"; private static final UUID ORIGIN_MEMBERSHIP_UUID = TestUuidGenerator.use(1); @@ -202,6 +204,34 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { .setRevertedAssetTx(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx()); } + private static final String EXPECTED_RESULT_FROM_GET_SINGLE = """ + { + "uuid": "99999999-9999-9999-9999-999999999999", + "membership.uuid": "11111111-1111-1111-1111-111111111111", + "membership.memberNumber": "M-1111100", + "transactionType": "TRANSFER", + "assetValue": -1024, + "valueDate": "2024-11-10", + "reference": "some transfer", + "comment": "some transfer to get reverted", + "adoptionAssetTx": { + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + "membership.uuid": "22222222-2222-2222-2222-222222222222", + "membership.memberNumber": "M-1234500", + "transactionType": "ADOPTION", + "assetValue": 1024, + "valueDate": "2024-11-10", + "reference": "related adoption", + "comment": "some reversal of a transfer asset tx", + "adoptionAssetTx.uuid": null, + "transferAssetTx.uuid": "99999999-9999-9999-9999-999999999999", + "revertedAssetTx.uuid": null, + "reversalAssetTx.uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc" + } + } + """; + + private static final String EXPECTED_RESULT_FROM_GET_LIST = """ [ { @@ -501,29 +531,43 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { "[membershipUuid must not be null but is \"null\"]"), // TODO.impl: should be membership.uuid, Spring validation-problem? MEMBERSHIP_UUID_NOT_FOUND_OR_NOT_ACCESSIBLE( - requestBody -> requestBody.with("membership.uuid", UNAVAILABLE_MEMBERSHIP_UUID.toString()), - "membership.uuid " + UNAVAILABLE_MEMBERSHIP_UUID + " not found"), + requestBody -> requestBody.with("membership.uuid", UNAVAILABLE_UUID), + "membership.uuid " + UNAVAILABLE_UUID + " not found"), MEMBERSHIP_UUID_AND_MEMBER_NUMBER_MUST_NOT_BE_GIVEN_BOTH( requestBody -> requestBody .with("transactionType", TRANSFER.name()) .with("assetValue", "-128.00") - .with("adoptingMembership.uuid", UNAVAILABLE_MEMBERSHIP_UUID.toString()) + .with("adoptingMembership.uuid", UNAVAILABLE_UUID) .with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER), "either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"), MEMBERSHIP_UUID_OR_MEMBER_NUMBER_MUST_BE_GIVEN( requestBody -> requestBody - .with("transactionType", TRANSFER.name()) + .with("transactionType", TRANSFER) .with("assetValue", "-128.00"), "either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType=TRANSFER"), REVERSAL_ASSET_TRANSACTION_REQUIRES_REVERTED_ASSET_TX_UUID( requestBody -> requestBody - .with("transactionType", REVERSAL.name()) + .with("transactionType", REVERSAL) .with("assetValue", "-128.00"), "REVERSAL asset transaction requires revertedAssetTx.uuid"), + REVERSAL_ASSET_TRANSACTION_REQUIRES_AVAILABLE_REVERTED_ASSET_TX_UUID( + requestBody -> requestBody + .with("transactionType", REVERSAL) + .with("assetValue", "-128.00") + .with("revertedAssetTx.uuid", UNAVAILABLE_UUID), + "revertedAssetTx.uuid " + UNAVAILABLE_UUID + " not found"), + + REVERSAL_ASSET_TRANSACTION_MUST_NEGATE_VALUE_OF_REVERTED_ASSET_TX( + requestBody -> requestBody + .with("transactionType", REVERSAL) + .with("assetValue", "128.00") + .with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID), + "given assetValue=128.00 but must be negative value from reverted asset tx: -64"), + TRANSACTION_TYPE_MISSING( requestBody -> requestBody.without("transactionType"), "[transactionType must not be null but is \"null\"]"), @@ -534,35 +578,40 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { ASSETS_VALUE_FOR_DEPOSIT_MUST_BE_POSITIVE( requestBody -> requestBody - .with("transactionType", "DEPOSIT") + .with("transactionType", DEPOSIT) .with("assetValue", -64.00), "[for DEPOSIT, assetValue must be positive but is \"-64.00\"]"), ASSETS_VALUE_FOR_DISBURSAL_MUST_BE_NEGATIVE( requestBody -> requestBody - .with("transactionType", "DISBURSAL") + .with("transactionType", DISBURSAL) .with("assetValue", 64.00), "[for DISBURSAL, assetValue must be negative but is \"64.00\"]"), - //TODO: other transaction types + ADOPTING_MEMBERSHIP_MUST_NOT_BE_THE_SAME( + requestBody -> requestBody + .with("transactionType", TRANSFER) + .with("assetValue", -64.00) + .with("adoptingMembership.uuid", ORIGIN_MEMBERSHIP_UUID), + "transferring and adopting membership must be different, but both are M-1111100"), ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE( requestBody -> requestBody - .with("transactionType", "TRANSFER") + .with("transactionType", TRANSFER) .with("assetValue", -64.00) .with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER), "adoptingMembership.memberNumber='M-1234699' not found or not accessible"), ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE( requestBody -> requestBody - .with("transactionType", "TRANSFER") + .with("transactionType", TRANSFER) .with("assetValue", -64.00) - .with("adoptingMembership.uuid", UNAVAILABLE_MEMBERSHIP_UUID.toString()), - "adoptingMembership.uuid='" + UNAVAILABLE_MEMBERSHIP_UUID + "' not found or not accessible"), + .with("adoptingMembership.uuid", UNAVAILABLE_UUID), + "adoptingMembership.uuid='" + UNAVAILABLE_UUID + "' not found or not accessible"), ASSETS_VALUE_MUST_NOT_BE_NULL( requestBody -> requestBody - .with("transactionType", "REVERSAL") + .with("transactionType", REVERSAL) .with("assetValue", 0.00), "[assetValue must not be 0 but is \"0.00\"]"), @@ -600,7 +649,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { // - set SINGLE_TEST_CASE_EXECUTION to true - see above // - select the test case enum value you want to run assumeThat(!SINGLE_TEST_CASE_EXECUTION || - testCase == BadRequestTestCases.MEMBERSHIP_UUID_OR_MEMBER_NUMBER_MUST_BE_GIVEN).isTrue(); + testCase == BadRequestTestCases.ADOPTING_MEMBERSHIP_MUST_NOT_BE_THE_SAME).isTrue(); // when mockMvc.perform(MockMvcRequestBuilders @@ -611,9 +660,9 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { .accept(MediaType.APPLICATION_JSON)) // then - .andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage))) .andExpect(jsonPath("statusCode", is(400))) .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage))) .andExpect(status().is4xxClientError()); } @@ -621,37 +670,37 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { REVERTING_SIMPLE_ASSET_TRANSACTION( requestBody -> requestBody - .with("transactionType", "REVERSAL") + .with("transactionType", REVERSAL) .with("assetValue", "64.00") .with("valueDate", "2024-10-15") .with("reference", "reversal of loss ref") .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), Expected.REVERT_LOSS_RESPONSE), TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER( requestBody -> requestBody - .with("transactionType", "TRANSFER") + .with("transactionType", TRANSFER) .with("assetValue", -64.00) .with("adoptingMembership.memberNumber", AVAILABLE_TARGET_MEMBER_NUMBER), Expected.TRANSFER_RESPONSE), TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_UUID( requestBody -> requestBody - .with("transactionType", "TRANSFER") + .with("transactionType", TRANSFER) .with("assetValue", -64.00) - .with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString()) - .with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()), + .with("membership.uuid", ORIGIN_MEMBERSHIP_UUID) + .with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID), Expected.TRANSFER_RESPONSE), REVERTING_TRANSFER_ASSET_TRANSACTION_IMPLICITLY_REVERTS_ADOPTING_ASSET_TRANSACTION( requestBody -> requestBody - .with("transactionType", "REVERSAL") + .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()), + .with("revertedAssetTx.uuid", SOME_EXISTING_TRANSFER_ASSET_TX_UUID), Expected.REVERT_TRANSFER_RESPONSE); private final Function givenBodyTransformation; @@ -790,6 +839,38 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { .andExpect(jsonPath("$", lenientlyEquals(testCase.expectedResponseBody))); } + @Test + void getSingleGeneratesProperJsonForAvailableUuid() throws Exception { + // given + when(coopAssetsTransactionRepo.findByUuid(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getUuid())) + .thenReturn(Optional.of(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/coopassetstransactions/" + SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getUuid()) + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$", lenientlyEquals(EXPECTED_RESULT_FROM_GET_SINGLE))); + } + + @Test + void getSingleGeneratesNotFoundForUnavailableUuid() throws Exception { + // given + when(coopAssetsTransactionRepo.findByUuid(UNAVAILABLE_UUID)).thenReturn(Optional.empty()); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/hs/office/coopassetstransactions/" + UNAVAILABLE_UUID) + .header("current-subject", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isNotFound()); + } + @Test void getListGeneratesProperJson() throws Exception { // given @@ -817,6 +898,11 @@ class HsOfficeCoopAssetsTransactionControllerRestTest { .andExpect(jsonPath("$", lenientlyEquals(EXPECTED_RESULT_FROM_GET_LIST))); } + @Test + void singleTestCaseExecutionMustBeDisabled() { + assertThat(SINGLE_TEST_CASE_EXECUTION).isFalse(); + } + @BeforeEach void initMocks() { TestUuidGenerator.start(DYNAMIC_UUID_START_INDEX); diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonBuilder.java b/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonBuilder.java index 35a29d90..482e20aa 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonBuilder.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/test/JsonBuilder.java @@ -27,7 +27,7 @@ public class JsonBuilder { * @param value JSON value * @return this JsonBuilder */ - public JsonBuilder with(final String key, final String value) { + public JsonBuilder with(final String key, final Object value) { try { jsonObject.put(key, value); } catch (JSONException e) {