OfficeScenarioTests CoopShares+Assets (#121)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/121 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
@@ -442,7 +442,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, 1002000, for cancellation D),
|
||||
34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, 1002000, for cancellation D),
|
||||
35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, 1909000, for subscription E),
|
||||
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00),
|
||||
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, REVERSAL, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00),
|
||||
358=CoopAssetsTransaction(M-1000300: 2000-12-06, DEPOSIT, 5120, 1000300, for subscription A),
|
||||
442=CoopAssetsTransaction(M-1015200: 2003-07-07, DEPOSIT, 64, 1015200),
|
||||
577=CoopAssetsTransaction(M-1000300: 2011-12-12, DEPOSIT, 1024, 1000300),
|
||||
@@ -795,23 +795,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
? HsOfficeCoopSharesTransactionType.SUBSCRIPTION
|
||||
: "UNSUBSCRIPTION".equals(rec.getString("action"))
|
||||
? HsOfficeCoopSharesTransactionType.CANCELLATION
|
||||
: HsOfficeCoopSharesTransactionType.ADJUSTMENT
|
||||
: HsOfficeCoopSharesTransactionType.REVERSAL
|
||||
)
|
||||
.shareCount(rec.getInteger("quantity"))
|
||||
.comment(rec.getString("comment"))
|
||||
.reference(member.getMemberNumber().toString())
|
||||
.build();
|
||||
|
||||
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.ADJUSTMENT) {
|
||||
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.REVERSAL) {
|
||||
final var negativeValue = -shareTransaction.getShareCount();
|
||||
final var adjustedShareTx = coopShares.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopSharesTransactionType.ADJUSTMENT &&
|
||||
final var revertedShareTx = coopShares.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopSharesTransactionType.REVERSAL &&
|
||||
a.getMembership() == shareTransaction.getMembership() &&
|
||||
a.getShareCount() == negativeValue)
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"cannot determine share reverse entry for adjustment " + shareTransaction));
|
||||
shareTransaction.setAdjustedShareTx(adjustedShareTx);
|
||||
"cannot determine share reverse entry for reversal " + shareTransaction));
|
||||
shareTransaction.setRevertedShareTx(revertedShareTx);
|
||||
}
|
||||
coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
|
||||
});
|
||||
@@ -837,7 +837,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
|
||||
|
||||
{
|
||||
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.ADJUSTMENT);
|
||||
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.REVERSAL);
|
||||
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
|
||||
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
|
||||
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
|
||||
@@ -865,16 +865,16 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
.reference(member.getMemberNumber().toString())
|
||||
.build();
|
||||
|
||||
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADJUSTMENT) {
|
||||
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) {
|
||||
final var negativeValue = assetTransaction.getAssetValue().negate();
|
||||
final var adjustedAssetTx = coopAssets.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.ADJUSTMENT &&
|
||||
final var revertedAssetTx = coopAssets.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL &&
|
||||
a.getMembership() == assetTransaction.getMembership() &&
|
||||
a.getAssetValue().equals(negativeValue))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"cannot determine asset reverse entry for adjustment " + assetTransaction));
|
||||
assetTransaction.setAdjustedAssetTx(adjustedAssetTx);
|
||||
"cannot determine asset reverse entry for reversal " + assetTransaction));
|
||||
assetTransaction.setRevertedAssetTx(revertedAssetTx);
|
||||
}
|
||||
|
||||
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
|
||||
|
@@ -55,7 +55,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
EntityManager em;
|
||||
|
||||
@Nested
|
||||
class ListCoopAssetsTransactions {
|
||||
class GetListOfCoopAssetsTransactions {
|
||||
|
||||
@Test
|
||||
void globalAdmin_canViewAllCoopAssetsTransactions() {
|
||||
@@ -109,21 +109,21 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
"valueDate": "2022-10-20",
|
||||
"reference": "ref 1000202-3",
|
||||
"comment": "some loss",
|
||||
"adjustmentAssetTx": {
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"reversalAssetTx": {
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": -128.00,
|
||||
"valueDate": "2022-10-21",
|
||||
"reference": "ref 1000202-3",
|
||||
"comment": "some adjustment"
|
||||
"comment": "some reversal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": -128.00,
|
||||
"valueDate": "2022-10-21",
|
||||
"reference": "ref 1000202-3",
|
||||
"comment": "some adjustment",
|
||||
"adjustedAssetTx": {
|
||||
"comment": "some reversal",
|
||||
"revertedAssetTx": {
|
||||
"transactionType": "DEPOSIT",
|
||||
"assetValue": 128.00,
|
||||
"valueDate": "2022-10-20",
|
||||
@@ -166,10 +166,10 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AddCoopAssetsTransaction {
|
||||
class PostNewCoopAssetTransaction {
|
||||
|
||||
@Test
|
||||
void globalAdmin_canAddCoopAssetsTransaction() {
|
||||
void globalAdmin_canPostNewCoopAssetTransaction() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||
@@ -214,7 +214,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_canAddCoopAssetsAdjustmentTransaction() {
|
||||
void globalAdmin_canAddCoopAssetsReversalTransaction() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||
@@ -238,12 +238,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
.body("""
|
||||
{
|
||||
"membership.uuid": "%s",
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": %s,
|
||||
"valueDate": "2022-10-30",
|
||||
"reference": "test ref adjustment",
|
||||
"comment": "some coop assets adjustment transaction",
|
||||
"reverseEntry.uuid": "%s"
|
||||
"reference": "test ref reversal",
|
||||
"comment": "some coop assets reversal transaction",
|
||||
"revertedAssetTx.uuid": "%s"
|
||||
}
|
||||
""".formatted(
|
||||
givenMembership.getUuid(),
|
||||
@@ -258,12 +258,12 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
|
||||
.body("uuid", isUuidValid())
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": -256.00,
|
||||
"valueDate": "2022-10-30",
|
||||
"reference": "test ref adjustment",
|
||||
"comment": "some coop assets adjustment transaction",
|
||||
"adjustedAssetTx": {
|
||||
"reference": "test ref reversal",
|
||||
"comment": "some coop assets reversal transaction",
|
||||
"revertedAssetTx": {
|
||||
"transactionType": "DEPOSIT",
|
||||
"assetValue": 256.00,
|
||||
"valueDate": "2022-10-20",
|
||||
|
@@ -77,7 +77,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "ADJUSTMENT")
|
||||
.with("transactionType", "REVERSAL")
|
||||
.with("assetValue", 0.00),
|
||||
"[assetValue must not be 0 but is \"0.00\"]"),
|
||||
|
||||
|
@@ -21,14 +21,14 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
|
||||
.build();
|
||||
|
||||
|
||||
final HsOfficeCoopAssetsTransactionEntity givenCoopAssetAdjustmentTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
final HsOfficeCoopAssetsTransactionEntity givenCoopAssetReversalTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.membership(TEST_MEMBERSHIP)
|
||||
.reference("some-ref")
|
||||
.valueDate(LocalDate.parse("2020-01-15"))
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.ADJUSTMENT)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.REVERSAL)
|
||||
.assetValue(new BigDecimal("-128.00"))
|
||||
.comment("some comment")
|
||||
.adjustedAssetTx(givenCoopAssetTransaction)
|
||||
.revertedAssetTx(givenCoopAssetTransaction)
|
||||
.build();
|
||||
|
||||
final HsOfficeCoopAssetsTransactionEntity givenEmptyCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder().build();
|
||||
@@ -41,12 +41,12 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringWithReverseEntryContainsReverseEntry() {
|
||||
givenCoopAssetTransaction.setAdjustedAssetTx(givenCoopAssetAdjustmentTransaction);
|
||||
void toStringWithRevertedAssetTxContainsRevertedAssetTx() {
|
||||
givenCoopAssetTransaction.setRevertedAssetTx(givenCoopAssetReversalTransaction);
|
||||
|
||||
final var result = givenCoopAssetTransaction.toString();
|
||||
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment, M-1000101:ADJ:-128.00)");
|
||||
assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref, some comment, M-1000101:REV:-128.00)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -69,7 +69,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.membership(givenMembership)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||
.assetValue(new BigDecimal("128.00"))
|
||||
.assetValue(new BigDecimal("6400.00"))
|
||||
.valueDate(LocalDate.parse("2022-10-18"))
|
||||
.reference("temp ref A")
|
||||
.build();
|
||||
@@ -98,7 +98,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.membership(givenMembership)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT)
|
||||
.assetValue(new BigDecimal("128.00"))
|
||||
.assetValue(new BigDecimal("6400.00"))
|
||||
.valueDate(LocalDate.parse("2022-10-18"))
|
||||
.reference("temp ref B")
|
||||
.build();
|
||||
@@ -142,18 +142,18 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:ADJ:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:DEP:+128.00)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-21, REVERSAL, -128.00, ref 1000101-3, some reversal, M-1000101:DEP:+128.00)",
|
||||
|
||||
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:ADJ:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:DEP:+128.00)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-21, REVERSAL, -128.00, ref 1000202-3, some reversal, M-1000202:DEP:+128.00)",
|
||||
|
||||
"CoopAssetsTransaction(M-1000303: 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000303: 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000303: 2022-10-20, DEPOSIT, 128.00, ref 1000303-3, some loss, M-1000303:ADJ:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -128.00, ref 1000303-3, some adjustment, M-1000303:DEP:+128.00)");
|
||||
"CoopAssetsTransaction(M-1000303: 2022-10-20, DEPOSIT, 128.00, ref 1000303-3, some loss, M-1000303:REV:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000303: 2022-10-21, REVERSAL, -128.00, ref 1000303-3, some reversal, M-1000303:DEP:+128.00)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -173,8 +173,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:ADJ:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -128.00, ref 1000202-3, some adjustment, M-1000202:DEP:+128.00)");
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-20, DEPOSIT, 128.00, ref 1000202-3, some loss, M-1000202:REV:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000202: 2022-10-21, REVERSAL, -128.00, ref 1000202-3, some reversal, M-1000202:DEP:+128.00)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,8 +211,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
|
||||
"CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:ADJ:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -128.00, ref 1000101-3, some adjustment, M-1000101:DEP:+128.00)");
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-20, DEPOSIT, 128.00, ref 1000101-3, some loss, M-1000101:REV:-128.00)",
|
||||
"CoopAssetsTransaction(M-1000101: 2022-10-21, REVERSAL, -128.00, ref 1000101-3, some reversal, M-1000101:DEP:+128.00)");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -62,7 +62,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ListCoopSharesTransactions {
|
||||
class getListOfCoopSharesTransactions {
|
||||
|
||||
@Test
|
||||
void globalAdmin_canViewAllCoopSharesTransactions() {
|
||||
@@ -108,21 +108,21 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
"valueDate": "2022-10-20",
|
||||
"reference": "ref 1000202-3",
|
||||
"comment": "some subscription",
|
||||
"adjustmentShareTx": {
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"reversalShareTx": {
|
||||
"transactionType": "REVERSAL",
|
||||
"shareCount": -2,
|
||||
"valueDate": "2022-10-21",
|
||||
"reference": "ref 1000202-4",
|
||||
"comment": "some adjustment"
|
||||
"comment": "some reversal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"transactionType": "REVERSAL",
|
||||
"shareCount": -2,
|
||||
"valueDate": "2022-10-21",
|
||||
"reference": "ref 1000202-4",
|
||||
"comment": "some adjustment",
|
||||
"adjustedShareTx": {
|
||||
"comment": "some reversal",
|
||||
"revertedShareTx": {
|
||||
"transactionType": "SUBSCRIPTION",
|
||||
"shareCount": 2,
|
||||
"valueDate": "2022-10-20",
|
||||
@@ -191,7 +191,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdmin_canAddCoopSharesAdjustmentTransaction() {
|
||||
void globalAdmin_canAddCoopSharesReversalTransaction() {
|
||||
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101);
|
||||
@@ -213,16 +213,16 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"membership.uuid": "%s",
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"shareCount": %s,
|
||||
"valueDate": "2022-10-30",
|
||||
"reference": "test ref adjustment",
|
||||
"comment": "some coop shares adjustment transaction",
|
||||
"adjustedShareTx.uuid": "%s"
|
||||
}
|
||||
""".formatted(
|
||||
{
|
||||
"membership.uuid": "%s",
|
||||
"transactionType": "REVERSAL",
|
||||
"shareCount": %s,
|
||||
"valueDate": "2022-10-30",
|
||||
"reference": "test reversal ref",
|
||||
"comment": "some coop shares reversal transaction",
|
||||
"revertedShareTx.uuid": "%s"
|
||||
}
|
||||
""".formatted(
|
||||
givenMembership.getUuid(),
|
||||
-givenTransaction.getShareCount(),
|
||||
givenTransaction.getUuid()))
|
||||
@@ -235,12 +235,12 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
|
||||
.body("uuid", isUuidValid())
|
||||
.body("", lenientlyEquals("""
|
||||
{
|
||||
"transactionType": "ADJUSTMENT",
|
||||
"transactionType": "REVERSAL",
|
||||
"shareCount": -13,
|
||||
"valueDate": "2022-10-30",
|
||||
"reference": "test ref adjustment",
|
||||
"comment": "some coop shares adjustment transaction",
|
||||
"adjustedShareTx": {
|
||||
"reference": "test reversal ref",
|
||||
"comment": "some coop shares reversal transaction",
|
||||
"revertedShareTx": {
|
||||
"transactionType": "SUBSCRIPTION",
|
||||
"shareCount": 13,
|
||||
"valueDate": "2022-10-20",
|
||||
|
@@ -73,7 +73,7 @@ class HsOfficeCoopSharesTransactionControllerRestTest {
|
||||
|
||||
SHARES_COUNT_MUST_NOT_BE_NULL(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "ADJUSTMENT")
|
||||
.with("transactionType", "REVERSAL")
|
||||
.with("shareCount", 0),
|
||||
"[shareCount must not be 0 but is \"0\"]"),
|
||||
|
||||
|
@@ -20,14 +20,14 @@ class HsOfficeCoopSharesTransactionEntityUnitTest {
|
||||
.build();
|
||||
|
||||
|
||||
final HsOfficeCoopSharesTransactionEntity givenCoopShareAdjustmentTransaction = HsOfficeCoopSharesTransactionEntity.builder()
|
||||
final HsOfficeCoopSharesTransactionEntity givenCoopShareReversalTransaction = HsOfficeCoopSharesTransactionEntity.builder()
|
||||
.membership(TEST_MEMBERSHIP)
|
||||
.reference("some-ref")
|
||||
.valueDate(LocalDate.parse("2020-01-15"))
|
||||
.transactionType(HsOfficeCoopSharesTransactionType.ADJUSTMENT)
|
||||
.transactionType(HsOfficeCoopSharesTransactionType.REVERSAL)
|
||||
.shareCount(-4)
|
||||
.comment("some comment")
|
||||
.adjustedShareTx(givenCoopSharesTransaction)
|
||||
.revertedShareTx(givenCoopSharesTransaction)
|
||||
.build();
|
||||
|
||||
final HsOfficeCoopSharesTransactionEntity givenEmptyCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder().build();
|
||||
@@ -40,12 +40,12 @@ class HsOfficeCoopSharesTransactionEntityUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringWithReverseEntryContainsReverseEntry() {
|
||||
givenCoopSharesTransaction.setAdjustedShareTx(givenCoopShareAdjustmentTransaction);
|
||||
void toStringWithRevertedAssetTxContainsRevertedAssetTx() {
|
||||
givenCoopSharesTransaction.setRevertedShareTx(givenCoopShareReversalTransaction);
|
||||
|
||||
final var result = givenCoopSharesTransaction.toString();
|
||||
|
||||
assertThat(result).isEqualTo("CoopShareTransaction(M-1000101: 2020-01-01, SUBSCRIPTION, 4, some-ref, some comment, M-1000101:ADJ:-4)");
|
||||
assertThat(result).isEqualTo("CoopShareTransaction(M-1000101: 2020-01-01, SUBSCRIPTION, 4, some-ref, some comment, M-1000101:REV:-4)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -141,18 +141,18 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
|
||||
"CoopShareTransaction(M-1000101: 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:ADJ:-2)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-21, REVERSAL, -2, ref 1000101-4, some reversal, M-1000101:SUB:+2)",
|
||||
|
||||
"CoopShareTransaction(M-1000202: 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
|
||||
"CoopShareTransaction(M-1000202: 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:ADJ:-2)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-21, REVERSAL, -2, ref 1000202-4, some reversal, M-1000202:SUB:+2)",
|
||||
|
||||
"CoopShareTransaction(M-1000303: 2010-03-15, SUBSCRIPTION, 4, ref 1000303-1, initial subscription)",
|
||||
"CoopShareTransaction(M-1000303: 2021-09-01, CANCELLATION, -2, ref 1000303-2, cancelling some)",
|
||||
"CoopShareTransaction(M-1000303: 2022-10-20, SUBSCRIPTION, 2, ref 1000303-3, some subscription, M-1000303:ADJ:-2)",
|
||||
"CoopShareTransaction(M-1000303: 2022-10-21, ADJUSTMENT, -2, ref 1000303-4, some adjustment, M-1000303:SUB:+2)");
|
||||
"CoopShareTransaction(M-1000303: 2022-10-20, SUBSCRIPTION, 2, ref 1000303-3, some subscription, M-1000303:REV:-2)",
|
||||
"CoopShareTransaction(M-1000303: 2022-10-21, REVERSAL, -2, ref 1000303-4, some reversal, M-1000303:SUB:+2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,8 +172,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopShareTransaction(M-1000202: 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)",
|
||||
"CoopShareTransaction(M-1000202: 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:ADJ:-2)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-21, ADJUSTMENT, -2, ref 1000202-4, some adjustment, M-1000202:SUB:+2)");
|
||||
"CoopShareTransaction(M-1000202: 2022-10-20, SUBSCRIPTION, 2, ref 1000202-3, some subscription, M-1000202:REV:-2)",
|
||||
"CoopShareTransaction(M-1000202: 2022-10-21, REVERSAL, -2, ref 1000202-4, some reversal, M-1000202:SUB:+2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -210,8 +210,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
||||
result,
|
||||
"CoopShareTransaction(M-1000101: 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)",
|
||||
"CoopShareTransaction(M-1000101: 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:ADJ:-2)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-21, ADJUSTMENT, -2, ref 1000101-4, some adjustment, M-1000101:SUB:+2)");
|
||||
"CoopShareTransaction(M-1000101: 2022-10-20, SUBSCRIPTION, 2, ref 1000101-3, some subscription, M-1000101:REV:-2)",
|
||||
"CoopShareTransaction(M-1000101: 2022-10-21, REVERSAL, -2, ref 1000101-4, some reversal, M-1000101:SUB:+2)");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
@@ -44,8 +45,36 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
@MockBean
|
||||
EntityManagerWrapper em;
|
||||
|
||||
@Nested
|
||||
class GetMemberships {
|
||||
|
||||
@Test
|
||||
void findMembershipByNonExistingMemberNumberReturnsEmptyList() throws Exception {
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/office/memberships?memberNumber=12345")
|
||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"partner.uuid": null,
|
||||
"memberNumberSuffix": "01",
|
||||
"validFrom": "2022-10-13",
|
||||
"membershipFeeBillable": "true"
|
||||
}
|
||||
""")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath("$", hasSize(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AddMembership {
|
||||
|
||||
@Test
|
||||
void respondBadRequest_ifPartnerUuidIsMissing() throws Exception {
|
||||
|
||||
@@ -98,7 +127,9 @@ public class HsOfficeMembershipControllerRestTest {
|
||||
.andExpect(status().is4xxClientError())
|
||||
.andExpect(jsonPath("statusCode", is(400)))
|
||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||
.andExpect(jsonPath("message", is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid)));
|
||||
.andExpect(jsonPath(
|
||||
"message",
|
||||
is("ERROR: [400] Unable to find Partner by partner.uuid: " + givenPartnerUuid)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
@@ -13,6 +13,12 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DontDeleteDefaultDe
|
||||
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.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;
|
||||
@@ -49,7 +55,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1010)
|
||||
@Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"})
|
||||
@Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"})
|
||||
void shouldCreateLegalPersonAsPartner() {
|
||||
new CreatePartner(this)
|
||||
.given("partnerNumber", 31010)
|
||||
@@ -71,7 +77,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1011)
|
||||
@Produces(explicitly = "Partner: Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"})
|
||||
@Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"})
|
||||
void shouldCreateNaturalPersonAsPartner() {
|
||||
new CreatePartner(this)
|
||||
.given("partnerNumber", 31011)
|
||||
@@ -148,7 +154,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1100)
|
||||
@Requires("Partner: Michelle Matthieu")
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldAmendContactData() {
|
||||
new AmendContactData(this)
|
||||
.given("partnerName", "Matthieu")
|
||||
@@ -158,7 +164,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1101)
|
||||
@Requires("Partner: Michelle Matthieu")
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldAddPhoneNumberToContactData() {
|
||||
new AddPhoneNumberToContactData(this)
|
||||
.given("partnerName", "Matthieu")
|
||||
@@ -169,7 +175,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1102)
|
||||
@Requires("Partner: Michelle Matthieu")
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldRemovePhoneNumberFromContactData() {
|
||||
new RemovePhoneNumberFromContactData(this)
|
||||
.given("partnerName", "Matthieu")
|
||||
@@ -179,7 +185,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1103)
|
||||
@Requires("Partner: Test AG")
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
void shouldReplaceContactData() {
|
||||
new ReplaceContactData(this)
|
||||
.given("partnerName", "Test AG")
|
||||
@@ -201,7 +207,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(1201)
|
||||
@Requires("Partner: Michelle Matthieu")
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldUpdatePersonData() {
|
||||
new ShouldUpdatePersonData(this)
|
||||
.given("oldFamilyName", "Matthieu")
|
||||
@@ -211,7 +217,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(2010)
|
||||
@Requires("Partner: Test AG")
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
@Produces("Debitor: Test AG - main debitor")
|
||||
void shouldCreateSelfDebitorForPartner() {
|
||||
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
|
||||
@@ -261,18 +267,18 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(2020)
|
||||
@Requires("Debitor: Test AG - main debitor")
|
||||
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||
@Disabled("see TODO.spec in DontDeleteDefaultDebitor")
|
||||
void shouldNotDeleteDefaultDebitor() {
|
||||
new DontDeleteDefaultDebitor(this)
|
||||
.given("partnerNumber", 31020)
|
||||
.given("partnerNumber", 31010)
|
||||
.given("debitorSuffix", "00")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3100)
|
||||
@Requires("Debitor: Test AG - main debitor")
|
||||
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||
@Produces("SEPA-Mandate: Test AG")
|
||||
void shouldCreateSepaMandateForDebitor() {
|
||||
new CreateSepaMandateForDebitor(this)
|
||||
@@ -313,12 +319,11 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(4000)
|
||||
@Requires("Partner: Test AG")
|
||||
@Produces("Membership: Test AG 00")
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
@Produces("Membership: M-3101000 - Test AG")
|
||||
void shouldCreateMembershipForPartner() {
|
||||
new CreateMembership(this)
|
||||
.given("partnerName", "Test AG")
|
||||
.given("memberNumberSuffix", "00")
|
||||
.given("validFrom", "2024-10-15")
|
||||
.given("newStatus", "ACTIVE")
|
||||
.given("membershipFeeBillable", "true")
|
||||
@@ -326,9 +331,87 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
.keep();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4201)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
@Produces("Coop-Shares SUBSCRIPTION Transaction")
|
||||
void shouldSubscribeCoopShares() {
|
||||
new CreateCoopSharesSubscriptionTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "sign 2024-01-15")
|
||||
.given("shareCount", 100)
|
||||
.given("comment", "Signing the Membership")
|
||||
.given("transactionDate", "2024-01-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4202)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldRevertCoopSharesSubscription() {
|
||||
new CreateCoopSharesRevertTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("comment", "reverting some incorrect transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4202)
|
||||
@Requires("Coop-Shares SUBSCRIPTION Transaction")
|
||||
@Produces("Coop-Shares CANCELLATION Transaction")
|
||||
void shouldCancelCoopSharesSubscription() {
|
||||
new CreateCoopSharesCancellationTransaction(this)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "cancel 2024-01-15")
|
||||
.given("sharesToCancel", 8)
|
||||
.given("comment", "Cancelling 8 Shares")
|
||||
.given("transactionDate", "2024-02-15")
|
||||
.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: Test AG 00")
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldCancelMembershipOfPartner() {
|
||||
new CancelMembership(this)
|
||||
.given("memberNumber", "3101000")
|
||||
|
@@ -4,6 +4,9 @@ import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class PathAssertion {
|
||||
|
||||
private final String path;
|
||||
@@ -14,10 +17,35 @@ public class PathAssertion {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
||||
return response -> response.path(path).contains(ScenarioTest.resolve(resolvableValue));
|
||||
return response -> {
|
||||
try {
|
||||
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 + "\")`" );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Consumer<HttpResponse> doesNotExist() {
|
||||
return response -> response.path(path).isNull(); // here, null Optional means key not found in JSON
|
||||
return response -> {
|
||||
try {
|
||||
response.path(path).isNull(); // here, null Optional means key not found in JSON
|
||||
} catch (final AssertionError e) {
|
||||
// without this, the error message is often lacking important context
|
||||
fail(e.getMessage() + " in `path(\"" + path + "\").doesNotExist()`" );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver;
|
||||
import net.hostsharing.hsadminng.lambda.Reducer;
|
||||
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
@@ -26,6 +27,8 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
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 org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public abstract class ScenarioTest extends ContextBasedTest {
|
||||
@@ -38,11 +41,11 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
public String toString() {
|
||||
return ObjectUtils.toString(uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final static Map<String, Alias<?>> aliases = new HashMap<>();
|
||||
private final static Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
private final static Map<String, Object> properties = new HashMap<>();
|
||||
public final TestReport testReport = new TestReport(aliases);
|
||||
|
||||
@LocalServerPort
|
||||
@@ -139,9 +142,9 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
}
|
||||
|
||||
static UUID uuid(final String nameWithPlaceholders) {
|
||||
final var resoledName = resolve(nameWithPlaceholders);
|
||||
final UUID alias = ofNullable(knowVariables().get(resoledName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null);
|
||||
assertThat(alias).as("alias '" + resoledName + "' not found in aliases nor in properties [" +
|
||||
final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS);
|
||||
final UUID alias = ofNullable(knowVariables().get(resolvedName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null);
|
||||
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
|
||||
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
|
||||
).isNotNull();
|
||||
return alias;
|
||||
@@ -162,13 +165,13 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String resolve(final String text) {
|
||||
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve();
|
||||
public static String resolve(final String text, final Resolver resolver) {
|
||||
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(resolver);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public static Object resolveTyped(final String text) {
|
||||
final var resolved = resolve(text);
|
||||
final var resolved = resolve(text, DROP_COMMENTS);
|
||||
try {
|
||||
return UUID.fromString(resolved);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
|
@@ -10,29 +10,39 @@ import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
|
||||
public class TemplateResolver {
|
||||
|
||||
private final static Pattern pattern = Pattern.compile(",(\\s*})", Pattern.MULTILINE);
|
||||
private static final String IF_NOT_FOUND_SYMBOL = "???";
|
||||
public enum Resolver {
|
||||
DROP_COMMENTS, // deletes comments ('#{whatever}' -> '')
|
||||
KEEP_COMMENTS // keep comments ('#{whatever}' -> 'whatever')
|
||||
}
|
||||
|
||||
enum PlaceholderPrefix {
|
||||
RAW('%') {
|
||||
@Override
|
||||
String convert(final Object value) {
|
||||
String convert(final Object value, final Resolver resolver) {
|
||||
return value != null ? value.toString() : "";
|
||||
}
|
||||
},
|
||||
JSON_QUOTED('$'){
|
||||
@Override
|
||||
String convert(final Object value) {
|
||||
String convert(final Object value, final Resolver resolver) {
|
||||
return jsonQuoted(value);
|
||||
}
|
||||
},
|
||||
URI_ENCODED('&'){
|
||||
@Override
|
||||
String convert(final Object value) {
|
||||
String convert(final Object value, final Resolver resolver) {
|
||||
return value != null ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : "";
|
||||
}
|
||||
},
|
||||
COMMENT('#'){
|
||||
@Override
|
||||
String convert(final Object value, final Resolver resolver) {
|
||||
return resolver == DROP_COMMENTS ? "" : value.toString();
|
||||
}
|
||||
};
|
||||
|
||||
private final char prefixChar;
|
||||
@@ -42,19 +52,24 @@ public class TemplateResolver {
|
||||
}
|
||||
|
||||
static boolean contains(final char givenChar) {
|
||||
return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar);
|
||||
return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar);
|
||||
}
|
||||
|
||||
static PlaceholderPrefix ofPrefixChar(final char givenChar) {
|
||||
return Arrays.stream(values()).filter(p -> p.prefixChar == givenChar).findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
abstract String convert(final Object value);
|
||||
abstract String convert(final Object value, final Resolver resolver);
|
||||
}
|
||||
|
||||
private static final Pattern COMMA_RIGHT_BEFORE_CLOSING_BRACE = Pattern.compile(",(\\s*})", Pattern.MULTILINE);
|
||||
private static final String IF_NOT_FOUND_SYMBOL = "???";
|
||||
|
||||
private final String template;
|
||||
private final Map<String, Object> properties;
|
||||
private final StringBuilder resolved = new StringBuilder();
|
||||
|
||||
private Resolver resolver;
|
||||
private int position = 0;
|
||||
|
||||
public TemplateResolver(final String template, final Map<String, Object> properties) {
|
||||
@@ -62,7 +77,8 @@ public class TemplateResolver {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
String resolve() {
|
||||
String resolve(final Resolver resolver) {
|
||||
this.resolver = resolver;
|
||||
final var resolved = copy();
|
||||
final var withoutDroppedLines = dropLinesWithNullProperties(resolved);
|
||||
final var result = removeDanglingCommas(withoutDroppedLines);
|
||||
@@ -70,7 +86,7 @@ public class TemplateResolver {
|
||||
}
|
||||
|
||||
private static String removeDanglingCommas(final String withoutDroppedLines) {
|
||||
return pattern.matcher(withoutDroppedLines).replaceAll("$1");
|
||||
return COMMA_RIGHT_BEFORE_CLOSING_BRACE.matcher(withoutDroppedLines).replaceAll("$1");
|
||||
}
|
||||
|
||||
private String dropLinesWithNullProperties(final String text) {
|
||||
@@ -119,10 +135,10 @@ public class TemplateResolver {
|
||||
placeholder.append(fetchChar());
|
||||
}
|
||||
}
|
||||
final var name = new TemplateResolver(placeholder.toString(), properties).resolve();
|
||||
final var value = propVal(name);
|
||||
final var content = new TemplateResolver(placeholder.toString(), properties).resolve(resolver);
|
||||
final var value = intro != '#' ? propVal(content) : content;
|
||||
resolved.append(
|
||||
PlaceholderPrefix.ofPrefixChar(intro).convert(value)
|
||||
PlaceholderPrefix.ofPrefixChar(intro).convert(value, resolver)
|
||||
);
|
||||
skipChar('}');
|
||||
}
|
||||
@@ -134,12 +150,12 @@ public class TemplateResolver {
|
||||
} else if (nameExpression.contains(IF_NOT_FOUND_SYMBOL)) {
|
||||
final var parts = StringUtils.split(nameExpression, IF_NOT_FOUND_SYMBOL);
|
||||
return Arrays.stream(parts).filter(Objects::nonNull).findFirst().orElseGet(() -> {
|
||||
if ( parts[parts.length-1].isEmpty() ) {
|
||||
// => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional
|
||||
return null;
|
||||
}
|
||||
// => last alternative element in expression was null and not optional
|
||||
throw new IllegalStateException("Missing required value in property-chain: " + nameExpression);
|
||||
if ( parts[parts.length-1].isEmpty() ) {
|
||||
// => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional
|
||||
return null;
|
||||
}
|
||||
// => last alternative element in expression was null and not optional
|
||||
throw new IllegalStateException("Missing required value in property-chain: " + nameExpression);
|
||||
});
|
||||
} else {
|
||||
final var val = properties.get(nameExpression);
|
||||
|
@@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TemplateResolverUnitTest {
|
||||
@@ -42,7 +43,7 @@ class TemplateResolverUnitTest {
|
||||
Map.entry("simple placeholder", "einfach"),
|
||||
Map.entry("nested placeholder", "verschachtelt"),
|
||||
Map.entry("with-special-chars", "3&3 AG")
|
||||
)).resolve();
|
||||
)).resolve(DROP_COMMENTS);
|
||||
|
||||
assertThat(resolved).isEqualTo("""
|
||||
with optional JSON quotes:
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.system.SystemProcess;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
|
||||
@@ -9,29 +11,41 @@ import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestReport {
|
||||
|
||||
private final Map<String, ?> aliases;
|
||||
private final StringBuilder markdownLog = new StringBuilder(); // records everything for debugging purposes
|
||||
public static final File BUILD_DOC_SCENARIOS = new File("build/doc/scenarios");
|
||||
private final static File markdownLogFile = new File(BUILD_DOC_SCENARIOS, ".last-debug-log.md");
|
||||
public static final SimpleDateFormat MM_DD_YYYY_HH_MM_SS = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss");
|
||||
|
||||
private PrintWriter markdownReport;
|
||||
private final Map<String, ?> aliases;
|
||||
private final PrintWriter markdownLog; // records everything for debugging purposes
|
||||
private File markdownReportFile;
|
||||
private PrintWriter markdownReport; // records only the use-case under test, without its pre-requisites
|
||||
private int silent; // do not print anything to test-report if >0
|
||||
|
||||
static {
|
||||
assertThat(BUILD_DOC_SCENARIOS.isDirectory() || BUILD_DOC_SCENARIOS.mkdirs())
|
||||
.as("mkdir " + BUILD_DOC_SCENARIOS).isTrue();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public TestReport(final Map<String, ?> aliases) {
|
||||
this.aliases = aliases;
|
||||
this.markdownLog = new PrintWriter(new FileWriter(markdownLogFile));
|
||||
}
|
||||
|
||||
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
||||
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
|
||||
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow();
|
||||
assertThat(new File("doc/scenarios/").isDirectory() || new File("doc/scenarios/").mkdirs()).as("mkdir doc/scenarios/").isTrue();
|
||||
markdownReport = new PrintWriter(new FileWriter("doc/scenarios/" + testMethodOrder + "-" + testMethodName + ".md"));
|
||||
print("## Scenario #" + testInfo.getTestMethod().map(TestReport::orderNumber).orElseThrow() + ": " +
|
||||
testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2"));
|
||||
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + "-" + testMethodName + ".md");
|
||||
markdownReport = new PrintWriter(new FileWriter(markdownReportFile));
|
||||
print("## Scenario #" + determineScenarioTitle(testInfo));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -45,7 +59,7 @@ public class TestReport {
|
||||
}
|
||||
|
||||
// but the debugLog should contain all output, even if silent
|
||||
markdownLog.append(outputWithCommentsForUuids);
|
||||
markdownLog.print(outputWithCommentsForUuids);
|
||||
}
|
||||
|
||||
public void printLine(final String output) {
|
||||
@@ -56,10 +70,32 @@ public class TestReport {
|
||||
printLine("\n" +output + "\n");
|
||||
}
|
||||
|
||||
void silent(final Runnable code) {
|
||||
silent++;
|
||||
code.run();
|
||||
silent--;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (markdownReport != null) {
|
||||
printPara("---");
|
||||
printPara("generated on " + MM_DD_YYYY_HH_MM_SS.format(new Date()) + " for branch " + currentGitBranch());
|
||||
markdownReport.close();
|
||||
System.out.println("SCENARIO REPORT: " + asClickableLink(markdownReportFile));
|
||||
}
|
||||
markdownLog.close();
|
||||
System.out.println("DEBUG LOG: " + asClickableLink(markdownLogFile));
|
||||
}
|
||||
|
||||
private static @NotNull String determineScenarioTitle(final TestInfo testInfo) {
|
||||
final var convertedTestMethodName =
|
||||
testInfo.getTestMethod().map(TestReport::orderNumber).orElseThrow() + ": " +
|
||||
testInfo.getTestMethod().map(Method::getName).map(t -> t.replaceAll("([a-z])([A-Z]+)", "$1 $2")).orElseThrow();
|
||||
return convertedTestMethodName.replaceAll(": should ", ": ");
|
||||
}
|
||||
|
||||
private String asClickableLink(final File file) {
|
||||
return file.toURI().toString().replace("file:/", "file:///");
|
||||
}
|
||||
|
||||
private static Object orderNumber(final Method method) {
|
||||
@@ -83,10 +119,16 @@ public class TestReport {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
void silent(final Runnable code) {
|
||||
silent++;
|
||||
code.run();
|
||||
silent--;
|
||||
@SneakyThrows
|
||||
private String currentGitBranch() {
|
||||
try {
|
||||
final var gitRevParse = new SystemProcess("git", "rev-parse", "--abbrev-ref", "HEAD");
|
||||
gitRevParse.execute();
|
||||
return gitRevParse.getStdOut().split("\\R", 2)[0];
|
||||
} catch (final IOException exc) {
|
||||
// TODO.test: the git call does not work in Jenkins, we have to find out why
|
||||
System.err.println(exc);
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.PathNotFoundException;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -33,6 +34,8 @@ import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.net.URLEncoder.encode;
|
||||
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 org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.platform.commons.util.StringUtils.isBlank;
|
||||
@@ -50,6 +53,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
private final Map<String, Object> givenProperties = new LinkedHashMap<>();
|
||||
|
||||
private String nextTitle; // just temporary to override resultAlias for sub-use-cases
|
||||
private String introduction;
|
||||
|
||||
public UseCase(final ScenarioTest testSuite) {
|
||||
this(testSuite, getResultAliasFromProducesAnnotationInCallStack());
|
||||
@@ -71,6 +75,9 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public final HttpResponse doRun() {
|
||||
if (introduction != null) {
|
||||
testReport.printPara(introduction);
|
||||
}
|
||||
testReport.printPara("### Given Properties");
|
||||
testReport.printLine("""
|
||||
| name | value |
|
||||
@@ -81,7 +88,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
testReport.silent(() ->
|
||||
requirements.forEach((alias, factory) -> {
|
||||
if (!ScenarioTest.containsAlias(alias)) {
|
||||
factory.apply(alias).run().keep();
|
||||
factory.apply(alias).run().keepAs(alias);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -95,6 +102,11 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
protected void verify(final HttpResponse response) {
|
||||
}
|
||||
|
||||
public UseCase<T> introduction(final String introduction) {
|
||||
this.introduction = introduction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final UseCase<T> given(final String propName, final Object propValue) {
|
||||
givenProperties.put(propName, propValue);
|
||||
ScenarioTest.putProperty(propName, propValue);
|
||||
@@ -106,11 +118,11 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public final void obtain(
|
||||
final String alias,
|
||||
final String title,
|
||||
final Supplier<HttpResponse> http,
|
||||
final Function<HttpResponse, String> extractor,
|
||||
final String... extraInfo) {
|
||||
withTitle(ScenarioTest.resolve(alias), () -> {
|
||||
withTitle(title, () -> {
|
||||
final var response = http.get().keep(extractor);
|
||||
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
||||
return response;
|
||||
@@ -118,15 +130,15 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public final void obtain(final String alias, final Supplier<HttpResponse> http, final String... extraInfo) {
|
||||
withTitle(ScenarioTest.resolve(alias), () -> {
|
||||
withTitle(alias, () -> {
|
||||
final var response = http.get().keep();
|
||||
Arrays.stream(extraInfo).forEach(testReport::printPara);
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
public HttpResponse withTitle(final String title, final Supplier<HttpResponse> code) {
|
||||
this.nextTitle = title;
|
||||
public HttpResponse withTitle(final String resolvableTitle, final Supplier<HttpResponse> code) {
|
||||
this.nextTitle = resolvableTitle;
|
||||
final var response = code.get();
|
||||
this.nextTitle = null;
|
||||
return response;
|
||||
@@ -134,7 +146,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
@SneakyThrows
|
||||
public final HttpResponse httpGet(final String uriPathWithPlaceholders) {
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS);
|
||||
final var request = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||
@@ -147,7 +159,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
@SneakyThrows
|
||||
public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS);
|
||||
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||
final var request = HttpRequest.newBuilder()
|
||||
.POST(BodyPublishers.ofString(requestBody))
|
||||
@@ -162,7 +174,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
@SneakyThrows
|
||||
public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS);
|
||||
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||
final var request = HttpRequest.newBuilder()
|
||||
.method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody))
|
||||
@@ -177,7 +189,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
@SneakyThrows
|
||||
public final HttpResponse httpDelete(final String uriPathWithPlaceholders) {
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
|
||||
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS);
|
||||
final var request = HttpRequest.newBuilder()
|
||||
.DELETE()
|
||||
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||
@@ -197,7 +209,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
final String title,
|
||||
final Supplier<UseCase.HttpResponse> http,
|
||||
final Consumer<UseCase.HttpResponse>... assertions) {
|
||||
withTitle(ScenarioTest.resolve(title), () -> {
|
||||
withTitle(title, () -> {
|
||||
final var response = http.get();
|
||||
Arrays.stream(assertions).forEach(assertion -> assertion.accept(response));
|
||||
return response;
|
||||
@@ -209,7 +221,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public String uriEncoded(final String text) {
|
||||
return encode(ScenarioTest.resolve(text), StandardCharsets.UTF_8);
|
||||
return encode(ScenarioTest.resolve(text, DROP_COMMENTS), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static class JsonTemplate {
|
||||
@@ -221,7 +233,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
String resolvePlaceholders() {
|
||||
return ScenarioTest.resolve(template);
|
||||
return ScenarioTest.resolve(template, DROP_COMMENTS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +278,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public HttpResponse keep(final Function<HttpResponse, String> extractor) {
|
||||
final var alias = nextTitle != null ? nextTitle : resultAlias;
|
||||
final var alias = nextTitle != null ? ScenarioTest.resolve(nextTitle, DROP_COMMENTS) : resultAlias;
|
||||
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
|
||||
|
||||
final var value = extractor.apply(this);
|
||||
@@ -276,15 +288,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpResponse keep() {
|
||||
final var alias = nextTitle != null ? nextTitle : resultAlias;
|
||||
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
|
||||
public HttpResponse keepAs(final String alias) {
|
||||
ScenarioTest.putAlias(
|
||||
alias,
|
||||
nonNullAlias(alias),
|
||||
new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid));
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpResponse keep() {
|
||||
final var alias = nextTitle != null ? ScenarioTest.resolve(nextTitle, DROP_COMMENTS) : resultAlias;
|
||||
assertThat(alias).as("cannot keep result, no title or alias found for locationUuid: " + locationUuid).isNotNull();
|
||||
|
||||
return keepAs(alias);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public HttpResponse expectArrayElements(final int expectedElementCount) {
|
||||
final var rootNode = objectMapper.readTree(response.body());
|
||||
@@ -298,20 +315,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
@SneakyThrows
|
||||
public String getFromBody(final String path) {
|
||||
return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path));
|
||||
return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Optional<String> getFromBodyAsOptional(final String path) {
|
||||
public <T> Optional<T> getFromBodyAsOptional(final String path) {
|
||||
try {
|
||||
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)));
|
||||
} catch (final Exception e) {
|
||||
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS)));
|
||||
} catch (final PathNotFoundException e) {
|
||||
return null; // means the property did not exist at all, not that it was there with value null
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public OptionalAssert<String> path(final String path) {
|
||||
public <T> OptionalAssert<T> path(final String path) {
|
||||
return assertThat(getFromBodyAsOptional(path));
|
||||
}
|
||||
|
||||
@@ -320,9 +337,9 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
// the title
|
||||
if (nextTitle != null) {
|
||||
testReport.printLine("\n### " + nextTitle + "\n");
|
||||
testReport.printLine("\n### " + ScenarioTest.resolve(nextTitle, KEEP_COMMENTS) + "\n");
|
||||
} else if (resultAlias != null) {
|
||||
testReport.printLine("\n### " + resultAlias + "\n");
|
||||
testReport.printLine("\n### Create " + resultAlias + "\n");
|
||||
} else {
|
||||
fail("please wrap the http...-call in the UseCase using `withTitle(...)`");
|
||||
}
|
||||
@@ -342,6 +359,13 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
testReport.printLine("```");
|
||||
testReport.printLine("");
|
||||
}
|
||||
|
||||
private String nonNullAlias(final String alias) {
|
||||
// This marker tag should not appear in the source-code, as here is nothing to fix.
|
||||
// But if it appears in generated Markdown files, it should show up when that marker tag is searched.
|
||||
final var onlyVisibleInGeneratedMarkdownNotInSource = new String(new char[]{'F', 'I', 'X', 'M', 'E'});
|
||||
return alias == null ? "unknown alias -- " + onlyVisibleInGeneratedMarkdownNotInSource : alias;
|
||||
}
|
||||
}
|
||||
|
||||
protected T self() {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
@@ -16,10 +16,18 @@ public class CreateMembership extends UseCase<CreateMembership> {
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
|
||||
obtain("Partner: %{partnerName}", () ->
|
||||
httpGet("/api/hs/office/partners?name=&{partnerName}")
|
||||
.expecting(OK).expecting(JSON),
|
||||
response -> response.expectArrayElements(1).getFromBody("[0].uuid"),
|
||||
"In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one."
|
||||
);
|
||||
|
||||
return httpPost("/api/hs/office/memberships", usingJsonBody("""
|
||||
{
|
||||
"partner.uuid": ${Partner: Test AG},
|
||||
"memberNumberSuffix": ${memberNumberSuffix},
|
||||
"partner.uuid": ${Partner: %{partnerName}},
|
||||
"memberNumberSuffix": ${%{memberNumberSuffix???}???00},
|
||||
"status": "ACTIVE",
|
||||
"validFrom": ${validFrom},
|
||||
"membershipFeeBillable": ${membershipFeeBillable}
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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}")
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesCancellationTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
public CreateCoopSharesCancellationTransaction(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
given("transactionType", "CANCELLATION");
|
||||
given("shareCount", "-%{sharesToCancel}");
|
||||
return super.run();
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
public CreateCoopSharesRevertTransaction(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
requires("CoopShares-Transaction with incorrect shareCount", alias ->
|
||||
new CreateCoopSharesSubscriptionTransaction(testSuite)
|
||||
.given("memberNumber", "3101000")
|
||||
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as revertedShareTx
|
||||
.given("shareCount", 100)
|
||||
.given("comment", "coop-shares subscription transaction with wrong share count")
|
||||
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
given("transactionType", "REVERSAL");
|
||||
given("shareCount", -100);
|
||||
given("revertedShareTx", uuid("CoopShares-Transaction with incorrect shareCount"));
|
||||
return super.run();
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesSubscriptionTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
public CreateCoopSharesSubscriptionTransaction(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
given("transactionType", "SUBSCRIPTION");
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public abstract class CreateCoopSharesTransaction extends UseCase<CreateCoopSharesTransaction> {
|
||||
|
||||
public CreateCoopSharesTransaction(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-Shares-%{transactionType} Transaction", () ->
|
||||
httpPost("/api/hs/office/coopsharestransactions", usingJsonBody("""
|
||||
{
|
||||
"membership.uuid": ${membershipUuid},
|
||||
"transactionType": ${transactionType},
|
||||
"reference": ${reference},
|
||||
"shareCount": ${shareCount},
|
||||
"comment": ${comment},
|
||||
"valueDate": ${transactionDate},
|
||||
"revertedShareTx.uuid": ${revertedShareTx???}
|
||||
}
|
||||
"""))
|
||||
.expecting(HttpStatus.CREATED).expecting(ContentType.JSON)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verify(final HttpResponse response) {
|
||||
verify("Verify Coop-Shares %{transactionType}-Transaction",
|
||||
() -> httpGet("/api/hs/office/coopsharestransactions/" + response.getLocationUuid())
|
||||
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
|
||||
path("transactionType").contains("%{transactionType}"),
|
||||
path("shareCount").contains("%{shareCount}"),
|
||||
path("comment").contains("%{comment}"),
|
||||
path("valueDate").contains("%{transactionDate}")
|
||||
);
|
||||
}
|
||||
}
|
@@ -16,6 +16,8 @@ public class CreatePartner extends UseCase<CreatePartner> {
|
||||
|
||||
public CreatePartner(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
introduction("A partner can be a client or a vendor, currently we only use them for clients.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Reference in New Issue
Block a user