From 1cb0ea10180ecb1e68b454792e2d45e83cc49a96 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 14:14:47 +0100 Subject: [PATCH 01/38] resolve placeholders in uriPath --- .../hs/office/scenarios/ScenarioTest.java | 3 +- .../hs/office/scenarios/TemplateResolver.java | 77 +++++++++++-------- .../scenarios/TemplateResolverUnitTest.java | 73 ++++++++++++++++++ .../hs/office/scenarios/UseCase.java | 12 ++- 4 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java index efc11c54..acf31795 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java @@ -12,6 +12,7 @@ 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.util.HashMap; @@ -35,7 +36,7 @@ public abstract class ScenarioTest extends ContextBasedTest { @Override public String toString() { - return uuid.toString(); + return ObjectUtils.toString(uuid); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index ccb8c96d..41507f36 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -1,9 +1,49 @@ package net.hostsharing.hsadminng.hs.office.scenarios; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Map; public class TemplateResolver { + enum PlaceholderPrefix { + RAW('%') { + @Override + String convert(final Object value) { + return value.toString(); + } + }, + JSON_QUOTED('$'){ + @Override + String convert(final Object value) { + return jsonQuoted(value); + } + }, + URI_ENCODED('&'){ + @Override + String convert(final Object value) { + return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8); + } + }; + + private final char prefixChar; + + PlaceholderPrefix(final char prefixChar) { + this.prefixChar = prefixChar; + } + + static boolean contains(final char givenChar) { + return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar); + } + + static PlaceholderPrefix ofPrefixChar(final char givenChar) { + return Arrays.stream(values()).filter(p -> p.prefixChar == givenChar).findFirst().orElseThrow(); + } + + abstract String convert(final Object value); + } + private final String template; private final Map properties; private final StringBuilder resolved = new StringBuilder(); @@ -21,7 +61,7 @@ public class TemplateResolver { private void copy() { while (hasMoreChars()) { - if ((currentChar() == '$' || currentChar() == '%') && nextChar() == '{') { + if (PlaceholderPrefix.contains(currentChar()) && nextChar() == '{') { startPlaceholder(currentChar()); } else { resolved.append(fetchChar()); @@ -41,7 +81,7 @@ public class TemplateResolver { if (currentChar() == '}') { --nested; placeholder.append(fetchChar()); - } else if ((currentChar() == '$' || currentChar() == '%') && nextChar() == '{') { + } else if (PlaceholderPrefix.contains (currentChar()) && nextChar() == '{') { ++nested; placeholder.append(fetchChar()); } else { @@ -50,11 +90,9 @@ public class TemplateResolver { } final var name = new TemplateResolver(placeholder.toString(), properties).resolve(); final var value = propVal(name); - if ( intro == '%') { - resolved.append(value); - } else { - resolved.append(optionallyQuoted(value)); - } + resolved.append( + PlaceholderPrefix.ofPrefixChar(intro).convert(value) + ); skipChar('}'); } @@ -104,7 +142,7 @@ public class TemplateResolver { return template.charAt(position+1); } - private static String optionallyQuoted(final Object value) { + private static String jsonQuoted(final Object value) { return switch (value) { case Boolean bool -> bool.toString(); case Number number -> number.toString(); @@ -112,27 +150,4 @@ public class TemplateResolver { default -> "\"" + value + "\""; }; } - - public static void main(String[] args) { - System.out.println( - new TemplateResolver(""" - etwas davor, - - ${einfacher Platzhalter}, - ${verschachtelter %{Name}}, - - und nochmal ohne Quotes: - - %{einfacher Platzhalter}, - %{verschachtelter %{Name}}, - - etwas danach. - """, - Map.ofEntries( - Map.entry("Name", "placeholder"), - Map.entry("einfacher Platzhalter", "simple placeholder"), - Map.entry("verschachtelter placeholder", "nested placeholder") - )).resolve()); - - } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java new file mode 100644 index 00000000..a3f00f2f --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java @@ -0,0 +1,73 @@ +package net.hostsharing.hsadminng.hs.office.scenarios; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class TemplateResolverUnitTest { + + @Test + void resolveTemplate() { + final var resolved = new TemplateResolver(""" + with optional JSON quotes: + + ${boolean}, + ${numeric}, + ${simple placeholder}, + ${nested %{name}}, + ${with-special-chars} + + and without quotes: + + %{boolean}, + %{numeric}, + %{simple placeholder}, + %{nested %{name}}, + %{with-special-chars} + + and uri-encoded: + + &{boolean}, + &{numeric}, + &{simple placeholder}, + &{nested %{name}}, + &{with-special-chars} + """, + Map.ofEntries( + Map.entry("name", "placeholder"), + Map.entry("boolean", true), + Map.entry("numeric", 42), + Map.entry("simple placeholder", "einfach"), + Map.entry("nested placeholder", "verschachtelt"), + Map.entry("with-special-chars", "3&3 AG") + )).resolve(); + + assertThat(resolved).isEqualTo(""" + with optional JSON quotes: + + true, + 42, + "einfach", + "verschachtelt", + "3&3 AG" + + and without quotes: + + true, + 42, + einfach, + verschachtelt, + 3&3 AG + + and uri-encoded: + + true, + 42, + einfach, + verschachtelt, + 3%263+AG + """); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 710e4ae1..bc5f58d7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -120,7 +120,8 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpGet(final String uriPath) { + public final HttpResponse httpGet(final String uriPathWithPlaceholders) { + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); final var request = HttpRequest.newBuilder() .GET() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) @@ -132,7 +133,8 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) { + public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .POST(BodyPublishers.ofString(requestBody)) @@ -146,7 +148,8 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpPatch(final String uriPath, final JsonTemplate bodyJsonTemplate) { + public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody)) @@ -160,7 +163,8 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpDelete(final String uriPath) { + public final HttpResponse httpDelete(final String uriPathWithPlaceholders) { + final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders); final var request = HttpRequest.newBuilder() .DELETE() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) -- 2.39.5 From e3027579e729c25761b029a1d5afbbe13b48cc19 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 14:15:23 +0100 Subject: [PATCH 02/38] explicitly fetch Debutor in CreateSepaMandateForDebitor --- .../scenarios/HsOfficeScenarioTests.java | 21 +++++++++++++------ .../debitor/CreateSepaMandateForDebitor.java | 20 ++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 85e86127..b3bfb049 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -173,10 +173,18 @@ class HsOfficeScenarioTests extends ScenarioTest { @Produces("SEPA-Mandate: Test AG") void shouldCreateSepaMandateForDebitor() { new CreateSepaMandateForDebitor(this) - .given("debitor", "Test AG") - .given("memberNumberSuffix", "00") - .given("validFrom", "2024-10-15") - .given("membershipFeeBillable", "true") + // existing debitor + .given("debitorNumber", "3101000") + + // new sepa-mandate + .given("mandateReference", "Test AG - main debitor") + .given("mandateAgreement", "2022-10-12") + .given("mandateValidFrom", "2024-10-15") + + // new bank-account + .given("bankAccountHolder", "Test AG - debit bank account") + .given("bankAccountIBAN", "DE02701500000000594937") + .given("bankAccountBIC", "SSKMDEMM") .doRun() .keep(); } @@ -186,8 +194,9 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("SEPA-Mandate: Test AG") void shouldInvalidateSepaMandateForDebitor() { new InvalidateSepaMandateForDebitor(this) - .given("sepaMandateUuid", "%{SEPA-Mandate: Test AG}") - .given("validUntil", "2025-09-30") + .given("debitorNumberNumber", "31010") + .given("bankAccountIBAN", "DE02701500000000594937") + .given("mandateValidUntil", "2025-09-30") .doRun(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSepaMandateForDebitor.java index 9bbba7a6..606e2320 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSepaMandateForDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSepaMandateForDebitor.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import static io.restassured.http.ContentType.JSON; import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.OK; public class CreateSepaMandateForDebitor extends UseCase { @@ -14,12 +15,19 @@ public class CreateSepaMandateForDebitor extends UseCase + httpGet("/api/hs/office/debitors?debitorNumber=&{debitorNumber}") + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].uuid") + ); + obtain("BankAccount: Test AG - debit bank account", () -> httpPost("/api/hs/office/bankaccounts", usingJsonBody(""" { - "holder": "Test AG - debit bank account", - "iban": "DE02701500000000594937", - "bic": "SSKMDEMM" + "holder": ${bankAccountHolder}, + "iban": ${bankAccountIBAN}, + "bic": ${bankAccountBIC} } """)) .expecting(CREATED).expecting(JSON) @@ -29,9 +37,9 @@ public class CreateSepaMandateForDebitor extends UseCase Date: Wed, 30 Oct 2024 14:53:08 +0100 Subject: [PATCH 03/38] generate HTML --- doc/scenarios/template.html | 124 ++++++++++++++++++++++++++++++++++++ doc/scenarios/to-html | 11 ++++ 2 files changed, 135 insertions(+) create mode 100644 doc/scenarios/template.html create mode 100755 doc/scenarios/to-html diff --git a/doc/scenarios/template.html b/doc/scenarios/template.html new file mode 100644 index 00000000..1bb16e52 --- /dev/null +++ b/doc/scenarios/template.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$for(author-meta)$ + +$endfor$ +$if(date-meta)$ + +$endif$ + $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ + +$if(quotes)$ + +$endif$ +$if(highlighting-css)$ + +$endif$ +$for(css)$ + +$endfor$ +$if(math)$ + $math$ +$endif$ +$for(header-includes)$ + $header-includes$ +$endfor$ + + + + + $if(title)$ + + $endif$ +
+
+ $if(toc)$ +
+
+ + $toc$ + +
+
+ $endif$ +
+ + $if(abstract)$ +

$abstract-title$

+ $abstract$ + $endif$ + + $for(include-before)$ + $include-before$ + $endfor$ +$body$ + $for(include-after)$ + $include-after$ + $endfor$ +
+
+
+ + + + diff --git a/doc/scenarios/to-html b/doc/scenarios/to-html new file mode 100755 index 00000000..a66ff4d0 --- /dev/null +++ b/doc/scenarios/to-html @@ -0,0 +1,11 @@ +#!/bin/bash +# This script loops over all markdown (.md) files in the current directory +# and converts each to an HTML file using pandoc using template.html. + +# Origin of the template (GPL v3.0): +# https://github.com/ryangrose/easy-pandoc-templates/blob/master/html/easy_template.html + +for file in *.md; do + pandoc "$file" --template template.html -o "${file%.md}.html" +done + -- 2.39.5 From 2eb1359fddd635f72e1fb47ee7146d75ed40ff55 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 15:05:17 +0100 Subject: [PATCH 04/38] archive scenario-test reports --- Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 5c1722d6..afcddce4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -45,6 +45,9 @@ pipeline { sourcePattern: 'src/main/java' ) + // archive scenario-test reports + archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true + // cleanup workspace cleanWs() } -- 2.39.5 From f23b619ff36041a62e9e1d9123c136090a7f82b5 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 15:12:06 +0100 Subject: [PATCH 05/38] use placeholder resolve for uriPaths in existing HTTP-calls --- .../hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java | 2 +- .../office/scenarios/debitor/DeleteSepaMandateForDebitor.java | 2 +- .../hs/office/scenarios/debitor/DontDeleteDefaultDebitor.java | 2 +- .../hsadminng/hs/office/scenarios/partner/DeletePartner.java | 2 +- .../subscription/RemoveOperationsContactFromPartner.java | 2 +- .../scenarios/subscription/UnsubscribeFromMailinglist.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java index 016f1a75..5813fe30 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java @@ -24,7 +24,7 @@ public class DeleteDebitor extends UseCase { @Override protected HttpResponse run() { - httpDelete("/api/hs/office/debitors/" + uuid("Debitor: Test AG - delete debitor")) + httpDelete("/api/hs/office/debitors/&{Debitor: Test AG - delete debitor}") .expecting(HttpStatus.NO_CONTENT); return null; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java index e5c9b94a..e9470a4e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java @@ -13,7 +13,7 @@ public class DeleteSepaMandateForDebitor extends UseCase @Override protected HttpResponse run() { - httpDelete("/api/hs/office/debitors/" + uuid("Debitor: Test AG - main debitor")) + httpDelete("/api/hs/office/debitors/&{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; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java index ae24dfd1..cd52d6ea 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java @@ -18,7 +18,7 @@ public class DeletePartner extends UseCase { @Override protected HttpResponse run() { - httpDelete("/api/hs/office/partners/" + uuid("Partner: Delete AG")) + httpDelete("/api/hs/office/partners/&{Partner: Delete AG}") .expecting(HttpStatus.NO_CONTENT); return null; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java index 64584075..6b12f2e6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java @@ -23,7 +23,7 @@ public class RemoveOperationsContactFromPartner extends UseCase Date: Wed, 30 Oct 2024 15:33:34 +0100 Subject: [PATCH 06/38] migrate name->iban in sepamandates API --- .../hs-office/hs-office-sepamandates.yaml | 2 +- ...ceSepaMandateControllerAcceptanceTest.java | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/resources/api-definition/hs-office/hs-office-sepamandates.yaml b/src/main/resources/api-definition/hs-office/hs-office-sepamandates.yaml index 3050ab79..724d8ece 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-sepamandates.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-sepamandates.yaml @@ -7,7 +7,7 @@ get: parameters: - $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/assumedRoles' - - name: name + - name: iban in: query required: false schema: diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 89b25f35..48ed0d9c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -8,7 +8,6 @@ import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountReposi import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; -import org.json.JSONException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -58,7 +57,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl class ListSepaMandates { @Test - void globalAdmin_canViewAllSepaMandates_ifNoCriteriaGiven() throws JSONException { + void globalAdmin_canViewAllSepaMandates_ifNoCriteriaGiven() { RestAssured // @formatter:off .given() @@ -97,6 +96,36 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl """)); // @formatter:on } + + @Test + void globalAdmin_canFindSepaMandateByName() { + + RestAssured // @formatter:off + .given() + .header("current-subject", "superuser-alex@hostsharing.net") + .port(port) + .when() + .get("http://localhost/api/hs/office/sepamandates?iban=DE02120300000000202051") + .then().log().all().assertThat() + .statusCode(200) + .contentType("application/json") + .log().all() + .body("", lenientlyEquals(""" + [ + { + "debitor": { "debitorNumber": 1000111 }, + "bankAccount": { + "iban": "DE02120300000000202051", + "holder": "First GmbH" + }, + "reference": "ref-10001-11", + "validFrom": "2022-10-01", + "validTo": "2026-12-31" + } + ] + """)); + // @formatter:on + } } @Nested -- 2.39.5 From 82f1ec057fd38330683e8dbacec7f118864dded8 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 15:45:42 +0100 Subject: [PATCH 07/38] explicitly fetch SEPA-Mandate in CreateSepaMandateForDebitor --- .../hs/office/scenarios/HsOfficeScenarioTests.java | 1 - .../debitor/InvalidateSepaMandateForDebitor.java | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index b3bfb049..6c727eb4 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -194,7 +194,6 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("SEPA-Mandate: Test AG") void shouldInvalidateSepaMandateForDebitor() { new InvalidateSepaMandateForDebitor(this) - .given("debitorNumberNumber", "31010") .given("bankAccountIBAN", "DE02701500000000594937") .given("mandateValidUntil", "2025-09-30") .doRun(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java index 9d13706e..c0d46034 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java @@ -15,9 +15,16 @@ public class InvalidateSepaMandateForDebitor extends UseCase + httpGet("/api/hs/office/sepamandates?iban=&{bankAccountIBAN}") + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].uuid"), + "With production data, the bank-account could be used in multiple SEPA-mandates, make sure to use the right one!" + ); + + return httpPatch("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}", usingJsonBody(""" { - "validUntil": ${validUntil} + "validUntil": ${mandateValidUntil} } """)) .expecting(OK).expecting(JSON); -- 2.39.5 From 139863ea839feeed43b6130c3999938055e4e289 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 15:46:13 +0100 Subject: [PATCH 08/38] generate HTML from scenario-test reports --- Jenkinsfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index afcddce4..f53872d2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -45,7 +45,11 @@ pipeline { sourcePattern: 'src/main/java' ) - // archive scenario-test reports + // archive scenario-test reports in HTML format + sh ''' + cd doc/scenarios + ./to-html + ''' archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true // cleanup workspace -- 2.39.5 From 922ff2e27f5cf02b05d109321352f604fe922523 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 15:55:46 +0100 Subject: [PATCH 09/38] explicitly fetch SEPA-Mandate in FinallyDeleteSepaMandateForDebitor --- .../scenarios/HsOfficeScenarioTests.java | 8 ++--- .../debitor/DeleteSepaMandateForDebitor.java | 20 ------------ .../FinallyDeleteSepaMandateForDebitor.java | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+), 24 deletions(-) delete mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 6c727eb4..4178b65a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -4,7 +4,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSepaMandateForDebitor; -import net.hostsharing.hsadminng.hs.office.scenarios.debitor.DeleteSepaMandateForDebitor; +import net.hostsharing.hsadminng.hs.office.scenarios.debitor.FinallyDeleteSepaMandateForDebitor; 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.membership.CreateMembership; @@ -202,9 +202,9 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(3109) @Requires("SEPA-Mandate: Test AG") - void shouldDeleteSepaMandateForDebitor() { - new DeleteSepaMandateForDebitor(this) - .given("sepaMandateUuid", "%{SEPA-Mandate: Test AG}") + void shouldFinallyDeleteSepaMandateForDebitor() { + new FinallyDeleteSepaMandateForDebitor(this) + .given("bankAccountIBAN", "DE02701500000000594937") .doRun(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java deleted file mode 100644 index e9470a4e..00000000 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteSepaMandateForDebitor.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.hostsharing.hsadminng.hs.office.scenarios.debitor; - -import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; -import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; -import org.springframework.http.HttpStatus; - - -public class DeleteSepaMandateForDebitor extends UseCase { - - public DeleteSepaMandateForDebitor(final ScenarioTest testSuite) { - super(testSuite); - } - - @Override - protected HttpResponse run() { - httpDelete("/api/hs/office/sepamandates/&{SEPA-Mandate: Test AG}") - .expecting(HttpStatus.NO_CONTENT); - return null; - } -} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java new file mode 100644 index 00000000..c1a95677 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java @@ -0,0 +1,31 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.debitor; + +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; + +public class FinallyDeleteSepaMandateForDebitor extends UseCase { + + public FinallyDeleteSepaMandateForDebitor(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain("SEPA-Mandate: %{bankAccountIBAN}", () -> + httpGet("/api/hs/office/sepamandates?iban=&{bankAccountIBAN}") + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].uuid"), + "With production data, the bank-account could be used in multiple SEPA-mandates, make sure to use the right one!" + ); + + // TODO.spec: When to allow actual deletion of SEPA-mandates? Add constraint accordingly. + httpDelete("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}") + .expecting(HttpStatus.NO_CONTENT); + return null; + } +} -- 2.39.5 From e554f96b141bb1839a7d2f3d8bcd374a96074ebf Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 30 Oct 2024 18:52:28 +0100 Subject: [PATCH 10/38] add pandoc to Jenkins agent Dockerfile --- etc/jenkinsAgent.Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/etc/jenkinsAgent.Dockerfile b/etc/jenkinsAgent.Dockerfile index 648e2f8e..f06e9e4f 100644 --- a/etc/jenkinsAgent.Dockerfile +++ b/etc/jenkinsAgent.Dockerfile @@ -1,10 +1,6 @@ FROM eclipse-temurin:21-jdk RUN apt-get update && \ - apt-get install -y bind9-utils && \ + apt-get install -y bind9-utils pandoc && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* - -# RUN mkdir /opt/app -# COPY japp.jar /opt -# CMD ["java", "-jar", "/opt/app/japp.jar"] -- 2.39.5 From 82900d7a808241259455bcdc6956f6fec27d80b0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 1 Nov 2024 14:23:47 +0100 Subject: [PATCH 11/38] introduce nullable properties (by ?) and drop-line (by ??) --- .../scenarios/HsOfficeScenarioTests.java | 34 +++++++++++++++++- .../hs/office/scenarios/TemplateResolver.java | 35 +++++++++++++++++-- .../scenarios/UseCaseNotImplementedYet.java | 16 +++++++++ .../scenarios/contact/AmendContactData.java | 19 ++++++++++ .../scenarios/partner/CreatePartner.java | 12 ++++--- 5 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCaseNotImplementedYet.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 4178b65a..0e55ba52 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import net.hostsharing.hsadminng.HsadminNgApplication; +import net.hostsharing.hsadminng.hs.office.scenarios.contact.AmendContactData; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSepaMandateForDebitor; @@ -44,7 +45,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1010) @Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Board of Directors"}) - void shouldCreatePartner() { + void shouldCreateLegalPersonAsPartner() { new CreatePartner(this) .given("partnerNumber", 31010) .given("personType", "LEGAL_PERSON") @@ -55,6 +56,21 @@ class HsOfficeScenarioTests extends ScenarioTest { .keep(); } + @Test + @Order(1011) + @Produces(explicitly = "Partner: Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"}) + void shouldCreateNaturalPersonAsPartner() { + new CreatePartner(this) + .given("partnerNumber", 31011) + .given("personType", "NATURAL_PERSON") + .given("givenName", "Michelle") + .given("familyName", "Matthieu") + .given("contactCaption", "Michelle Matthieu") + .given("emailAddress", "michelle.matthieu@example.org") + .doRun() + .keep(); + } + @Test @Order(1020) @Requires("Person: Test AG") @@ -106,6 +122,22 @@ class HsOfficeScenarioTests extends ScenarioTest { .doRun(); } + @Test + @Order(1100) + void shouldAmendContactData() { + new AmendContactData(this) + .given("partnerNumber", 31020) + .doRun(); + } + + @Test + @Order(1101) + void shouldReplaceContactData() { + new UseCaseNotImplementedYet(this) + .given("partnerNumber", 31020) + .doRun(); + } + @Test @Order(2010) @Requires("Partner: Test AG") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index 41507f36..13e01324 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -4,8 +4,11 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public class TemplateResolver { + private final static Pattern pattern = Pattern.compile(",\\s*([}\\]])", Pattern.MULTILINE); enum PlaceholderPrefix { RAW('%') { @@ -55,11 +58,23 @@ public class TemplateResolver { } String resolve() { - copy(); - return resolved.toString(); + final var resolved = copy(); + final var withoutDroppedLines = removeDroppedLinkes(resolved); + final var result = removeSpareCommas(withoutDroppedLines); + return result; } - private void copy() { + private static String removeSpareCommas(final String withoutDroppedLines) { + return pattern.matcher(withoutDroppedLines).replaceAll("$1"); + } + + private String removeDroppedLinkes(final String text) { + return Arrays.stream(text.split("\n")) + .filter(line -> !line.contains("<<>")) + .collect(Collectors.joining("\n")); + } + + private String copy() { while (hasMoreChars()) { if (PlaceholderPrefix.contains(currentChar()) && nextChar() == '{') { startPlaceholder(currentChar()); @@ -67,6 +82,7 @@ public class TemplateResolver { resolved.append(fetchChar()); } } + return resolved.toString(); } private boolean hasMoreChars() { @@ -97,6 +113,18 @@ public class TemplateResolver { } private Object propVal(final String name) { + if (name.endsWith("??")) { + final String pureName = name.substring(0, name.length() - "??".length()); + final var val = properties.get(pureName); + if (val == null) { + return "<<>"; + } + return val; + } + if (name.endsWith("?")) { + final String pureName = name.substring(0, name.length() - "?".length()); + return properties.get(pureName); + } final var val = properties.get(name); if (val == null) { throw new IllegalStateException("Missing required property: " + name); @@ -144,6 +172,7 @@ public class TemplateResolver { private static String jsonQuoted(final Object value) { return switch (value) { + case null -> null; case Boolean bool -> bool.toString(); case Number number -> number.toString(); case String string -> "\"" + string.replace("\n", "\\n") + "\""; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCaseNotImplementedYet.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCaseNotImplementedYet.java new file mode 100644 index 00000000..d21d6413 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCaseNotImplementedYet.java @@ -0,0 +1,16 @@ +package net.hostsharing.hsadminng.hs.office.scenarios; + +import static org.assertj.core.api.Assumptions.assumeThat; + +public class UseCaseNotImplementedYet extends UseCase { + + public UseCaseNotImplementedYet(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + assumeThat(false).isTrue(); // makes the test gray + return null; + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java new file mode 100644 index 00000000..360f0756 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -0,0 +1,19 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.contact; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; + +import static org.assertj.core.api.Assumptions.assumeThat; + +public class AmendContactData extends UseCase { + + public AmendContactData(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + assumeThat(false).isTrue(); // makes the test gray + return null; + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index c96acbdf..0aa485ad 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -28,17 +28,19 @@ public class CreatePartner extends UseCase { "Even in production data we expect this query to return just a single result." // TODO.impl: add constraint? ); - obtain("Person: %{tradeName}", () -> + obtain("Partner-Person", () -> httpPost("/api/hs/office/persons", usingJsonBody(""" { "personType": ${personType}, - "tradeName": ${tradeName} + "tradeName": ${tradeName??}, + "givenName": ${givenName??}, + "familyName": ${familyName??} } """)) .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) ); - obtain("Contact: %{tradeName} - Board of Directors", () -> + obtain("Contact: %{contactCaption}", () -> httpPost("/api/hs/office/contacts", usingJsonBody(""" { "caption": ${contactCaption}, @@ -55,8 +57,8 @@ public class CreatePartner extends UseCase { "partnerNumber": ${partnerNumber}, "partnerRel": { "anchorUuid": ${Person: Hostsharing eG}, - "holderUuid": ${Person: %{tradeName}}, - "contactUuid": ${Contact: %{tradeName} - Board of Directors} + "holderUuid": ${Partner-Person}, + "contactUuid": ${Contact: %{contactCaption}} }, "details": { "registrationOffice": "Registergericht Hamburg", -- 2.39.5 From bd9c79a39d4a62824a3d7e3b22072ee031cd3d0e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 1 Nov 2024 16:32:51 +0100 Subject: [PATCH 12/38] now use ??? and allow infix notiation for two optional values --- .../hs/office/scenarios/ScenarioTest.java | 1 - .../hs/office/scenarios/TemplateResolver.java | 49 +++++++++++-------- .../hs/office/scenarios/TestReport.java | 4 +- .../scenarios/partner/CreatePartner.java | 12 ++--- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java index acf31795..4600584a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/ScenarioTest.java @@ -69,7 +69,6 @@ public abstract class ScenarioTest extends ContextBasedTest { @AfterEach void cleanup() { // final TestInfo testInfo properties.clear(); - // FIXME: Delete all aliases as well to force HTTP GET queries in each scenario? testReport.close(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index 13e01324..494f785f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -1,5 +1,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; +import org.apache.commons.lang3.StringUtils; + import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -8,13 +10,15 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; public class TemplateResolver { - private final static Pattern pattern = Pattern.compile(",\\s*([}\\]])", Pattern.MULTILINE); + + private final static Pattern pattern = Pattern.compile(",(\\s*})", Pattern.MULTILINE); + private static final String IF_NOT_FOUND_SYMBOL = "???"; enum PlaceholderPrefix { RAW('%') { @Override String convert(final Object value) { - return value.toString(); + return value != null ? value.toString() : ""; } }, JSON_QUOTED('$'){ @@ -26,7 +30,7 @@ public class TemplateResolver { URI_ENCODED('&'){ @Override String convert(final Object value) { - return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8); + return value != null ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : ""; } }; @@ -59,21 +63,26 @@ public class TemplateResolver { String resolve() { final var resolved = copy(); - final var withoutDroppedLines = removeDroppedLinkes(resolved); - final var result = removeSpareCommas(withoutDroppedLines); + final var withoutDroppedLines = dropLinesWithNullProperties(resolved); + final var result = removeDanglingCommas(withoutDroppedLines); return result; } - private static String removeSpareCommas(final String withoutDroppedLines) { + private static String removeDanglingCommas(final String withoutDroppedLines) { return pattern.matcher(withoutDroppedLines).replaceAll("$1"); } - private String removeDroppedLinkes(final String text) { + private String dropLinesWithNullProperties(final String text) { return Arrays.stream(text.split("\n")) - .filter(line -> !line.contains("<<>")) + .filter(TemplateResolver::keepLine) .collect(Collectors.joining("\n")); } + private static boolean keepLine(final String line) { + final var trimmed = line.trim(); + return !trimmed.endsWith("null,") && !trimmed.endsWith("null"); + } + private String copy() { while (hasMoreChars()) { if (PlaceholderPrefix.contains(currentChar()) && nextChar() == '{') { @@ -113,23 +122,21 @@ public class TemplateResolver { } private Object propVal(final String name) { - if (name.endsWith("??")) { - final String pureName = name.substring(0, name.length() - "??".length()); - final var val = properties.get(pureName); + if (name.endsWith(IF_NOT_FOUND_SYMBOL)) { + final String pureName = name.substring(0, name.length() - IF_NOT_FOUND_SYMBOL.length()); + return properties.get(pureName); + } else if (name.contains(IF_NOT_FOUND_SYMBOL)) { + final var parts = StringUtils.split(name, IF_NOT_FOUND_SYMBOL); + final var head = properties.get(parts[0]); + final var tail = properties.get(parts[1]); + return head != null ? head : tail; // FIXME: What if tail is null as well? + } else { + final var val = properties.get(name); if (val == null) { - return "<<>"; + throw new IllegalStateException("Missing required property: " + name); } return val; } - if (name.endsWith("?")) { - final String pureName = name.substring(0, name.length() - "?".length()); - return properties.get(pureName); - } - final var val = properties.get(name); - if (val == null) { - throw new IllegalStateException("Missing required property: " + name); - } - return val; } private void skipChar(final char expectedChar) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java index 02123a14..8aba4319 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TestReport.java @@ -57,7 +57,9 @@ public class TestReport { } public void close() { - markdownReport.close(); + if (markdownReport != null) { + markdownReport.close(); + } } private static Object orderNumber(final Method method) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index 0aa485ad..1b408c9c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -28,13 +28,13 @@ public class CreatePartner extends UseCase { "Even in production data we expect this query to return just a single result." // TODO.impl: add constraint? ); - obtain("Partner-Person", () -> + obtain("Person: %{tradeName???%{givenName???} %{familyName???}}", () -> httpPost("/api/hs/office/persons", usingJsonBody(""" { - "personType": ${personType}, - "tradeName": ${tradeName??}, - "givenName": ${givenName??}, - "familyName": ${familyName??} + "personType": ${personType???}, + "tradeName": ${tradeName???}, + "givenName": ${givenName???}, + "familyName": ${familyName???} } """)) .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) @@ -57,7 +57,7 @@ public class CreatePartner extends UseCase { "partnerNumber": ${partnerNumber}, "partnerRel": { "anchorUuid": ${Person: Hostsharing eG}, - "holderUuid": ${Partner-Person}, + "holderUuid": ${Person: %{tradeName???%{givenName???} %{familyName???}}}, "contactUuid": ${Contact: %{contactCaption}} }, "details": { -- 2.39.5 From 96e740f0d50a679785817576957afc090443c97f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 1 Nov 2024 16:48:11 +0100 Subject: [PATCH 13/38] now use ??? and allow infix notiation for more than two alternative values --- .../hs/office/scenarios/TemplateResolver.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index 494f785f..1e0f35ae 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -6,6 +6,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -121,19 +122,24 @@ public class TemplateResolver { skipChar('}'); } - private Object propVal(final String name) { - if (name.endsWith(IF_NOT_FOUND_SYMBOL)) { - final String pureName = name.substring(0, name.length() - IF_NOT_FOUND_SYMBOL.length()); + private Object propVal(final String nameExpression) { + if (nameExpression.endsWith(IF_NOT_FOUND_SYMBOL)) { + final String pureName = nameExpression.substring(0, nameExpression.length() - IF_NOT_FOUND_SYMBOL.length()); return properties.get(pureName); - } else if (name.contains(IF_NOT_FOUND_SYMBOL)) { - final var parts = StringUtils.split(name, IF_NOT_FOUND_SYMBOL); - final var head = properties.get(parts[0]); - final var tail = properties.get(parts[1]); - return head != null ? head : tail; // FIXME: What if tail is null as well? + } else if (nameExpression.contains(IF_NOT_FOUND_SYMBOL)) { + final var parts = StringUtils.split(nameExpression, IF_NOT_FOUND_SYMBOL); + return Arrays.stream(parts).filter(Objects::nonNull).findFirst().orElseGet(() -> { + if ( parts[parts.length-1].isEmpty() ) { + // => whole expression ends with IF_NOT_FOUND_SYMBOL, thus last null element was optional + return null; + } + // => last alternative element in expression was null and not optional + throw new IllegalStateException("Missing required value in property-chain: " + nameExpression); + }); } else { - final var val = properties.get(name); + final var val = properties.get(nameExpression); if (val == null) { - throw new IllegalStateException("Missing required property: " + name); + throw new IllegalStateException("Missing required property: " + nameExpression); } return val; } -- 2.39.5 From 33ceebf1d7cf61f62cdac02f9c078bb88018b05f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 2 Nov 2024 16:14:37 +0100 Subject: [PATCH 14/38] add postalAddresss+phoneNumber to partner contacts --- .../hs/office/scenarios/HsOfficeScenarioTests.java | 10 ++++++++++ .../hs/office/scenarios/partner/CreatePartner.java | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 0e55ba52..ca9c5483 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -51,6 +51,11 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("personType", "LEGAL_PERSON") .given("tradeName", "Test AG") .given("contactCaption", "Test AG - Board of Directors") + .given("postalAddress", """ + Shanghai-Allee 1 + 20123 Hamburg + """) + .given("officePhoneNumber", "+49 40 654321-0") .given("emailAddress", "board-of-directors@test-ag.example.org") .doRun() .keep(); @@ -66,6 +71,11 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("givenName", "Michelle") .given("familyName", "Matthieu") .given("contactCaption", "Michelle Matthieu") + .given("postalAddress", """ + An der Wandse 34 + 22123 Hamburg + """) + .given("officePhoneNumber", "+49 40 123456") .given("emailAddress", "michelle.matthieu@example.org") .doRun() .keep(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index 1b408c9c..47c302c8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -44,8 +44,12 @@ public class CreatePartner extends UseCase { httpPost("/api/hs/office/contacts", usingJsonBody(""" { "caption": ${contactCaption}, + "postalAddress": ${postalAddress???}, + "phoneNumbers": { + "office": ${officePhoneNumber???} + }, "emailAddresses": { - "main": ${emailAddress} + "main": ${emailAddress???} } } """)) -- 2.39.5 From db9da74fb90a024156c1d7d7d3892b4267ccc5ea Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 2 Nov 2024 17:05:30 +0100 Subject: [PATCH 15/38] implement scenario shouldAmendContactData --- .../scenarios/HsOfficeScenarioTests.java | 4 ++- .../scenarios/contact/AmendContactData.java | 27 +++++++++++++++++-- .../debitor/CreateSelfDebitorForPartner.java | 3 +-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index ca9c5483..9677120b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -134,9 +134,11 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1100) + @Requires("Partner: Michelle Matthieu") void shouldAmendContactData() { new AmendContactData(this) - .given("partnerNumber", 31020) + .given("partnerName", "Matthieu") + .given("newEmailAddress", "michelle@matthieu.example.org") .doRun(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index 360f0756..49e611bb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -2,8 +2,10 @@ package net.hostsharing.hsadminng.hs.office.scenarios.contact; import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import org.springframework.http.HttpStatus; -import static org.assertj.core.api.Assumptions.assumeThat; +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; public class AmendContactData extends UseCase { @@ -13,7 +15,28 @@ public class AmendContactData extends UseCase { @Override protected HttpResponse run() { - assumeThat(false).isTrue(); // makes the test gray + + obtain("partnerPersonUuid", () -> + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), + "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + ); + + httpPatch("/api/hs/office/contacts/%{partnerPersonUuid}", usingJsonBody(""" + { + "caption": ${contactCaption???}, + "postalAddress": ${postalAddress???}, + "phoneNumbers": { + "office": ${officePhoneNumber???} + }, + "emailAddresses": { + "main": ${newEmailAddress???} + } + } + """)) + .expecting(HttpStatus.OK); + return null; } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java index 91a21a00..d815bc61 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java @@ -19,8 +19,7 @@ public class CreateSelfDebitorForPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].holder.uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one.", - "**HINT**: With production data, you might get multiple results and have to decide which is the right one." + "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("BankAccount: Test AG - refund bank account", () -> -- 2.39.5 From ff275ec4f313c3a409a6fb67b96442a774d816ea Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 2 Nov 2024 17:49:30 +0100 Subject: [PATCH 16/38] implement scenario shouldReplaceContactData --- .../scenarios/HsOfficeScenarioTests.java | 19 +++++-- .../scenarios/contact/AmendContactData.java | 10 ++-- .../scenarios/contact/ReplaceContactData.java | 52 +++++++++++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 9677120b..1817416b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.hs.office.scenarios.contact.AmendContactData; +import net.hostsharing.hsadminng.hs.office.scenarios.contact.ReplaceContactData; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSepaMandateForDebitor; @@ -44,19 +45,19 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1010) - @Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Board of Directors"}) + @Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"}) void shouldCreateLegalPersonAsPartner() { new CreatePartner(this) .given("partnerNumber", 31010) .given("personType", "LEGAL_PERSON") .given("tradeName", "Test AG") - .given("contactCaption", "Test AG - Board of Directors") + .given("contactCaption", "Test AG - Hamburg") .given("postalAddress", """ Shanghai-Allee 1 20123 Hamburg """) .given("officePhoneNumber", "+49 40 654321-0") - .given("emailAddress", "board-of-directors@test-ag.example.org") + .given("emailAddress", "hamburg@test-ag.example.org") .doRun() .keep(); } @@ -144,9 +145,17 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1101) + @Requires("Partner: Test AG") void shouldReplaceContactData() { - new UseCaseNotImplementedYet(this) - .given("partnerNumber", 31020) + new ReplaceContactData(this) + .given("partnerName", "Test AG") + .given("newContactCaption", "Test AG - Norden") + .given("newPostalAddress", """ + Am Hafen 11 + 26506 Norden + """) + .given("newOfficePhoneNumber", "+49 4931 654321-0") + .given("newEmailAddress", "norden@test-ag.example.org") .doRun(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index 49e611bb..0b265789 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -16,19 +16,19 @@ public class AmendContactData extends UseCase { @Override protected HttpResponse run() { - obtain("partnerPersonUuid", () -> + obtain("partnerContactuid", () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); - httpPatch("/api/hs/office/contacts/%{partnerPersonUuid}", usingJsonBody(""" + httpPatch("/api/hs/office/contacts/%{partnerContactuid}", usingJsonBody(""" { - "caption": ${contactCaption???}, - "postalAddress": ${postalAddress???}, + "caption": ${newContactCaption???}, + "postalAddress": ${newPostalAddress???}, "phoneNumbers": { - "office": ${officePhoneNumber???} + "office": ${newOfficePhoneNumber???} }, "emailAddresses": { "main": ${newEmailAddress???} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java new file mode 100644 index 00000000..4bfe91eb --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java @@ -0,0 +1,52 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.contact; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.OK; + +public class ReplaceContactData extends UseCase { + + public ReplaceContactData(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain("partnerRelationUuid", () -> + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].uuid"), + "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + ); + + obtain("Contact: %{newContactCaption}", () -> + httpPost("/api/hs/office/contacts", usingJsonBody(""" + { + "caption": ${newContactCaption}, + "postalAddress": ${newPostalAddress???}, + "phoneNumbers": { + "phone": ${newOfficePhoneNumber???} + }, + "emailAddresses": { + "main": ${newEmailAddress???} + } + } + """)) + .expecting(CREATED).expecting(JSON), + "Please check first if that contact already exists, if so, use it's UUID below." + ); + + httpPatch("/api/hs/office/relations/%{partnerRelationUuid}", usingJsonBody(""" + { + "contactUuid": ${Contact: %{newContactCaption}} + } + """)) + .expecting(OK); + + return null; + } +} -- 2.39.5 From b7351f8d31d5964e5e2afd31b79356300ffdc85a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 3 Nov 2024 08:53:58 +0100 Subject: [PATCH 17/38] fix typo in placeholder --- .../hs/office/scenarios/contact/AmendContactData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index 0b265789..e6275369 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -16,14 +16,14 @@ public class AmendContactData extends UseCase { @Override protected HttpResponse run() { - obtain("partnerContactuid", () -> + obtain("partnerContactUuid", () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); - httpPatch("/api/hs/office/contacts/%{partnerContactuid}", usingJsonBody(""" + httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "caption": ${newContactCaption???}, "postalAddress": ${newPostalAddress???}, -- 2.39.5 From 5977eaf8285742ad11ede3c5d600a213509f27eb Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 3 Nov 2024 08:54:52 +0100 Subject: [PATCH 18/38] fix grammar in hint text --- .../hsadminng/hs/office/scenarios/contact/AmendContactData.java | 2 +- .../hs/office/scenarios/contact/ReplaceContactData.java | 2 +- .../scenarios/debitor/CreateExternalDebitorForPartner.java | 2 +- .../office/scenarios/debitor/CreateSelfDebitorForPartner.java | 2 +- .../office/scenarios/partner/AddOperationsContactToPartner.java | 2 +- .../hs/office/scenarios/partner/AddRepresentativeToPartner.java | 2 +- .../subscription/RemoveOperationsContactFromPartner.java | 2 +- .../office/scenarios/subscription/SubscribeToMailinglist.java | 2 +- .../scenarios/subscription/UnsubscribeFromMailinglist.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index e6275369..273ab663 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -20,7 +20,7 @@ public class AmendContactData extends UseCase { httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java index 4bfe91eb..529885c8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java @@ -20,7 +20,7 @@ public class ReplaceContactData extends UseCase { httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("Contact: %{newContactCaption}", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateExternalDebitorForPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateExternalDebitorForPartner.java index e9afdcc2..194d4513 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateExternalDebitorForPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateExternalDebitorForPartner.java @@ -26,7 +26,7 @@ public class CreateExternalDebitorForPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("BankAccount: Billing GmbH - refund bank account", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java index d815bc61..fc16adb3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/CreateSelfDebitorForPartner.java @@ -19,7 +19,7 @@ public class CreateSelfDebitorForPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].holder.uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("BankAccount: Test AG - refund bank account", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java index 6e41ce76..0f9a9aa5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java @@ -22,7 +22,7 @@ public class AddOperationsContactToPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("Person: %{operationsContactGivenName} %{operationsContactFamilyName}", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java index cb5c8136..1e4dd156 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java @@ -22,7 +22,7 @@ public class AddRepresentativeToPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("Person: %{representativeGivenName} %{representativeFamilyName}", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java index 6b12f2e6..08f3f53d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java @@ -20,7 +20,7 @@ public class RemoveOperationsContactFromPartner extends UseCase response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); return httpDelete("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeToMailinglist.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeToMailinglist.java index 3c84603f..3e4ae74b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeToMailinglist.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeToMailinglist.java @@ -22,7 +22,7 @@ public class SubscribeToMailinglist extends UseCase { httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); obtain("Person: %{subscriberGivenName} %{subscriberFamilyName}", () -> diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java index 36e1da24..6d2b7668 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java @@ -22,7 +22,7 @@ public class UnsubscribeFromMailinglist extends UseCase response.expectArrayElements(1).getFromBody("[0].uuid"), - "In production data this query could result in multiple outputs. In that case, you have to find out which is the right one." + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); return httpDelete("/api/hs/office/relations/&{Subscription: %{subscriberEMailAddress}}") -- 2.39.5 From 848fb0aae87c1d0a6566a71bb12dc4937954159a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 3 Nov 2024 18:10:37 +0100 Subject: [PATCH 19/38] add scenario shouldAddPhoneNumberToContactData --- .../scenarios/HsOfficeScenarioTests.java | 12 +++++ .../hs/office/scenarios/UseCase.java | 20 +++++++- .../contact/AddPhoneNumberToContactData.java | 47 +++++++++++++++++++ .../scenarios/contact/AmendContactData.java | 4 +- 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 1817416b..33b96a1a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import net.hostsharing.hsadminng.HsadminNgApplication; +import net.hostsharing.hsadminng.hs.office.scenarios.contact.AddPhoneNumberToContactData; import net.hostsharing.hsadminng.hs.office.scenarios.contact.AmendContactData; import net.hostsharing.hsadminng.hs.office.scenarios.contact.ReplaceContactData; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebitorForPartner; @@ -145,6 +146,17 @@ class HsOfficeScenarioTests extends ScenarioTest { @Test @Order(1101) + @Requires("Partner: Michelle Matthieu") + void shouldAddPhoneNumberToContactData() { + new AddPhoneNumberToContactData(this) + .given("partnerName", "Matthieu") + .given("newOfficePhoneNumberKey", "mobile") + .given("newOfficePhoneNumber", "+49 152 1234567") + .doRun(); + } + + @Test + @Order(1103) @Requires("Partner: Test AG") void shouldReplaceContactData() { new ReplaceContactData(this) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index bc5f58d7..5f0706d1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -80,11 +80,16 @@ public abstract class UseCase> { } }) ); - return run(); + final var response = run(); + verify(); + return response; } protected abstract HttpResponse run(); + protected void verify() { + } + public final UseCase given(final String propName, final Object propValue) { givenProperties.put(propName, propValue); ScenarioTest.putProperty(propName, propValue); @@ -106,6 +111,17 @@ public abstract class UseCase> { }); } +// public final void validate( +// final String title, +// final Supplier http, +// final Function extractor, +// final String... extraInfo) { +// withTitle(ScenarioTest.resolve(title), () -> { +// http.get(); +// Arrays.stream(extraInfo).forEach(testReport::printPara); +// }); +// } + public final void obtain(final String alias, final Supplier http, final String... extraInfo) { withTitle(ScenarioTest.resolve(alias), () -> { http.get().keep(); @@ -267,7 +283,7 @@ public abstract class UseCase> { @SneakyThrows public String getFromBody(final String path) { - return JsonPath.parse(response.body()).read(path); + return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)); } @SneakyThrows diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java new file mode 100644 index 00000000..89d67e25 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java @@ -0,0 +1,47 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.contact; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpStatus.OK; + +public class AddPhoneNumberToContactData extends UseCase { + + public AddPhoneNumberToContactData(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain("partnerContactUuid", () -> + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." + ); + + httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" + { + "phoneNumbers": { + ${newOfficePhoneNumberKey}: ${newOfficePhoneNumber} + } + } + """)) + .expecting(HttpStatus.OK); + + return null; + } + + @Override + protected void verify() { + testSuite.testReport.printPara("### Verify"); + assertThat( + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1).getFromBody("[0].contact.phoneNumbers.%{newOfficePhoneNumberKey}") + ).isEqualTo(ScenarioTest.resolve("%{newOfficePhoneNumber}")); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index 273ab663..e4f81140 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -17,8 +17,8 @@ public class AmendContactData extends UseCase { protected HttpResponse run() { obtain("partnerContactUuid", () -> - httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) - .expecting(OK).expecting(JSON), + httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); -- 2.39.5 From 5989d4ab4f2a46a1814a7867587377834be6882a Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 07:09:21 +0100 Subject: [PATCH 20/38] add verify section --- .../hs/office/scenarios/UseCase.java | 6 +++ .../contact/AddPhoneNumberToContactData.java | 43 +++++++++++++------ .../scenarios/contact/PathAssertion.java | 19 ++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 5f0706d1..5437981e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.SneakyThrows; import net.hostsharing.hsadminng.reflection.AnnotationFinder; import org.apache.commons.collections4.map.LinkedMap; +import org.assertj.core.api.AbstractStringAssert; import org.hibernate.AssertionFailure; import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -286,6 +287,11 @@ public abstract class UseCase> { return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)); } + @SneakyThrows + public AbstractStringAssert path(final String path) { + return assertThat(getFromBody(path)); + } + @SneakyThrows private void reportRequestAndResponse(final HttpMethod httpMethod, final String uri, final String requestBody) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java index 89d67e25..1f4e6176 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java @@ -4,8 +4,10 @@ import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import org.springframework.http.HttpStatus; +import java.util.function.Consumer; +import java.util.function.Supplier; + import static io.restassured.http.ContentType.JSON; -import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.http.HttpStatus.OK; public class AddPhoneNumberToContactData extends UseCase { @@ -17,20 +19,21 @@ public class AddPhoneNumberToContactData extends UseCase - httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + obtain( + "partnerContactUuid", + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" - { - "phoneNumbers": { - ${newOfficePhoneNumberKey}: ${newOfficePhoneNumber} + { + "phoneNumbers": { + ${newOfficePhoneNumberKey}: ${newOfficePhoneNumber} + } } - } - """)) + """)) .expecting(HttpStatus.OK); return null; @@ -38,10 +41,24 @@ public class AddPhoneNumberToContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.phoneNumbers.%{newOfficePhoneNumberKey}").isEqualTo("%{newOfficePhoneNumber}") + ); + } + + private PathAssertion path(final String path) { + return new PathAssertion(path); + } + + private void validate( + final String title, + final Supplier http, + final Consumer assertion) { + testSuite.testReport.printPara("### " + title); + final var response = http.get(); + assertion.accept(response); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java new file mode 100644 index 00000000..317695c9 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java @@ -0,0 +1,19 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.contact; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; + +import java.util.function.Consumer; + +public class PathAssertion { + + private final String path; + + public PathAssertion(final String path) { + this.path = path; + } + + public Consumer isEqualTo(final String resolvableValue) { + return response -> response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue)); + } +} -- 2.39.5 From 2b81afff4d663d13685e4e3b453e276ee3bb56c2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 14:08:58 +0100 Subject: [PATCH 21/38] fix missing title and add some verification in AddPhoneNumberToContactData and ReplaceContactData --- .../hs/office/scenarios/UseCase.java | 31 ++++++++++++------- .../contact/AddPhoneNumberToContactData.java | 23 +++----------- .../scenarios/contact/PathAssertion.java | 2 +- .../scenarios/contact/ReplaceContactData.java | 24 ++++++++++---- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 5437981e..55acd226 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -6,6 +6,7 @@ import com.jayway.jsonpath.JsonPath; import io.restassured.http.ContentType; import lombok.Getter; import lombok.SneakyThrows; +import net.hostsharing.hsadminng.hs.office.scenarios.contact.PathAssertion; import net.hostsharing.hsadminng.reflection.AnnotationFinder; import org.apache.commons.collections4.map.LinkedMap; import org.assertj.core.api.AbstractStringAssert; @@ -26,6 +27,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -112,17 +114,6 @@ public abstract class UseCase> { }); } -// public final void validate( -// final String title, -// final Supplier http, -// final Function extractor, -// final String... extraInfo) { -// withTitle(ScenarioTest.resolve(title), () -> { -// http.get(); -// Arrays.stream(extraInfo).forEach(testReport::printPara); -// }); -// } - public final void obtain(final String alias, final Supplier http, final String... extraInfo) { withTitle(ScenarioTest.resolve(alias), () -> { http.get().keep(); @@ -130,7 +121,7 @@ public abstract class UseCase> { }); } - private void withTitle(final String title, final Runnable code) { + public void withTitle(final String title, final Runnable code) { this.nextTitle = title; code.run(); this.nextTitle = null; @@ -193,6 +184,20 @@ public abstract class UseCase> { return new HttpResponse(HttpMethod.DELETE, uriPath, null, response); } + protected PathAssertion path(final String path) { + return new PathAssertion(path); + } + + protected void validate( + final String title, + final Supplier.HttpResponse> http, + final Consumer.HttpResponse> assertion) { + withTitle(ScenarioTest.resolve(title), () -> { + final var response = http.get(); + assertion.accept(response); + }); + } + public final UUID uuid(final String alias) { return ScenarioTest.uuid(alias); } @@ -300,6 +305,8 @@ public abstract class UseCase> { testReport.printLine("\n### " + nextTitle + "\n"); } else if (resultAlias != null) { testReport.printLine("\n### " + resultAlias + "\n"); + } else { + testReport.printLine("\n### FIXME\n"); } // the request diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java index 1f4e6176..4764c18b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java @@ -4,8 +4,6 @@ import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import org.springframework.http.HttpStatus; -import java.util.function.Consumer; -import java.util.function.Supplier; import static io.restassured.http.ContentType.JSON; import static org.springframework.http.HttpStatus.OK; @@ -27,14 +25,16 @@ public class AddPhoneNumberToContactData extends UseCase + httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "phoneNumbers": { ${newOfficePhoneNumberKey}: ${newOfficePhoneNumber} } } """)) - .expecting(HttpStatus.OK); + .expecting(HttpStatus.OK) + ); return null; } @@ -42,23 +42,10 @@ public class AddPhoneNumberToContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].contact.phoneNumbers.%{newOfficePhoneNumberKey}").isEqualTo("%{newOfficePhoneNumber}") ); } - - private PathAssertion path(final String path) { - return new PathAssertion(path); - } - - private void validate( - final String title, - final Supplier http, - final Consumer assertion) { - testSuite.testReport.printPara("### " + title); - final var response = http.get(); - assertion.accept(response); - } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java index 317695c9..4c7bd0c2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java @@ -13,7 +13,7 @@ public class PathAssertion { this.path = path; } - public Consumer isEqualTo(final String resolvableValue) { + public Consumer.HttpResponse> isEqualTo(final String resolvableValue) { return response -> response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue)); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java index 529885c8..89faf2c5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java @@ -40,13 +40,25 @@ public class ReplaceContactData extends UseCase { "Please check first if that contact already exists, if so, use it's UUID below." ); - httpPatch("/api/hs/office/relations/%{partnerRelationUuid}", usingJsonBody(""" - { - "contactUuid": ${Contact: %{newContactCaption}} - } - """)) - .expecting(OK); + withTitle("Replace the Contact-Reference in the Partner-Relation", () -> + httpPatch("/api/hs/office/relations/%{partnerRelationUuid}", usingJsonBody(""" + { + "contactUuid": ${Contact: %{newContactCaption}} + } + """)) + .expecting(OK) + ); return null; } + + @Override + protected void verify() { + validate( + "Verify if the Contact-Relation Got Replaced in the Partner-Relation", + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.caption").isEqualTo("%{newContactCaption}") + ); + } } -- 2.39.5 From 42c4d4102e116d4e3d67a4c1f6ae4d04c826b3a1 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 17:47:32 +0100 Subject: [PATCH 22/38] implement RemovePhoneNumberFromContactData and support NULL for a null which needs to be kept in output --- .../scenarios/HsOfficeScenarioTests.java | 15 +++++- .../hs/office/scenarios/TemplateResolver.java | 5 ++ .../hs/office/scenarios/UseCase.java | 43 +++++++++++----- .../contact/AddPhoneNumberToContactData.java | 4 +- .../scenarios/contact/AmendContactData.java | 6 ++- .../scenarios/contact/PathAssertion.java | 10 +++- .../RemovePhoneNumberFromContactData.java | 50 +++++++++++++++++++ .../scenarios/contact/ReplaceContactData.java | 2 +- .../scenarios/debitor/DeleteDebitor.java | 6 ++- .../FinallyDeleteSepaMandateForDebitor.java | 6 +-- .../InvalidateSepaMandateForDebitor.java | 6 ++- .../scenarios/partner/DeletePartner.java | 6 ++- .../office/scenarios/person/CreatePerson.java | 6 ++- .../RemoveOperationsContactFromPartner.java | 16 ++++-- .../UnsubscribeFromMailinglist.java | 6 ++- 15 files changed, 147 insertions(+), 40 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java index 33b96a1a..07723a1e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/HsOfficeScenarioTests.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios; import net.hostsharing.hsadminng.HsadminNgApplication; import net.hostsharing.hsadminng.hs.office.scenarios.contact.AddPhoneNumberToContactData; import net.hostsharing.hsadminng.hs.office.scenarios.contact.AmendContactData; +import net.hostsharing.hsadminng.hs.office.scenarios.contact.RemovePhoneNumberFromContactData; import net.hostsharing.hsadminng.hs.office.scenarios.contact.ReplaceContactData; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateExternalDebitorForPartner; import net.hostsharing.hsadminng.hs.office.scenarios.debitor.CreateSelfDebitorForPartner; @@ -150,8 +151,18 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldAddPhoneNumberToContactData() { new AddPhoneNumberToContactData(this) .given("partnerName", "Matthieu") - .given("newOfficePhoneNumberKey", "mobile") - .given("newOfficePhoneNumber", "+49 152 1234567") + .given("phoneNumberKeyToAdd", "mobile") + .given("phoneNumberToAdd", "+49 152 1234567") + .doRun(); + } + + @Test + @Order(1102) + @Requires("Partner: Michelle Matthieu") + void shouldRemovePhoneNumberFromContactData() { + new RemovePhoneNumberFromContactData(this) + .given("partnerName", "Matthieu") + .given("phoneNumberKeyToRemove", "office") .doRun(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java index 1e0f35ae..aaa8855a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolver.java @@ -76,6 +76,7 @@ public class TemplateResolver { private String dropLinesWithNullProperties(final String text) { return Arrays.stream(text.split("\n")) .filter(TemplateResolver::keepLine) + .map(TemplateResolver::keptNullValues) .collect(Collectors.joining("\n")); } @@ -84,6 +85,10 @@ public class TemplateResolver { return !trimmed.endsWith("null,") && !trimmed.endsWith("null"); } + private static String keptNullValues(final String line) { + return line.replace(": NULL", ": null"); + } + private String copy() { while (hasMoreChars()) { if (PlaceholderPrefix.contains(currentChar()) && nextChar() == '{') { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 55acd226..acb1d33f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -9,7 +9,7 @@ import lombok.SneakyThrows; import net.hostsharing.hsadminng.hs.office.scenarios.contact.PathAssertion; import net.hostsharing.hsadminng.reflection.AnnotationFinder; import org.apache.commons.collections4.map.LinkedMap; -import org.assertj.core.api.AbstractStringAssert; +import org.assertj.core.api.OptionalAssert; import org.hibernate.AssertionFailure; import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; @@ -33,6 +34,7 @@ import java.util.function.Supplier; import static java.net.URLEncoder.encode; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; import static org.junit.platform.commons.util.StringUtils.isBlank; import static org.junit.platform.commons.util.StringUtils.isNotBlank; @@ -109,22 +111,25 @@ public abstract class UseCase> { final Function extractor, final String... extraInfo) { withTitle(ScenarioTest.resolve(alias), () -> { - http.get().keep(extractor); + final var response = http.get().keep(extractor); Arrays.stream(extraInfo).forEach(testReport::printPara); + return response; }); } public final void obtain(final String alias, final Supplier http, final String... extraInfo) { withTitle(ScenarioTest.resolve(alias), () -> { - http.get().keep(); + final var response = http.get().keep(); Arrays.stream(extraInfo).forEach(testReport::printPara); + return response; }); } - public void withTitle(final String title, final Runnable code) { + public HttpResponse withTitle(final String title, final Supplier code) { this.nextTitle = title; - code.run(); + final var response = code.get(); this.nextTitle = null; + return response; } @SneakyThrows @@ -190,11 +195,12 @@ public abstract class UseCase> { protected void validate( final String title, - final Supplier.HttpResponse> http, - final Consumer.HttpResponse> assertion) { + final Supplier http, + final Consumer assertion) { withTitle(ScenarioTest.resolve(title), () -> { final var response = http.get(); assertion.accept(response); + return response; }); } @@ -219,7 +225,7 @@ public abstract class UseCase> { } } - public class HttpResponse { + public final class HttpResponse { @Getter private final java.net.http.HttpResponse response; @@ -258,7 +264,7 @@ public abstract class UseCase> { return this; } - public void keep(final Function extractor) { + public HttpResponse keep(final Function extractor) { final var alias = nextTitle != null ? nextTitle : resultAlias; assertThat(alias).as("cannot keep result, no alias found").isNotNull(); @@ -266,14 +272,16 @@ public abstract class UseCase> { ScenarioTest.putAlias( alias, new ScenarioTest.Alias<>(UseCase.this.getClass(), UUID.fromString(value))); + return this; } - public void keep() { + public HttpResponse keep() { final var alias = nextTitle != null ? nextTitle : resultAlias; assertThat(alias).as("cannot keep result, no alias found").isNotNull(); ScenarioTest.putAlias( alias, new ScenarioTest.Alias<>(UseCase.this.getClass(), locationUuid)); + return this; } @SneakyThrows @@ -293,8 +301,17 @@ public abstract class UseCase> { } @SneakyThrows - public AbstractStringAssert path(final String path) { - return assertThat(getFromBody(path)); + public Optional getFromBodyAsOptional(final String path) { + try { + return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path))); + } catch (final Exception e) { + return null; // means the property did not exist at all, not that it was there with value null + } + } + + @SneakyThrows + public OptionalAssert path(final String path) { + return assertThat(getFromBodyAsOptional(path)); } @SneakyThrows @@ -306,7 +323,7 @@ public abstract class UseCase> { } else if (resultAlias != null) { testReport.printLine("\n### " + resultAlias + "\n"); } else { - testReport.printLine("\n### FIXME\n"); + fail("please wrap the http...-call in the UseCase using `withTitle(...)`"); } // the request diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java index 4764c18b..0905cd4f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java @@ -29,7 +29,7 @@ public class AddPhoneNumberToContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), - path("[0].contact.phoneNumbers.%{newOfficePhoneNumberKey}").isEqualTo("%{newOfficePhoneNumber}") + path("[0].contact.phoneNumbers.%{phoneNumberKeyToAdd}").contains("%{phoneNumberToAdd}") ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java index e4f81140..8f45d83a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AmendContactData.java @@ -23,7 +23,8 @@ public class AmendContactData extends UseCase { "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); - httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" + withTitle("Patch the New Phone Number Into the Contact", () -> + httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "caption": ${newContactCaption???}, "postalAddress": ${newPostalAddress???}, @@ -35,7 +36,8 @@ public class AmendContactData extends UseCase { } } """)) - .expecting(HttpStatus.OK); + .expecting(HttpStatus.OK) + ); return null; } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java index 4c7bd0c2..a6925fde 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios.contact; import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse; import java.util.function.Consumer; @@ -13,7 +14,12 @@ public class PathAssertion { this.path = path; } - public Consumer.HttpResponse> isEqualTo(final String resolvableValue) { - return response -> response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue)); + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Consumer contains(final String resolvableValue) { + return response -> response.path(path).contains(ScenarioTest.resolve(resolvableValue)); + } + + public Consumer doesNotExist() { + return response -> response.path(path).isNull(); // here, null Optional means key not found in JSON } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java new file mode 100644 index 00000000..55ce710a --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java @@ -0,0 +1,50 @@ +package net.hostsharing.hsadminng.hs.office.scenarios.contact; + +import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; + +public class RemovePhoneNumberFromContactData extends UseCase { + + public RemovePhoneNumberFromContactData(final ScenarioTest testSuite) { + super(testSuite); + } + + @Override + protected HttpResponse run() { + + obtain( + "partnerContactUuid", + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON), + response -> response.expectArrayElements(1).getFromBody("[0].contact.uuid"), + "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." + ); + + withTitle("Patch the Additional Phone-Number into the Contact", () -> + httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" + { + "phoneNumbers": { + ${phoneNumberKeyToRemove}: NULL + } + } + """)) + .expecting(HttpStatus.OK) + ); + + return null; + } + + @Override + protected void verify() { + validate( + "Verify if the New Phone Number Got Added", + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.phoneNumbers.%{phoneNumberKeyToRemove}").doesNotExist() + ); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java index 89faf2c5..300f5c1e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java @@ -58,7 +58,7 @@ public class ReplaceContactData extends UseCase { "Verify if the Contact-Relation Got Replaced in the Partner-Relation", () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), - path("[0].contact.caption").isEqualTo("%{newContactCaption}") + path("[0].contact.caption").contains("%{newContactCaption}") ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java index 5813fe30..19a0f159 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DeleteDebitor.java @@ -24,8 +24,10 @@ public class DeleteDebitor extends UseCase { @Override protected HttpResponse run() { - httpDelete("/api/hs/office/debitors/&{Debitor: Test AG - delete debitor}") - .expecting(HttpStatus.NO_CONTENT); + withTitle("Delete the Debitor using its UUID", () -> + httpDelete("/api/hs/office/debitors/&{Debitor: Test AG - delete debitor}") + .expecting(HttpStatus.NO_CONTENT) + ); return null; } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java index c1a95677..224a98f3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java @@ -24,8 +24,8 @@ public class FinallyDeleteSepaMandateForDebitor extends UseCase httpDelete("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}") + .expecting(HttpStatus.NO_CONTENT) + ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java index c0d46034..3af160c7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/InvalidateSepaMandateForDebitor.java @@ -22,11 +22,13 @@ public class InvalidateSepaMandateForDebitor extends UseCase + httpPatch("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}", usingJsonBody(""" { "validUntil": ${mandateValidUntil} } """)) - .expecting(OK).expecting(JSON); + .expecting(OK).expecting(JSON) + ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java index cd52d6ea..4453d959 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java @@ -18,8 +18,10 @@ public class DeletePartner extends UseCase { @Override protected HttpResponse run() { - httpDelete("/api/hs/office/partners/&{Partner: Delete AG}") - .expecting(HttpStatus.NO_CONTENT); + withTitle("Delete Partner by its UUID", () -> + httpDelete("/api/hs/office/partners/&{Partner: Delete AG}") + .expecting(HttpStatus.NO_CONTENT) + ); return null; } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/CreatePerson.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/CreatePerson.java index 56db97e8..4a43503c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/CreatePerson.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/CreatePerson.java @@ -14,12 +14,14 @@ public class CreatePerson extends UseCase { @Override protected HttpResponse run() { - return httpPost("/api/hs/office/persons", usingJsonBody(""" + return withTitle("Create the Person", () -> + httpPost("/api/hs/office/persons", usingJsonBody(""" { "personType": ${personType}, "tradeName": ${tradeName} } """)) - .expecting(HttpStatus.CREATED).expecting(ContentType.JSON); + .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) + ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java index 08f3f53d..8989cb0d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java @@ -1,7 +1,7 @@ package net.hostsharing.hsadminng.hs.office.scenarios.subscription; -import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import static io.restassured.http.ContentType.JSON; import static org.springframework.http.HttpStatus.NO_CONTENT; @@ -16,14 +16,20 @@ public class RemoveOperationsContactFromPartner extends UseCase - httpGet("/api/hs/office/relations?relationType=OPERATIONS&name=" + uriEncoded("%{operationsContactPerson}")) + obtain("Operations-Contact: %{operationsContactPerson}", + () -> + httpGet("/api/hs/office/relations?relationType=OPERATIONS&name=" + uriEncoded( + "%{operationsContactPerson}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), "In production, data this query could result in multiple outputs. In that case, you have to find out which is the right one." ); - return httpDelete("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") - .expecting(NO_CONTENT); + withTitle("Delete the Contact", () -> + httpDelete("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") + .expecting(NO_CONTENT) + ); + + return null; } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java index 6d2b7668..dfb17ea1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/UnsubscribeFromMailinglist.java @@ -25,7 +25,9 @@ public class UnsubscribeFromMailinglist extends UseCase + httpDelete("/api/hs/office/relations/&{Subscription: %{subscriberEMailAddress}}") + .expecting(NO_CONTENT) + ); } } -- 2.39.5 From 8342aedff4da5bd6e90d476cd02c51ca6d39da16 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 17:55:04 +0100 Subject: [PATCH 23/38] improve readme and cleanup --- .../{contact => }/PathAssertion.java | 4 +-- .../hsadminng/hs/office/scenarios/README.md | 25 ++++++++++++++++++- .../hs/office/scenarios/UseCase.java | 1 - 3 files changed, 25 insertions(+), 5 deletions(-) rename src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/{contact => }/PathAssertion.java (77%) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java similarity index 77% rename from src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java rename to src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java index a6925fde..ffd9df8e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/PathAssertion.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/PathAssertion.java @@ -1,7 +1,5 @@ -package net.hostsharing.hsadminng.hs.office.scenarios.contact; +package net.hostsharing.hsadminng.hs.office.scenarios; -import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; -import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse; import java.util.function.Consumer; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md index 36e7c3c8..037b151a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md @@ -63,4 +63,27 @@ Here, use-cases can be re-used, usually with different data. ### The Use-Case Itself -The use-case +The use-case is implemented by the `run()`-method which contains HTTP-calls. + +Each HTTP-call is wrapped into either `obtain(...)` to keep the result in a placeholder variable, +the variable name is also used as a title. +Or it's wrapped into a `withTitle(...)` to assign a title. + +The HTTP-call is followed by some assertions, e.g. the HTTP status and JSON-path-expression-matchers. + +Use `${...}` for placeholders which need to be replaced with JSON quotes +(e.g. strings are quoted, numbers are not), +`%{...}` for placeholders which need to be rendered raw +and `&{...}` for placeholders which need to get URI-encoded. + +Properties with null-values are removed from the JSON. +If you need to keep a null-value, e.g. to delete a property, +use `NULL` (all caps). + + +### The Use-Case Verification + +The verification-step is implemented by the `verify()`-method which usually contains a HTTP-HTTP-call. + +It can also contain a JSON-path verification to check if a certain value is in the result. + diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index acb1d33f..2788c21c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -6,7 +6,6 @@ import com.jayway.jsonpath.JsonPath; import io.restassured.http.ContentType; import lombok.Getter; import lombok.SneakyThrows; -import net.hostsharing.hsadminng.hs.office.scenarios.contact.PathAssertion; import net.hostsharing.hsadminng.reflection.AnnotationFinder; import org.apache.commons.collections4.map.LinkedMap; import org.assertj.core.api.OptionalAssert; -- 2.39.5 From 691c7efc825b8142f5b669fcc65279d4ee7d6801 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 19:07:40 +0100 Subject: [PATCH 24/38] verify CreatePartner --- .../hsadminng/hs/office/scenarios/UseCase.java | 5 +++-- .../contact/AddPhoneNumberToContactData.java | 2 +- .../contact/RemovePhoneNumberFromContactData.java | 2 +- .../scenarios/contact/ReplaceContactData.java | 2 +- .../hs/office/scenarios/partner/CreatePartner.java | 14 ++++++++++++-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 2788c21c..8cbea0d6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -20,6 +20,7 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Arrays; import java.util.LinkedHashMap; @@ -192,7 +193,7 @@ public abstract class UseCase> { return new PathAssertion(path); } - protected void validate( + protected void verify( final String title, final Supplier http, final Consumer assertion) { @@ -208,7 +209,7 @@ public abstract class UseCase> { } public String uriEncoded(final String text) { - return encode(ScenarioTest.resolve(text)); + return encode(ScenarioTest.resolve(text), StandardCharsets.UTF_8); } public static class JsonTemplate { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java index 0905cd4f..3e837195 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/AddPhoneNumberToContactData.java @@ -41,7 +41,7 @@ public class AddPhoneNumberToContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java index 55ce710a..c499ee71 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/RemovePhoneNumberFromContactData.java @@ -40,7 +40,7 @@ public class RemovePhoneNumberFromContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java index 300f5c1e..b4916dda 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/contact/ReplaceContactData.java @@ -54,7 +54,7 @@ public class ReplaceContactData extends UseCase { @Override protected void verify() { - validate( + verify( "Verify if the Contact-Relation Got Replaced in the Partner-Relation", () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index 47c302c8..eb13bd4a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -28,7 +28,7 @@ public class CreatePartner extends UseCase { "Even in production data we expect this query to return just a single result." // TODO.impl: add constraint? ); - obtain("Person: %{tradeName???%{givenName???} %{familyName???}}", () -> + obtain("Person: %{%{tradeName???}???%{givenName???} %{familyName???}}", () -> httpPost("/api/hs/office/persons", usingJsonBody(""" { "personType": ${personType???}, @@ -61,7 +61,7 @@ public class CreatePartner extends UseCase { "partnerNumber": ${partnerNumber}, "partnerRel": { "anchorUuid": ${Person: Hostsharing eG}, - "holderUuid": ${Person: %{tradeName???%{givenName???} %{familyName???}}}, + "holderUuid": ${Person: %{%{tradeName???}???%{givenName???} %{familyName???}}}, "contactUuid": ${Contact: %{contactCaption}} }, "details": { @@ -72,4 +72,14 @@ public class CreatePartner extends UseCase { """)) .expecting(HttpStatus.CREATED).expecting(ContentType.JSON); } + + @Override + protected void verify() { + verify( + "Verify the New Partner Relation", + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{%{tradeName???}???%{givenName???} %{familyName???}}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.caption").contains("%{contactCaption}") + ); + } } -- 2.39.5 From 9146b79b14cdd47de9d64714cda3bfc48554d977 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Nov 2024 19:14:46 +0100 Subject: [PATCH 25/38] verify AddRepresentativeToPartner --- .../scenarios/partner/AddRepresentativeToPartner.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java index 1e4dd156..71ebd675 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java @@ -65,4 +65,14 @@ public class AddRepresentativeToPartner extends UseCase httpGet("/api/hs/office/relations?relationType=REPRESENTATIVE&personData=" + uriEncoded("%{representativeFamilyName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.caption").contains("%{representativeGivenName} %{representativeFamilyName}") + ); + } } -- 2.39.5 From 09b104bb83a7325efca550c816f5585e1094dd53 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 08:49:33 +0100 Subject: [PATCH 26/38] verify AddOperationsContactToPartner --- .../partner/AddOperationsContactToPartner.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java index 0f9a9aa5..6c1fd1dd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddOperationsContactToPartner.java @@ -64,4 +64,15 @@ public class AddOperationsContactToPartner extends UseCase httpGet("/api/hs/office/relations?relationType=OPERATIONS&personData=" + uriEncoded( + "%{operationsContactFamilyName}")) + .expecting(OK).expecting(JSON).expectArrayElements(1), + path("[0].contact.caption").contains("%{operationsContactGivenName} %{operationsContactFamilyName}") + ); + } } -- 2.39.5 From f3c52c417b70041e39193f9700054a271cf4fc7f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 09:30:24 +0100 Subject: [PATCH 27/38] verify RemoveOperationsContactFromPartner --- .../hsadminng/hs/office/scenarios/UseCase.java | 4 ++-- .../partner/AddRepresentativeToPartner.java | 2 +- .../hs/office/scenarios/partner/CreatePartner.java | 3 +-- .../RemoveOperationsContactFromPartner.java | 12 ++++++++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java index 8cbea0d6..77aa7b93 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/UseCase.java @@ -196,10 +196,10 @@ public abstract class UseCase> { protected void verify( final String title, final Supplier http, - final Consumer assertion) { + final Consumer... assertions) { withTitle(ScenarioTest.resolve(title), () -> { final var response = http.get(); - assertion.accept(response); + Arrays.stream(assertions).forEach(assertion -> assertion.accept(response)); return response; }); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java index 71ebd675..1914cbb3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/AddRepresentativeToPartner.java @@ -69,7 +69,7 @@ public class AddRepresentativeToPartner extends UseCase httpGet("/api/hs/office/relations?relationType=REPRESENTATIVE&personData=" + uriEncoded("%{representativeFamilyName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].contact.caption").contains("%{representativeGivenName} %{representativeFamilyName}") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index eb13bd4a..fa952023 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -78,8 +78,7 @@ public class CreatePartner extends UseCase { verify( "Verify the New Partner Relation", () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{%{tradeName???}???%{givenName???} %{familyName???}}")) - .expecting(OK).expecting(JSON).expectArrayElements(1), - path("[0].contact.caption").contains("%{contactCaption}") + .expecting(OK).expecting(JSON).expectArrayElements(1) ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java index 8989cb0d..0b199a55 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java @@ -25,11 +25,19 @@ public class RemoveOperationsContactFromPartner extends UseCase + return withTitle("Delete the Contact", () -> httpDelete("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") .expecting(NO_CONTENT) ); + } - return null; + @Override + protected void verify() { + verify( + "Verify the New OPERATIONS Relation", + () -> httpGet("/api/hs/office/relations?relationType=OPERATIONS&personData=" + uriEncoded( + "%{operationsContactFamilyName}")) + .expecting(OK).expecting(JSON).expectArrayElements(0) + ); } } -- 2.39.5 From 1a14d2e19e920771cf636121b409f99df8124524 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 09:30:45 +0100 Subject: [PATCH 28/38] migrate to-html -> build.gradle --- Jenkinsfile | 3 +-- build.gradle | 42 ++++++++++++++++++++++++++++++++++++++++++ doc/scenarios/to-html | 11 ----------- 3 files changed, 43 insertions(+), 13 deletions(-) delete mode 100755 doc/scenarios/to-html diff --git a/Jenkinsfile b/Jenkinsfile index f53872d2..d664920f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -47,8 +47,7 @@ pipeline { // archive scenario-test reports in HTML format sh ''' - cd doc/scenarios - ./to-html + gw convertMarkdownToHtml ''' archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true diff --git a/build.gradle b/build.gradle index 96b16673..547088f5 100644 --- a/build.gradle +++ b/build.gradle @@ -391,3 +391,45 @@ tasks.named("dependencyUpdates").configure { isNonStable(it.candidate.version) } } + + +// Generate HTML from Markdown scenario-test-reports using Pandoc: +tasks.register('convertMarkdownToHtml') { + description = 'Generates HTML from Markdown scenario-test-reports using Pandoc.' + group = 'Conversion' + + // Define the template file and input directory + def templateFile = file('doc/scenarios/template.html') + + // Task configuration and execution + doFirst { + // Check if pandoc is installed + try { + exec { + commandLine 'pandoc', '--version' + } + } catch (Exception) { + throw new GradleException("Pandoc is not installed or not found in the system path.") + } + + // Check if the template file exists + if (!templateFile.exists()) { + throw new GradleException("Template file 'doc/scenarios/template.html' not found.") + } + } + + doLast { + // Gather all Markdown files in the current directory + fileTree(dir: '.', include: 'doc/scenarios/*.md').each { file -> + // Corrected way to create the output file path + def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html')) + + // Execute pandoc for each markdown file + exec { + commandLine 'pandoc', file.absolutePath, '--template', templateFile.absolutePath, '-o', outputFile.absolutePath + } + + println "Converted ${file.name} to ${outputFile.name}" + } + } +} diff --git a/doc/scenarios/to-html b/doc/scenarios/to-html deleted file mode 100755 index a66ff4d0..00000000 --- a/doc/scenarios/to-html +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# This script loops over all markdown (.md) files in the current directory -# and converts each to an HTML file using pandoc using template.html. - -# Origin of the template (GPL v3.0): -# https://github.com/ryangrose/easy-pandoc-templates/blob/master/html/easy_template.html - -for file in *.md; do - pandoc "$file" --template template.html -o "${file%.md}.html" -done - -- 2.39.5 From 5255a3f3656685bd3418b4d6064572e8b39bf2b6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 09:47:03 +0100 Subject: [PATCH 29/38] fix failing tests --- .../hs/office/scenarios/TemplateResolverUnitTest.java | 2 +- .../hs/office/scenarios/partner/CreatePartner.java | 2 +- .../subscription/RemoveOperationsContactFromPartner.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java index a3f00f2f..2d63e204 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/TemplateResolverUnitTest.java @@ -68,6 +68,6 @@ class TemplateResolverUnitTest { einfach, verschachtelt, 3%263+AG - """); + """.trim()); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java index fa952023..eb7e7d59 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/CreatePartner.java @@ -77,7 +77,7 @@ public class CreatePartner extends UseCase { protected void verify() { verify( "Verify the New Partner Relation", - () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{%{tradeName???}???%{givenName???} %{familyName???}}")) + () -> httpGet("/api/hs/office/relations?relationType=PARTNER&contactData=&{contactCaption}") .expecting(OK).expecting(JSON).expectArrayElements(1) ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java index 0b199a55..c30019e7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/RemoveOperationsContactFromPartner.java @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.UseCase; import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; @@ -35,9 +36,8 @@ public class RemoveOperationsContactFromPartner extends UseCase httpGet("/api/hs/office/relations?relationType=OPERATIONS&personData=" + uriEncoded( - "%{operationsContactFamilyName}")) - .expecting(OK).expecting(JSON).expectArrayElements(0) + () -> httpGet("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") + .expecting(NOT_FOUND) ); } } -- 2.39.5 From 8e6a47c948001195a622049559f8c796ea533a99 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 10:42:23 +0100 Subject: [PATCH 30/38] document ??? operator of template engine --- .../hostsharing/hsadminng/hs/office/scenarios/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md index 037b151a..e8384ba2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md @@ -76,9 +76,16 @@ Use `${...}` for placeholders which need to be replaced with JSON quotes `%{...}` for placeholders which need to be rendered raw and `&{...}` for placeholders which need to get URI-encoded. +If `???` is added before the closing brace, the property is optional. +This means, if it's not available in the properties, `null` is used. + Properties with null-values are removed from the JSON. If you need to keep a null-value, e.g. to delete a property, -use `NULL` (all caps). +use `NULL` (all caps) in the template (not the variable value). + +A special syntax is the infix `???`-operator like in: `${%{var1???}???%{var2???}%{var3???}}`. +In this case the first non-null value is used. + ### The Use-Case Verification -- 2.39.5 From 80ce31270c53bbd6e286b60307e1f85feb5c60fd Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 11:19:30 +0100 Subject: [PATCH 31/38] separate test stages --- Jenkinsfile | 16 ++++++++++++++-- build.gradle | 13 ++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d664920f..2f311a0a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,7 +28,19 @@ pipeline { stage ('Compile & Test') { steps { - sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze' + sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' + } + } + + stage ('Import-Tests') { + steps { + sh './gradlew importOfficeData importHostingAssets' + } + } + + stage ('Scenario-Tests') { + steps { + sh './gradlew scenrioTests' } } } @@ -47,7 +59,7 @@ pipeline { // archive scenario-test reports in HTML format sh ''' - gw convertMarkdownToHtml + ./gradlew convertMarkdownToHtml ''' archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true diff --git a/build.gradle b/build.gradle index 547088f5..ed7a290d 100644 --- a/build.gradle +++ b/build.gradle @@ -255,7 +255,7 @@ test { 'net.hostsharing.hsadminng.**.generated.**', ] useJUnitPlatform { - excludeTags 'import' + excludeTags 'importOfficeData', 'importHostingData', 'scenarioTest' } } jacocoTestReport { @@ -344,6 +344,17 @@ tasks.register('importHostingAssets', Test) { mustRunAfter spotlessJava } +tasks.register('scenarioTests', Test) { + useJUnitPlatform { + includeTags 'scenarioTest' + } + + group 'verification' + description 'run the import jobs as tests' + + mustRunAfter spotlessJava +} + // pitest mutation testing pitest { targetClasses = ['net.hostsharing.hsadminng.**'] -- 2.39.5 From 125e2274e2d9dd34ee190f1c4305302602c194ce Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 11:35:14 +0100 Subject: [PATCH 32/38] move AnnotationFinder to test buildset --- .../net/hostsharing/hsadminng/reflection/AnnotationFinder.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main => test}/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java (100%) diff --git a/src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java b/src/test/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java similarity index 100% rename from src/main/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java rename to src/test/java/net/hostsharing/hsadminng/reflection/AnnotationFinder.java -- 2.39.5 From 8f45907f5db283c25903c4d0fd98910576707ffa Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 11:54:15 +0100 Subject: [PATCH 33/38] fix spelling scenarioTests --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2f311a0a..ba92b2f7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -40,7 +40,7 @@ pipeline { stage ('Scenario-Tests') { steps { - sh './gradlew scenrioTests' + sh './gradlew scenarioTests' } } } -- 2.39.5 From 7bcf1a45b54520ab328dde0c955b5be98c342fef Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 12:24:55 +0100 Subject: [PATCH 34/38] parallel tests in pipeline --- Jenkinsfile | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ba92b2f7..047a3828 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -26,23 +26,33 @@ pipeline { } } - stage ('Compile & Test') { + stage ('Compile') { steps { - sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' + sh './gradlew clean processSpring compileJava compileTestJava --no-daemon' } } - stage ('Import-Tests') { - steps { - sh './gradlew importOfficeData importHostingAssets' - } + stage ('Tests') { + stage('Tests') { + parallel { + stage('Unit-/Integration/Acceptance-Tests') { + steps { + sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' + } + } + stage('Import-Tests') { + steps { + sh './gradlew importOfficeData importHostingAssets --no-daemon' + } + } + stage ('Scenario-Tests') { + steps { + sh './gradlew scenarioTests --no-daemon' + } + } + } } - stage ('Scenario-Tests') { - steps { - sh './gradlew scenarioTests' - } - } } post { -- 2.39.5 From 7c9b584b97fa4414850506ff7f2e47e9a6f2085f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 12:29:20 +0100 Subject: [PATCH 35/38] fix parallel tests in pipeline --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 047a3828..e15bf80a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,8 +51,8 @@ pipeline { } } } + } } - } post { -- 2.39.5 From e9bbd78617f1223881ebc661e3bd4e3bf674e0a2 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 12:32:20 +0100 Subject: [PATCH 36/38] fix parallel tests in pipeline 2 --- Jenkinsfile | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e15bf80a..ca29d193 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,22 +33,20 @@ pipeline { } stage ('Tests') { - stage('Tests') { - parallel { - stage('Unit-/Integration/Acceptance-Tests') { - steps { - sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' - } + parallel { + stage('Unit-/Integration/Acceptance-Tests') { + steps { + sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' } - stage('Import-Tests') { - steps { - sh './gradlew importOfficeData importHostingAssets --no-daemon' - } + } + stage('Import-Tests') { + steps { + sh './gradlew importOfficeData importHostingAssets --no-daemon' } - stage ('Scenario-Tests') { - steps { - sh './gradlew scenarioTests --no-daemon' - } + } + stage ('Scenario-Tests') { + steps { + sh './gradlew scenarioTests --no-daemon' } } } -- 2.39.5 From 511450f6f2ae8a952f8036716521e89c82785093 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 12:39:39 +0100 Subject: [PATCH 37/38] check stage --- Jenkinsfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index ca29d193..81ba4b84 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,6 +51,12 @@ pipeline { } } } + + stage ('Check') { + steps { + sh './gradlew check --no-daemon' + } + } } post { -- 2.39.5 From 5ed7d6cbd29c498beee77cead638c42c06a03231 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 5 Nov 2024 12:40:01 +0100 Subject: [PATCH 38/38] check stage -x pitest -x dependencyCheckAnalyze --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 81ba4b84..dc466d28 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -54,7 +54,7 @@ pipeline { stage ('Check') { steps { - sh './gradlew check --no-daemon' + sh './gradlew check -x pitest -x dependencyCheckAnalyze --no-daemon' } } } -- 2.39.5