From b45639878ed360ef245ae3f12a967686f553fc53 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 28 Nov 2024 15:04:20 +0100 Subject: [PATCH] refactor scenario-tests into inner classes --- .../hs/migration/BaseOfficeDataImport.java | 12 +- .../scenarios/HsOfficeScenarioTests.java | 869 ++++++++++-------- .../test/scenarios/ScenarioTest.java | 88 +- 3 files changed, 535 insertions(+), 434 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java b/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java index 3ab17692..ed97041f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/migration/BaseOfficeDataImport.java @@ -880,17 +880,19 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction); }); - coopAssets.values().forEach(assetTransaction -> { + coopAssets.entrySet().forEach(entry -> { + final var legacyId = entry.getKey(); + final var assetTransaction = entry.getValue(); if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) { - connectToRelatedRevertedAssetTx(assetTransaction); + connectToRelatedRevertedAssetTx(legacyId, assetTransaction); } if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.TRANSFER) { - connectToRelatedAdoptionAssetTx(assetTransaction); + connectToRelatedAdoptionAssetTx(legacyId, assetTransaction); } }); } - private static void connectToRelatedRevertedAssetTx(final HsOfficeCoopAssetsTransactionEntity assetTransaction) { + private static void connectToRelatedRevertedAssetTx(final int legacyId, final HsOfficeCoopAssetsTransactionEntity assetTransaction) { final var negativeValue = assetTransaction.getAssetValue().negate(); final var revertedAssetTx = coopAssets.values().stream().filter(a -> a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL && @@ -903,7 +905,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport { //revertedAssetTx.setAssetReversalTx(assetTransaction); } - private static void connectToRelatedAdoptionAssetTx(final HsOfficeCoopAssetsTransactionEntity assetTransaction) { + private static void connectToRelatedAdoptionAssetTx(final Integer legacyId, final HsOfficeCoopAssetsTransactionEntity assetTransaction) { final var negativeValue = assetTransaction.getAssetValue().negate(); final var adoptionAssetTx = coopAssets.values().stream().filter(a -> a.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADOPTION && diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 90220a20..d59bfc89 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -35,11 +35,14 @@ import net.hostsharing.hsadminng.test.scenarios.Requires; import net.hostsharing.hsadminng.test.scenarios.ScenarioTest; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension; +import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; @@ -56,431 +59,487 @@ import org.springframework.test.annotation.DirtiesContext; "hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}" } ) -@DirtiesContext -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +//@DirtiesContext +@TestClassOrder(ClassOrderer.OrderAnnotation.class) @ExtendWith(IgnoreOnFailureExtension.class) class HsOfficeScenarioTests extends ScenarioTest { - @Test - @Order(1010) - @Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = { "Person: Test AG", "Contact: Test AG - Hamburg" }) - void shouldCreateLegalPersonAsPartner() { - new CreatePartner(this) - .given("partnerNumber", "P-31010") - .given("personType", "LEGAL_PERSON") - .given("tradeName", "Test AG") - .given("contactCaption", "Test AG - Hamburg") - .given("postalAddress", """ - "firm": "Test AG", - "street": "Shanghai-Allee 1", - "zipcode": "20123", - "city": "Hamburg", - "country": "Germany" - """) - .given("officePhoneNumber", "+49 40 654321-0") - .given("emailAddress", "hamburg@test-ag.example.org") - .doRun() - .keep(); + @Nested + @Order(10) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class PartnerScenarios { + + @Test + @Order(1010) + @Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = { "Person: Test AG", "Contact: Test AG - Hamburg" }) + void shouldCreateLegalPersonAsPartner() { + new CreatePartner(scenarioTest) + .given("partnerNumber", "P-31010") + .given("personType", "LEGAL_PERSON") + .given("tradeName", "Test AG") + .given("contactCaption", "Test AG - Hamburg") + .given( + "postalAddress", """ + "firm": "Test AG", + "street": "Shanghai-Allee 1", + "zipcode": "20123", + "city": "Hamburg", + "country": "Germany" + """) + .given("officePhoneNumber", "+49 40 654321-0") + .given("emailAddress", "hamburg@test-ag.example.org") + .doRun() + .keep(); + } + + @Test + @Order(1011) + @Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", + implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" }) + void shouldCreateNaturalPersonAsPartner() { + new CreatePartner(scenarioTest) + .given("partnerNumber", "P-31011") + .given("personType", "NATURAL_PERSON") + .given("givenName", "Michelle") + .given("familyName", "Matthieu") + .given("contactCaption", "Michelle Matthieu") + .given( + "postalAddress", """ + "name": "Michelle Matthieu", + "street": "An der Wandse 34", + "zipcode": "22123", + "city": "Hamburg", + "country": "Germany" + """) + .given("officePhoneNumber", "+49 40 123456") + .given("emailAddress", "michelle.matthieu@example.org") + .doRun() + .keep(); + } + + @Test + @Order(1020) + @Requires("Person: Test AG") + @Produces("Representative: Tracy Trust for Test AG") + void shouldAddRepresentativeToPartner() { + new AddRepresentativeToPartner(scenarioTest) + .given("partnerPersonTradeName", "Test AG") + .given("representativeFamilyName", "Trust") + .given("representativeGivenName", "Tracy") + .given( + "representativePostalAddress", """ + "name": "Michelle Matthieu", + "street": "An der Alster 100", + "zipcode": "20000", + "city": "Hamburg", + "country": "Germany" + """) + .given("representativePhoneNumber", "+49 40 123456") + .given("representativeEMailAddress", "tracy.trust@example.org") + .doRun() + .keep(); + } + + @Test + @Order(1030) + @Requires("Person: Test AG") + @Produces("Operations-Contact: Dennis Krause for Test AG") + void shouldAddOperationsContactToPartner() { + new AddOperationsContactToPartner(scenarioTest) + .given("partnerPersonTradeName", "Test AG") + .given("operationsContactFamilyName", "Krause") + .given("operationsContactGivenName", "Dennis") + .given("operationsContactPhoneNumber", "+49 9932 587741") + .given("operationsContactEMailAddress", "dennis.krause@example.org") + .doRun() + .keep(); + } + + @Test + @Order(1039) + @Requires("Operations-Contact: Dennis Krause for Test AG") + void shouldRemoveOperationsContactFromPartner() { + new RemoveOperationsContactFromPartner(scenarioTest) + .given("operationsContactPerson", "Dennis Krause") + .doRun(); + } + + @Test + @Order(1090) + void shouldDeletePartner() { + new DeletePartner(scenarioTest) + .given("partnerNumber", "P-31020") + .doRun(); + } } - @Test - @Order(1011) - @Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", - implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" }) - void shouldCreateNaturalPersonAsPartner() { - new CreatePartner(this) - .given("partnerNumber", "P-31011") - .given("personType", "NATURAL_PERSON") - .given("givenName", "Michelle") - .given("familyName", "Matthieu") - .given("contactCaption", "Michelle Matthieu") - .given("postalAddress", """ - "name": "Michelle Matthieu", - "street": "An der Wandse 34", - "zipcode": "22123", - "city": "Hamburg", - "country": "Germany" - """) - .given("officePhoneNumber", "+49 40 123456") - .given("emailAddress", "michelle.matthieu@example.org") - .doRun() - .keep(); + @Nested + @Order(11) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class PartnerContactScenarios { + + @Test + @Order(1100) + @Requires("Partner: P-31011 - Michelle Matthieu") + void shouldAmendContactData() { + new AmendContactData(scenarioTest) + .given("partnerName", "Matthieu") + .given("newEmailAddress", "michelle@matthieu.example.org") + .doRun(); + } + + @Test + @Order(1101) + @Requires("Partner: P-31011 - Michelle Matthieu") + void shouldAddPhoneNumberToContactData() { + new AddPhoneNumberToContactData(scenarioTest) + .given("partnerName", "Matthieu") + .given("phoneNumberKeyToAdd", "mobile") + .given("phoneNumberToAdd", "+49 152 1234567") + .doRun(); + } + + @Test + @Order(1102) + @Requires("Partner: P-31011 - Michelle Matthieu") + void shouldRemovePhoneNumberFromContactData() { + new RemovePhoneNumberFromContactData(scenarioTest) + .given("partnerName", "Matthieu") + .given("phoneNumberKeyToRemove", "office") + .doRun(); + } + + @Test + @Order(1103) + @Requires("Partner: P-31010 - Test AG") + void shouldReplaceContactData() { + new ReplaceContactData(scenarioTest) + .given("partnerName", "Test AG") + .given("newContactCaption", "Test AG - China") + .given("newPostalAddress", """ + "firm": "Test AG", + "name": "Fi Zhong-Kha", + "building": "Thi Chi Koh Building", + "street": "No.2 Commercial Second Street", + "district": "Niushan Wei Wu", + "city": "Dongguan City", + "province": "Guangdong Province", + "country": "China" + """) + .given("newOfficePhoneNumber", "++15 999 654321") + .given("newEmailAddress", "norden@test-ag.example.org") + .doRun(); + } } - @Test - @Order(1020) - @Requires("Person: Test AG") - @Produces("Representative: Tracy Trust for Test AG") - void shouldAddRepresentativeToPartner() { - new AddRepresentativeToPartner(this) - .given("partnerPersonTradeName", "Test AG") - .given("representativeFamilyName", "Trust") - .given("representativeGivenName", "Tracy") - .given("representativePostalAddress", """ - "name": "Michelle Matthieu", - "street": "An der Alster 100", - "zipcode": "20000", - "city": "Hamburg", - "country": "Germany" - """) - .given("representativePhoneNumber", "+49 40 123456") - .given("representativeEMailAddress", "tracy.trust@example.org") - .doRun() - .keep(); + @Nested + @Order(12) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class PartnerPersonScenarios { + + @Test + @Order(1201) + @Requires("Partner: P-31011 - Michelle Matthieu") + void shouldUpdatePersonData() { + new ShouldUpdatePersonData(scenarioTest) + .given("oldFamilyName", "Matthieu") + .given("newFamilyName", "Matthieu-Zhang") + .doRun(); + } } - @Test - @Order(1030) - @Requires("Person: Test AG") - @Produces("Operations-Contact: Dennis Krause for Test AG") - void shouldAddOperationsContactToPartner() { - new AddOperationsContactToPartner(this) - .given("partnerPersonTradeName", "Test AG") - .given("operationsContactFamilyName", "Krause") - .given("operationsContactGivenName", "Dennis") - .given("operationsContactPhoneNumber", "+49 9932 587741") - .given("operationsContactEMailAddress", "dennis.krause@example.org") - .doRun() - .keep(); + @Nested + @Order(20) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class DebitorScenarios { + @Test + @Order(2010) + @Requires("Partner: P-31010 - Test AG") + @Produces("Debitor: D-3101000 - Test AG - main debitor") + void shouldCreateSelfDebitorForPartner() { + new CreateSelfDebitorForPartner(scenarioTest) + .given("partnerPersonTradeName", "Test AG") + .given("billingContactCaption", "Test AG - billing department") + .given("billingContactEmailAddress", "billing@test-ag.example.org") + .given("debitorNumberSuffix", "00") // TODO.impl: could be assigned automatically, but is not yet + .given("billable", true) + .given("vatId", "VAT123456") + .given("vatCountryCode", "DE") + .given("vatBusiness", true) + .given("vatReverseCharge", false) + .given("defaultPrefix", "tst") + .doRun() + .keep(); + } + + @Test + @Order(2011) + @Requires("Person: Test AG") + @Produces("Debitor: D-3101001 - Test AG - main debitor") + void shouldCreateExternalDebitorForPartner() { + new CreateExternalDebitorForPartner(scenarioTest) + .given("partnerPersonTradeName", "Test AG") + .given("billingContactCaption", "Billing GmbH - billing department") + .given("billingContactEmailAddress", "billing@test-ag.example.org") + .given("debitorNumberSuffix", "01") + .given("billable", true) + .given("vatId", "VAT123456") + .given("vatCountryCode", "DE") + .given("vatBusiness", true) + .given("vatReverseCharge", false) + .given("defaultPrefix", "tsx") + .doRun() + .keep(); + } + + @Test + @Order(2020) + @Requires("Person: Test AG") + @Produces(explicitly = "Debitor: D-3101000 - Test AG - delete debitor", permanent = false) + void shouldDeleteDebitor() { + new DeleteDebitor(scenarioTest) + .given("partnerNumber", "P-31020") + .given("debitorSuffix", "02") + .doRun(); + } + + @Test + @Order(2020) + @Requires("Debitor: D-3101000 - Test AG - main debitor") + @Disabled("see TODO.spec in DontDeleteDefaultDebitor") + void shouldNotDeleteDefaultDebitor() { + new DontDeleteDefaultDebitor(scenarioTest) + .given("partnerNumber", "P-31010") + .given("debitorSuffix", "00") + .doRun(); + } } - @Test - @Order(1039) - @Requires("Operations-Contact: Dennis Krause for Test AG") - void shouldRemoveOperationsContactFromPartner() { - new RemoveOperationsContactFromPartner(this) - .given("operationsContactPerson", "Dennis Krause") - .doRun(); + @Nested + @Order(31) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class SepaMandateScenarios { + + @Test + @Order(3100) + @Requires("Debitor: D-3101000 - Test AG - main debitor") + @Produces("SEPA-Mandate: Test AG") + void shouldCreateSepaMandateForDebitor() { + new CreateSepaMandateForDebitor(scenarioTest) + // existing debitor + .given("debitorNumber", "D-3101000") + + // new sepa-mandate + .given("mandateReference", "Test AG - main debitor") + .given("mandateAgreement", "2022-10-12") + .given("mandateValidFrom", "2024-10-15") + + // new bank-account + .given("bankAccountHolder", "Test AG - debit bank account") + .given("bankAccountIBAN", "DE02701500000000594937") + .given("bankAccountBIC", "SSKMDEMM") + .doRun() + .keep(); + } + + @Test + @Order(3108) + @Requires("SEPA-Mandate: Test AG") + void shouldInvalidateSepaMandateForDebitor() { + new InvalidateSepaMandateForDebitor(scenarioTest) + .given("bankAccountIBAN", "DE02701500000000594937") + .given("mandateValidUntil", "2025-09-30") + .doRun(); + } + + @Test + @Order(3109) + @Requires("SEPA-Mandate: Test AG") + void shouldFinallyDeleteSepaMandateForDebitor() { + new FinallyDeleteSepaMandateForDebitor(scenarioTest) + .given("bankAccountIBAN", "DE02701500000000594937") + .doRun(); + } } - @Test - @Order(1090) - void shouldDeletePartner() { - new DeletePartner(this) - .given("partnerNumber", "P-31020") - .doRun(); + @Nested + @Order(40) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class MembershipScenarios { + + @Test + @Order(4000) + @Requires("Partner: P-31010 - Test AG") + @Produces("Membership: M-3101000 - Test AG") + void shouldCreateMembershipForPartner() { + new CreateMembership(scenarioTest) + .given("partnerName", "Test AG") + .given("validFrom", "2024-10-15") + .given("newStatus", "ACTIVE") + .given("membershipFeeBillable", "true") + .doRun() + .keep(); + } + + @Test + @Order(4090) + @Requires("Membership: M-3101000 - Test AG") + void shouldCancelMembershipOfPartner() { + new CancelMembership(scenarioTest) + .given("memberNumber", "M-3101000") + .given("validTo", "2025-12-30") + .given("newStatus", "CANCELLED") + .doRun(); + } } - @Test - @Order(1100) - @Requires("Partner: P-31011 - Michelle Matthieu") - void shouldAmendContactData() { - new AmendContactData(this) - .given("partnerName", "Matthieu") - .given("newEmailAddress", "michelle@matthieu.example.org") - .doRun(); + @Nested + @Order(42) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class CoopSharesScenarios { + + @Test + @Order(4201) + @Requires("Membership: M-3101000 - Test AG") + @Produces("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") + void shouldSubscribeCoopShares() { + new CreateCoopSharesSubscriptionTransaction(scenarioTest) + .given("memberNumber", "M-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(scenarioTest) + .given("memberNumber", "M-3101000") + .given("comment", "reverting some incorrect transaction") + .given("dateOfIncorrectTransaction", "2024-02-15") + .doRun(); + } + + @Test + @Order(4202) + @Requires("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") + @Produces("Coop-Shares M-3101000 - Test AG - CANCELLATION Transaction") + void shouldCancelCoopSharesSubscription() { + new CreateCoopSharesCancellationTransaction(scenarioTest) + .given("memberNumber", "M-3101000") + .given("reference", "cancel 2024-01-15") + .given("sharesToCancel", 8) + .given("comment", "Cancelling 8 Shares") + .given("transactionDate", "2024-02-15") + .doRun(); + } } - @Test - @Order(1101) - @Requires("Partner: P-31011 - Michelle Matthieu") - void shouldAddPhoneNumberToContactData() { - new AddPhoneNumberToContactData(this) - .given("partnerName", "Matthieu") - .given("phoneNumberKeyToAdd", "mobile") - .given("phoneNumberToAdd", "+49 152 1234567") - .doRun(); + @Nested + @Order(43) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class CoopAssetsScenarios { + + @Test + @Order(4301) + @Requires("Membership: M-3101000 - Test AG") + @Produces("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") + void shouldSubscribeCoopAssets() { + new CreateCoopAssetsDepositTransaction(scenarioTest) + .given("memberNumber", "M-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 CreateCoopAssetsRevertSimpleTransaction(scenarioTest) + .given("memberNumber", "M-3101000") + .given("comment", "reverting some incorrect transaction") + .given("dateOfIncorrectTransaction", "2024-02-15") + .doRun(); + } + + @Test + @Order(4303) + @Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") + @Produces("Coop-Assets M-3101000 - Test AG - DISBURSAL Transaction") + void shouldDisburseCoopAssets() { + new CreateCoopAssetsDisbursalTransaction(scenarioTest) + .given("memberNumber", "M-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(4304) + @Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") + @Produces(explicitly = "Coop-Assets M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership M-4303000") + void shouldTransferCoopAssets() { + new CreateCoopAssetsTransferTransaction(scenarioTest) + .given("transferringMemberNumber", "M-3101000") + .given("adoptingMemberNumber", "M-4303000") + .given("reference", "transfer 2024-12-31") + .given("valueToTransfer", 2 * 64) + .given("comment", "transfer assets from M-3101000 to M-4303000") + .given("transactionDate", "2024-12-31") + .doRun(); + } + + @Test + @Order(4305) + @Requires("Membership M-4303000") + void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() { + new CreateCoopAssetsRevertTransferTransaction(scenarioTest) + .given("transferringMemberNumber", "M-3101000") + .given("adoptingMemberNumber", "M-4303000") + .given("transferredValue", 2 * 64) + .given("comment", "reverting some incorrect transfer transaction") + .given("dateOfIncorrectTransaction", "2024-02-15") + .doRun(); + } } - @Test - @Order(1102) - @Requires("Partner: P-31011 - Michelle Matthieu") - void shouldRemovePhoneNumberFromContactData() { - new RemovePhoneNumberFromContactData(this) - .given("partnerName", "Matthieu") - .given("phoneNumberKeyToRemove", "office") - .doRun(); - } + @Nested + @Order(50) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class SubscriptionScenarios { - @Test - @Order(1103) - @Requires("Partner: P-31010 - Test AG") - void shouldReplaceContactData() { - new ReplaceContactData(this) - .given("partnerName", "Test AG") - .given("newContactCaption", "Test AG - China") - .given("newPostalAddress", """ - "firm": "Test AG", - "name": "Fi Zhong-Kha", - "building": "Thi Chi Koh Building", - "street": "No.2 Commercial Second Street", - "district": "Niushan Wei Wu", - "city": "Dongguan City", - "province": "Guangdong Province", - "country": "China" - """) - .given("newOfficePhoneNumber", "++15 999 654321") - .given("newEmailAddress", "norden@test-ag.example.org") - .doRun(); - } + @Test + @Order(5000) + @Requires("Person: Test AG") + @Produces("Subscription: Michael Miller to operations-announce") + void shouldSubscribeNewPersonAndContactToMailinglist() { + new SubscribeToMailinglist(scenarioTest) + // TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists? + .given("partnerPersonTradeName", "Test AG") + .given("subscriberFamilyName", "Miller") + .given("subscriberGivenName", "Michael") + .given("subscriberEMailAddress", "michael.miller@example.org") + .given("mailingList", "operations-announce") + .doRun() + .keep(); + } - @Test - @Order(1201) - @Requires("Partner: P-31011 - Michelle Matthieu") - void shouldUpdatePersonData() { - new ShouldUpdatePersonData(this) - .given("oldFamilyName", "Matthieu") - .given("newFamilyName", "Matthieu-Zhang") - .doRun(); - } - - @Test - @Order(2010) - @Requires("Partner: P-31010 - Test AG") - @Produces("Debitor: D-3101000 - Test AG - main debitor") - void shouldCreateSelfDebitorForPartner() { - new CreateSelfDebitorForPartner(this) - .given("partnerPersonTradeName", "Test AG") - .given("billingContactCaption", "Test AG - billing department") - .given("billingContactEmailAddress", "billing@test-ag.example.org") - .given("debitorNumberSuffix", "00") // TODO.impl: could be assigned automatically, but is not yet - .given("billable", true) - .given("vatId", "VAT123456") - .given("vatCountryCode", "DE") - .given("vatBusiness", true) - .given("vatReverseCharge", false) - .given("defaultPrefix", "tst") - .doRun() - .keep(); - } - - @Test - @Order(2011) - @Requires("Person: Test AG") - @Produces("Debitor: D-3101001 - Test AG - main debitor") - void shouldCreateExternalDebitorForPartner() { - new CreateExternalDebitorForPartner(this) - .given("partnerPersonTradeName", "Test AG") - .given("billingContactCaption", "Billing GmbH - billing department") - .given("billingContactEmailAddress", "billing@test-ag.example.org") - .given("debitorNumberSuffix", "01") - .given("billable", true) - .given("vatId", "VAT123456") - .given("vatCountryCode", "DE") - .given("vatBusiness", true) - .given("vatReverseCharge", false) - .given("defaultPrefix", "tsx") - .doRun() - .keep(); - } - - @Test - @Order(2020) - @Requires("Person: Test AG") - @Produces(explicitly = "Debitor: D-3101000 - Test AG - delete debitor", permanent = false) - void shouldDeleteDebitor() { - new DeleteDebitor(this) - .given("partnerNumber", "P-31020") - .given("debitorSuffix", "02") - .doRun(); - } - - @Test - @Order(2020) - @Requires("Debitor: D-3101000 - Test AG - main debitor") - @Disabled("see TODO.spec in DontDeleteDefaultDebitor") - void shouldNotDeleteDefaultDebitor() { - new DontDeleteDefaultDebitor(this) - .given("partnerNumber", "P-31010") - .given("debitorSuffix", "00") - .doRun(); - } - - @Test - @Order(3100) - @Requires("Debitor: D-3101000 - Test AG - main debitor") - @Produces("SEPA-Mandate: Test AG") - void shouldCreateSepaMandateForDebitor() { - new CreateSepaMandateForDebitor(this) - // existing debitor - .given("debitorNumber", "D-3101000") - - // new sepa-mandate - .given("mandateReference", "Test AG - main debitor") - .given("mandateAgreement", "2022-10-12") - .given("mandateValidFrom", "2024-10-15") - - // new bank-account - .given("bankAccountHolder", "Test AG - debit bank account") - .given("bankAccountIBAN", "DE02701500000000594937") - .given("bankAccountBIC", "SSKMDEMM") - .doRun() - .keep(); - } - - @Test - @Order(3108) - @Requires("SEPA-Mandate: Test AG") - void shouldInvalidateSepaMandateForDebitor() { - new InvalidateSepaMandateForDebitor(this) - .given("bankAccountIBAN", "DE02701500000000594937") - .given("mandateValidUntil", "2025-09-30") - .doRun(); - } - - @Test - @Order(3109) - @Requires("SEPA-Mandate: Test AG") - void shouldFinallyDeleteSepaMandateForDebitor() { - new FinallyDeleteSepaMandateForDebitor(this) - .given("bankAccountIBAN", "DE02701500000000594937") - .doRun(); - } - - @Test - @Order(4000) - @Requires("Partner: P-31010 - Test AG") - @Produces("Membership: M-3101000 - Test AG") - void shouldCreateMembershipForPartner() { - new CreateMembership(this) - .given("partnerName", "Test AG") - .given("validFrom", "2024-10-15") - .given("newStatus", "ACTIVE") - .given("membershipFeeBillable", "true") - .doRun() - .keep(); - } - - @Test - @Order(4201) - @Requires("Membership: M-3101000 - Test AG") - @Produces("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") - void shouldSubscribeCoopShares() { - new CreateCoopSharesSubscriptionTransaction(this) - .given("memberNumber", "M-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", "M-3101000") - .given("comment", "reverting some incorrect transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); - } - - @Test - @Order(4202) - @Requires("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") - @Produces("Coop-Shares M-3101000 - Test AG - CANCELLATION Transaction") - void shouldCancelCoopSharesSubscription() { - new CreateCoopSharesCancellationTransaction(this) - .given("memberNumber", "M-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 M-3101000 - Test AG - DEPOSIT Transaction") - void shouldSubscribeCoopAssets() { - new CreateCoopAssetsDepositTransaction(this) - .given("memberNumber", "M-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 CreateCoopAssetsRevertSimpleTransaction(this) - .given("memberNumber", "M-3101000") - .given("comment", "reverting some incorrect transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); - } - - @Test - @Order(4303) - @Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") - @Produces("Coop-Assets M-3101000 - Test AG - DISBURSAL Transaction") - void shouldDisburseCoopAssets() { - new CreateCoopAssetsDisbursalTransaction(this) - .given("memberNumber", "M-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(4304) - @Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") - @Produces(explicitly = "Coop-Assets M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership M-4303000") - void shouldTransferCoopAssets() { - new CreateCoopAssetsTransferTransaction(this) - .given("transferringMemberNumber", "M-3101000") - .given("adoptingMemberNumber", "M-4303000") - .given("reference", "transfer 2024-12-31") - .given("valueToTransfer", 2 * 64) - .given("comment", "transfer assets from M-3101000 to M-4303000") - .given("transactionDate", "2024-12-31") - .doRun(); - } - - @Test - @Order(4305) - @Requires("Membership M-4303000") - void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() { - new CreateCoopAssetsRevertTransferTransaction(this) - .given("transferringMemberNumber", "M-3101000") - .given("adoptingMemberNumber", "M-4303000") - .given("transferredValue", 2*64) - .given("comment", "reverting some incorrect transfer transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); - } - - @Test - @Order(4900) - @Requires("Membership: M-3101000 - Test AG") - void shouldCancelMembershipOfPartner() { - new CancelMembership(this) - .given("memberNumber", "M-3101000") - .given("validTo", "2025-12-30") - .given("newStatus", "CANCELLED") - .doRun(); - } - - @Test - @Order(5000) - @Requires("Person: Test AG") - @Produces("Subscription: Michael Miller to operations-announce") - void shouldSubscribeNewPersonAndContactToMailinglist() { - new SubscribeToMailinglist(this) - // TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists? - .given("partnerPersonTradeName", "Test AG") - .given("subscriberFamilyName", "Miller") - .given("subscriberGivenName", "Michael") - .given("subscriberEMailAddress", "michael.miller@example.org") - .given("mailingList", "operations-announce") - .doRun() - .keep(); - } - - @Test - @Order(5001) - @Requires("Subscription: Michael Miller to operations-announce") - void shouldUnsubscribeNewPersonAndContactToMailinglist() { - new UnsubscribeFromMailinglist(this) - .given("mailingList", "operations-announce") - .given("subscriberEMailAddress", "michael.miller@example.org") - .doRun(); + @Test + @Order(5001) + @Requires("Subscription: Michael Miller to operations-announce") + void shouldUnsubscribeNewPersonAndContactToMailinglist() { + new UnsubscribeFromMailinglist(scenarioTest) + .given("mailingList", "operations-announce") + .given("subscriberEMailAddress", "michael.miller@example.org") + .doRun(); + } } } diff --git a/src/test/java/net/hostsharing/hsadminng/test/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/test/scenarios/ScenarioTest.java index d996d12b..6944e245 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/test/scenarios/ScenarioTest.java @@ -3,11 +3,13 @@ package net.hostsharing.hsadminng.test.scenarios; import lombok.SneakyThrows; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; -import net.hostsharing.hsadminng.test.scenarios.TemplateResolver.Resolver; import net.hostsharing.hsadminng.lambda.Reducer; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; +import net.hostsharing.hsadminng.test.scenarios.TemplateResolver.Resolver; import org.apache.commons.collections4.SetUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; @@ -15,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.server.LocalServerPort; import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.HashMap; @@ -25,8 +28,10 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.Arrays.asList; +import static java.util.Arrays.stream; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.test.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static org.assertj.core.api.Assertions.assertThat; @@ -35,6 +40,8 @@ public abstract class ScenarioTest extends ContextBasedTest { final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented + protected ScenarioTest scenarioTest = this; + record Alias>(Class useCase, UUID uuid) { @Override @@ -43,6 +50,7 @@ public abstract class ScenarioTest extends ContextBasedTest { } } + private final static Map> aliases = new HashMap<>(); private final static Map properties = new HashMap<>(); @@ -94,37 +102,67 @@ public abstract class ScenarioTest extends ContextBasedTest { @SneakyThrows private void callRequiredProducers(final Method currentTestMethod) { - final var testMethodRequired = Optional.of(currentTestMethod) + final var testMethodRequires = Optional.of(currentTestMethod) .map(m -> m.getAnnotation(Requires.class)) .map(Requires::value) .orElse(null); - if (testMethodRequired != null) { - for (Method potentialProducerMethod : getClass().getDeclaredMethods()) { + if (testMethodRequires != null) { + for (Method potentialProducerMethod : getPotentialProducerMethods()) { final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class); - if (producesAnnot != null) { - final var testMethodProduces = allOf( - producesAnnot.value(), - producesAnnot.explicitly(), - producesAnnot.implicitly()); - // @formatter:off - if ( // that method can produce something required - testMethodProduces.contains(testMethodRequired) && + final var testMethodProduces = allOf( + producesAnnot.value(), + producesAnnot.explicitly(), + producesAnnot.implicitly()); + // @formatter:off + if ( // that method can produce something required + testMethodProduces.contains(testMethodRequires) && - // and it does not produce anything we already have (would cause errors) - SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty() - ) { - assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod); + // and it does not produce anything we already have (would cause errors) + SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty() + ) { + assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod); - // then we recursively produce the pre-requisites of the producer method - callRequiredProducers(potentialProducerMethod); + // then we recursively produce the pre-requisites of the producer method + callRequiredProducers(potentialProducerMethod); - // and finally we call the producer method - potentialProducerMethod.setAccessible(true); - potentialProducerMethod.invoke(this); - } - // @formatter:on + // and finally we call the producer method + invokeProducerMethod(this, potentialProducerMethod); + + assertThat(knowVariables().containsKey(testMethodRequires)) + .as("@Producer(\"" + testMethodRequires + "\") did not produce") + .isTrue(); } + // @formatter:on } + + assertThat(knowVariables().containsKey(testMethodRequires)) + .as("no @Producer for @Required(\"" + testMethodRequires + "\") found") + .isTrue(); + } + } + + private Method @NotNull [] getPotentialProducerMethods() { + final var methodsDeclaredInOuterTestClass = stream(getClass().getDeclaredMethods()) + .filter(m -> m.getAnnotation(Produces.class) != null) + .toArray(Method[]::new); + final var methodsDeclaredInInnerTestClasses = stream(getClass().getDeclaredClasses()) + .map(Class::getDeclaredMethods).flatMap(Stream::of) + .filter(m -> m.getAnnotation(Produces.class) != null) + .toArray(Method[]::new); + return ArrayUtils.addAll(methodsDeclaredInOuterTestClass, methodsDeclaredInInnerTestClasses); + } + + @SneakyThrows + private void invokeProducerMethod(final ScenarioTest scenarioTest, final Method producerMethod) { + producerMethod.setAccessible(true); + if (producerMethod.getDeclaringClass() == scenarioTest.getClass()) { + producerMethod.invoke(this); + } else { + final var innerClassConstructor = producerMethod.getDeclaringClass() + .getDeclaredConstructor(scenarioTest.getClass()); + innerClassConstructor.setAccessible(true); + final var inner = innerClassConstructor.newInstance(this); + producerMethod.invoke(inner); } } @@ -146,7 +184,9 @@ public abstract class ScenarioTest extends ContextBasedTest { static UUID uuid(final String nameWithPlaceholders) { 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); + 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();