sepa-mandate tests

This commit is contained in:
Michael Hoennig 2024-10-22 17:27:46 +02:00
parent 876b3c526e
commit 2507ad4542
6 changed files with 163 additions and 25 deletions

View File

@ -3,6 +3,9 @@ package net.hostsharing.hsadminng.hs.office.usecases;
import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.CreateExternalDebitorForPartner; import net.hostsharing.hsadminng.hs.office.usecases.debitor.CreateExternalDebitorForPartner;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.CreateSelfDebitorForPartner; import net.hostsharing.hsadminng.hs.office.usecases.debitor.CreateSelfDebitorForPartner;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.CreateSepaMandataForDebitor;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.DeleteSepaMandataForDebitor;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.InvalidateSepaMandataForDebitor;
import net.hostsharing.hsadminng.hs.office.usecases.membership.CreateMembership; import net.hostsharing.hsadminng.hs.office.usecases.membership.CreateMembership;
import net.hostsharing.hsadminng.hs.office.usecases.partner.CreatePartner; import net.hostsharing.hsadminng.hs.office.usecases.partner.CreatePartner;
import net.hostsharing.hsadminng.hs.office.usecases.debitor.DeleteDebitor; import net.hostsharing.hsadminng.hs.office.usecases.debitor.DeleteDebitor;
@ -48,6 +51,7 @@ class HsOfficeUseCasesTest extends UseCaseTest {
@Test @Test
@Order(2010) @Order(2010)
@Requires("Partner: Test AG") @Requires("Partner: Test AG")
@Produces("Debitor: Test AG - main debitor")
void shouldCreateSelfDebitorForPartner() { void shouldCreateSelfDebitorForPartner() {
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor") new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
.given("partnerPersonUuid", "%{Person: Test AG}") .given("partnerPersonUuid", "%{Person: Test AG}")
@ -94,6 +98,39 @@ class HsOfficeUseCasesTest extends UseCaseTest {
.doRun(); .doRun();
} }
@Test
@Order(3100)
@Requires("Debitor: Test AG - main debitor")
@Produces("SEPA-Mandate: Test AG")
void shouldCreateSepaMandateForDebitor() {
new CreateSepaMandataForDebitor(this, "SEPA-Mandate: Test AG")
.given("debitor", "Test AG")
.given("memberNumberSuffix", "00")
.given("validFrom", "2024-10-15")
.given("membershipFeeBillable", "true")
.doRun()
.keep();
}
@Test
@Order(3108)
@Requires("SEPA-Mandate: Test AG")
void shouldInvalidateSepaMandateForDebitor() {
new InvalidateSepaMandataForDebitor(this)
.given("sepaMandateUuid", "%{SEPA-Mandate: Test AG}")
.given("validUntil", "2025-09-30")
.doRun();
}
@Test
@Order(3109)
@Requires("SEPA-Mandate: Test AG")
void shouldDeleteSepaMandateForDebitor() {
new DeleteSepaMandataForDebitor(this)
.given("sepaMandateUuid", "%{SEPA-Mandate: Test AG}")
.doRun();
}
@Test @Test
@Order(3000) @Order(3000)
@Requires("Partner: Test AG") @Requires("Partner: Test AG")

View File

@ -14,6 +14,7 @@ import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
public abstract class UseCase<T extends UseCase<?>> { public abstract class UseCase<T extends UseCase<?>> {
@ -87,6 +88,18 @@ public abstract class UseCase<T extends UseCase<?>> {
return new HttpResponse(HttpMethod.POST, uriPath, body, response); return new HttpResponse(HttpMethod.POST, uriPath, body, response);
} }
public final HttpResponse httpPatch(final String uriPath, final JsonTemplate bodyJsonTemplate) {
final var body = bodyJsonTemplate.resolvePlaceholders();
final var uri = "http://localhost" + uriPath;
final var response = RestAssured.given()
.header("current-subject", UseCaseTest.RUN_AS_USER)
.contentType(ContentType.JSON)
.body(body)
.port(testSuite.port)
.when().patch(uri);
return new HttpResponse(HttpMethod.PATCH, uriPath, body, response);
}
public final HttpResponse httpDelete(final String uriPath) { public final HttpResponse httpDelete(final String uriPath) {
final var response = RestAssured.given() final var response = RestAssured.given()
.header("current-subject", UseCaseTest.RUN_AS_USER) .header("current-subject", UseCaseTest.RUN_AS_USER)
@ -99,7 +112,7 @@ public abstract class UseCase<T extends UseCase<?>> {
return UseCaseTest.getAlias(alias).uuid(); return UseCaseTest.getAlias(alias).uuid();
} }
static class JsonTemplate { public static class JsonTemplate {
private final String template; private final String template;
@ -160,8 +173,10 @@ public abstract class UseCase<T extends UseCase<?>> {
} }
public void keep() { public void keep() {
final var alias = nextTitle != null ? nextTitle : resultAlias;
assertThat(alias).as("cannot keep result, no alias found").isNotNull();
UseCaseTest.putAlias( UseCaseTest.putAlias(
nextTitle != null ? nextTitle : resultAlias, alias,
new UseCaseTest.Alias<>(UseCase.this.getClass(), locationUuid)); new UseCaseTest.Alias<>(UseCase.this.getClass(), locationUuid));
} }
} }

View File

@ -19,12 +19,12 @@ import org.springframework.boot.test.web.server.LocalServerPort;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
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;
@ -35,9 +35,10 @@ import static org.assertj.core.api.Assertions.assertThat;
public abstract class UseCaseTest extends ContextBasedTest { public abstract class UseCaseTest 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
//String producesAlias;
// @Getter // @Getter
// public static TestInfo currentTestInfo; // public static TestInfo currentTestInfo;
@Getter @Getter
private PrintWriter markdownFile; private PrintWriter markdownFile;
@ -60,7 +61,7 @@ public abstract class UseCaseTest extends ContextBasedTest {
@BeforeEach @BeforeEach
void init(final TestInfo testInfo) { void init(final TestInfo testInfo) {
createHostsharingPerson(); createHostsharingPerson();
callRequiredProducers(testInfo); testInfo.getTestMethod().ifPresent(this::callRequiredProducers);
createTestLogMarkdownFile(testInfo); createTestLogMarkdownFile(testInfo);
} }
@ -98,40 +99,41 @@ public abstract class UseCaseTest extends ContextBasedTest {
private void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException { private void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow(); final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow(); final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow();
markdownFile = new PrintWriter(new FileWriter( testMethodOrder + "-" + testMethodName + ".md")); markdownFile = new PrintWriter(new FileWriter(testMethodOrder + "-" + testMethodName + ".md"));
log("## Testcase " + testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2")); log("## Testcase " + testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2"));
} }
private void callRequiredProducers(final TestInfo testInfo) throws IllegalAccessException, InvocationTargetException { @SneakyThrows
final var testMethodRequired = testInfo.getTestMethod() private void callRequiredProducers(final Method currentTestMethod) {
final var testMethodRequired = 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 (testMethodRequired != null) {
for (Method testMethod : getClass().getDeclaredMethods()) { for (Method potentialProducerMethod : getClass().getDeclaredMethods()) {
final var producesAnnot = testMethod.getAnnotation(Produces.class); final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class);
if (producesAnnot != null) { if (producesAnnot != null) {
final var testMethodProduces = allOf(producesAnnot.value(), producesAnnot.explicitly(), producesAnnot.implicitly()); final var testMethodProduces = allOf(
producesAnnot.value(),
producesAnnot.explicitly(),
producesAnnot.implicitly());
// @formatter:off
if ( // that method can produce something required if ( // that method can produce something required
testMethodProduces.contains(testMethodRequired) && testMethodProduces.contains(testMethodRequired) &&
// and it does not produce anything we already have (would cause errors) // and it does not produce anything we already have (would cause errors)
SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty()) SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty()
) {
// then we recursively produce the pre-requisites of the producer method
callRequiredProducers(potentialProducerMethod);
// then we call the producer method // and finally we call the producer method
testMethod.invoke(this); potentialProducerMethod.invoke(this);
}
// @formatter:on
} }
} }
} }
// public void requires(final String alias) {
// assumeThat(UseCaseTest.containsAlias(alias))
// .as("skipping because alias '" + alias + "' not found, @Produces(...) missing?")
// .isTrue();
// log("depends on [" + alias + "](" + UseCaseTest.getAlias(alias).useCase().getSimpleName() + ".md)");
// }
} }
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) {

View File

@ -0,0 +1,39 @@
package net.hostsharing.hsadminng.hs.office.usecases.debitor;
import net.hostsharing.hsadminng.hs.office.usecases.UseCase;
import net.hostsharing.hsadminng.hs.office.usecases.UseCaseTest;
import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.CREATED;
public class CreateSepaMandataForDebitor extends UseCase<CreateSepaMandataForDebitor> {
public CreateSepaMandataForDebitor(final UseCaseTest testSuite, final String resultAlias) {
super(testSuite, resultAlias);
}
@Override
protected HttpResponse run() {
keep("BankAccount: Test AG - debit bank account", () ->
httpPost("/api/hs/office/bankaccounts", usingJsonBody("""
{
"holder": "Test AG - debit bank account",
"iban": "DE02701500000000594937",
"bic": "SSKMDEMM"
}
"""))
.expecting(CREATED).expecting(JSON)
);
return httpPost("/api/hs/office/sepamandates", usingJsonBody("""
{
"debitorUuid": ${Debitor: Test AG - main debitor},
"bankAccountUuid": ${BankAccount: Test AG - debit bank account},
"reference": "Test AG - main debitor",
"agreement": "2022-10-12",
"validFrom": "2022-10-13"
}
"""))
.expecting(CREATED).expecting(JSON);
}
}

View File

@ -0,0 +1,20 @@
package net.hostsharing.hsadminng.hs.office.usecases.debitor;
import net.hostsharing.hsadminng.hs.office.usecases.UseCase;
import net.hostsharing.hsadminng.hs.office.usecases.UseCaseTest;
import org.springframework.http.HttpStatus;
public class DeleteSepaMandataForDebitor extends UseCase<DeleteSepaMandataForDebitor> {
public DeleteSepaMandataForDebitor(final UseCaseTest testSuite) {
super(testSuite);
}
@Override
protected HttpResponse run() {
httpDelete("/api/hs/office/sepamandates/" + uuid("SEPA-Mandate: Test AG"))
.expecting(HttpStatus.NO_CONTENT);
return null;
}
}

View File

@ -0,0 +1,25 @@
package net.hostsharing.hsadminng.hs.office.usecases.debitor;
import net.hostsharing.hsadminng.hs.office.usecases.UseCase;
import net.hostsharing.hsadminng.hs.office.usecases.UseCaseTest;
import static io.restassured.http.ContentType.JSON;
import static org.springframework.http.HttpStatus.OK;
public class InvalidateSepaMandataForDebitor extends UseCase<InvalidateSepaMandataForDebitor> {
public InvalidateSepaMandataForDebitor(final UseCaseTest testSuite) {
super(testSuite);
}
@Override
protected HttpResponse run() {
return httpPatch("/api/hs/office/sepamandates/" + uuid("SEPA-Mandate: Test AG"), usingJsonBody("""
{
"validUntil": ${validUntil}
}
"""))
.expecting(OK).expecting(JSON);
}
}