add coop assets+shares scenario tests

This commit is contained in:
Michael Hoennig 2024-11-14 14:30:00 +01:00
parent 5f454a2ebe
commit 9a9e3e2e73
13 changed files with 213 additions and 11 deletions

View File

@ -0,0 +1,28 @@
package net.hostsharing.hsadminng.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class FormattedBigDecimalDeserializer extends JsonDeserializer<BigDecimal> {
@Override
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final var numberStr = p.getText();
try {
final var format = NumberFormat.getInstance(Locale.GERMANY);
if (format instanceof DecimalFormat) {
((DecimalFormat) format).setParseBigDecimal(true);
}
return (BigDecimal) format.parse(numberStr);
} catch (final ParseException e) {
throw new IOException("Failed to parse BigDecimal from string: " + numberStr, e);
}
}
}

View File

@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.openapitools.jackson.nullable.JsonNullableModule;
import org.springframework.context.annotation.Bean;
@ -9,15 +10,25 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.math.BigDecimal;
@Configuration
public class JsonObjectMapperConfiguration {
@Bean
@Primary
public Jackson2ObjectMapperBuilder customObjectMapper() {
// HOWTO: add JSON converters and specify other JSON mapping configurations
return new Jackson2ObjectMapperBuilder()
.modulesToInstall(formattedBigDecimalModule())
.modules(new JsonNullableModule(), new JavaTimeModule())
.featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS, JsonParser.Feature.ALLOW_COMMENTS)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
private SimpleModule formattedBigDecimalModule() {
final var module = new SimpleModule();
module.addDeserializer(BigDecimal.class, new FormattedBigDecimalDeserializer());
return module;
}
}

View File

