working hasGlobalAdminRole and prepare for Micrometer metrics with Spring Security #127

Merged
hsh-michaelhoennig merged 13 commits from feature/fix-hasGlobalAdminRole into master 2024-12-03 12:39:23 +01:00
48 changed files with 862 additions and 701 deletions
Showing only changes of commit e3cb2a2f1b - Show all commits

View File

@ -68,6 +68,7 @@ public class ArchitectureTest {
"..hs.hosting.asset", "..hs.hosting.asset",
"..hs.hosting.asset.validators", "..hs.hosting.asset.validators",
"..hs.hosting.asset.factories", "..hs.hosting.asset.factories",
"..hs.scenarios",
"..errors", "..errors",
"..mapper", "..mapper",
"..ping", "..ping",
@ -160,9 +161,11 @@ public class ArchitectureTest {
.that().resideInAPackage("..hs.office.(*)..") .that().resideInAPackage("..hs.office.(*)..")
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage( .resideInAnyPackage(
"..hs.office.(*)..",
"..hs.office.(*)..", "..hs.office.(*)..",
"..hs.booking.(*)..", "..hs.booking.(*)..",
"..hs.hosting.(*)..", "..hs.hosting.(*)..",
"..hs.scenarios",
"..hs.migration", "..hs.migration",
"..rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest "..rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest
); );

View File

@ -880,7 +880,9 @@ 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(assetTransaction);
} }

View File

