Compare commits
2 Commits
fde50a0454
...
87e8576ce7
Author | SHA1 | Date | |
---|---|---|---|
|
87e8576ce7 | ||
|
f899f447b2 |
@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebit
|
|||||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner;
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSepaMandataForDebitor;
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSepaMandataForDebitor;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteSepaMandataForDebitor;
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteSepaMandataForDebitor;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DontDeleteDefaultDebitor;
|
||||||
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.InvalidateSepaMandateForDebitor;
|
import net.hostsharing.hsadminng.hs.office.scenarios.debitor.InvalidateSepaMandateForDebitor;
|
||||||
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.AddOperationsContactToPartner;
|
import net.hostsharing.hsadminng.hs.office.scenarios.partner.AddOperationsContactToPartner;
|
||||||
@ -16,18 +17,27 @@ import net.hostsharing.hsadminng.hs.office.scenarios.subscription.RemoveOperatio
|
|||||||
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.rbac.test.JpaAttempt;
|
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
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.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
|
@Tag("scenarioTest")
|
||||||
@SpringBootTest(
|
@SpringBootTest(
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
classes = { HsadminNgApplication.class, JpaAttempt.class },
|
||||||
|
properties = {
|
||||||
|
"spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///scenariosTC}",
|
||||||
|
"spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}",
|
||||||
|
"spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}",
|
||||||
|
"hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
@Tag("useCaseTest")
|
@DirtiesContext
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
class HsOfficeScenarioTests extends ScenarioTest {
|
class HsOfficeScenarioTests extends ScenarioTest {
|
||||||
|
|
||||||
@ -146,6 +156,17 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
.doRun();
|
.doRun();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(2020)
|
||||||
|
@Requires("Debitor: Test AG - main debitor")
|
||||||
|
@Disabled("FIXME: Should not delete, but does delete, but we need it for subsequent tests. See TODO.spec inside.")
|
||||||
|
void shouldNotDeleteDefaultDebitor() {
|
||||||
|
new DontDeleteDefaultDebitor(this)
|
||||||
|
.given("partnerNumber", 31020)
|
||||||
|
.given("debitorSuffix", "00")
|
||||||
|
.doRun();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3100)
|
@Order(3100)
|
||||||
@Requires("Debitor: Test AG - main debitor")
|
@Requires("Debitor: Test AG - main debitor")
|
||||||
@ -197,6 +218,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
|||||||
@Produces("Subscription: Michael Miller to operations-announce")
|
@Produces("Subscription: Michael Miller to operations-announce")
|
||||||
void shouldSubscribeNewPersonAndContactToMailinglist() {
|
void shouldSubscribeNewPersonAndContactToMailinglist() {
|
||||||
new SubscribeToMailinglist(this)
|
new SubscribeToMailinglist(this)
|
||||||
|
// TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists?
|
||||||
.given("partnerPersonUuid", "%{Person: Test AG}")
|
.given("partnerPersonUuid", "%{Person: Test AG}")
|
||||||
.given("subscriberFamilyName", "Miller")
|
.given("subscriberFamilyName", "Miller")
|
||||||
.given("subscriberGivenName", "Michael")
|
.given("subscriberGivenName", "Michael")
|
||||||
|
@ -35,10 +35,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
public abstract class ScenarioTest extends ContextBasedTest {
|
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
|
||||||
//String producesAlias;
|
|
||||||
|
|
||||||
// @Getter
|
|
||||||
// public static TestInfo currentTestInfo;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private PrintWriter markdownFile;
|
private PrintWriter markdownFile;
|
||||||
@ -107,7 +103,7 @@ public abstract class ScenarioTest 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("doc/scenarios/" + testMethodOrder + "-" + testMethodName + ".md"));
|
||||||
print("## Scenario: " + testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2"));
|
print("## Scenario: " + testMethodName.replaceAll("([a-z])([A-Z]+)", "$1 $2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||||
|
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import net.hostsharing.hsadminng.reflection.AnnotationFinder;
|
import net.hostsharing.hsadminng.reflection.AnnotationFinder;
|
||||||
import org.apache.commons.collections4.map.LinkedMap;
|
import org.apache.commons.collections4.map.LinkedMap;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
@ -8,10 +9,13 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.HttpStatusCode;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.client.RestClient;
|
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpRequest.BodyPublishers;
|
||||||
|
import java.net.http.HttpResponse.BodyHandlers;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -21,11 +25,10 @@ import java.util.function.Supplier;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.platform.commons.util.StringUtils.isBlank;
|
import static org.junit.platform.commons.util.StringUtils.isBlank;
|
||||||
import static org.junit.platform.commons.util.StringUtils.isNotBlank;
|
import static org.junit.platform.commons.util.StringUtils.isNotBlank;
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
|
||||||
|
|
||||||
public abstract class UseCase<T extends UseCase<?>> {
|
public abstract class UseCase<T extends UseCase<?>> {
|
||||||
|
|
||||||
private static final RestClient restClient = RestClient.create();
|
private static final HttpClient client = HttpClient.newHttpClient();
|
||||||
|
|
||||||
private final ScenarioTest testSuite;
|
private final ScenarioTest testSuite;
|
||||||
private final Map<String, Function<String, UseCase<?>>> requirements = new LinkedMap<>();
|
private final Map<String, Function<String, UseCase<?>>> requirements = new LinkedMap<>();
|
||||||
@ -79,34 +82,45 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
this.nextTitle = null;
|
this.nextTitle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) {
|
public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) {
|
||||||
final var body = bodyJsonTemplate.resolvePlaceholders();
|
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||||
final var response = restClient.post()
|
final var request = HttpRequest.newBuilder()
|
||||||
.uri("http://localhost:" + testSuite.port + uriPath)
|
.method(HttpMethod.POST.toString(), BodyPublishers.ofString(requestBody))
|
||||||
|
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
||||||
.contentType(APPLICATION_JSON)
|
.timeout(Duration.ofSeconds(10))
|
||||||
.body(body)
|
.build();
|
||||||
.retrieve();
|
final var response = client.send(request, BodyHandlers.ofString());
|
||||||
return new HttpResponse(HttpMethod.POST, uriPath, body, response);
|
return new HttpResponse(HttpMethod.POST, uriPath, requestBody, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
public final HttpResponse httpPatch(final String uriPath, final JsonTemplate bodyJsonTemplate) {
|
public final HttpResponse httpPatch(final String uriPath, final JsonTemplate bodyJsonTemplate) {
|
||||||
final var body = bodyJsonTemplate.resolvePlaceholders();
|
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
|
||||||
final var response = restClient.patch()
|
final var request = HttpRequest.newBuilder()
|
||||||
.uri("http://localhost:" + testSuite.port + uriPath)
|
.method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody))
|
||||||
|
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
||||||
.contentType(APPLICATION_JSON)
|
.timeout(Duration.ofSeconds(10))
|
||||||
.body(body)
|
.build();
|
||||||
.retrieve();
|
final var response = client.send(request, BodyHandlers.ofString());
|
||||||
return new HttpResponse(HttpMethod.PATCH, uriPath, body, response);
|
return new HttpResponse(HttpMethod.PATCH, uriPath, requestBody, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
public final HttpResponse httpDelete(final String uriPath) {
|
public final HttpResponse httpDelete(final String uriPath) {
|
||||||
final var response = restClient.delete()
|
final var request = HttpRequest.newBuilder()
|
||||||
.uri("http://localhost:" + testSuite.port + uriPath)
|
.DELETE()
|
||||||
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
|
||||||
.retrieve();
|
.header("Content-Type", "application/json")
|
||||||
return new HttpResponse(HttpMethod.DELETE, uriPath, null, response);
|
.header("current-subject", ScenarioTest.RUN_AS_USER)
|
||||||
|
.timeout(Duration.ofSeconds(10))
|
||||||
|
.build();
|
||||||
|
final var response = client.send(request, BodyHandlers.ofString());
|
||||||
|
return new HttpResponse(HttpMethod.PATCH, uriPath, null, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final UUID uuid(final String alias) {
|
public final UUID uuid(final String alias) {
|
||||||
@ -128,20 +142,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
|
|
||||||
public class HttpResponse {
|
public class HttpResponse {
|
||||||
|
|
||||||
private final ResponseEntity<String> response;
|
private final java.net.http.HttpResponse<String> response;
|
||||||
private final HttpStatusCode status;
|
private final HttpStatus status;
|
||||||
private UUID locationUuid;
|
private UUID locationUuid;
|
||||||
|
|
||||||
public HttpResponse(
|
public HttpResponse(
|
||||||
final HttpMethod httpMethod,
|
final HttpMethod httpMethod,
|
||||||
final String uri,
|
final String uri,
|
||||||
final String requestBody,
|
final String requestBody,
|
||||||
final RestClient.ResponseSpec responseSpec
|
final java.net.http.HttpResponse<String> response
|
||||||
) {
|
) {
|
||||||
response = responseSpec.toEntity(String.class);
|
this.response = response;
|
||||||
status = this.response.getStatusCode();
|
this.status = HttpStatus.valueOf(response.statusCode());
|
||||||
if (this.status.value() == HttpStatus.CREATED.value()) {
|
if (this.status == HttpStatus.CREATED) {
|
||||||
final var location = response.getHeaders().getLocation().toString();
|
final var location = response.headers().firstValue("Location").orElseThrow();
|
||||||
assertThat(location).startsWith("http://localhost:");
|
assertThat(location).startsWith("http://localhost:");
|
||||||
locationUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1));
|
locationUuid = UUID.fromString(location.substring(location.lastIndexOf('/') + 1));
|
||||||
}
|
}
|
||||||
@ -156,22 +170,20 @@ public abstract class UseCase<T extends UseCase<?>> {
|
|||||||
printLine(requestBody + "=> status: " + status + " " +
|
printLine(requestBody + "=> status: " + status + " " +
|
||||||
(locationUuid != null ? locationUuid : ""));
|
(locationUuid != null ? locationUuid : ""));
|
||||||
if (!status.is2xxSuccessful()) {
|
if (!status.is2xxSuccessful()) {
|
||||||
printLine(responseSpec.body(String.class)); // FIXME: prettyPrint
|
printLine(response.body()); // FIXME: prettyPrint
|
||||||
}
|
}
|
||||||
printLine("```");
|
printLine("```");
|
||||||
printLine("");
|
printLine("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse expecting(final HttpStatus httpStatus) {
|
public HttpResponse expecting(final HttpStatus httpStatus) {
|
||||||
assertThat(HttpStatus.valueOf(response.getStatusCode().value())).isEqualTo(httpStatus);
|
assertThat(HttpStatus.valueOf(response.statusCode())).isEqualTo(httpStatus);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse expecting(final ContentType contentType) {
|
public HttpResponse expecting(final ContentType contentType) {
|
||||||
assertThat(response.getHeaders().getContentType())
|
assertThat(response.headers().firstValue("content-type"))
|
||||||
.isNotNull()
|
.contains(contentType.toString());
|
||||||
.extracting(Object::toString)
|
|
||||||
.isEqualTo(contentType.toString());
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
public class DontDeleteDefaultDebitor extends UseCase<DontDeleteDefaultDebitor> {
|
||||||
|
|
||||||
|
public DontDeleteDefaultDebitor(final ScenarioTest testSuite) {
|
||||||
|
super(testSuite);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HttpResponse run() {
|
||||||
|
httpDelete("/api/hs/office/debitors/" + uuid("Debitor: Test AG - main debitor"))
|
||||||
|
// TODO.spec: should be CONFLICT or CLIENT_ERROR for Debitor "00" - but how to delete Partners?
|
||||||
|
.expecting(HttpStatus.NO_CONTENT);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user