OfficeScenarioTests CoopShares+Assets #121

Merged
hsh-michaelhoennig merged 39 commits from feature/use-case-acceptance-tests-4 into master 2024-11-15 11:54:19 +01:00
7 changed files with 136 additions and 15 deletions
Showing only changes of commit e8b74ad1c0 - Show all commits

View File

@ -46,6 +46,7 @@ public class CustomErrorResponse {
this.path = path;
this.statusCode = status.value();
this.statusPhrase = status.getReasonPhrase();
// HOWTO: debug serverside error response - set a breakpoint here
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
}
}

View File

@ -16,6 +16,8 @@ import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
import static java.util.Optional.ofNullable;
@RestController
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@ -39,7 +41,8 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
context.define(currentSubject, assumedRoles);
final var entities = ( memberNumber != null)
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
// FIXME: RestTest for the case that findMembershipByMemberNumber returns null
? ofNullable(membershipRepo.findMembershipByMemberNumber(memberNumber)).stream().toList()
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,

View File

@ -12,6 +12,7 @@ import net.hostsharing.hsadminng.hs.office.scenarios.debitor.FinallyDeleteSepaMa
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.CancelMembership;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CoopSharesTransactionUseCase;
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
import net.hostsharing.hsadminng.hs.office.scenarios.partner.AddOperationsContactToPartner;
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
@ -49,7 +50,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1010)
@Produces(explicitly = "Partner: Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"})
@Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = {"Person: Test AG", "Contact: Test AG - Hamburg"})
void shouldCreateLegalPersonAsPartner() {
new CreatePartner(this)
.given("partnerNumber", 31010)
@ -71,7 +72,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1011)
@Produces(explicitly = "Partner: Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"})
@Produces(explicitly = "Partner: P-31011 - Michelle Matthieu", implicitly = {"Person: Michelle Matthieu", "Contact: Michelle Matthieu"})
void shouldCreateNaturalPersonAsPartner() {
new CreatePartner(this)
.given("partnerNumber", 31011)
@ -148,7 +149,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1100)
@Requires("Partner: Michelle Matthieu")
@Requires("Partner: P-31011 - Michelle Matthieu")
void shouldAmendContactData() {
new AmendContactData(this)
.given("partnerName", "Matthieu")
@ -158,7 +159,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1101)
@Requires("Partner: Michelle Matthieu")
@Requires("Partner: P-31011 - Michelle Matthieu")
void shouldAddPhoneNumberToContactData() {
new AddPhoneNumberToContactData(this)
.given("partnerName", "Matthieu")
@ -169,7 +170,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1102)
@Requires("Partner: Michelle Matthieu")
@Requires("Partner: P-31011 - Michelle Matthieu")
void shouldRemovePhoneNumberFromContactData() {
new RemovePhoneNumberFromContactData(this)
.given("partnerName", "Matthieu")
@ -179,7 +180,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1103)
@Requires("Partner: Test AG")
@Requires("Partner: P-31010 - Test AG")
void shouldReplaceContactData() {
new ReplaceContactData(this)
.given("partnerName", "Test AG")
@ -201,7 +202,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(1201)
@Requires("Partner: Michelle Matthieu")
@Requires("Partner: P-31011 - Michelle Matthieu")
void shouldUpdatePersonData() {
new ShouldUpdatePersonData(this)
.given("oldFamilyName", "Matthieu")
@ -211,7 +212,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(2010)
@Requires("Partner: Test AG")
@Requires("Partner: P-31010 - Test AG")
@Produces("Debitor: Test AG - main debitor")
void shouldCreateSelfDebitorForPartner() {
new CreateSelfDebitorForPartner(this, "Debitor: Test AG - main debitor")
@ -313,7 +314,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
@Test
@Order(4000)
@Requires("Partner: Test AG")
@Requires("Partner: P-31010 - Test AG")
@Produces("Membership: Test AG 00")
void shouldCreateMembershipForPartner() {
new CreateMembership(this)
@ -326,6 +327,51 @@ class HsOfficeScenarioTests extends ScenarioTest {
.keep();
}
@Test
@Order(4200)
@Requires("Membership: Test AG 00")
@Produces("Coop-Shares SUBSCRIPTION Transaction")
void testCoopSharesSubscriptionTransaction() {
new CoopSharesTransactionUseCase(this)
.given("memberNumber", "3101000")
.given("transactionType", "SUBSCRIPTION")
.given("reference", "sign 2024-01-15")
.given("shareCount", "100")
.given("comment", "Signing the Membership")
.given("transactionDate", "2024-01-15")
.doRun();
}
@Test
@Order(4201)
@Requires("Coop-Shares SUBSCRIPTION Transaction")
@Produces("Coop-Shares ADJUSTMENT Transaction")
void testCoopSharesAdjustmentTransaction() {
new CoopSharesTransactionUseCase(this)
.given("memberNumber", "3102000")
.given("transactionType", "ADJUSTMENT")
.given("reference", "adjust 2024-01-16")
.given("shareCount", "-90")
.given("comment", "Cancelling 90 Shares, correcting wrong number of digits in subscription")
.given("transactionDate", "2024-01-16")
.doRun();
}
@Test
@Order(4202)
@Requires("Coop-Shares SUBSCRIPTION Transaction")
@Produces("Coop-Shares CANCELLATION Transaction")
void testCoopSharesCancellationTransaction() {
new CoopSharesTransactionUseCase(this)
.given("memberNumber", "3102000")
.given("transactionType", "CANCELLATION")
.given("reference", "cancel 2024-01-15")
.given("shareCount", "8")
.given("comment", "Cancelling 8 Shares")
.given("transactionDate", "2024-02-15")
.doRun();
}
@Test
@Order(4900)
@Requires("Membership: Test AG 00")

View File

@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse;
import java.util.function.Consumer;
import static org.junit.jupiter.api.Assertions.fail;
public class PathAssertion {
private final String path;
@ -14,10 +16,25 @@ public class PathAssertion {
@SuppressWarnings({ "unchecked", "rawtypes" })
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
return response -> response.path(path).contains(ScenarioTest.resolve(resolvableValue));
return response -> {
try {
// FIXME: typed check instead of .map(Object::toString) possible?
response.path(path).map(Object::toString).contains(ScenarioTest.resolve(resolvableValue));
} catch (final AssertionError e) {
// without this, the error message is often lacking important context
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
}
};
}
public Consumer<HttpResponse> doesNotExist() {
return response -> response.path(path).isNull(); // here, null Optional means key not found in JSON
return response -> {
try {
response.path(path).isNull(); // here, null Optional means key not found in JSON
} catch (final AssertionError e) {
// without this, the error message is often lacking important context
fail(e.getMessage() + " in `path(\"" + path + "\").doesNotExist()`" );
}
};
}
}

View File

@ -302,16 +302,17 @@ public abstract class UseCase<T extends UseCase<?>> {
}
@SneakyThrows
public Optional<String> getFromBodyAsOptional(final String path) {
public <T> Optional<T> getFromBodyAsOptional(final String path) {
try {
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path)));
} catch (final Exception e) {
// FIXME: catch more precise exception class
return null; // means the property did not exist at all, not that it was there with value null
}
}
@SneakyThrows
public OptionalAssert<String> path(final String path) {
public <T> OptionalAssert<T> path(final String path) {
return assertThat(getFromBodyAsOptional(path));
}

View File

@ -0,0 +1,51 @@
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
import io.restassured.http.ContentType;
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 CoopSharesTransactionUseCase extends UseCase<CoopSharesTransactionUseCase> {
public CoopSharesTransactionUseCase(final ScenarioTest testSuite) {
super(testSuite);
}
@Override
protected HttpResponse run() {
obtain("membershipUuid", () ->
httpGet("/api/hs/office/memberships?memberNumber=&{memberNumber}")
.expecting(OK).expecting(JSON).expectArrayElements(1),
response -> response.getFromBody("$[0].uuid")
);
return httpPost("/api/hs/office/coopsharestransactions", usingJsonBody("""
{
"membership.uuid": ${membershipUuid},
"transactionType": ${transactionType},
"reference": ${reference},
"shareCount": ${shareCount},
"comment": ${comment},
"valueDate": ${transactionDate}
}
"""))
.expecting(HttpStatus.CREATED).expecting(ContentType.JSON);
}
@Override
protected void verify(final HttpResponse response) {
verify("Verify Coop-Shares %{transactionType}-Transaction",
() -> httpGet("/api/hs/office/coopsharestransactions/" + response.getLocationUuid())
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
// path("transactionType").contains("%{transactionType}"),
// path("memberNumber").contains("%{memberNumber}"),
path("shareCount").contains("%{shareCount}"),
path("comment").contains("%{comment}"),
path("valueDate").contains("%{transactionDate}")
);
}
}

View File

@ -16,9 +16,11 @@ public class CreateMembership extends UseCase<CreateMembership> {
@Override
protected HttpResponse run() {
// FIXME: httpGet "partner.uuid": ${Partner: Test AG}
return httpPost("/api/hs/office/memberships", usingJsonBody("""
{
"partner.uuid": ${Partner: Test AG},
"partner.uuid": ${Partner: P-31010 - Test AG},
"memberNumberSuffix": ${memberNumberSuffix},
"status": "ACTIVE",
"validFrom": ${validFrom},