refactoring scenario-tests #126
@ -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 &&
|
||||||
|
@ -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,21 +59,27 @@ 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 {
|
||||||
|
|
||||||
|
@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 +97,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 +122,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 +145,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 +159,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 +167,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 +192,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 +203,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 +213,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 +230,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 +277,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 +297,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 +308,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 +346,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 +356,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 +381,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 +416,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 +428,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 +436,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 +461,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")
|
||||||
@ -410,7 +473,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@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)
|
||||||
@ -424,7 +487,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@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")
|
||||||
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")
|
||||||
@ -438,32 +501,27 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@Order(4305)
|
@Order(4305)
|
||||||
@Requires("Membership M-4303000")
|
@Requires("Membership M-4303000")
|
||||||
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 +536,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();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,21 +102,20 @@ 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(testMethodRequired) &&
|
testMethodProduces.contains(testMethodRequires) &&
|
||||||
|
|
||||||
// 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()
|
||||||
@ -119,13 +126,44 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
|||||||
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))
|
||||||
|
.as("@Producer(\"" + testMethodRequires + "\") did not produce")
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
// @formatter:on
|
// @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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
|
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
|
||||||
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user