@ -4,6 +4,7 @@ import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.stream.Stream;
// HOWTO: convert data types for exchange between PostgreSQL and Java/Hibernate/JPA-Entities
@Converter(autoApply = true)
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {

View File

@ -12,10 +12,13 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.FinallyDeleteSepaMa
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DontDeleteDefaultDebitor;
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.InvalidateSepaMandateForDebitor;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CancelMembership;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateCoopSharesCancellationTransaction;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateCoopSharesRevertTransaction;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateCoopSharesSubscriptionTransaction;
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.CreateCoopAssetsDisbursalTransaction;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransaction;
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.CreateCoopSharesSubscriptionTransaction;
import net.hostsharing.hsadminng.hs.office.scenarios.partner.AddOperationsContactToPartner;
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteDebitor;
@ -348,7 +351,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
void shouldRevertCoopSharesSubscription() {
new CreateCoopSharesRevertTransaction(this)
.given("memberNumber", "3101000")
.given("comment", "reverting some incorrect subscription")
.given("comment", "reverting some incorrect transaction")
.given("dateOfIncorrectTransaction", "2024-02-15")
.doRun();
}
@ -367,6 +370,45 @@ class HsOfficeScenarioTests extends ScenarioTest {
.doRun();
}
@Test
@Order(4301)
@Requires("Membership: M-3101000 - Test AG")
@Produces("Coop-Assets DEPOSIT Transaction")
void shouldSubscribeCoopAssets() {
new CreateCoopAssetsDepositTransaction(this)
.given("memberNumber", "3101000")
.given("reference", "sign 2024-01-15")
.given("assetValue", 100*64)
.given("comment", "disposal for initial shares")
.given("transactionDate", "2024-01-15")
.doRun();
}
@Test
@Order(4302)
@Requires("Membership: M-3101000 - Test AG")
void shouldRevertCoopAssetsSubscription() {
new CreateCoopAssetsRevertTransaction(this)
.given("memberNumber", "3101000")
.given("comment", "reverting some incorrect transaction")
.given("dateOfIncorrectTransaction", "2024-02-15")
.doRun();
}
@Test
@Order(4302)
@Requires("Coop-Assets DEPOSIT Transaction")
@Produces("Coop-Assets DISBURSAL Transaction")
void shouldDisburseCoopAssets() {
new CreateCoopAssetsDisbursalTransaction(this)
.given("memberNumber", "3101000")
.given("reference", "cancel 2024-01-15")
.given("valueToDisburse", 8*64)
.given("comment", "disbursal according to shares cancellation")
.given("transactionDate", "2024-02-15")
.doRun();
}
@Test
@Order(4900)
@Requires("Membership: M-3101000 - Test AG")

View File

@ -19,7 +19,7 @@ public class PathAssertion {
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
return response -> {
try {
response.path(path).map(Object::toString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
response.path(path).map(this::asString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
} catch (final AssertionError e) {
// without this, the error message is often lacking important context
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
@ -37,4 +37,15 @@ 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();
}
}

View File

@ -0,0 +1,12 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
public class CreateCoopAssetsDepositTransaction extends CreateCoopAssetsTransaction {
public CreateCoopAssetsDepositTransaction(final ScenarioTest testSuite) {
super(testSuite);
given("transactionType", "DEPOSIT");
}
}

View File

@ -0,0 +1,17 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
public class CreateCoopAssetsDisbursalTransaction extends CreateCoopAssetsTransaction {
public CreateCoopAssetsDisbursalTransaction(final ScenarioTest testSuite) {
super(testSuite);
}
@Override
protected HttpResponse run() {
given("transactionType", "DISBURSAL");
given("assetValue", "-%{valueToDisburse}");
return super.run();
}
}

View File

@ -0,0 +1,27 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransaction {
public CreateCoopAssetsRevertTransaction(final ScenarioTest testSuite) {
super(testSuite);
requires("CoopAssets-Transaction with incorrect assetValue", alias ->
new CreateCoopAssetsDepositTransaction(testSuite)
.given("memberNumber", "3101000")
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedAssetTx
.given("assetValue", 10)
.given("comment", "coop-assets deposit transaction with wrong asset value")
.given("transactionDate", "%{dateOfIncorrectTransaction}")
);
}
@Override
protected HttpResponse run() {
given("transactionType", "REVERSAL");
given("assetValue", -100);
given("revertedAssetTx", uuid("CoopAssets-Transaction with incorrect assetValue"));
return super.run();
}
}

View File

@ -0,0 +1,53 @@
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 static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.OK;
public abstract class CreateCoopAssetsTransaction extends UseCase<CreateCoopAssetsTransaction> {
public CreateCoopAssetsTransaction(final ScenarioTest testSuite) {
super(testSuite);
}
@Override
protected HttpResponse run() {
obtain("#{Find }membershipUuid", () ->
httpGet("/api/hs/office/memberships?memberNumber=&{memberNumber}")
.expecting(OK).expecting(JSON).expectArrayElements(1),
response -> response.getFromBody("$[0].uuid")
);
return withTitle("Create the Coop-Assets-%{transactionType} Transaction", () ->
httpPost("/api/hs/office/coopassetstransactions", usingJsonBody("""
{
"membership.uuid": ${membershipUuid},
"transactionType": ${transactionType},
"reference": ${reference},
"assetValue": ${assetValue},
"comment": ${comment},
"valueDate": ${transactionDate},
"revertedAssetTx.uuid": ${revertedAssetTx???}
}
"""))
.expecting(HttpStatus.CREATED).expecting(ContentType.JSON)
);
}
@Override
protected void verify(final HttpResponse response) {
verify("Verify Coop-Assets %{transactionType}-Transaction",
() -> httpGet("/api/hs/office/coopassetstransactions/" + response.getLocationUuid())
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
path("transactionType").contains("%{transactionType}"),
path("assetValue").contains("%{assetValue}"),
path("comment").contains("%{comment}"),
path("valueDate").contains("%{transactionDate}")
);
}
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
@ -12,7 +12,7 @@ public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransacti
.given("memberNumber", "3101000")
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedShareTx
.given("shareCount", 100)
.given("comment", "reverting subscription transaction with wrong share count")
.given("comment", "coop-shares subscription transaction with wrong share count")
.given("transactionDate", "%{dateOfIncorrectTransaction}")
);
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
@ -23,7 +23,7 @@ public abstract class CreateCoopSharesTransaction extends UseCase<CreateCoopShar
response -> response.getFromBody("$[0].uuid")
);
return withTitle("Create the CoopShares-%{transactionType} Transaction", () ->
return withTitle("Create the Coop-Shares-%{transactionType} Transaction", () ->
httpPost("/api/hs/office/coopsharestransactions", usingJsonBody("""
{
"membership.uuid": ${membershipUuid},