@ -30,14 +30,20 @@ import net.hostsharing.hsadminng.hs.office.scenarios.person.ShouldUpdatePersonDa
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.RemoveOperationsContactFromPartner; import net.hostsharing.hsadminng.hs.office.scenarios.subscription.RemoveOperationsContactFromPartner;
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.SubscribeToMailinglist; import net.hostsharing.hsadminng.hs.office.scenarios.subscription.SubscribeToMailinglist;
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.UnsubscribeFromMailinglist; import net.hostsharing.hsadminng.hs.office.scenarios.subscription.UnsubscribeFromMailinglist;
import net.hostsharing.hsadminng.hs.scenarios.Produces;
import net.hostsharing.hsadminng.hs.scenarios.Requires;
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import net.hostsharing.hsadminng.test.DisableSecurityConfig; import net.hostsharing.hsadminng.test.DisableSecurityConfig;
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;
@ -57,20 +63,26 @@ import org.springframework.test.context.ActiveProfiles;
) )
@ActiveProfiles("test") @ActiveProfiles("test")
@DirtiesContext @DirtiesContext
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestClassOrder(ClassOrderer.OrderAnnotation.class)
@ExtendWith(IgnoreOnFailureExtension.class) @ExtendWith(IgnoreOnFailureExtension.class)
class HsOfficeScenarioTests extends ScenarioTest { class HsOfficeScenarioTests extends ScenarioTest {
@Nested
@Order(10)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PartnerScenarios {
@Test @Test
@Order(1010) @Order(1010)
@Produces(explicitly = "Partner: P-31010 - 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() { void shouldCreateLegalPersonAsPartner() {
new CreatePartner(this) new CreatePartner(scenarioTest)
.given("partnerNumber", "P-31010") .given("partnerNumber", "P-31010")
.given("personType", "LEGAL_PERSON") .given("personType", "LEGAL_PERSON")
.given("tradeName", "Test AG") .given("tradeName", "Test AG")
.given("contactCaption", "Test AG - Hamburg") .given("contactCaption", "Test AG - Hamburg")
.given("postalAddress", """ .given(
"postalAddress", """
"firm": "Test AG", "firm": "Test AG",
"street": "Shanghai-Allee 1", "street": "Shanghai-Allee 1",
"zipcode": "20123", "zipcode": "20123",
@ -88,13 +100,14 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", @Produces(explicitly = "Partner: P-31011 - Michelle Matthieu",
implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" }) implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" })
void shouldCreateNaturalPersonAsPartner() { void shouldCreateNaturalPersonAsPartner() {
new CreatePartner(this) new CreatePartner(scenarioTest)
.given("partnerNumber", "P-31011") .given("partnerNumber", "P-31011")
.given("personType", "NATURAL_PERSON") .given("personType", "NATURAL_PERSON")
.given("givenName", "Michelle") .given("givenName", "Michelle")
.given("familyName", "Matthieu") .given("familyName", "Matthieu")
.given("contactCaption", "Michelle Matthieu") .given("contactCaption", "Michelle Matthieu")
.given("postalAddress", """ .given(
"postalAddress", """
"name": "Michelle Matthieu", "name": "Michelle Matthieu",
"street": "An der Wandse 34", "street": "An der Wandse 34",
"zipcode": "22123", "zipcode": "22123",
@ -112,11 +125,12 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Person: Test AG") @Requires("Person: Test AG")
@Produces("Representative: Tracy Trust for Test AG") @Produces("Representative: Tracy Trust for Test AG")
void shouldAddRepresentativeToPartner() { void shouldAddRepresentativeToPartner() {
new AddRepresentativeToPartner(this) new AddRepresentativeToPartner(scenarioTest)
.given("partnerPersonTradeName", "Test AG") .given("partnerPersonTradeName", "Test AG")
.given("representativeFamilyName", "Trust") .given("representativeFamilyName", "Trust")
.given("representativeGivenName", "Tracy") .given("representativeGivenName", "Tracy")
.given("representativePostalAddress", """ .given(
"representativePostalAddress", """
"name": "Michelle Matthieu", "name": "Michelle Matthieu",
"street": "An der Alster 100", "street": "An der Alster 100",
"zipcode": "20000", "zipcode": "20000",
@ -134,7 +148,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Person: Test AG") @Requires("Person: Test AG")
@Produces("Operations-Contact: Dennis Krause for Test AG") @Produces("Operations-Contact: Dennis Krause for Test AG")
void shouldAddOperationsContactToPartner() { void shouldAddOperationsContactToPartner() {
new AddOperationsContactToPartner(this) new AddOperationsContactToPartner(scenarioTest)
.given("partnerPersonTradeName", "Test AG") .given("partnerPersonTradeName", "Test AG")
.given("operationsContactFamilyName", "Krause") .given("operationsContactFamilyName", "Krause")
.given("operationsContactGivenName", "Dennis") .given("operationsContactGivenName", "Dennis")
@ -148,7 +162,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(1039) @Order(1039)
@Requires("Operations-Contact: Dennis Krause for Test AG") @Requires("Operations-Contact: Dennis Krause for Test AG")
void shouldRemoveOperationsContactFromPartner() { void shouldRemoveOperationsContactFromPartner() {
new RemoveOperationsContactFromPartner(this) new RemoveOperationsContactFromPartner(scenarioTest)
.given("operationsContactPerson", "Dennis Krause") .given("operationsContactPerson", "Dennis Krause")
.doRun(); .doRun();
} }
@ -156,16 +170,22 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test @Test
@Order(1090) @Order(1090)
void shouldDeletePartner() { void shouldDeletePartner() {
new DeletePartner(this) new DeletePartner(scenarioTest)
.given("partnerNumber", "P-31020") .given("partnerNumber", "P-31020")
.doRun(); .doRun();
} }
}
@Nested
@Order(11)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PartnerContactScenarios {
@Test @Test
@Order(1100) @Order(1100)
@Requires("Partner: P-31011 - Michelle Matthieu") @Requires("Partner: P-31011 - Michelle Matthieu")
void shouldAmendContactData() { void shouldAmendContactData() {
new AmendContactData(this) new AmendContactData(scenarioTest)
.given("partnerName", "Matthieu") .given("partnerName", "Matthieu")
.given("newEmailAddress", "michelle@matthieu.example.org") .given("newEmailAddress", "michelle@matthieu.example.org")
.doRun(); .doRun();
@ -175,7 +195,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(1101) @Order(1101)
@Requires("Partner: P-31011 - Michelle Matthieu") @Requires("Partner: P-31011 - Michelle Matthieu")
void shouldAddPhoneNumberToContactData() { void shouldAddPhoneNumberToContactData() {
new AddPhoneNumberToContactData(this) new AddPhoneNumberToContactData(scenarioTest)
.given("partnerName", "Matthieu") .given("partnerName", "Matthieu")
.given("phoneNumberKeyToAdd", "mobile") .given("phoneNumberKeyToAdd", "mobile")
.given("phoneNumberToAdd", "+49 152 1234567") .given("phoneNumberToAdd", "+49 152 1234567")
@ -186,7 +206,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(1102) @Order(1102)
@Requires("Partner: P-31011 - Michelle Matthieu") @Requires("Partner: P-31011 - Michelle Matthieu")
void shouldRemovePhoneNumberFromContactData() { void shouldRemovePhoneNumberFromContactData() {
new RemovePhoneNumberFromContactData(this) new RemovePhoneNumberFromContactData(scenarioTest)
.given("partnerName", "Matthieu") .given("partnerName", "Matthieu")
.given("phoneNumberKeyToRemove", "office") .given("phoneNumberKeyToRemove", "office")
.doRun(); .doRun();
@ -196,7 +216,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(1103) @Order(1103)
@Requires("Partner: P-31010 - Test AG") @Requires("Partner: P-31010 - Test AG")
void shouldReplaceContactData() { void shouldReplaceContactData() {
new ReplaceContactData(this) new ReplaceContactData(scenarioTest)
.given("partnerName", "Test AG") .given("partnerName", "Test AG")
.given("newContactCaption", "Test AG - China") .given("newContactCaption", "Test AG - China")
.given("newPostalAddress", """ .given("newPostalAddress", """
@ -213,23 +233,34 @@ class HsOfficeScenarioTests extends ScenarioTest {
.given("newEmailAddress", "norden@test-ag.example.org") .given("newEmailAddress", "norden@test-ag.example.org")
.doRun(); .doRun();
} }
}
@Nested
@Order(12)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PartnerPersonScenarios {
@Test @Test
@Order(1201) @Order(1201)
@Requires("Partner: P-31011 - Michelle Matthieu") @Requires("Partner: P-31011 - Michelle Matthieu")
void shouldUpdatePersonData() { void shouldUpdatePersonData() {
new ShouldUpdatePersonData(this) new ShouldUpdatePersonData(scenarioTest)
.given("oldFamilyName", "Matthieu") .given("oldFamilyName", "Matthieu")
.given("newFamilyName", "Matthieu-Zhang") .given("newFamilyName", "Matthieu-Zhang")
.doRun(); .doRun();
} }
}
@Nested
@Order(20)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class DebitorScenarios {
@Test @Test
@Order(2010) @Order(2010)
@Requires("Partner: P-31010 - Test AG") @Requires("Partner: P-31010 - Test AG")
@Produces("Debitor: D-3101000 - Test AG - main debitor") @Produces("Debitor: D-3101000 - Test AG - main debitor")
void shouldCreateSelfDebitorForPartner() { void shouldCreateSelfDebitorForPartner() {
new CreateSelfDebitorForPartner(this) new CreateSelfDebitorForPartner(scenarioTest)
.given("partnerPersonTradeName", "Test AG") .given("partnerPersonTradeName", "Test AG")
.given("billingContactCaption", "Test AG - billing department") .given("billingContactCaption", "Test AG - billing department")
.given("billingContactEmailAddress", "billing@test-ag.example.org") .given("billingContactEmailAddress", "billing@test-ag.example.org")
@ -249,7 +280,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Person: Test AG") @Requires("Person: Test AG")
@Produces("Debitor: D-3101001 - Test AG - main debitor") @Produces("Debitor: D-3101001 - Test AG - main debitor")
void shouldCreateExternalDebitorForPartner() { void shouldCreateExternalDebitorForPartner() {
new CreateExternalDebitorForPartner(this) new CreateExternalDebitorForPartner(scenarioTest)
.given("partnerPersonTradeName", "Test AG") .given("partnerPersonTradeName", "Test AG")
.given("billingContactCaption", "Billing GmbH - billing department") .given("billingContactCaption", "Billing GmbH - billing department")
.given("billingContactEmailAddress", "billing@test-ag.example.org") .given("billingContactEmailAddress", "billing@test-ag.example.org")
@ -269,7 +300,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Person: Test AG") @Requires("Person: Test AG")
@Produces(explicitly = "Debitor: D-3101000 - Test AG - delete debitor", permanent = false) @Produces(explicitly = "Debitor: D-3101000 - Test AG - delete debitor", permanent = false)
void shouldDeleteDebitor() { void shouldDeleteDebitor() {
new DeleteDebitor(this) new DeleteDebitor(scenarioTest)
.given("partnerNumber", "P-31020") .given("partnerNumber", "P-31020")
.given("debitorSuffix", "02") .given("debitorSuffix", "02")
.doRun(); .doRun();
@ -280,18 +311,24 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Debitor: D-3101000 - Test AG - main debitor") @Requires("Debitor: D-3101000 - Test AG - main debitor")
@Disabled("see TODO.spec in DontDeleteDefaultDebitor") @Disabled("see TODO.spec in DontDeleteDefaultDebitor")
void shouldNotDeleteDefaultDebitor() { void shouldNotDeleteDefaultDebitor() {
new DontDeleteDefaultDebitor(this) new DontDeleteDefaultDebitor(scenarioTest)
.given("partnerNumber", "P-31010") .given("partnerNumber", "P-31010")
.given("debitorSuffix", "00") .given("debitorSuffix", "00")
.doRun(); .doRun();
} }
}
@Nested
@Order(31)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SepaMandateScenarios {
@Test @Test
@Order(3100) @Order(3100)
@Requires("Debitor: D-3101000 - Test AG - main debitor") @Requires("Debitor: D-3101000 - Test AG - main debitor")
@Produces("SEPA-Mandate: Test AG") @Produces("SEPA-Mandate: Test AG")
void shouldCreateSepaMandateForDebitor() { void shouldCreateSepaMandateForDebitor() {
new CreateSepaMandateForDebitor(this) new CreateSepaMandateForDebitor(scenarioTest)
// existing debitor // existing debitor
.given("debitorNumber", "D-3101000") .given("debitorNumber", "D-3101000")
@ -312,7 +349,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(3108) @Order(3108)
@Requires("SEPA-Mandate: Test AG") @Requires("SEPA-Mandate: Test AG")
void shouldInvalidateSepaMandateForDebitor() { void shouldInvalidateSepaMandateForDebitor() {
new InvalidateSepaMandateForDebitor(this) new InvalidateSepaMandateForDebitor(scenarioTest)
.given("bankAccountIBAN", "DE02701500000000594937") .given("bankAccountIBAN", "DE02701500000000594937")
.given("mandateValidUntil", "2025-09-30") .given("mandateValidUntil", "2025-09-30")
.doRun(); .doRun();
@ -322,17 +359,23 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(3109) @Order(3109)
@Requires("SEPA-Mandate: Test AG") @Requires("SEPA-Mandate: Test AG")
void shouldFinallyDeleteSepaMandateForDebitor() { void shouldFinallyDeleteSepaMandateForDebitor() {
new FinallyDeleteSepaMandateForDebitor(this) new FinallyDeleteSepaMandateForDebitor(scenarioTest)
.given("bankAccountIBAN", "DE02701500000000594937") .given("bankAccountIBAN", "DE02701500000000594937")
.doRun(); .doRun();
} }
}
@Nested
@Order(40)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MembershipScenarios {
@Test @Test
@Order(4000) @Order(4000)
@Requires("Partner: P-31010 - Test AG") @Requires("Partner: P-31010 - Test AG")
@Produces("Membership: M-3101000 - Test AG") @Produces("Membership: M-3101000 - Test AG")
void shouldCreateMembershipForPartner() { void shouldCreateMembershipForPartner() {
new CreateMembership(this) new CreateMembership(scenarioTest)
.given("partnerName", "Test AG") .given("partnerName", "Test AG")
.given("validFrom", "2024-10-15") .given("validFrom", "2024-10-15")
.given("newStatus", "ACTIVE") .given("newStatus", "ACTIVE")
@ -341,12 +384,29 @@ class HsOfficeScenarioTests extends ScenarioTest {
.keep(); .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();
}
}
@Nested
@Order(42)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class CoopSharesScenarios {
@Test @Test
@Order(4201) @Order(4201)
@Requires("Membership: M-3101000 - Test AG") @Requires("Membership: M-3101000 - Test AG")
@Produces("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") @Produces("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction")
void shouldSubscribeCoopShares() { void shouldSubscribeCoopShares() {
new CreateCoopSharesSubscriptionTransaction(this) new CreateCoopSharesSubscriptionTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("reference", "sign 2024-01-15") .given("reference", "sign 2024-01-15")
.given("shareCount", 100) .given("shareCount", 100)
@ -359,7 +419,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(4202) @Order(4202)
@Requires("Membership: M-3101000 - Test AG") @Requires("Membership: M-3101000 - Test AG")
void shouldRevertCoopSharesSubscription() { void shouldRevertCoopSharesSubscription() {
new CreateCoopSharesRevertTransaction(this) new CreateCoopSharesRevertTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("comment", "reverting some incorrect transaction") .given("comment", "reverting some incorrect transaction")
.given("dateOfIncorrectTransaction", "2024-02-15") .given("dateOfIncorrectTransaction", "2024-02-15")
@ -371,7 +431,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Requires("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction") @Requires("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction")
@Produces("Coop-Shares M-3101000 - Test AG - CANCELLATION Transaction") @Produces("Coop-Shares M-3101000 - Test AG - CANCELLATION Transaction")
void shouldCancelCoopSharesSubscription() { void shouldCancelCoopSharesSubscription() {
new CreateCoopSharesCancellationTransaction(this) new CreateCoopSharesCancellationTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("reference", "cancel 2024-01-15") .given("reference", "cancel 2024-01-15")
.given("sharesToCancel", 8) .given("sharesToCancel", 8)
@ -379,13 +439,19 @@ class HsOfficeScenarioTests extends ScenarioTest {
.given("transactionDate", "2024-02-15") .given("transactionDate", "2024-02-15")
.doRun(); .doRun();
} }
}
@Nested
@Order(43)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class CoopAssetsScenarios {
@Test @Test
@Order(4301) @Order(4301)
@Requires("Membership: M-3101000 - Test AG") @Requires("Membership: M-3101000 - Test AG")
@Produces("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") @Produces("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
void shouldSubscribeCoopAssets() { void shouldSubscribeCoopAssets() {
new CreateCoopAssetsDepositTransaction(this) new CreateCoopAssetsDepositTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("reference", "sign 2024-01-15") .given("reference", "sign 2024-01-15")
.given("assetValue", 100 * 64) .given("assetValue", 100 * 64)
@ -398,7 +464,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(4302) @Order(4302)
@Requires("Membership: M-3101000 - Test AG") @Requires("Membership: M-3101000 - Test AG")
void shouldRevertCoopAssetsSubscription() { void shouldRevertCoopAssetsSubscription() {
new CreateCoopAssetsRevertSimpleTransaction(this) new CreateCoopAssetsRevertSimpleTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("comment", "reverting some incorrect transaction") .given("comment", "reverting some incorrect transaction")
.given("dateOfIncorrectTransaction", "2024-02-15") .given("dateOfIncorrectTransaction", "2024-02-15")
@ -407,10 +473,10 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test @Test
@Order(4303) @Order(4303)
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") @Requires("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
@Produces("Coop-Assets M-3101000 - Test AG - DISBURSAL Transaction") @Produces("Coop-Assets: M-3101000 - Test AG - DISBURSAL Transaction")
void shouldDisburseCoopAssets() { void shouldDisburseCoopAssets() {
new CreateCoopAssetsDisbursalTransaction(this) new CreateCoopAssetsDisbursalTransaction(scenarioTest)
.given("memberNumber", "M-3101000") .given("memberNumber", "M-3101000")
.given("reference", "cancel 2024-01-15") .given("reference", "cancel 2024-01-15")
.given("valueToDisburse", 8 * 64) .given("valueToDisburse", 8 * 64)
@ -421,10 +487,10 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test @Test
@Order(4304) @Order(4304)
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction") @Requires("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
@Produces(explicitly = "Coop-Assets M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership M-4303000") @Produces(explicitly = "Coop-Assets: M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership: M-4303000 - New AG")
void shouldTransferCoopAssets() { void shouldTransferCoopAssets() {
new CreateCoopAssetsTransferTransaction(this) new CreateCoopAssetsTransferTransaction(scenarioTest)
.given("transferringMemberNumber", "M-3101000") .given("transferringMemberNumber", "M-3101000")
.given("adoptingMemberNumber", "M-4303000") .given("adoptingMemberNumber", "M-4303000")
.given("reference", "transfer 2024-12-31") .given("reference", "transfer 2024-12-31")
@ -436,34 +502,29 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test @Test
@Order(4305) @Order(4305)
@Requires("Membership M-4303000") @Requires("Coop-Assets: M-3101000 - Test AG - TRANSFER Transaction")
void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() { void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() {
new CreateCoopAssetsRevertTransferTransaction(this) new CreateCoopAssetsRevertTransferTransaction(scenarioTest)
.given("transferringMemberNumber", "M-3101000") .given("transferringMemberNumber", "M-3101000")
.given("adoptingMemberNumber", "M-4303000") .given("adoptingMemberNumber", "M-4303000")
.given("transferredValue", 2*64) .given("transferredValue", 2 * 64)
.given("comment", "reverting some incorrect transfer transaction") .given("comment", "reverting some incorrect transfer transaction")
.given("dateOfIncorrectTransaction", "2024-02-15") .given("dateOfIncorrectTransaction", "2024-02-15")
.doRun(); .doRun();
} }
@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();
} }
@Nested
@Order(50)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SubscriptionScenarios {
@Test @Test
@Order(5000) @Order(5000)
@Requires("Person: Test AG") @Requires("Person: Test AG")
@Produces("Subscription: Michael Miller to operations-announce") @Produces("Subscription: Michael Miller to operations-announce")
void shouldSubscribeNewPersonAndContactToMailinglist() { void shouldSubscribeNewPersonAndContactToMailinglist() {
new SubscribeToMailinglist(this) new SubscribeToMailinglist(scenarioTest)
// TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists? // 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("partnerPersonTradeName", "Test AG")
.given("subscriberFamilyName", "Miller") .given("subscriberFamilyName", "Miller")
@ -478,9 +539,10 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Order(5001) @Order(5001)
@Requires("Subscription: Michael Miller to operations-announce") @Requires("Subscription: Michael Miller to operations-announce")
void shouldUnsubscribeNewPersonAndContactToMailinglist() { void shouldUnsubscribeNewPersonAndContactToMailinglist() {
new UnsubscribeFromMailinglist(this) new UnsubscribeFromMailinglist(scenarioTest)
.given("mailingList", "operations-announce") .given("mailingList", "operations-announce")
.given("subscriberEMailAddress", "michael.miller@example.org") .given("subscriberEMailAddress", "michael.miller@example.org")
.doRun(); .doRun();
} }
}
} }

View File

@ -1,16 +0,0 @@
package net.hostsharing.hsadminng.hs.office.scenarios;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(METHOD)
@Retention(RUNTIME)
public @interface Produces {
String value() default ""; // same as explicitly, makes it possible to omit the property name
String explicitly() default ""; // same as value
String[] implicitly() default {};
boolean permanent() default true; // false means that the object gets deleted again in the process
}

View File

@ -1,200 +0,0 @@
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;
import org.apache.commons.collections4.SetUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
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.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
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 {
final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented
record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) {
@Override
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<>();
public final TestReport testReport = new TestReport(aliases);
@LocalServerPort
Integer port;
@Autowired
HsOfficePersonRepository personRepo;
@Autowired
JpaAttempt jpaAttempt;
@SneakyThrows
@BeforeEach
void init(final TestInfo testInfo) {
createHostsharingPerson();
try {
testInfo.getTestMethod().ifPresent(this::callRequiredProducers);
testReport.createTestLogMarkdownFile(testInfo);
} catch (Exception exc) {
throw exc;
}
}
@AfterEach
void cleanup() { // final TestInfo testInfo
properties.clear();
testReport.close();
}
private void createHostsharingPerson() {
jpaAttempt.transacted(() ->
{
context.define("superuser-alex@hostsharing.net");
aliases.put(
"Person: Hostsharing eG",
new Alias<>(
null,
personRepo.findPersonByOptionalNameLike("Hostsharing eG")
.stream()
.map(HsOfficePersonEntity::getUuid)
.reduce(Reducer::toSingleElement).orElseThrow())
);
}
);
}
@SneakyThrows
private void callRequiredProducers(final Method currentTestMethod) {
final var testMethodRequired = Optional.of(currentTestMethod)
.map(m -> m.getAnnotation(Requires.class))
.map(Requires::value)
.orElse(null);
if (testMethodRequired != null) {
for (Method potentialProducerMethod : getClass().getDeclaredMethods()) {
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) &&
// 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);
// and finally we call the producer method
potentialProducerMethod.invoke(this);
}
// @formatter:on
}
}
}
}
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
final var all = new HashSet<String>();
if (!value.isEmpty()) {
all.add(value);
}
if (!explicitly.isEmpty()) {
all.add(explicitly);
}
all.addAll(asList(implicitly));
return all;
}
static boolean containsAlias(final String alias) {
return aliases.containsKey(alias);
}
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);
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
).isNotNull();
return alias;
}
static void putAlias(final String name, final Alias<?> value) {
aliases.put(name, value);
}
static void putProperty(final String name, final Object value) {
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
}
static void removeProperty(final String propName) {
properties.remove(propName);
}
static Map<String, Object> knowVariables() {
final var map = new LinkedHashMap<String, Object>();
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
map.putAll(ScenarioTest.properties);
return map;
}
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 resolvableText) {
final var resolved = resolve(resolvableText, DROP_COMMENTS);
try {
return UUID.fromString(resolved);
} catch (final IllegalArgumentException e) {
// ignore and just use the String value
}
return resolved;
}
public static <T> T resolveTyped(final String resolvableText, final Class<T> valueType) {
final var resolvedValue = resolve(resolvableText, DROP_COMMENTS);
if (valueType == BigDecimal.class) {
//noinspection unchecked
return (T) new BigDecimal(resolvedValue);
}
//noinspection unchecked
return (T) resolvedValue;
}
}

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.contact; package net.hostsharing.hsadminng.hs.office.scenarios.contact;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.contact; package net.hostsharing.hsadminng.hs.office.scenarios.contact;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.contact; package net.hostsharing.hsadminng.hs.office.scenarios.contact;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.contact; package net.hostsharing.hsadminng.hs.office.scenarios.contact;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.person.CreatePerson; import net.hostsharing.hsadminng.hs.office.scenarios.person.CreatePerson;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.CREATED;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
public class DeleteDebitor extends UseCase<DeleteDebitor> { public class DeleteDebitor extends UseCase<DeleteDebitor> {

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
public class DontDeleteDefaultDebitor extends UseCase<DontDeleteDefaultDebitor> { public class DontDeleteDefaultDebitor extends UseCase<DontDeleteDefaultDebitor> {

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.debitor; package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership; package net.hostsharing.hsadminng.hs.office.scenarios.membership;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership; package net.hostsharing.hsadminng.hs.office.scenarios.membership;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopAssetsDepositTransaction extends CreateCoopAssetsTransaction { public class CreateCoopAssetsDepositTransaction extends CreateCoopAssetsTransaction {

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopAssetsDisbursalTransaction extends CreateCoopAssetsTransaction { public class CreateCoopAssetsDisbursalTransaction extends CreateCoopAssetsTransaction {

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopAssetsRevertSimpleTransaction extends CreateCoopAssetsTransaction { public class CreateCoopAssetsRevertSimpleTransaction extends CreateCoopAssetsTransaction {

View File

@ -1,13 +1,13 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import java.math.BigDecimal; import java.math.BigDecimal;
import static net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest.resolveTyped; import static net.hostsharing.hsadminng.hs.scenarios.ScenarioTest.resolveTyped;
public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsTransaction { public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsTransaction {

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,10 +1,10 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership; import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner; import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransaction { public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransaction {
@ -19,7 +19,7 @@ public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransac
.given("emailAddress", "board-of-directors@new-ag.example.org") .given("emailAddress", "board-of-directors@new-ag.example.org")
); );
requires("Membership: New AG", alias -> new CreateMembership(testSuite) requires("Membership: %{adoptingMemberNumber} - New AG", alias -> new CreateMembership(testSuite)
.given("memberNumber", toPartnerNumber("%{adoptingMemberNumber}")) .given("memberNumber", toPartnerNumber("%{adoptingMemberNumber}"))
.given("partnerName", "New AG") .given("partnerName", "New AG")
.given("validFrom", "2024-11-15") .given("validFrom", "2024-11-15")

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopSharesCancellationTransaction extends CreateCoopSharesTransaction { public class CreateCoopSharesCancellationTransaction extends CreateCoopSharesTransaction {

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransaction { public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransaction {

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
public class CreateCoopSharesSubscriptionTransaction extends CreateCoopSharesTransaction { public class CreateCoopSharesSubscriptionTransaction extends CreateCoopSharesTransaction {

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares; package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.partner; package net.hostsharing.hsadminng.hs.office.scenarios.partner;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.partner; package net.hostsharing.hsadminng.hs.office.scenarios.partner;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.partner; package net.hostsharing.hsadminng.hs.office.scenarios.partner;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.partner; package net.hostsharing.hsadminng.hs.office.scenarios.partner;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
public class DeletePartner extends UseCase<DeletePartner> { public class DeletePartner extends UseCase<DeletePartner> {

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.person; package net.hostsharing.hsadminng.hs.office.scenarios.person;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
public class CreatePerson extends UseCase<CreatePerson> { public class CreatePerson extends UseCase<CreatePerson> {

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.person; package net.hostsharing.hsadminng.hs.office.scenarios.person;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.subscription; package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.NOT_FOUND;

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.scenarios.subscription; package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.scenarios.subscription; package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.UseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
import static io.restassured.http.ContentType.JSON; import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.NO_CONTENT;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
public final class JsonOptional<V> { public final class JsonOptional<V> {

View File

@ -1,10 +1,10 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse; import net.hostsharing.hsadminng.hs.scenarios.UseCase.HttpResponse;
import java.util.function.Consumer; import java.util.function.Consumer;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
public class PathAssertion { public class PathAssertion {

View File

@ -0,0 +1,46 @@
package net.hostsharing.hsadminng.hs.scenarios;
import lombok.experimental.UtilityClass;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.HashSet;
import java.util.Set;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Arrays.asList;
@Target(METHOD)
@Retention(RUNTIME)
public @interface Produces {
String value() default ""; // same as explicitly, makes it possible to omit the property name
String explicitly() default ""; // same as value
String[] implicitly() default {};
boolean permanent() default true; // false means that the object gets deleted again in the process
@UtilityClass
final class Aggregator {
public static Set<String> producedAliases(final Produces producesAnnot) {
return allOf(
producesAnnot.value(),
producesAnnot.explicitly(),
producesAnnot.implicitly());
}
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
final var all = new HashSet<String>();
if (!value.isEmpty()) {
all.add(value);
}
if (!explicitly.isEmpty()) {
all.add(explicitly);
}
all.addAll(asList(implicitly));
return all;
}
}
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;

View File

@ -0,0 +1,257 @@
package net.hostsharing.hsadminng.hs.scenarios;
import lombok.SneakyThrows;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.lambda.Reducer;
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import net.hostsharing.hsadminng.hs.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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.server.LocalServerPort;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Stack;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.scenarios.Produces.Aggregator.producedAliases;
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class ScenarioTest extends ContextBasedTest {
final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented
private final Stack<String> currentTestMethodProduces = new Stack<>();
protected ScenarioTest scenarioTest = this;
Optional<String> takeProducedAlias() {
if (currentTestMethodProduces.isEmpty()) {
return Optional.empty();
}
return Optional.of(currentTestMethodProduces.pop());
}
record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) {
@Override
public String toString() {
return Objects.toString(uuid);
}
}
private final static Map<String, Alias<?>> aliases = new HashMap<>();
private final static Map<String, Object> properties = new HashMap<>();
public final TestReport testReport = new TestReport(aliases);
@LocalServerPort
Integer port;
@Autowired
HsOfficePersonRepository personRepo;
@Autowired
JpaAttempt jpaAttempt;
@SneakyThrows
@BeforeEach
void beforeScenario(final TestInfo testInfo) {
createHostsharingPerson();
try {
testInfo.getTestMethod().ifPresent(currentTestMethod -> {
callRequiredProducers(currentTestMethod);
keepProducesAlias(currentTestMethod);
});
testReport.createTestLogMarkdownFile(testInfo);
} catch (Exception exc) {
throw exc;
}
}
@AfterEach
void afterScenario(final TestInfo testInfo) { // final TestInfo testInfo
testInfo.getTestMethod() .ifPresent(currentTestMethod -> {
// FIXME: extract to method
final var producesAnnot = currentTestMethod.getAnnotation(Produces.class);
if (producesAnnot != null && producesAnnot.permanent()) {
final var testMethodProduces = producedAliases(producesAnnot);
testMethodProduces.forEach(declaredAlias ->
assertThat(knowVariables().containsKey(declaredAlias))
.as("@Producer method " + currentTestMethod.getName() +
" did declare but not produce \"" + declaredAlias + "\"")
.isTrue() );
}
});
properties.clear();
testReport.close();
}
private void createHostsharingPerson() {
jpaAttempt.transacted(() ->
{
context.define("superuser-alex@hostsharing.net");
aliases.put(
"Person: Hostsharing eG",
new Alias<>(
null,
personRepo.findPersonByOptionalNameLike("Hostsharing eG")
.stream()
.map(HsOfficePersonEntity::getUuid)
.reduce(Reducer::toSingleElement).orElseThrow())
);
}
);
}
@SneakyThrows
private void callRequiredProducers(final Method currentTestMethod) {
final var testMethodRequires = Optional.of(currentTestMethod)
.map(m -> m.getAnnotation(Requires.class))
.map(Requires::value)
.orElse(null);
if (testMethodRequires != null) {
for (Method potentialProducerMethod : getPotentialProducerMethods()) {
final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class);
final var testMethodProduces = producedAliases(producesAnnot);
// @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);
// then we recursively produce the pre-requisites of the producer method
callRequiredProducers(potentialProducerMethod);
keepProducesAlias(currentTestMethod);
// and finally we call the producer method
invokeProducerMethod(this, potentialProducerMethod);
}
// @formatter:on
}
assertThat(knowVariables().containsKey(testMethodRequires))
.as("no @Producer for @Required(\"" + testMethodRequires + "\") found")
.isTrue();
}
}
private void keepProducesAlias(final Method currentTestMethod) {
final var producesAnnot = currentTestMethod.getAnnotation(Produces.class);
if (producesAnnot != null) {
final var producesAlias = isNotBlank(producesAnnot.value()) ? producesAnnot.value() : producesAnnot.explicitly();
assertThat(producesAlias)
.as(currentTestMethod.getName() + " must define either value or explicit for @Produces")
.isNotNull();
this.currentTestMethodProduces.push(producesAlias);
}
}
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);
}
}
static boolean containsAlias(final String alias) {
return aliases.containsKey(alias);
}
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);
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
).isNotNull();
return alias;
}
static void putAlias(final String name, final Alias<?> value) {
aliases.put(name, value);
}
static void putProperty(final String name, final Object value) {
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
}
static void removeProperty(final String propName) {
properties.remove(propName);
}
static Map<String, Object> knowVariables() {
final var map = new LinkedHashMap<String, Object>();
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
map.putAll(ScenarioTest.properties);
return map;
}
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 resolvableText) {
final var resolved = resolve(resolvableText, DROP_COMMENTS);
try {
return UUID.fromString(resolved);
} catch (final IllegalArgumentException e) {
// ignore and just use the String value
}
return resolved;
}
public static <T> T resolveTyped(final String resolvableText, final Class<T> valueType) {
final var resolvedValue = resolve(resolvableText, DROP_COMMENTS);
if (valueType == BigDecimal.class) {
//noinspection unchecked
return (T) new BigDecimal(resolvedValue);
}
//noinspection unchecked
return (T) resolvedValue;
}
}

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -10,7 +10,7 @@ import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
public class TemplateResolver { public class TemplateResolver {

View File

@ -1,10 +1,10 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Map; import java.util.Map;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
class TemplateResolverUnitTest { class TemplateResolverUnitTest {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.system.SystemProcess; import net.hostsharing.hsadminng.system.SystemProcess;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -35,8 +35,8 @@ import java.util.function.Supplier;
import static java.net.URLEncoder.encode; import static java.net.URLEncoder.encode;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS;
import static net.hostsharing.hsadminng.test.DebuggerDetection.isDebuggerAttached; import static net.hostsharing.hsadminng.test.DebuggerDetection.isDebuggerAttached;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -71,10 +71,8 @@ public abstract class UseCase<T extends UseCase<?>> {
} }
public final void requires(final String alias, final Function<String, UseCase<?>> useCaseFactory) { public final void requires(final String alias, final Function<String, UseCase<?>> useCaseFactory) {
if (!ScenarioTest.containsAlias(alias)) {
requirements.put(alias, useCaseFactory); requirements.put(alias, useCaseFactory);
} }
}
public final HttpResponse doRun() { public final HttpResponse doRun() {
if (introduction != null) { if (introduction != null) {
@ -89,13 +87,15 @@ public abstract class UseCase<T extends UseCase<?>> {
testReport.printLine(""); testReport.printLine("");
testReport.silent(() -> testReport.silent(() ->
requirements.forEach((alias, factory) -> { requirements.forEach((alias, factory) -> {
if (!ScenarioTest.containsAlias(alias)) { final var resolvedAlias = ScenarioTest.resolve(alias, DROP_COMMENTS);
factory.apply(alias).run().keepAs(alias); if (!ScenarioTest.containsAlias(resolvedAlias)) {
factory.apply(resolvedAlias).run().keepAs(resolvedAlias);
} }
}) })
); );
final var response = run(); final var response = run();
verify(response); verify(response);
keepInProduceAlias(response);
resetProperties(); resetProperties();
@ -113,7 +113,7 @@ public abstract class UseCase<T extends UseCase<?>> {
} }
public final UseCase<T> given(final String propName, final Object propValue) { public final UseCase<T> given(final String propName, final Object propValue) {
givenProperties.put(propName, ScenarioTest.resolve(propValue == null ? null : propValue.toString(), KEEP_COMMENTS)); givenProperties.put(propName, ScenarioTest.resolve(propValue == null ? null : propValue.toString(), TemplateResolver.Resolver.KEEP_COMMENTS));
ScenarioTest.putProperty(propName, propValue); ScenarioTest.putProperty(propName, propValue);
return this; return this;
} }
@ -244,6 +244,13 @@ public abstract class UseCase<T extends UseCase<?>> {
} }
private void keepInProduceAlias(final HttpResponse response) {
final var producedAlias = testSuite.takeProducedAlias();
if (response != null) {
producedAlias.ifPresent(response::keepAs);
}
}
private static Duration seconds(final int secondsIfNoDebuggerAttached) { private static Duration seconds(final int secondsIfNoDebuggerAttached) {
return isDebuggerAttached() ? Duration.ofHours(1) : Duration.ofSeconds(secondsIfNoDebuggerAttached); return isDebuggerAttached() ? Duration.ofHours(1) : Duration.ofSeconds(secondsIfNoDebuggerAttached);
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.hs.office.scenarios; package net.hostsharing.hsadminng.hs.scenarios;
import static org.assertj.core.api.Assumptions.assumeThat; import static org.assertj.core.api.Assumptions.assumeThat;