implement coop-asset-TRANSFER-transaction reversal #125
@ -27,11 +27,13 @@ import java.util.UUID;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
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.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;
|
||||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.LOSS;
|
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.LOSS;
|
||||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.TRANSFER;
|
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
||||||
@ -66,7 +68,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
fromValueDate,
|
fromValueDate,
|
||||||
toValueDate);
|
toValueDate);
|
||||||
|
|
||||||
final var resources = mapper.mapList(entities, HsOfficeCoopAssetsTransactionResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
final var resources = mapper.mapList(
|
||||||
|
entities,
|
||||||
|
HsOfficeCoopAssetsTransactionResource.class,
|
||||||
|
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
return ResponseEntity.ok(resources);
|
return ResponseEntity.ok(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +111,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeCoopAssetsTransactionResource.class));
|
final var resource = mapper.map(
|
||||||
|
result.get(),
|
||||||
|
HsOfficeCoopAssetsTransactionResource.class,
|
||||||
|
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||||
|
return ResponseEntity.ok(resource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +140,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
private static void validateCreditTransaction(
|
private static void validateCreditTransaction(
|
||||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
||||||
final ArrayList<String> violations) {
|
final ArrayList<String> violations) {
|
||||||
if (List.of(DISBURSAL, TRANSFER, CLEARING, LOSS).contains(requestBody.getTransactionType())
|
if (List.of(DISBURSAL, HsOfficeCoopAssetsTransactionTypeResource.TRANSFER, CLEARING, LOSS)
|
||||||
|
.contains(requestBody.getTransactionType())
|
||||||
&& requestBody.getAssetValue().signum() > 0) {
|
&& requestBody.getAssetValue().signum() > 0) {
|
||||||
violations.add("for %s, assetValue must be negative but is \"%.2f\"".formatted(
|
violations.add("for %s, assetValue must be negative but is \"%.2f\"".formatted(
|
||||||
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
||||||
@ -147,57 +157,108 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO.refa: this logic needs to get extracted to a service
|
||||||
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.setMembershipUuid(entity.getMembership().getUuid());
|
||||||
resource.setMembershipMemberNumber(entity.getMembership().getTaggedMemberNumber());
|
resource.setMembershipMemberNumber(entity.getMembership().getTaggedMemberNumber());
|
||||||
|
|
||||||
if (entity.getReversalAssetTx() != null) {
|
withNonNull(
|
||||||
resource.getReversalAssetTx().setRevertedAssetTxUuid(entity.getUuid());
|
resource.getReversalAssetTx(), reversalAssetTxResource -> {
|
||||||
resource.getReversalAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
reversalAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||||
resource.getReversalAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
reversalAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||||
}
|
reversalAssetTxResource.setRevertedAssetTxUuid(entity.getUuid());
|
||||||
|
withNonNull(
|
||||||
|
entity.getAdoptionAssetTx(), adoptionAssetTx ->
|
||||||
|
reversalAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||||
|
withNonNull(
|
||||||
|
entity.getTransferAssetTx(), transferAssetTxResource ->
|
||||||
|
reversalAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||||
|
});
|
||||||
|
|
||||||
if (entity.getRevertedAssetTx() != null) {
|
withNonNull(
|
||||||
resource.getRevertedAssetTx().setReversalAssetTxUuid(entity.getUuid());
|
resource.getRevertedAssetTx(), revertAssetTxResource -> {
|
||||||
resource.getRevertedAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
revertAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||||
resource.getRevertedAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
revertAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||||
}
|
revertAssetTxResource.setReversalAssetTxUuid(entity.getUuid());
|
||||||
|
withNonNull(
|
||||||
|
entity.getRevertedAssetTx().getAdoptionAssetTx(), adoptionAssetTx ->
|
||||||
|
revertAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||||
|
withNonNull(
|
||||||
|
entity.getRevertedAssetTx().getTransferAssetTx(), transferAssetTxResource ->
|
||||||
|
revertAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||||
|
});
|
||||||
|
|
||||||
if (entity.getAdoptionAssetTx() != null) {
|
withNonNull(
|
||||||
resource.getAdoptionAssetTx().setTransferAssetTxUuid(entity.getUuid());
|
resource.getAdoptionAssetTx(), adoptionAssetTxResource -> {
|
||||||
resource.getAdoptionAssetTx().setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
adoptionAssetTxResource.setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
||||||
resource.getAdoptionAssetTx().setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
adoptionAssetTxResource.setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
||||||
}
|
adoptionAssetTxResource.setTransferAssetTxUuid(entity.getUuid());
|
||||||
|
withNonNull(
|
||||||
|
entity.getAdoptionAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||||
|
adoptionAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||||
|
});
|
||||||
|
|
||||||
if (entity.getTransferAssetTx() != null) {
|
withNonNull(
|
||||||
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
resource.getTransferAssetTx(), transferAssetTxResource -> {
|
||||||
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
|
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
|
||||||
resource.getTransferAssetTx().setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
resource.getTransferAssetTx()
|
||||||
}
|
.setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
||||||
|
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
||||||
|
withNonNull(
|
||||||
|
entity.getTransferAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||||
|
transferAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO.refa: this logic needs to get extracted to a service
|
||||||
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) {
|
||||||
final HsOfficeMembershipEntity membership = ofNullable(emw.find(HsOfficeMembershipEntity.class, resource.getMembershipUuid()))
|
final HsOfficeMembershipEntity membership = ofNullable(emw.find(
|
||||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] membership.uuid %s not found".formatted(
|
HsOfficeMembershipEntity.class,
|
||||||
|
resource.getMembershipUuid()))
|
||||||
|
.orElseThrow(() -> new EntityNotFoundException("membership.uuid %s not found".formatted(
|
||||||
resource.getMembershipUuid())));
|
resource.getMembershipUuid())));
|
||||||
entity.setMembership(membership);
|
entity.setMembership(membership);
|
||||||
}
|
}
|
||||||
if (resource.getRevertedAssetTxUuid() != null) {
|
|
||||||
|
if (entity.getTransactionType() == REVERSAL) {
|
||||||
|
if (resource.getRevertedAssetTxUuid() == null) {
|
||||||
|
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("revertedAssetTx.uuid %s not found".formatted(
|
||||||
resource.getRevertedAssetTxUuid())));
|
resource.getRevertedAssetTxUuid())));
|
||||||
|
revertedAssetTx.setReversalAssetTx(entity);
|
||||||
entity.setRevertedAssetTx(revertedAssetTx);
|
entity.setRevertedAssetTx(revertedAssetTx);
|
||||||
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
||||||
throw new ValidationException("given assetValue=" + resource.getAssetValue() +
|
throw new ValidationException("given assetValue=" + resource.getAssetValue() +
|
||||||
" but must be negative value from reverted asset tx: " + revertedAssetTx.getAssetValue());
|
" but must be negative value from reverted asset tx: " + revertedAssetTx.getAssetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (revertedAssetTx.getTransactionType() == TRANSFER) {
|
||||||
|
final var adoptionAssetTx = revertedAssetTx.getAdoptionAssetTx();
|
||||||
|
final var adoptionReversalAssetTx = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.transactionType(REVERSAL)
|
||||||
|
.membership(adoptionAssetTx.getMembership())
|
||||||
|
.revertedAssetTx(adoptionAssetTx)
|
||||||
|
.assetValue(adoptionAssetTx.getAssetValue().negate())
|
||||||
|
.comment(resource.getComment())
|
||||||
|
.reference(resource.getReference())
|
||||||
|
.valueDate(resource.getValueDate())
|
||||||
|
.build();
|
||||||
|
adoptionAssetTx.setReversalAssetTx(adoptionReversalAssetTx);
|
||||||
|
adoptionReversalAssetTx.setRevertedAssetTx(adoptionAssetTx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER) {
|
||||||
final var adoptingMembership = determineAdoptingMembership(resource);
|
final var adoptingMembership = determineAdoptingMembership(resource);
|
||||||
if (adoptingMembership != null) {
|
if ( entity.getMembership() == adoptingMembership) {
|
||||||
final var adoptingAssetTx = coopAssetsTransactionRepo.save(createAdoptingAssetTx(entity, 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);
|
entity.setAdoptionAssetTx(adoptingAssetTx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -206,11 +267,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
final var adoptingMembershipUuid = resource.getAdoptingMembershipUuid();
|
final var adoptingMembershipUuid = resource.getAdoptingMembershipUuid();
|
||||||
final var adoptingMembershipMemberNumber = resource.getAdoptingMembershipMemberNumber();
|
final var adoptingMembershipMemberNumber = resource.getAdoptingMembershipMemberNumber();
|
||||||
if (adoptingMembershipUuid != null && adoptingMembershipMemberNumber != null) {
|
if (adoptingMembershipUuid != null && adoptingMembershipMemberNumber != null) {
|
||||||
throw new IllegalArgumentException(
|
throw new ValidationException(
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
resource.getTransactionType() == TRANSFER
|
resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER
|
||||||
? "[400] either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
? "either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
||||||
: "[400] adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
: "adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
||||||
+ resource.getTransactionType());
|
+ resource.getTransactionType());
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@ -232,13 +293,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
|||||||
+ "' not found or not accessible");
|
+ "' not found or not accessible");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.getTransactionType() == TRANSFER) {
|
|
||||||
throw new ValidationException(
|
throw new ValidationException(
|
||||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType="
|
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType="
|
||||||
+ TRANSFER);
|
+ HsOfficeCoopAssetsTransactionTypeResource.TRANSFER);
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HsOfficeCoopAssetsTransactionEntity createAdoptingAssetTx(
|
private HsOfficeCoopAssetsTransactionEntity createAdoptingAssetTx(
|
||||||
|
@ -98,21 +98,21 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
|||||||
private String comment;
|
private String comment;
|
||||||
|
|
||||||
// Optionally, the UUID of the corresponding transaction for a reversal transaction.
|
// Optionally, the UUID of the corresponding transaction for a reversal transaction.
|
||||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
@OneToOne
|
||||||
@JoinColumn(name = "revertedassettxuuid")
|
@JoinColumn(name = "revertedassettxuuid")
|
||||||
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
||||||
|
|
||||||
// and the other way around
|
// and the other way around
|
||||||
@OneToOne(mappedBy = "revertedAssetTx")
|
@OneToOne(mappedBy = "revertedAssetTx", cascade = CascadeType.PERSIST)
|
||||||
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
||||||
|
|
||||||
// Optionally, the UUID of the corresponding transaction for a transfer transaction.
|
// Optionally, the UUID of the corresponding transaction for a transfer transaction.
|
||||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
@OneToOne(cascade = CascadeType.PERSIST)
|
||||||
@JoinColumn(name = "assetadoptiontxuuid")
|
@JoinColumn(name = "assetadoptiontxuuid")
|
||||||
private HsOfficeCoopAssetsTransactionEntity adoptionAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity adoptionAssetTx;
|
||||||
|
|
||||||
// and the other way around
|
// and the other way around
|
||||||
@OneToOne(mappedBy = "adoptionAssetTx")
|
@OneToOne(mappedBy = "adoptionAssetTx", cascade = CascadeType.PERSIST)
|
||||||
private HsOfficeCoopAssetsTransactionEntity transferAssetTx;
|
private HsOfficeCoopAssetsTransactionEntity transferAssetTx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.hostsharing.hsadminng.lambda;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class WithNonNull {
|
||||||
|
public static <T> void withNonNull(final T target, final Consumer<T> code) {
|
||||||
|
if (target != null ) {
|
||||||
|
code.accept(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,21 +35,41 @@ create table if not exists hs_office.coopassettx
|
|||||||
--changeset michael.hoennig:hs-office-coopassets-BUSINESS-RULES endDelimiter:--//
|
--changeset michael.hoennig:hs-office-coopassets-BUSINESS-RULES endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
alter table hs_office.coopassettx
|
-- Not as CHECK constraints because those cannot be deferrable,
|
||||||
add constraint reversal_asset_tx_must_have_reverted_asset_tx
|
-- but we need these constraints deferrable because the rows are linked to each other.
|
||||||
check (transactionType <> 'REVERSAL' or revertedAssetTxUuid is not null);
|
|
||||||
|
|
||||||
alter table hs_office.coopassettx
|
CREATE OR REPLACE FUNCTION validate_transaction_type()
|
||||||
add constraint non_reversal_asset_tx_must_not_have_reverted_asset_tx
|
RETURNS TRIGGER AS $$
|
||||||
check (transactionType = 'REVERSAL' or revertedAssetTxUuid is null or transactionType = 'REVERSAL');
|
BEGIN
|
||||||
|
-- REVERSAL transactions must have revertedAssetTxUuid
|
||||||
|
IF NEW.transactionType = 'REVERSAL' AND NEW.revertedAssetTxUuid IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'REVERSAL transactions must have revertedAssetTxUuid';
|
||||||
|
END IF;
|
||||||
|
|
||||||
alter table hs_office.coopassettx
|
-- Non-REVERSAL transactions must not have revertedAssetTxUuid
|
||||||
add constraint transfer_asset_tx_must_have_adopted_asset_tx
|
IF NEW.transactionType != 'REVERSAL' AND NEW.revertedAssetTxUuid IS NOT NULL THEN
|
||||||
check (transactionType <> 'TRANSFER' or assetAdoptionTxUuid is not null);
|
RAISE EXCEPTION 'Non-REVERSAL transactions must not have revertedAssetTxUuid';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- TRANSFER transactions must have assetAdoptionTxUuid
|
||||||
|
IF NEW.transactionType = 'TRANSFER' AND NEW.assetAdoptionTxUuid IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'TRANSFER transactions must have assetAdoptionTxUuid';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Non-TRANSFER transactions must not have assetAdoptionTxUuid
|
||||||
|
IF NEW.transactionType != 'TRANSFER' AND NEW.assetAdoptionTxUuid IS NOT NULL THEN
|
||||||
|
RAISE EXCEPTION 'Non-TRANSFER transactions must not have assetAdoptionTxUuid';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Attach the trigger to the table
|
||||||
|
CREATE TRIGGER enforce_transaction_constraints
|
||||||
|
AFTER INSERT OR UPDATE ON hs_office.coopassettx
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION validate_transaction_type();
|
||||||
|
|
||||||
alter table hs_office.coopassettx
|
|
||||||
add constraint non_transfer_asset_tx_must_not_have_adopted_asset_tx
|
|
||||||
check (transactionType = 'TRANSFER' or assetAdoptionTxUuid is null);
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
@ -10,6 +10,7 @@ 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 net.hostsharing.hsadminng.test.TestUuidGenerator;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
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;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -24,12 +25,20 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
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.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.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.Assertions.assertThat;
|
||||||
|
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,7 +51,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
class HsOfficeCoopAssetsTransactionControllerRestTest {
|
class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||||
|
|
||||||
private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.use(0);
|
// 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_UUID = TestUuidGenerator.use(0);
|
||||||
private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699";
|
private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699";
|
||||||
|
|
||||||
private static final UUID ORIGIN_MEMBERSHIP_UUID = TestUuidGenerator.use(1);
|
private static final UUID ORIGIN_MEMBERSHIP_UUID = TestUuidGenerator.use(1);
|
||||||
@ -65,9 +80,11 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
.memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER))
|
.memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// the following refs might change if impl changes
|
// The following refs depend on the implementation of the respective implementation and might change if it changes.
|
||||||
private static final UUID NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.ref(4);
|
// The same TestUuidGenerator.ref(#) does NOT mean the UUIDs refer to the same entity,
|
||||||
private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(5);
|
// 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(DYNAMIC_UUID_START_INDEX);
|
||||||
|
private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(DYNAMIC_UUID_START_INDEX);
|
||||||
|
|
||||||
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()
|
||||||
@ -80,6 +97,402 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static UUID SOME_REVERTED_DISBURSAL_ASSET_TX_UUID = TestUuidGenerator.use(7);
|
||||||
|
private final static UUID SOME_DISBURSAL_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(8);
|
||||||
|
private final HsOfficeCoopAssetsTransactionEntity SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_REVERTED_DISBURSAL_ASSET_TX_UUID)
|
||||||
|
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||||
|
.transactionType(DISBURSAL)
|
||||||
|
.assetValue(BigDecimal.valueOf(-128.00))
|
||||||
|
.valueDate(LocalDate.parse("2024-10-15"))
|
||||||
|
.reference("some disbursal")
|
||||||
|
.comment("some disbursal to get reverted")
|
||||||
|
.reversalAssetTx(
|
||||||
|
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_DISBURSAL_REVERSAL_ASSET_TX_UUID)
|
||||||
|
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||||
|
.transactionType(REVERSAL)
|
||||||
|
.assetValue(BigDecimal.valueOf(128.00))
|
||||||
|
.valueDate(LocalDate.parse("2024-10-20"))
|
||||||
|
.reference("some reversal")
|
||||||
|
.comment("some reversal of a disbursal asset tx")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
{
|
||||||
|
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY.getReversalAssetTx().setRevertedAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static UUID SOME_REVERTED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.use(9);
|
||||||
|
private final static UUID SOME_TRANSFER_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(10);
|
||||||
|
private final static UUID SOME_REVERTED_ADOPTION_ASSET_TX_UUID = TestUuidGenerator.use(11);
|
||||||
|
private final static UUID SOME_ADOPTION_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(12);
|
||||||
|
final HsOfficeCoopAssetsTransactionEntity SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_REVERTED_TRANSFER_ASSET_TX_UUID)
|
||||||
|
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||||
|
.transactionType(TRANSFER)
|
||||||
|
.assetValue(BigDecimal.valueOf(-1024))
|
||||||
|
.valueDate(LocalDate.parse("2024-11-10"))
|
||||||
|
.reference("some transfer")
|
||||||
|
.comment("some transfer to get reverted")
|
||||||
|
.adoptionAssetTx(
|
||||||
|
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_REVERTED_ADOPTION_ASSET_TX_UUID)
|
||||||
|
.membership(AVAILABLE_MEMBER_ENTITY)
|
||||||
|
.transactionType(ADOPTION)
|
||||||
|
.assetValue(BigDecimal.valueOf(1024))
|
||||||
|
.valueDate(LocalDate.parse("2024-11-10"))
|
||||||
|
.reference("related adoption")
|
||||||
|
.comment("some reversal of a transfer asset tx")
|
||||||
|
.reversalAssetTx(
|
||||||
|
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_ADOPTION_REVERSAL_ASSET_TX_UUID)
|
||||||
|
.membership(AVAILABLE_MEMBER_ENTITY)
|
||||||
|
.transactionType(REVERSAL)
|
||||||
|
.assetValue(BigDecimal.valueOf(1024))
|
||||||
|
.valueDate(LocalDate.parse("2024-11-11"))
|
||||||
|
.reference("some reversal")
|
||||||
|
.comment("some adoption asset tx reversal")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.reversalAssetTx(
|
||||||
|
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||||
|
.uuid(SOME_TRANSFER_REVERSAL_ASSET_TX_UUID)
|
||||||
|
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||||
|
.transactionType(REVERSAL)
|
||||||
|
.assetValue(BigDecimal.valueOf(1024))
|
||||||
|
.valueDate(LocalDate.parse("2024-11-11"))
|
||||||
|
.reference("some transfer")
|
||||||
|
.comment("some transfer asset tx reversal")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
{
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx()
|
||||||
|
.setTransferAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getReversalAssetTx()
|
||||||
|
.setRevertedAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx().getReversalAssetTx()
|
||||||
|
.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 = """
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"uuid": "33333333-3333-3333-3333-333333333333",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "LOSS",
|
||||||
|
"assetValue": -64,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some loss asset tx ref",
|
||||||
|
"comment": "some loss asset tx comment",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "44444444-4444-4444-4444-444444444444",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "TRANSFER",
|
||||||
|
"assetValue": -256,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some transfer asset tx ref",
|
||||||
|
"comment": "some transfer asset tx comment",
|
||||||
|
"adoptionAssetTx": {
|
||||||
|
"uuid": "55555555-5555-5555-5555-555555555555",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "TRANSFER",
|
||||||
|
"assetValue": 256,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some adoption asset tx ref",
|
||||||
|
"comment": "some adoption asset tx comment",
|
||||||
|
"adoptionAssetTx.uuid": null,
|
||||||
|
"transferAssetTx.uuid": "44444444-4444-4444-4444-444444444444",
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": null
|
||||||
|
},
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "55555555-5555-5555-5555-555555555555",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "TRANSFER",
|
||||||
|
"assetValue": 256,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some adoption asset tx ref",
|
||||||
|
"comment": "some adoption asset tx comment",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": {
|
||||||
|
"uuid": "44444444-4444-4444-4444-444444444444",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "TRANSFER",
|
||||||
|
"assetValue": -256,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some transfer asset tx ref",
|
||||||
|
"comment": "some transfer asset tx comment",
|
||||||
|
"adoptionAssetTx.uuid": "55555555-5555-5555-5555-555555555555",
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": null
|
||||||
|
},
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "DISBURSAL",
|
||||||
|
"assetValue": -128.0,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some disbursal",
|
||||||
|
"comment": "some disbursal to get reverted",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": {
|
||||||
|
"uuid": "88888888-8888-8888-8888-888888888888",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 128.0,
|
||||||
|
"valueDate": "2024-10-20",
|
||||||
|
"reference": "some reversal",
|
||||||
|
"comment": "some reversal of a disbursal asset tx",
|
||||||
|
"adoptionAssetTx.uuid": null,
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"reversalAssetTx.uuid": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "88888888-8888-8888-8888-888888888888",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 128.0,
|
||||||
|
"valueDate": "2024-10-20",
|
||||||
|
"reference": "some reversal",
|
||||||
|
"comment": "some reversal of a disbursal asset tx",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": {
|
||||||
|
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "DISBURSAL",
|
||||||
|
"assetValue": -128.0,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some disbursal",
|
||||||
|
"comment": "some disbursal to get reverted",
|
||||||
|
"adoptionAssetTx.uuid": null,
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": "88888888-8888-8888-8888-888888888888"
|
||||||
|
},
|
||||||
|
"reversalAssetTx": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": {
|
||||||
|
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 1024,
|
||||||
|
"valueDate": "2024-11-11",
|
||||||
|
"reference": "some transfer",
|
||||||
|
"comment": "some transfer asset tx reversal",
|
||||||
|
"adoptionAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": "99999999-9999-9999-9999-999999999999",
|
||||||
|
"reversalAssetTx.uuid": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": null,
|
||||||
|
"transferAssetTx": {
|
||||||
|
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "DISBURSAL",
|
||||||
|
"assetValue": -128.0,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some disbursal",
|
||||||
|
"comment": "some disbursal to get reverted",
|
||||||
|
"adoptionAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": "88888888-8888-8888-8888-888888888888"
|
||||||
|
},
|
||||||
|
"revertedAssetTx": null,
|
||||||
|
"reversalAssetTx": {
|
||||||
|
"uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc",
|
||||||
|
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||||
|
"membership.memberNumber": "M-1234500",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 1024,
|
||||||
|
"valueDate": "2024-11-11",
|
||||||
|
"reference": "some reversal",
|
||||||
|
"comment": "some adoption asset tx reversal",
|
||||||
|
"adoptionAssetTx.uuid": null,
|
||||||
|
"transferAssetTx.uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"revertedAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||||
|
"reversalAssetTx.uuid": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 1024,
|
||||||
|
"valueDate": "2024-11-11",
|
||||||
|
"reference": "some transfer",
|
||||||
|
"comment": "some transfer asset tx reversal",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": {
|
||||||
|
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"membership.memberNumber": "M-1111100",
|
||||||
|
"transactionType": "DISBURSAL",
|
||||||
|
"assetValue": -128.0,
|
||||||
|
"valueDate": "2024-10-15",
|
||||||
|
"reference": "some disbursal",
|
||||||
|
"comment": "some disbursal to get reverted",
|
||||||
|
"adoptionAssetTx.uuid": null,
|
||||||
|
"transferAssetTx.uuid": null,
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||||
|
},
|
||||||
|
"reversalAssetTx": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc",
|
||||||
|
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||||
|
"membership.memberNumber": "M-1234500",
|
||||||
|
"transactionType": "REVERSAL",
|
||||||
|
"assetValue": 1024,
|
||||||
|
"valueDate": "2024-11-11",
|
||||||
|
"reference": "some reversal",
|
||||||
|
"comment": "some adoption asset tx reversal",
|
||||||
|
"adoptionAssetTx": null,
|
||||||
|
"transferAssetTx": null,
|
||||||
|
"revertedAssetTx": {
|
||||||
|
"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": "77777777-7777-7777-7777-777777777777",
|
||||||
|
"revertedAssetTx.uuid": null,
|
||||||
|
"reversalAssetTx.uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"
|
||||||
|
},
|
||||||
|
"reversalAssetTx": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
""";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
MockMvc mockMvc;
|
MockMvc mockMvc;
|
||||||
|
|
||||||
@ -117,6 +530,44 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
requestBody -> requestBody.without("membership.uuid"),
|
requestBody -> requestBody.without("membership.uuid"),
|
||||||
"[membershipUuid must not be null but is \"null\"]"), // TODO.impl: should be membership.uuid, Spring validation-problem?
|
"[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_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_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)
|
||||||
|
.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)
|
||||||
|
.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(
|
TRANSACTION_TYPE_MISSING(
|
||||||
requestBody -> requestBody.without("transactionType"),
|
requestBody -> requestBody.without("transactionType"),
|
||||||
"[transactionType must not be null but is \"null\"]"),
|
"[transactionType must not be null but is \"null\"]"),
|
||||||
@ -127,35 +578,40 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
|
|
||||||
ASSETS_VALUE_FOR_DEPOSIT_MUST_BE_POSITIVE(
|
ASSETS_VALUE_FOR_DEPOSIT_MUST_BE_POSITIVE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "DEPOSIT")
|
.with("transactionType", DEPOSIT)
|
||||||
.with("assetValue", -64.00),
|
.with("assetValue", -64.00),
|
||||||
"[for DEPOSIT, assetValue must be positive but is \"-64.00\"]"),
|
"[for DEPOSIT, assetValue must be positive but is \"-64.00\"]"),
|
||||||
|
|
||||||
ASSETS_VALUE_FOR_DISBURSAL_MUST_BE_NEGATIVE(
|
ASSETS_VALUE_FOR_DISBURSAL_MUST_BE_NEGATIVE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "DISBURSAL")
|
.with("transactionType", DISBURSAL)
|
||||||
.with("assetValue", 64.00),
|
.with("assetValue", 64.00),
|
||||||
"[for DISBURSAL, assetValue must be negative but is \"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(
|
ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "TRANSFER")
|
.with("transactionType", TRANSFER)
|
||||||
.with("assetValue", -64.00)
|
.with("assetValue", -64.00)
|
||||||
.with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER),
|
.with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER),
|
||||||
"adoptingMembership.memberNumber='M-1234699' not found or not accessible"),
|
"adoptingMembership.memberNumber='M-1234699' not found or not accessible"),
|
||||||
|
|
||||||
ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE(
|
ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "TRANSFER")
|
.with("transactionType", TRANSFER)
|
||||||
.with("assetValue", -64.00)
|
.with("assetValue", -64.00)
|
||||||
.with("adoptingMembership.uuid", UNAVAILABLE_MEMBERSHIP_UUID.toString()),
|
.with("adoptingMembership.uuid", UNAVAILABLE_UUID),
|
||||||
"adoptingMembership.uuid='" + UNAVAILABLE_MEMBERSHIP_UUID + "' not found or not accessible"),
|
"adoptingMembership.uuid='" + UNAVAILABLE_UUID + "' not found or not accessible"),
|
||||||
|
|
||||||
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "REVERSAL")
|
.with("transactionType", REVERSAL)
|
||||||
.with("assetValue", 0.00),
|
.with("assetValue", 0.00),
|
||||||
"[assetValue must not be 0 but is \"0.00\"]"),
|
"[assetValue must not be 0 but is \"0.00\"]"),
|
||||||
|
|
||||||
@ -190,8 +646,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_MUST_NOT_BE_THE_SAME).isTrue();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
mockMvc.perform(MockMvcRequestBuilders
|
mockMvc.perform(MockMvcRequestBuilders
|
||||||
@ -202,9 +660,9 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
.accept(MediaType.APPLICATION_JSON))
|
.accept(MediaType.APPLICATION_JSON))
|
||||||
|
|
||||||
// then
|
// then
|
||||||
.andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage)))
|
|
||||||
.andExpect(jsonPath("statusCode", is(400)))
|
.andExpect(jsonPath("statusCode", is(400)))
|
||||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||||
|
.andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage)))
|
||||||
.andExpect(status().is4xxClientError());
|
.andExpect(status().is4xxClientError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,28 +670,38 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
|
|
||||||
REVERTING_SIMPLE_ASSET_TRANSACTION(
|
REVERTING_SIMPLE_ASSET_TRANSACTION(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.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),
|
||||||
Expected.REVERT_RESPONSE),
|
Expected.REVERT_LOSS_RESPONSE),
|
||||||
|
|
||||||
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER(
|
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "TRANSFER")
|
.with("transactionType", TRANSFER)
|
||||||
.with("assetValue", -64.00)
|
.with("assetValue", -64.00)
|
||||||
.with("adoptingMembership.memberNumber", AVAILABLE_TARGET_MEMBER_NUMBER),
|
.with("adoptingMembership.memberNumber", AVAILABLE_TARGET_MEMBER_NUMBER),
|
||||||
Expected.TRANSFER_RESPONSE),
|
Expected.TRANSFER_RESPONSE),
|
||||||
|
|
||||||
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_UUID(
|
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_UUID(
|
||||||
requestBody -> requestBody
|
requestBody -> requestBody
|
||||||
.with("transactionType", "TRANSFER")
|
.with("transactionType", TRANSFER)
|
||||||
.with("assetValue", -64.00)
|
.with("assetValue", -64.00)
|
||||||
.with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString())
|
.with("membership.uuid", ORIGIN_MEMBERSHIP_UUID)
|
||||||
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()),
|
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID),
|
||||||
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),
|
||||||
|
Expected.REVERT_TRANSFER_RESPONSE);
|
||||||
|
|
||||||
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
|
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
|
||||||
private final String expectedResponseBody;
|
private final String expectedResponseBody;
|
||||||
@ -251,7 +719,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}",
|
||||||
@ -259,9 +727,10 @@ 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,
|
||||||
|
"reversalAssetTx": null,
|
||||||
"transferAssetTx": null,
|
"transferAssetTx": null,
|
||||||
"revertedAssetTx": {
|
"revertedAssetTx": {
|
||||||
"uuid": "%{SOME_EXISTING_LOSS_ASSET_TX_UUID}",
|
"uuid": "%{SOME_EXISTING_LOSS_ASSET_TX_UUID}",
|
||||||
@ -279,7 +748,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());
|
||||||
@ -303,20 +774,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": {
|
||||||
|
"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
|
||||||
@ -331,12 +839,77 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
|||||||
.andExpect(jsonPath("$", lenientlyEquals(testCase.expectedResponseBody)));
|
.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
|
||||||
|
when(coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(null, null, null))
|
||||||
|
.thenReturn(List.of(
|
||||||
|
SOME_EXISTING_LOSS_ASSET_TX_ENTITY,
|
||||||
|
SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY,
|
||||||
|
SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY,
|
||||||
|
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY,
|
||||||
|
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY.getReversalAssetTx(),
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY,
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx(),
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getReversalAssetTx(),
|
||||||
|
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx().getReversalAssetTx()
|
||||||
|
));
|
||||||
|
|
||||||
|
// when
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders
|
||||||
|
.get("/api/hs/office/coopassetstransactions")
|
||||||
|
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
|
||||||
|
// then
|
||||||
|
.andExpect(status().is2xxSuccessful())
|
||||||
|
.andExpect(jsonPath("$", lenientlyEquals(EXPECTED_RESULT_FROM_GET_LIST)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void singleTestCaseExecutionMustBeDisabled() {
|
||||||
|
assertThat(SINGLE_TEST_CASE_EXECUTION).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void initMocks() {
|
void initMocks() {
|
||||||
TestUuidGenerator.start(4);
|
TestUuidGenerator.start(DYNAMIC_UUID_START_INDEX);
|
||||||
|
|
||||||
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);
|
||||||
@ -346,6 +919,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);
|
||||||
|
@ -16,7 +16,8 @@ import net.hostsharing.hsadminng.hs.office.scenarios.membership.CancelMembership
|
|||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDepositTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDepositTransaction;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDisbursalTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDisbursalTransaction;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertSimpleTransaction;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransferTransaction;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsTransferTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsTransferTransaction;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesCancellationTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesCancellationTransaction;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesRevertTransaction;
|
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesRevertTransaction;
|
||||||
@ -30,7 +31,6 @@ import net.hostsharing.hsadminng.hs.office.scenarios.subscription.RemoveOperatio
|
|||||||
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.SubscribeToMailinglist;
|
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.SubscribeToMailinglist;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.UnsubscribeFromMailinglist;
|
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.UnsubscribeFromMailinglist;
|
||||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||||
import net.hostsharing.hsadminng.test.IgnoreOnFailure;
|
|
||||||
import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension;
|
import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
@ -395,7 +395,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@Order(4302)
|
@Order(4302)
|
||||||
@Requires("Membership: M-3101000 - Test AG")
|
@Requires("Membership: M-3101000 - Test AG")
|
||||||
void shouldRevertCoopAssetsSubscription() {
|
void shouldRevertCoopAssetsSubscription() {
|
||||||
new CreateCoopAssetsRevertTransaction(this)
|
new CreateCoopAssetsRevertSimpleTransaction(this)
|
||||||
.given("memberNumber", "M-3101000")
|
.given("memberNumber", "M-3101000")
|
||||||
.given("comment", "reverting some incorrect transaction")
|
.given("comment", "reverting some incorrect transaction")
|
||||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||||
@ -419,13 +419,13 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(4304)
|
@Order(4304)
|
||||||
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction")
|
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction")
|
||||||
@Produces("Coop-Assets M-3101000 - Test AG - TRANSFER Transaction")
|
@Produces(explicitly = "Coop-Assets M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership M-4303000")
|
||||||
void shouldTransferCoopAssets() {
|
void shouldTransferCoopAssets() {
|
||||||
new CreateCoopAssetsTransferTransaction(this)
|
new CreateCoopAssetsTransferTransaction(this)
|
||||||
.given("transferringMemberNumber", "M-3101000")
|
.given("transferringMemberNumber", "M-3101000")
|
||||||
.given("adoptingMemberNumber", "M-4303000")
|
.given("adoptingMemberNumber", "M-4303000")
|
||||||
.given("reference", "transfer 2024-12-31")
|
.given("reference", "transfer 2024-12-31")
|
||||||
.given("valueToDisburse", 2 * 64)
|
.given("valueToTransfer", 2 * 64)
|
||||||
.given("comment", "transfer assets from M-3101000 to M-4303000")
|
.given("comment", "transfer assets from M-3101000 to M-4303000")
|
||||||
.given("transactionDate", "2024-12-31")
|
.given("transactionDate", "2024-12-31")
|
||||||
.doRun();
|
.doRun();
|
||||||
@ -433,11 +433,12 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(4305)
|
@Order(4305)
|
||||||
@Requires("Coop-Assets M-3101000 - Test AG - TRANSFER Transaction")
|
@Requires("Membership M-4303000")
|
||||||
@IgnoreOnFailure("TODO.impl: reverting transfers is not implemented yet")
|
void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() {
|
||||||
void shouldRevertCoopAssetsTransfer() {
|
new CreateCoopAssetsRevertTransferTransaction(this)
|
||||||
new CreateCoopAssetsRevertTransaction(this)
|
.given("transferringMemberNumber", "M-3101000")
|
||||||
.given("memberNumber", "M-3101000")
|
.given("adoptingMemberNumber", "M-4303000")
|
||||||
|
.given("transferredValue", 2*64)
|
||||||
.given("comment", "reverting some incorrect transfer transaction")
|
.given("comment", "reverting some incorrect transfer transaction")
|
||||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||||
.doRun();
|
.doRun();
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||||
|
|
||||||
|
|
||||||
|
public final class JsonOptional<V> {
|
||||||
|
|
||||||
|
private final boolean jsonValueGiven;
|
||||||
|
private final V jsonValue;
|
||||||
|
|
||||||
|
private JsonOptional() {
|
||||||
|
this.jsonValueGiven = false;
|
||||||
|
this.jsonValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonOptional(final V jsonValue) {
|
||||||
|
this.jsonValueGiven = true;
|
||||||
|
this.jsonValue = jsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> JsonOptional<T> ofValue(final T value) {
|
||||||
|
return new JsonOptional<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> JsonOptional<T> notGiven() {
|
||||||
|
return new JsonOptional<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public V given() {
|
||||||
|
if (!jsonValueGiven) {
|
||||||
|
throw new IllegalStateException("JSON value was not given");
|
||||||
|
}
|
||||||
|
return jsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String givenAsString() {
|
||||||
|
if (jsonValue instanceof Double doubleValue) {
|
||||||
|
if (doubleValue % 1 == 0) {
|
||||||
|
return String.valueOf(doubleValue.intValue()); // avoid trailing ".0"
|
||||||
|
} else {
|
||||||
|
return doubleValue.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonValue == null ? null : jsonValue.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ public class PathAssertion {
|
|||||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
||||||
return response -> {
|
return response -> {
|
||||||
try {
|
try {
|
||||||
response.path(path).map(this::asString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
||||||
} catch (final AssertionError e) {
|
} catch (final AssertionError e) {
|
||||||
// without this, the error message is often lacking important context
|
// without this, the error message is often lacking important context
|
||||||
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
|
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
|
||||||
@ -37,15 +37,4 @@ public class PathAssertion {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String asString(final Object value) {
|
|
||||||
if (value instanceof Double doubleValue) {
|
|
||||||
if (doubleValue % 1 == 0) {
|
|
||||||
return String.valueOf(doubleValue.intValue()); // avoid trailing ".0"
|
|
||||||
} else {
|
|
||||||
return doubleValue.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import org.springframework.boot.test.web.server.LocalServerPort;
|
|||||||
import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils;
|
import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -160,6 +161,10 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
|
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void removeProperty(final String propName) {
|
||||||
|
properties.remove(propName);
|
||||||
|
}
|
||||||
|
|
||||||
static Map<String, Object> knowVariables() {
|
static Map<String, Object> knowVariables() {
|
||||||
final var map = new LinkedHashMap<String, Object>();
|
final var map = new LinkedHashMap<String, Object>();
|
||||||
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
|
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
|
||||||
@ -172,8 +177,8 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object resolveTyped(final String text) {
|
public static Object resolveTyped(final String resolvableText) {
|
||||||
final var resolved = resolve(text, DROP_COMMENTS);
|
final var resolved = resolve(resolvableText, DROP_COMMENTS);
|
||||||
try {
|
try {
|
||||||
return UUID.fromString(resolved);
|
return UUID.fromString(resolved);
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
@ -182,4 +187,14 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T resolveTyped(final String resolvableText, final Class<T> valueType) {
|
||||||
|
final var resolvedValue = resolve(resolvableText, DROP_COMMENTS);
|
||||||
|
if (valueType == BigDecimal.class) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) new BigDecimal(resolvedValue);
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) resolvedValue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,12 @@ import java.io.IOException;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static java.lang.String.join;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class TestReport {
|
public class TestReport {
|
||||||
@ -41,9 +44,12 @@ public class TestReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
||||||
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
|
final var testMethodName = testInfo.getTestMethod().map(Method::getName)
|
||||||
|
.map(TestReport::chopShouldPrefix)
|
||||||
|
.map(TestReport::splitMixedCaseIntoSeparateWords)
|
||||||
|
.orElseThrow();
|
||||||
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow();
|
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow();
|
||||||
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + "-" + testMethodName + ".md");
|
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + ": " + testMethodName + ".md");
|
||||||
markdownReport = new PrintWriter(new FileWriter(markdownReportFile));
|
markdownReport = new PrintWriter(new FileWriter(markdownReportFile));
|
||||||
print("## Scenario #" + determineScenarioTitle(testInfo));
|
print("## Scenario #" + determineScenarioTitle(testInfo));
|
||||||
}
|
}
|
||||||
@ -119,6 +125,20 @@ public class TestReport {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String chopShouldPrefix(final String text) {
|
||||||
|
return text.replaceAll("^should", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String splitMixedCaseIntoSeparateWords(final String text) {
|
||||||
|
final var WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))");
|
||||||
|
final var matcher = WORD_FINDER.matcher(text);
|
||||||
|
final var words = new ArrayList<String>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
words.add(matcher.group(0));
|
||||||
|
}
|
||||||
|
return join(" ", words);
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private String currentGitBranch() {
|
private String currentGitBranch() {
|
||||||
try {
|
try {
|
||||||
|
@ -9,13 +9,14 @@ import lombok.Getter;
|
|||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.hostsharing.hsadminng.reflection.AnnotationFinder;
|
import net.hostsharing.hsadminng.reflection.AnnotationFinder;
|
||||||
import org.apache.commons.collections4.map.LinkedMap;
|
import org.apache.commons.collections4.map.LinkedMap;
|
||||||
import org.assertj.core.api.OptionalAssert;
|
import org.assertj.core.api.AbstractStringAssert;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
@ -27,13 +28,13 @@ import java.util.Arrays;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static java.net.URLEncoder.encode;
|
import static java.net.URLEncoder.encode;
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS;
|
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS;
|
||||||
import static net.hostsharing.hsadminng.test.DebuggerDetection.isDebuggerAttached;
|
import static net.hostsharing.hsadminng.test.DebuggerDetection.isDebuggerAttached;
|
||||||
@ -95,6 +96,9 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
);
|
);
|
||||||
final var response = run();
|
final var response = run();
|
||||||
verify(response);
|
verify(response);
|
||||||
|
|
||||||
|
resetProperties();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +113,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final UseCase<T> given(final String propName, final Object propValue) {
|
public final UseCase<T> given(final String propName, final Object propValue) {
|
||||||
givenProperties.put(propName, propValue);
|
givenProperties.put(propName, ScenarioTest.resolve(propValue == null ? null : propValue.toString(), KEEP_COMMENTS));
|
||||||
ScenarioTest.putProperty(propName, propValue);
|
ScenarioTest.putProperty(propName, propValue);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -206,7 +210,8 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
return new PathAssertion(path);
|
return new PathAssertion(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void verify(
|
@SafeVarargs
|
||||||
|
protected final void verify(
|
||||||
final String title,
|
final String title,
|
||||||
final Supplier<UseCase.HttpResponse> http,
|
final Supplier<UseCase.HttpResponse> http,
|
||||||
final Consumer<UseCase.HttpResponse>... assertions) {
|
final Consumer<UseCase.HttpResponse>... assertions) {
|
||||||
@ -236,12 +241,17 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
String resolvePlaceholders() {
|
String resolvePlaceholders() {
|
||||||
return ScenarioTest.resolve(template, DROP_COMMENTS);
|
return ScenarioTest.resolve(template, DROP_COMMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Duration seconds(final int secondsIfNoDebuggerAttached) {
|
private static Duration seconds(final int secondsIfNoDebuggerAttached) {
|
||||||
return isDebuggerAttached() ? Duration.ofHours(1) : Duration.ofSeconds(secondsIfNoDebuggerAttached);
|
return isDebuggerAttached() ? Duration.ofHours(1) : Duration.ofSeconds(secondsIfNoDebuggerAttached);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetProperties() {
|
||||||
|
givenProperties.forEach((propName, val) -> ScenarioTest.removeProperty(propName));
|
||||||
|
}
|
||||||
|
|
||||||
public final class HttpResponse {
|
public final class HttpResponse {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ -319,22 +329,25 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public String getFromBody(final String path) {
|
public <V> V getFromBody(final String path) {
|
||||||
return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS));
|
final var body = response.body();
|
||||||
|
final var resolvedPath = ScenarioTest.resolve(path, DROP_COMMENTS);
|
||||||
|
return JsonPath.parse(body).read(resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public <T> Optional<T> getFromBodyAsOptional(final String path) {
|
public <V> JsonOptional<V> getFromBodyAsOptional(final String path) {
|
||||||
try {
|
try {
|
||||||
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS)));
|
return JsonOptional.ofValue(getFromBody(path));
|
||||||
} catch (final PathNotFoundException e) {
|
} catch (final PathNotFoundException e) {
|
||||||
return null; // means the property did not exist at all, not that it was there with value null
|
return JsonOptional.notGiven();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public <T> OptionalAssert<T> path(final String path) {
|
public AbstractStringAssert<?> path(final String path) {
|
||||||
return assertThat(getFromBodyAsOptional(path));
|
return assertThat(getFromBodyAsOptional(path).givenAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@ -396,4 +409,12 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
private String title(String resultAlias) {
|
private String title(String resultAlias) {
|
||||||
return getClass().getSimpleName().replaceAll("([a-z])([A-Z]+)", "$1 $2") + " => " + resultAlias;
|
return getClass().getSimpleName().replaceAll("([a-z])([A-Z]+)", "$1 $2") + " => " + resultAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final var properties = givenProperties.entrySet().stream()
|
||||||
|
.map(e -> "\t" + e.getKey() + "=" + e.getValue())
|
||||||
|
.collect(joining("\n"));
|
||||||
|
return getClass().getSimpleName() + "(\n\t" + properties + "\n)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,15 @@ package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
|||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||||
|
|
||||||
public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransaction {
|
public class CreateCoopAssetsRevertSimpleTransaction extends CreateCoopAssetsTransaction {
|
||||||
|
|
||||||
public CreateCoopAssetsRevertTransaction(final ScenarioTest testSuite) {
|
public CreateCoopAssetsRevertSimpleTransaction(final ScenarioTest testSuite) {
|
||||||
super(testSuite);
|
super(testSuite);
|
||||||
|
|
||||||
requires("CoopAssets-Transaction with incorrect assetValue", alias ->
|
requires("CoopAssets-Transaction with incorrect assetValue", alias ->
|
||||||
new CreateCoopAssetsDepositTransaction(testSuite)
|
new CreateCoopAssetsDepositTransaction(testSuite)
|
||||||
.given("memberNumber", "%{memberNumber}")
|
.given("memberNumber", "%{memberNumber}")
|
||||||
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as relatedAssetTx
|
.given("reference", "sign %{dateOfIncorrectTransaction}") // same text as relatedAssetTx
|
||||||
.given("assetValue", 10)
|
.given("assetValue", 10)
|
||||||
.given("comment", "coop-assets deposit transaction with wrong asset value")
|
.given("comment", "coop-assets deposit transaction with wrong asset value")
|
||||||
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||||
@ -21,7 +21,9 @@ public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransacti
|
|||||||
protected HttpResponse run() {
|
protected HttpResponse run() {
|
||||||
given("transactionType", "REVERSAL");
|
given("transactionType", "REVERSAL");
|
||||||
given("assetValue", -10);
|
given("assetValue", -10);
|
||||||
|
given("reference", "sign %{dateOfIncorrectTransaction}"); // same text as relatedAssetTx
|
||||||
given("revertedAssetTx", uuid("CoopAssets-Transaction with incorrect assetValue"));
|
given("revertedAssetTx", uuid("CoopAssets-Transaction with incorrect assetValue"));
|
||||||
|
given("transactionDate", "%{dateOfIncorrectTransaction}");
|
||||||
return super.run();
|
return super.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||||
|
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest.resolveTyped;
|
||||||
|
|
||||||
|
public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsTransaction {
|
||||||
|
|
||||||
|
public CreateCoopAssetsRevertTransferTransaction(final ScenarioTest testSuite) {
|
||||||
|
super(testSuite);
|
||||||
|
|
||||||
|
requires("Accidental CoopAssets-TRANSFER-Transaction", alias ->
|
||||||
|
new CreateCoopAssetsTransferTransaction(testSuite)
|
||||||
|
.given("reference", "transfer %{dateOfIncorrectTransaction}")
|
||||||
|
.given("valueToTransfer", "%{transferredValue}")
|
||||||
|
.given("comment", "accidental transfer of assets from %{transferringMemberNumber} to %{adoptingMemberNumber}")
|
||||||
|
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpResponse run() {
|
||||||
|
given("transactionType", "REVERSAL");
|
||||||
|
given("assetValue", "%{transferredValue}");
|
||||||
|
given("reference", "sign %{dateOfIncorrectTransaction}"); // same text as relatedAssetTx
|
||||||
|
given("revertedAssetTx", uuid("Accidental CoopAssets-TRANSFER-Transaction"));
|
||||||
|
given("transactionDate", "%{dateOfIncorrectTransaction}");
|
||||||
|
return super.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void verify(final UseCase<CreateCoopAssetsTransaction>.HttpResponse response) {
|
||||||
|
super.verify(response);
|
||||||
|
|
||||||
|
final var revertedAssetTxUuid = response.getFromBody("revertedAssetTx.uuid");
|
||||||
|
given("negativeAssetValue", resolveTyped("%{transferredValue}", BigDecimal.class).negate());
|
||||||
|
|
||||||
|
verify("Verify Reverted Coop-Assets TRANSFER-Transaction",
|
||||||
|
() -> httpGet("/api/hs/office/coopassetstransactions/" + revertedAssetTxUuid)
|
||||||
|
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
|
||||||
|
path("assetValue").contains("%{negativeAssetValue}"),
|
||||||
|
path("comment").contains("%{comment}"),
|
||||||
|
path("valueDate").contains("%{transactionDate}")
|
||||||
|
);
|
||||||
|
|
||||||
|
final var adoptionAssetTxUuid = response.getFromBody("revertedAssetTx.['adoptionAssetTx.uuid']");
|
||||||
|
|
||||||
|
verify("Verify Related Coop-Assets ADOPTION-Transaction Also Got Reverted",
|
||||||
|
() -> httpGet("/api/hs/office/coopassetstransactions/" + adoptionAssetTxUuid)
|
||||||
|
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
|
||||||
|
path("reversalAssetTx.['transferAssetTx.uuid']").contains(revertedAssetTxUuid.toString())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransac
|
|||||||
);
|
);
|
||||||
|
|
||||||
requires("Membership: New AG", alias -> new CreateMembership(testSuite)
|
requires("Membership: New AG", alias -> new CreateMembership(testSuite)
|
||||||
.given("partnerNumber", toPartnerNumber("%{adoptingMemberNumber}"))
|
.given("memberNumber", toPartnerNumber("%{adoptingMemberNumber}"))
|
||||||
.given("partnerName", "New AG")
|
.given("partnerName", "New AG")
|
||||||
.given("validFrom", "2024-11-15")
|
.given("validFrom", "2024-11-15")
|
||||||
.given("newStatus", "ACTIVE")
|
.given("newStatus", "ACTIVE")
|
||||||
@ -34,8 +34,7 @@ public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransac
|
|||||||
|
|
||||||
given("memberNumber", "%{transferringMemberNumber}");
|
given("memberNumber", "%{transferringMemberNumber}");
|
||||||
given("transactionType", "TRANSFER");
|
given("transactionType", "TRANSFER");
|
||||||
given("assetValue", "-%{valueToDisburse}");
|
given("assetValue", "-%{valueToTransfer}");
|
||||||
given("assetValue", "-%{valueToDisburse}");
|
|
||||||
return super.run();
|
return super.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package net.hostsharing.hsadminng.lambda;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class WithNonNullUnitTest {
|
||||||
|
|
||||||
|
Boolean didRun = null;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withNonNullRunsBodyIfNotNull() {
|
||||||
|
didRun = false;
|
||||||
|
withNonNull("test", nonNullValue -> {
|
||||||
|
assertThat(nonNullValue).isEqualTo("test");
|
||||||
|
didRun = true;
|
||||||
|
} );
|
||||||
|
assertThat(didRun).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withNonNullDoesNotRunBodyIfNull() {
|
||||||
|
didRun = false;
|
||||||
|
withNonNull(null, nonNullValue -> {
|
||||||
|
didRun = true;
|
||||||
|
} );
|
||||||
|
assertThat(didRun).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ public class JsonBuilder {
|
|||||||
* @param value JSON value
|
* @param value JSON value
|
||||||
* @return this JsonBuilder
|
* @return this JsonBuilder
|
||||||
*/
|
*/
|
||||||
public JsonBuilder with(final String key, final String value) {
|
public JsonBuilder with(final String key, final Object value) {
|
||||||
try {
|
try {
|
||||||
jsonObject.put(key, value);
|
jsonObject.put(key, value);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user