refactoring scenario-tests #126

Merged
hsh-michaelhoennig merged 7 commits from feature/refactoring-scenario-tests into master 2024-11-29 14:03:35 +01:00
3 changed files with 535 additions and 434 deletions
Showing only changes of commit b45639878e - Show all commits

View File

@ -880,17 +880,19 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction); 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) { if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) {
connectToRelatedRevertedAssetTx(assetTransaction); connectToRelatedRevertedAssetTx(legacyId, assetTransaction);
} }
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.TRANSFER) { 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 negativeValue = assetTransaction.getAssetValue().negate();
final var revertedAssetTx = coopAssets.values().stream().filter(a -> final var revertedAssetTx = coopAssets.values().stream().filter(a ->
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL && a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL &&
@ -903,7 +905,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
//revertedAssetTx.setAssetReversalTx(assetTransaction); //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 negativeValue = assetTransaction.getAssetValue().negate();
final var adoptionAssetTx = coopAssets.values().stream().filter(a -> final var adoptionAssetTx = coopAssets.values().stream().filter(a ->
a.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADOPTION && a.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADOPTION &&

View File

@ -35,11 +35,14 @@ import net.hostsharing.hsadminng.test.scenarios.Requires;
import net.hostsharing.hsadminng.test.scenarios.ScenarioTest; import net.hostsharing.hsadminng.test.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension; import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestClassOrder;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest; 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}" "hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}"
} }
) )
@DirtiesContext //@DirtiesContext
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestClassOrder(ClassOrderer.OrderAnnotation.class)
@ExtendWith(IgnoreOnFailureExtension.class) @ExtendWith(IgnoreOnFailureExtension.class)
class HsOfficeScenarioTests extends ScenarioTest { class HsOfficeScenarioTests extends ScenarioTest {
@Test @Nested
@Order(1010) @Order(10)
@Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = { "Person: Test AG", "Contact: Test AG - Hamburg" }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
void shouldCreateLegalPersonAsPartner() { class PartnerScenarios {
new CreatePartner(this)
.given("partnerNumber", "P-31010") @Test
.given("personType", "LEGAL_PERSON") @Order(1010)
.given("tradeName", "Test AG") @Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = { "Person: Test AG", "Contact: Test AG - Hamburg" })
.given("contactCaption", "Test AG - Hamburg") void shouldCreateLegalPersonAsPartner() {
.given("postalAddress", """ new CreatePartner(scenarioTest)
"firm": "Test AG", .given("partnerNumber", "P-31010")
"street": "Shanghai-Allee 1", .given("personType", "LEGAL_PERSON")
"zipcode": "20123", .given("tradeName", "Test AG")
"city": "Hamburg", .given("contactCaption", "Test AG - Hamburg")
"country": "Germany" .given(
""") "postalAddress", """
.given("officePhoneNumber", "+49 40 654321-0") "firm": "Test AG",
.given("emailAddress", "hamburg@test-ag.example.org") "street": "Shanghai-Allee 1",
.doRun() "zipcode": "20123",
.keep(); "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 @Nested
@Order(1011) @Order(11)
@Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" }) class PartnerContactScenarios {
void shouldCreateNaturalPersonAsPartner() {
new CreatePartner(this) @Test
.given("partnerNumber", "P-31011") @Order(1100)
.given("personType", "NATURAL_PERSON") @Requires("Partner: P-31011 - Michelle Matthieu")
.given("givenName", "Michelle") void shouldAmendContactData() {
.given("familyName", "Matthieu") new AmendContactData(scenarioTest)
.given("contactCaption", "Michelle Matthieu") .given("partnerName", "Matthieu")
.given("postalAddress", """ .given("newEmailAddress", "michelle@matthieu.example.org")
"name": "Michelle Matthieu", .doRun();
"street": "An der Wandse 34", }
"zipcode": "22123",
"city": "Hamburg", @Test
"country": "Germany" @Order(1101)
""") @Requires("Partner: P-31011 - Michelle Matthieu")
.given("officePhoneNumber", "+49 40 123456") void shouldAddPhoneNumberToContactData() {
.given("emailAddress", "michelle.matthieu@example.org") new AddPhoneNumberToContactData(scenarioTest)
.doRun() .given("partnerName", "Matthieu")
.keep(); .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 @Nested
@Order(1020) @Order(12)
@Requires("Person: Test AG") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@Produces("Representative: Tracy Trust for Test AG") class PartnerPersonScenarios {
void shouldAddRepresentativeToPartner() {
new AddRepresentativeToPartner(this) @Test
.given("partnerPersonTradeName", "Test AG") @Order(1201)
.given("representativeFamilyName", "Trust") @Requires("Partner: P-31011 - Michelle Matthieu")
.given("representativeGivenName", "Tracy") void shouldUpdatePersonData() {
.given("representativePostalAddress", """ new ShouldUpdatePersonData(scenarioTest)
"name": "Michelle Matthieu", .given("oldFamilyName", "Matthieu")
"street": "An der Alster 100", .given("newFamilyName", "Matthieu-Zhang")
"zipcode": "20000", .doRun();
"city": "Hamburg", }
"country": "Germany"
""")
.given("representativePhoneNumber", "+49 40 123456")
.given("representativeEMailAddress", "tracy.trust@example.org")
.doRun()
.keep();
} }
@Test @Nested
@Order(1030) @Order(20)
@Requires("Person: Test AG") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@Produces("Operations-Contact: Dennis Krause for Test AG") class DebitorScenarios {
void shouldAddOperationsContactToPartner() { @Test
new AddOperationsContactToPartner(this) @Order(2010)
.given("partnerPersonTradeName", "Test AG") @Requires("Partner: P-31010 - Test AG")
.given("operationsContactFamilyName", "Krause") @Produces("Debitor: D-3101000 - Test AG - main debitor")
.given("operationsContactGivenName", "Dennis") void shouldCreateSelfDebitorForPartner() {
.given("operationsContactPhoneNumber", "+49 9932 587741") new CreateSelfDebitorForPartner(scenarioTest)
.given("operationsContactEMailAddress", "dennis.krause@example.org") .given("partnerPersonTradeName", "Test AG")
.doRun() .given("billingContactCaption", "Test AG - billing department")
.keep(); .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 @Nested
@Order(1039) @Order(31)
@Requires("Operations-Contact: Dennis Krause for Test AG") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
void shouldRemoveOperationsContactFromPartner() { class SepaMandateScenarios {
new RemoveOperationsContactFromPartner(this)
.given("operationsContactPerson", "Dennis Krause") @Test
.doRun(); @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 @Nested
@Order(1090) @Order(40)
void shouldDeletePartner() { @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
new DeletePartner(this) class MembershipScenarios {
.given("partnerNumber", "P-31020")
.doRun(); @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 @Nested
@Order(1100) @Order(42)
@Requires("Partner: P-31011 - Michelle Matthieu") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
void shouldAmendContactData() { class CoopSharesScenarios {
new AmendContactData(this)
.given("partnerName", "Matthieu") @Test
.given("newEmailAddress", "michelle@matthieu.example.org") @Order(4201)
.doRun(); @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 @Nested
@Order(1101) @Order(43)
@Requires("Partner: P-31011 - Michelle Matthieu") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
void shouldAddPhoneNumberToContactData() { class CoopAssetsScenarios {
new AddPhoneNumberToContactData(this)
.given("partnerName", "Matthieu") @Test
.given("phoneNumberKeyToAdd", "mobile") @Order(4301)
.given("phoneNumberToAdd", "+49 152 1234567") @Requires("Membership: M-3101000 - Test AG")
.doRun(); @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 @Nested
@Order(1102) @Order(50)
@Requires("Partner: P-31011 - Michelle Matthieu") @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
void shouldRemovePhoneNumberFromContactData() { class SubscriptionScenarios {
new RemovePhoneNumberFromContactData(this)
.given("partnerName", "Matthieu")
.given("phoneNumberKeyToRemove", "office")
.doRun();
}
@Test @Test
@Order(1103) @Order(5000)
@Requires("Partner: P-31010 - Test AG") @Requires("Person: Test AG")
void shouldReplaceContactData() { @Produces("Subscription: Michael Miller to operations-announce")
new ReplaceContactData(this) void shouldSubscribeNewPersonAndContactToMailinglist() {
.given("partnerName", "Test AG") new SubscribeToMailinglist(scenarioTest)
.given("newContactCaption", "Test AG - China") // TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists?
.given("newPostalAddress", """ .given("partnerPersonTradeName", "Test AG")
"firm": "Test AG", .given("subscriberFamilyName", "Miller")
"name": "Fi Zhong-Kha", .given("subscriberGivenName", "Michael")
"building": "Thi Chi Koh Building", .given("subscriberEMailAddress", "michael.miller@example.org")
"street": "No.2 Commercial Second Street", .given("mailingList", "operations-announce")
"district": "Niushan Wei Wu", .doRun()
"city": "Dongguan City", .keep();
"province": "Guangdong Province", }
"country": "China"
""")
.given("newOfficePhoneNumber", "++15 999 654321")
.given("newEmailAddress", "norden@test-ag.example.org")
.doRun();
}
@Test @Test
@Order(1201) @Order(5001)
@Requires("Partner: P-31011 - Michelle Matthieu") @Requires("Subscription: Michael Miller to operations-announce")
void shouldUpdatePersonData() { void shouldUnsubscribeNewPersonAndContactToMailinglist() {
new ShouldUpdatePersonData(this) new UnsubscribeFromMailinglist(scenarioTest)
.given("oldFamilyName", "Matthieu") .given("mailingList", "operations-announce")
.given("newFamilyName", "Matthieu-Zhang") .given("subscriberEMailAddress", "michael.miller@example.org")
.doRun(); .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();
} }
} }

View File

@ -3,11 +3,13 @@ package net.hostsharing.hsadminng.test.scenarios;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; 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.lambda.Reducer;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; 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.collections4.SetUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo; 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.springframework.boot.test.web.server.LocalServerPort;
import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils; import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashMap; import java.util.HashMap;
@ -25,8 +28,10 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.test.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.test.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
import static org.assertj.core.api.Assertions.assertThat; 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 final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented
protected ScenarioTest scenarioTest = this;
record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) { record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) {
@Override @Override
@ -43,6 +50,7 @@ public abstract class ScenarioTest extends ContextBasedTest {
} }
} }
private final static Map<String, Alias<?>> aliases = new HashMap<>(); 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<>();
@ -94,37 +102,67 @@ public abstract class ScenarioTest extends ContextBasedTest {
@SneakyThrows @SneakyThrows
private void callRequiredProducers(final Method currentTestMethod) { 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(m -> m.getAnnotation(Requires.class))
.map(Requires::value) .map(Requires::value)
.orElse(null); .orElse(null);
if (testMethodRequired != null) { if (testMethodRequires != null) {
for (Method potentialProducerMethod : getClass().getDeclaredMethods()) { for (Method potentialProducerMethod : getPotentialProducerMethods()) {
final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class); final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class);
if (producesAnnot != null) { final var testMethodProduces = allOf(
final var testMethodProduces = allOf( producesAnnot.value(),
producesAnnot.value(), producesAnnot.explicitly(),
producesAnnot.explicitly(), producesAnnot.implicitly());
producesAnnot.implicitly()); // @formatter:off
// @formatter:off if ( // that method can produce something required
if ( // that method can produce something required testMethodProduces.contains(testMethodRequires) &&
testMethodProduces.contains(testMethodRequired) &&
// and it does not produce anything we already have (would cause errors) // and it does not produce anything we already have (would cause errors)
SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty() SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty()
) { ) {
assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod); assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod);
// then we recursively produce the pre-requisites of the producer method // then we recursively produce the pre-requisites of the producer method
callRequiredProducers(potentialProducerMethod); callRequiredProducers(potentialProducerMethod);
// and finally we call the producer method // and finally we call the producer method
potentialProducerMethod.setAccessible(true); invokeProducerMethod(this, potentialProducerMethod);
potentialProducerMethod.invoke(this);
} assertThat(knowVariables().containsKey(testMethodRequires))
// @formatter:on .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) { static UUID uuid(final String nameWithPlaceholders) {
final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS); 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 [" + assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]" knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
).isNotNull(); ).isNotNull();