From d282885cc9a85d6c0d864cd3f05e475ae07952c6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 10 Nov 2025 11:03:26 +0100 Subject: [PATCH] Story #374 [ACCOUNTS] create initial hsadminng-profile for (new/existing) person (#208) Co-authored-by: Michael Hoennig Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/208 Reviewed-by: Timotheus Pokorra Co-authored-by: Michael Hoennig Co-committed-by: Michael Hoennig --- build.gradle.kts | 16 +- .../hs/accounts/HsProfileController.java | 69 ++++-- .../hs/accounts/HsProfileEntity.java | 3 + .../accounts/profile-schemas.yaml | 9 +- .../HsProfileControllerAcceptanceTest.java | 6 +- .../scenarios/BaseProfileUseCase.java | 9 +- ...va => CreateProfileForExistingPerson.java} | 15 +- .../scenarios/CreateProfileForNewPerson.java | 72 ++++++ .../accounts/scenarios/CurrentLoginUser.java | 3 +- .../scenarios/ProfileScenarioTests.java | 231 ++++++++++++------ .../hs/accounts/scenarios/UpdateProfile.java | 49 ++-- .../scenarios/HsOfficeScenarioTests.java | 121 ++++----- .../contact/AddPhoneNumberToContactData.java | 11 +- .../scenarios/contact/AmendContactData.java | 9 +- .../RemovePhoneNumberFromContactData.java | 11 +- .../scenarios/contact/ReplaceContactData.java | 13 +- .../CreateExternalDebitorForPartner.java | 9 +- .../debitor/CreateSelfDebitorForPartner.java | 9 +- ...torForPartnerWithIdenticalContactData.java | 5 +- .../debitor/CreateSepaMandateForDebitor.java | 7 +- .../scenarios/debitor/DeleteDebitor.java | 7 +- .../debitor/DontDeleteDefaultDebitor.java | 4 +- .../FinallyDeleteSepaMandateForDebitor.java | 6 +- .../InvalidateSepaMandateForDebitor.java | 5 +- .../membership/CancelMembership.java | 7 +- .../membership/CreateMembership.java | 7 +- ...teCoopAssetsRevertTransferTransaction.java | 5 +- .../CreateCoopAssetsTransaction.java | 7 +- .../CreateCoopSharesTransaction.java | 7 +- .../AddOperationsContactToPartner.java | 11 +- .../partner/AddRepresentativeToPartner.java | 11 +- .../scenarios/partner/CreatePartner.java | 11 +- .../scenarios/partner/DeletePartner.java | 7 +- ...ceDeceasedPartnerWithCommunityOfHeirs.java | 20 +- .../office/scenarios/person/CreatePerson.java | 4 +- .../person/ShouldUpdatePersonData.java | 12 +- .../RemoveOperationsContactFromPartner.java | 7 +- ...ExistingPersonAndContactToMailinglist.java | 9 +- ...cribeNewPersonAndContactToMailinglist.java | 5 +- .../UnsubscribeFromMailinglist.java | 5 +- .../hsadminng/hs/scenarios/FakeLoginUser.java | 24 ++ .../hsadminng/hs/scenarios/Requires.java | 2 +- .../hsadminng/hs/scenarios/ScenarioTest.java | 40 +-- .../hsadminng/hs/scenarios/UseCase.java | 40 ++- 44 files changed, 598 insertions(+), 342 deletions(-) rename src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/{CreateProfile.java => CreateProfileForExistingPerson.java} (76%) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForNewPerson.java create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/scenarios/FakeLoginUser.java diff --git a/build.gradle.kts b/build.gradle.kts index e7a9260f..c9f0802a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -285,6 +285,7 @@ tasks.compileJava { configure { java { // Configure formatting steps + removeUnusedImports() leadingTabsToSpaces(4) endWithNewline() @@ -429,6 +430,20 @@ tasks.named("jacocoTestCoverageVerification") { } } +// HOWTO: run all unit-tests - this is useful in an IDE: gw-test anyTest +tasks.register("anyTest") { + useJUnitPlatform() + + testLogging { + events("passed", "skipped", "failed", "standardOut", "standardError") + showStandardStreams = true + } + + group = "verification" + description = "runs all unit-tests which do not need a database" + + mustRunAfter(tasks.named("spotlessJava")) +} // HOWTO: run all unit-tests which don't need a database: gw-test unitTest tasks.register("unitTest") { @@ -591,7 +606,6 @@ tasks.named("dependencyUpdates") { } } - // Generate HTML from Markdown scenario-test-reports using Pandoc: tasks.register("convertMarkdownToHtml") { description = "Generates HTML from Markdown scenario-test-reports using Pandoc." diff --git a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileController.java b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileController.java index c17de533..6afb5a8a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileController.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.accounts; import io.micrometer.core.annotation.Timed; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import lombok.AllArgsConstructor; import lombok.val; import net.hostsharing.hsadminng.accounts.generated.api.v1.api.ProfileApi; import net.hostsharing.hsadminng.accounts.generated.api.v1.model.CurrentLoginUserResource; @@ -13,7 +14,9 @@ import net.hostsharing.hsadminng.accounts.generated.api.v1.model.RbacSubjectReso import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ScopeResource; import net.hostsharing.hsadminng.config.MessageTranslator; import net.hostsharing.hsadminng.errors.ForbiddenException; +import net.hostsharing.hsadminng.errors.Validate; import net.hostsharing.hsadminng.hs.office.person.HsOfficePerson; +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.mapper.StrictMapper; @@ -23,15 +26,18 @@ import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity; import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository; import net.hostsharing.hsadminng.rbac.subject.RealSubjectEntity; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import jakarta.persistence.EntityNotFoundException; import jakarta.validation.ValidationException; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -107,17 +113,19 @@ public class HsProfileController implements ProfileApi { final ProfileInsertResource body ) { context.define(); // without assumed roles, otherwise we cannot access the subject anymore + final LoginContext originalLoginContext = new LoginContext(context); // first create and save the subject to get its UUID val newlySavedSubject = createSubject(body.getNickname()); + // switch to the new subject to get access to its own subject RBAC object + context.define("activate newly created self-service subject", null, body.getNickname(), null); + // afterward, create and save the profile entity with the subject's UUID val newProfileEntity = mapper.map( body, HsProfileEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); - validateOnCreate(newProfileEntity); + validateOnCreate(originalLoginContext, newProfileEntity); - // switch to the new subject to get access to its own subject RBAC object - context.define("activate newly created self-service subject", null, body.getNickname(), null); newProfileEntity.setSubject(em.merge(newlySavedSubject)); // attached to EM by the new subject em.persist(newProfileEntity); // newProfileEntity.uuid == newlySavedSubject.uuid => do not use repository! @@ -154,10 +162,11 @@ public class HsProfileController implements ProfileApi { final ProfilePatchResource body ) { context.define(); // without assumed roles, otherwise we cannot access the subject anymore + final LoginContext originalLoginContext = new LoginContext(context); val current = profileRepo.findByUuid(profileUuid).orElseThrow(); - validateBeforePatch(current, body); + validateBeforePatch(originalLoginContext, current, body); new HsProfileEntityPatcher(scopeMapper, current).apply(body); validateOnUpdate(current); @@ -187,13 +196,15 @@ public class HsProfileController implements ProfileApi { return ResponseEntity.ok(result); } - private void validateBeforePatch(final HsProfileEntity current, final ProfilePatchResource body) { + private void validateBeforePatch(final LoginContext originalLoginContext, final HsProfileEntity current, final ProfilePatchResource body) { + validateReferencedPersonToBeRepresentedByLoginUserPerson(originalLoginContext, current); + if (!context.isGlobalAdmin() && !current.isActive() && body.getActive()) throw new ForbiddenException("Only global admins are allowed to activate an inactive profile"); } - private void validateOnCreate(final HsProfileEntity newProfileEntity) { - validateReferencedPersonToBeRepresentedByLoginUserPerson(newProfileEntity); + private void validateOnCreate(final LoginContext originalLoginContext, final HsProfileEntity newProfileEntity) { + validateReferencedPersonToBeRepresentedByLoginUserPerson(originalLoginContext, newProfileEntity); validateNormalUsersOnlyAccessPublicScopes(newProfileEntity); validateNaturalPersonRequirementOfScopes(newProfileEntity); } @@ -208,20 +219,16 @@ public class HsProfileController implements ProfileApi { validateOwnHsadminProfileMustNotBeRemoved(profileEntity); } - private void validateReferencedPersonToBeRepresentedByLoginUserPerson(final HsProfileEntity newProfileEntity) { - if (context.isGlobalAdmin()) { + private void validateReferencedPersonToBeRepresentedByLoginUserPerson(final LoginContext originalLoginContext, final HsProfileEntity profileEntity) { + if (originalLoginContext.isGlobalAdmin) { return; } - val referredPersonUuid = newProfileEntity.getPerson().getUuid(); - val currentSubjectUuid = context.fetchCurrentSubjectUuid(); - val loginPersonUuid = profileRepo.findByUuid(currentSubjectUuid) - .map(HsProfileEntity::getPerson) - .map(HsOfficePerson::getUuid) - .orElseThrow(); + val referredPersonUuid = profileEntity.getPerson().getUuid(); + val loginPersonUuid = originalLoginContext.profile.getPerson().getUuid(); val representedPersonUuids = realPersonRepo.findPersonsRepresentedByPersonWithUuid(loginPersonUuid) .stream().map(HsOfficePerson::getUuid).toList(); if ( !representedPersonUuids.contains(referredPersonUuid)) { - throw new ValidationException( + throw new ForbiddenException( messageTranslator.translate( "profile.access-denied-to-person-with-uuid-{0}-not-represented-by-currently-logged-in-person", loginPersonUuid)); @@ -233,7 +240,7 @@ public class HsProfileController implements ProfileApi { .filter(c -> !c.isPublicAccess() && !context.isGlobalAdmin() ) .toList(); if (!forbiddenScopes.isEmpty()) { - throw new ValidationException( + throw new ForbiddenException( messageTranslator.translate( "profile.access-denied-for-scopes-{0}", toDisplay(forbiddenScopes) @@ -330,7 +337,19 @@ public class HsProfileController implements ProfileApi { } final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - val person = realPersonRepo.findByUuid(resource.getPersonUuid()).orElseThrow( + + Validate.validate("person, person.uuid").exactlyOne(resource.getPerson(), resource.getPersonUuid()); + if ( resource.getPersonUuid() != null) { + entity.setPerson(realPersonRepo.findByUuid(resource.getPersonUuid()).orElseThrow( + () -> new NoSuchElementException("cannot find Person by 'person.uuid': " + resource.getPersonUuid()) + )); + } else { + entity.setPerson(realPersonRepo.save( + mapper.map(resource.getPerson(), HsOfficePersonRealEntity.class) + ) ); + } + + val person = realPersonRepo.findByUuid(entity.getPerson().getUuid()).orElseThrow( () -> new EntityNotFoundException( messageTranslator.translate("general.{0}-{1}-not-found-or-not-accessible", "personUuid", resource.getPersonUuid()) ) @@ -339,4 +358,18 @@ public class HsProfileController implements ProfileApi { entity.setScopes(scopeMapper.mapProfileToScopeEntities(resource.getScopes())); entity.setPassword(resource.getPassword()); }; + + @AllArgsConstructor + private class LoginContext { + final HsProfileEntity profile; + final boolean isGlobalAdmin; + + public LoginContext(final Context context) { + val subjectUuid = context.fetchCurrentSubjectUuid(); + profile = profileRepo.findByUuid(subjectUuid) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, + "subject " + context.fetchCurrentSubject() + " has no profile")); + isGlobalAdmin = context.isGlobalAdmin(); + } + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileEntity.java index d3b8bcaf..4493c354 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsProfileEntity.java @@ -45,6 +45,9 @@ public class HsProfileEntity implements BaseEntity, Stringifyab @MapsId @OneToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "uuid", nullable = false, updatable = false, referencedColumnName = "uuid") + // Must be the real subject, so that representative persons can access profiles+subjects of represented persons. + // Otherwise, we would also need to allow RBAC grants to subject roles. + // This also means that each access has to be checked explicitly (same subject or represented subject). private RealSubjectEntity subject; @ManyToOne(optional = false, fetch = FetchType.EAGER) diff --git a/src/main/resources/api-definition/accounts/profile-schemas.yaml b/src/main/resources/api-definition/accounts/profile-schemas.yaml index 4da9ec84..26a5aa9d 100644 --- a/src/main/resources/api-definition/accounts/profile-schemas.yaml +++ b/src/main/resources/api-definition/accounts/profile-schemas.yaml @@ -84,6 +84,8 @@ components: person.uuid: type: string format: uuid + person: + $ref: '../hs-office/hs-office-person-schemas.yaml#/components/schemas/HsOfficePersonInsert' nickname: type: string pattern: '^[a-z][a-z0-9]{1,8}-[a-z0-9]{1,10}$' # TODO.spec: pattern for login nickname @@ -112,8 +114,11 @@ components: items: $ref: 'scope-schemas.yaml#/components/schemas/Scope' required: - - person.uuid - nickname - active + # soon we might need to be able to use this: + # https://community.smartbear.com/discussions/swaggerostools/defining-conditional-attributes-in-openapi/222410 + # For now we just describe the conditionally required properties: + description: + Either `person.uuid` or `person` need to be given. additionalProperties: false - diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsProfileControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsProfileControllerAcceptanceTest.java index 25dae980..cea44dc2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsProfileControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsProfileControllerAcceptanceTest.java @@ -202,7 +202,7 @@ class HsProfileControllerAcceptanceTest extends ContextBasedTestWithCleanup { .when() .post("http://localhost/api/hs/accounts/profiles") .then().log().all().assertThat() - .statusCode(400) + .statusCode(403) .contentType("application/json") .body("message", containsString("wird von der eingeloggten Person nicht repräsentiert")); // @formatter:on @@ -246,7 +246,7 @@ class HsProfileControllerAcceptanceTest extends ContextBasedTestWithCleanup { .when() .post("http://localhost/api/hs/accounts/profiles") .then().log().all().assertThat() - .statusCode(400) + .statusCode(403) .contentType("application/json") .body("message", containsString("Zugriff auf Geltungsbereich verweigert: 'MATRIX:internal', 'SSH:internal'")); // @formatter:on @@ -326,7 +326,7 @@ class HsProfileControllerAcceptanceTest extends ContextBasedTestWithCleanup { .when() .patch("http://localhost/api/hs/accounts/profiles/" + drewProfileUuid) .then().log().all().assertThat() - .statusCode(400) + .statusCode(403) .contentType("application/json") .body("message", containsString("Zugriff auf Geltungsbereich verweigert: 'MATRIX:internal', 'SSH:internal'")); // @formatter:on diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/BaseProfileUseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/BaseProfileUseCase.java index 6595a623..c9100490 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/BaseProfileUseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/BaseProfileUseCase.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.accounts.scenarios; import lombok.SneakyThrows; import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ScopeResource; +import net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.apache.commons.lang3.tuple.Pair; @@ -9,19 +10,23 @@ import org.apache.commons.lang3.tuple.Pair; import java.util.Arrays; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public abstract class BaseProfileUseCase> extends UseCase { - public BaseProfileUseCase(final ScenarioTest testSuite) { + protected final FakeLoginUser asLoginUser; + + public BaseProfileUseCase(final ScenarioTest testSuite, final FakeLoginUser asLoginUser) { super(testSuite); + this.asLoginUser = asLoginUser; } @SneakyThrows protected ScopeResource[] fetchScopeResourcesByDescriptorPairs(final String descriptPairsVarName) { final var requestedScopes = ScenarioTest.getTypedVariable("scopes", Pair[].class); final var existingScopesJson = withTitle("Fetch Available Account Scopes", () -> - httpGet("/api/hs/accounts/scopes").expecting(OK).expecting(JSON) + httpGet(asGlobalAgent(), "/api/hs/accounts/scopes").expecting(OK).expecting(JSON) ).getResponse().body(); final var existingScopes = objectMapper.readValue(existingScopesJson, ScopeResource[].class); return Arrays.stream(requestedScopes) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfile.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForExistingPerson.java similarity index 76% rename from src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfile.java rename to src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForExistingPerson.java index 13799dd4..43c959b0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfile.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForExistingPerson.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.accounts.scenarios; import io.restassured.http.ContentType; +import net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; @@ -8,10 +9,10 @@ import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; import static org.springframework.http.HttpStatus.OK; -public class CreateProfile extends BaseProfileUseCase { +public class CreateProfileForExistingPerson extends BaseProfileUseCase { - public CreateProfile(final ScenarioTest testSuite) { - super(testSuite); + public CreateProfileForExistingPerson(final ScenarioTest testSuite, final FakeLoginUser asLoginUser) { + super(testSuite, asLoginUser); introduction("A set of profile contains the login data for an RBAC subject."); } @@ -20,7 +21,7 @@ public class CreateProfile extends BaseProfileUseCase { protected HttpResponse run() { obtain("Person: %{personGivenName} %{personFamilyName}", () -> - httpGet("/api/hs/office/persons?name=%{personFamilyName}") + httpGet(asLoginUser, "/api/hs/office/persons?name=%{personFamilyName}") .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), "In real situations we have more precise measures to find the related person." @@ -31,7 +32,7 @@ public class CreateProfile extends BaseProfileUseCase { ); return obtain("newProfile", () -> - httpPost("/api/hs/accounts/profiles", usingJsonBody(""" + httpPost(asLoginUser, "/api/hs/accounts/profiles", usingJsonBody(""" { "person.uuid": ${Person: %{personGivenName} %{personFamilyName}}, "nickname": ${nickname}, @@ -51,10 +52,10 @@ public class CreateProfile extends BaseProfileUseCase { } @Override - protected void verify(final UseCase.HttpResponse response) { + protected void verify(final UseCase.HttpResponse response) { verify( "Verify the new Profile", - () -> httpGet("/api/hs/accounts/profiles/%{newProfile}") + () -> httpGet(asLoginUser, "/api/hs/accounts/profiles/%{newProfile}") .expecting(OK).expecting(JSON), path("uuid").contains("%{newProfile}"), path("nickname").contains("%{nickname}"), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForNewPerson.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForNewPerson.java new file mode 100644 index 00000000..79b9a329 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateProfileForNewPerson.java @@ -0,0 +1,72 @@ +package net.hostsharing.hsadminng.hs.accounts.scenarios; + +import io.restassured.http.ContentType; +import net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser; +import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; +import net.hostsharing.hsadminng.hs.scenarios.UseCase; +import org.springframework.http.HttpStatus; + +import static io.restassured.http.ContentType.JSON; +import static org.springframework.http.HttpStatus.OK; + +public class CreateProfileForNewPerson extends BaseProfileUseCase { + + public CreateProfileForNewPerson(final ScenarioTest testSuite, final FakeLoginUser asLoginUser) { + super(testSuite, asLoginUser); + + introduction("A set of profile contains the login data for an RBAC subject."); + } + + @Override + protected HttpResponse run() { + + given("resolvedScopes", + fetchScopeResourcesByDescriptorPairs("scopes") + ); + + return obtain("newProfile", () -> + httpPost(asLoginUser, "/api/hs/accounts/profiles", usingJsonBody(""" + { + "person": { + "personType": "NATURAL_PERSON", + "salutation": "Hallo", + "title": null, + "givenName": ${personGivenName}, + "familyName": ${personFamilyName} + }, + "nickname": ${nickname}, + "emailAddress": ${emailAddress}, + "smsNumber": ${smsNumber}, + "password": ${password}, + "totpSecrets": @{totpSecrets}, + "phonePassword": ${phonePassword}, + "globalUid": %{globalUid}, + "globalGid": %{globalGid}, + "active": %{active}, + "scopes": @{resolvedScopes} + } + """)) + .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) + ); + } + + @Override + protected void verify(final UseCase.HttpResponse response) { + obtain("Person: %{personGivenName} %{personFamilyName}", () -> + httpGet(asLoginUser, "/api/hs/office/persons?name=%{personFamilyName}") + .expecting(OK).expecting(JSON), + personResponse -> personResponse.expectArrayElements(1).getFromBody("[0].uuid"), + "In real situations we have more precise measures to find the related person." + ); + + verify( + "Verify the new Profile", + () -> httpGet(asLoginUser, "/api/hs/accounts/profiles/%{newProfile}") + .expecting(OK).expecting(JSON), + path("uuid").contains("%{newProfile}"), + path("nickname").contains("%{nickname}"), + path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}"), + path("totpSecrets").contains("@{totpSecrets}") + ); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CurrentLoginUser.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CurrentLoginUser.java index 13df58a2..9afa4254 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CurrentLoginUser.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CurrentLoginUser.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static net.hostsharing.hsadminng.hs.scenarios.ScenarioTest.bearerTemplate; import static net.hostsharing.hsadminng.hs.scenarios.ScenarioTest.resolve; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; @@ -24,7 +25,7 @@ public class CurrentLoginUser extends UseCase { protected HttpResponse run() { obtain("Person: %{personGivenName}", () -> - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{personGivenName}")) + httpGet(asGlobalAgent(), "/api/hs/office/persons?name=" + uriEncoded("%{personGivenName}")) .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." diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/ProfileScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/ProfileScenarioTests.java index 5276b419..fa612d3d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/ProfileScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/ProfileScenarioTests.java @@ -13,103 +13,174 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.http.HttpStatus; + +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.as; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; class ProfileScenarioTests extends ScenarioTest { - @SneakyThrows - @BeforeEach - protected void beforeScenario(final TestInfo testInfo) { - super.beforeScenario(testInfo); + @SneakyThrows + @BeforeEach + protected void beforeScenario(final TestInfo testInfo) { + super.beforeScenario(testInfo); + } + + @Nested + @Order(10) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class RbacContextScenarios { + + @Test + @Order(1010) + @Produces("RBAC Context") + void shouldFetchRbacContext() { + new FetchRbacContext(scenarioTest) + .given("subjectName", "superuser-fran@hostsharing.net") + .given("assumedRoles", "rbactest.package#xxx00:ADMIN;rbactest.package#yyy00:ADMIN") + .given("expectedToBeGlobalAdmin", true) + .thenExpect(HttpStatus.OK) + .keep(); } + } - @Nested - @Order(10) - @TestMethodOrder(MethodOrderer.OrderAnnotation.class) - class RbacContextScenarios { + @Nested + @Order(20) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class CurrentLoginUserScenarios { - @Test - @Order(1010) - @Produces("RBAC Context") - void shouldFetchRbacContext() { - new FetchRbacContext(scenarioTest) - .given("subjectName", "superuser-fran@hostsharing.net") - .given("assumedRoles", "rbactest.package#xxx00:ADMIN;rbactest.package#yyy00:ADMIN") - .given("expectedToBeGlobalAdmin", true) - .doRun() - .keep(); - } + @Test + @Order(2010) + @Produces("Current Login User") + void shouldFetchCurrentLoginUser() { + new CurrentLoginUser(scenarioTest) + .given("subjectName", "superuser-fran@hostsharing.net") + .given("personGivenName", "Fran") + .given("expectedToBeGlobalAdmin", true) + .thenExpect(HttpStatus.OK) + .keep(); } + } - @Nested - @Order(20) - @TestMethodOrder(MethodOrderer.OrderAnnotation.class) - class CurrentLoginUserScenarios { + @Nested + @Order(30) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class ProfileScenarios { - @Test - @Order(2010) - @Produces("Current Login User") - void shouldFetchCurrentLoginUser() { - new CurrentLoginUser(scenarioTest) - .given("subjectName", "superuser-fran@hostsharing.net") - .given("personGivenName", "Fran") - .given("expectedToBeGlobalAdmin", true) - .doRun() - .keep(); - } - } - - @Nested - @Order(30) - @TestMethodOrder(MethodOrderer.OrderAnnotation.class) - class ProfileScenarios { - - @Test - @Order(1010) - @Produces(explicitly = "Profile: firby-susan", implicitly = { "Person: Susan Firby" }) - void shouldCreateInitialProfileForExistingNaturalPerson() { - new CreateProfile(scenarioTest) - // to find a specific existing person - .given("personFamilyName", "Firby") - .given("personGivenName", "Susan") - // a login name, to be stored in the new RBAC subject - .given("nickname", "firby-susan") - // initial profile - .given("emailAddress", "susan.firby@example.com") - .given("smsNumber", "+49123456789") - .given("password", "my raw password") - .given("totpSecrets", Array.of("initialSecret")) - .given("phonePassword", "securePass123") - .given("globalUid", 21011) - .given("globalGid", 21011) - .given("active", true) + @Test + @Order(1010) + @Produces( + explicitly = "Profile: susan-firby", + implicitly = {"Person: Susan Firby"}) + void shouldCreateInitialProfileForExistingNaturalPerson() { + new CreateProfileForExistingPerson(scenarioTest, asGlobalAgent()) + // to find a specific existing person + .given("personFamilyName", "Firby") + .given("personGivenName", "Susan") + // a login name, to be stored in the new RBAC subject + .given("nickname", "firby-susan") + // initial profile + .given("emailAddress", "susan.firby@example.com") + .given("smsNumber", "+49123456789") + .given("password", "my raw password") + .given("totpSecrets", Array.of("initialSecret")) + .given("phonePassword", "securePass123") + .given("globalUid", 21011) + .given("globalGid", 21011) + .given("active", true) .given( "scopes", Array.of( Pair.of("HSADMIN", "prod") )) - .doRun() - .keep(); - } + .thenExpect(HttpStatus.OK) + .keep(); + } - @Test - @Order(1020) - @Requires("Profile: firby-susan") - void shouldUpdateProfile() { - new UpdateProfile(scenarioTest) - // the profile to update - .given("profileUuid", "%{Profile: firby-susan}") - // updated profile - .given("active", false) - .given("totpSecrets", Array.of("initialSecret", "additionalSecret")) - .given("emailAddress", "susan.firby@example.org") - .given("password", "my new raw password") - .given("phonePassword", "securePass987") - .given("smsNumber", "+49987654321") + @Test + @Order(1020) + @Requires("Profile: susan-firby") + void naturalPersonShouldBeAbleToUpdateTheirOwnProfile() { + new UpdateProfile(scenarioTest, as("firby-susan")) + // the profile to update + .given("profileUuid", "%{Profile: susan-firby}") + // updated profile + .given("active", false) + .given("totpSecrets", Array.of("initialSecret", "additionalSecret")) + .given("emailAddress", "susan.firby@example.org") + .given("password", "my new raw password") + .given("phonePassword", "securePass987") + .given("smsNumber", "+49987654321") + .given("scopes", Array.of(Pair.of("HSADMIN", "prod"), Pair.of("SSH", "external"))) + .thenExpect(HttpStatus.OK); + } + + @Test + @Order(1100) + @Produces( + explicitly = "Profile: peter-newman", + implicitly = {"Person: Peter Newman"}) + void shouldCreateInitialProfileForNewNaturalPerson() { + new CreateProfileForNewPerson(scenarioTest, asGlobalAgent()) + // to find a specific existing person + .given("personFamilyName", "Newman") + .given("personGivenName", "Peter") + // a login name, to be stored in the new RBAC subject + .given("nickname", "newman-peter") + // initial profile + .given("emailAddress", "peter.newman@example.com") + .given("smsNumber", "+49123456789") + .given("password", "my raw password") + .given("totpSecrets", Array.of("initialSecret")) + .given("phonePassword", "securePass123") + .given("globalUid", 21012) + .given("globalGid", 21012) + .given("active", true) + .given("scopes", Array.of(Pair.of("HSADMIN", "prod"))) + .thenExpect(HttpStatus.OK) + .keep(); + } + + @Test + @Order(1110) + @Requires("Profile: peter-newman") + void newNaturalPersonShouldBeAbleToUpdateTheirOwnProfile() { + new UpdateProfile(scenarioTest, as("newman-peter")) + // the profile to update + .given("profileUuid", "%{Profile: peter-newman}") + // updated profile + .given("active", false) + .given("totpSecrets", Array.of("initialSecret", "additionalSecret")) + .given("emailAddress", "peter.newman@example.org") + .given("password", "my new raw password") + .given("phonePassword", "securePass987") + .given("smsNumber", "+49987654321") .given( "scopes", Array.of( Pair.of("HSADMIN", "prod"), - Pair.of("SSH", "internal") + Pair.of("SSH", "external") )) - .doRun(); - } + .thenExpect(HttpStatus.OK); } + + @Test + @Order(1120) + @Requires({"Profile: peter-newman", "Profile: susan-firby"}) + // Usually, scenario tests just test positive cases, but in the case of account profiles, security is extra important, + // thus I've added also some negative cases like this one for documentation reasons. + // More negative cases are tested in "so-called" Acceptance and in RestTests. + void anotherNaturalPersonShouldNotBeAbleToUpdateOthersProfile() { + new UpdateProfile(scenarioTest, as("firby-susan")) + // the profile to update + .given("profileUuid", "%{Profile: peter-newman}") + // updated profile + .given("active", false) + .given("totpSecrets", Array.of("initialSecret", "additionalSecret")) + .given("emailAddress", "peter.newman@example.org") + .given("password", "my new raw password") + .given("phonePassword", "securePass987") + .given("smsNumber", "+49987654321") + .given("scopes", Array.of(Pair.of("HSADMIN", "prod"), Pair.of("SSH", "external"))) + .thenExpect(HttpStatus.FORBIDDEN); + } + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateProfile.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateProfile.java index 17cab890..07bec358 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateProfile.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateProfile.java @@ -1,52 +1,61 @@ package net.hostsharing.hsadminng.hs.accounts.scenarios; import io.restassured.http.ContentType; +import lombok.val; +import net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static org.assertj.core.api.Fail.fail; import static org.springframework.http.HttpStatus.OK; public class UpdateProfile extends BaseProfileUseCase { - public UpdateProfile(final ScenarioTest testSuite) { - super(testSuite); + public UpdateProfile(final ScenarioTest testSuite, final FakeLoginUser asLoginUser) { + super(testSuite, asLoginUser); introduction("A set of profile contains the login data for an RBAC subject."); } @Override - protected HttpResponse run() { + protected HttpResponse run(final HttpStatus expectedStatus) { given("resolvedScopes", fetchScopeResourcesByDescriptorPairs("scopes") ); - withTitle("Patch the Changes to the existing Profile", () -> - httpPatch("/api/hs/accounts/profiles/%{profileUuid}", usingJsonBody(""" - { - "active": %{active}, - "totpSecrets": @{totpSecrets}, - "emailAddress": ${emailAddress}, - "phonePassword": ${phonePassword}, - "smsNumber": ${smsNumber}, - "scopes": @{resolvedScopes} - } - """)) - .reportWithResponse().expecting(HttpStatus.OK).expecting(ContentType.JSON) - .extractValue("nickname", "nickname") - .extractValue("totpSecrets", "totpSecrets") - ); + return withTitle("Patch the Changes to the existing Profile", () -> { + val response = httpPatch( + asLoginUser, "/api/hs/accounts/profiles/%{profileUuid}", usingJsonBody(""" + { + "active": %{active}, + "totpSecrets": @{totpSecrets}, + "emailAddress": ${emailAddress}, + "phonePassword": ${phonePassword}, + "smsNumber": ${smsNumber}, + "scopes": @{resolvedScopes} + } + """)) + .reportWithResponse().expecting(expectedStatus); - return null; + return switch (expectedStatus) { + case OK -> response.expecting(ContentType.JSON) + .extractValue("nickname", "nickname") + .extractValue("totpSecrets", "totpSecrets"); + case FORBIDDEN -> response.expecting(ContentType.JSON); + default -> fail("unexpected response: " + response); + }; + } + ); } @Override protected void verify(final UseCase.HttpResponse response) { verify( "Verify the Patched Profile", - () -> httpGet("/api/hs/accounts/profiles/%{profileUuid}") + () -> httpGet(asLoginUser, "/api/hs/accounts/profiles/%{profileUuid}") .expecting(OK).expecting(JSON), path("uuid").contains("%{newProfile}"), path("nickname").contains("%{nickname}"), 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 79ae0a9b..fd0e455e 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,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; class HsOfficeScenarioTests extends ScenarioTest { @@ -104,8 +105,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("officePhoneNumber", "+49 40 654321-0") .given("emailAddress", "hamburg@test-ag.example.org") .given("registrationOffice", "Registergericht Hamburg") - .given("registrationNumber", "1234567") - .doRun() + .given("registrationNumber", "1234567").thenExpect(HttpStatus.OK) .keep(); } @@ -132,8 +132,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("emailAddress", "michelle.matthieu@example.org") .given("birthday", "1951-03-25") .given("birthPlace", "Neustadt a.d.R.") - .given("birthName", "Eichbaum") - .doRun() + .given("birthName", "Eichbaum").thenExpect(HttpStatus.OK) .keep(); } @@ -155,8 +154,7 @@ class HsOfficeScenarioTests extends ScenarioTest { "country": "Germany" """) .given("representativePhoneNumber", "+49 40 123456") - .given("representativeEMailAddress", "tracy.trust@example.org") - .doRun() + .given("representativeEMailAddress", "tracy.trust@example.org").thenExpect(HttpStatus.OK) .keep(); } @@ -170,8 +168,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("operationsContactFamilyName", "Krause") .given("operationsContactGivenName", "Dennis") .given("operationsContactPhoneNumber", "+49 9932 587741") - .given("operationsContactEMailAddress", "dennis.krause@example.org") - .doRun() + .given("operationsContactEMailAddress", "dennis.krause@example.org").thenExpect(HttpStatus.OK) .keep(); } @@ -180,16 +177,14 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("Operations-Contact: Dennis Krause for Test AG") void shouldRemoveOperationsContactFromPartner() { new RemoveOperationsContactFromPartner(scenarioTest) - .given("operationsContactPerson", "Dennis Krause") - .doRun(); + .given("operationsContactPerson", "Dennis Krause").thenExpect(HttpStatus.OK); } @Test @Order(1090) void shouldDeletePartner() { new DeletePartner(scenarioTest) - .given("partnerNumber", "P-31020") - .doRun(); + .given("partnerNumber", "P-31020").thenExpect(HttpStatus.OK); } } @@ -204,8 +199,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldAmendContactData() { new AmendContactData(scenarioTest) .given("partnerName", "Matthieu") - .given("newEmailAddress", "michelle@matthieu.example.org") - .doRun(); + .given("newEmailAddress", "michelle@matthieu.example.org").thenExpect(HttpStatus.OK); } @Test @@ -215,8 +209,7 @@ class HsOfficeScenarioTests extends ScenarioTest { new AddPhoneNumberToContactData(scenarioTest) .given("partnerName", "Matthieu") .given("phoneNumberKeyToAdd", "mobile") - .given("phoneNumberToAdd", "+49 152 1234567") - .doRun(); + .given("phoneNumberToAdd", "+49 152 1234567").thenExpect(HttpStatus.OK); } @Test @@ -225,8 +218,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldRemovePhoneNumberFromContactData() { new RemovePhoneNumberFromContactData(scenarioTest) .given("partnerName", "Matthieu") - .given("phoneNumberKeyToRemove", "office") - .doRun(); + .given("phoneNumberKeyToRemove", "office").thenExpect(HttpStatus.OK); } @Test @@ -247,8 +239,7 @@ class HsOfficeScenarioTests extends ScenarioTest { "country": "China" """) .given("newOfficePhoneNumber", "++15 999 654321") - .given("newEmailAddress", "norden@test-ag.example.org") - .doRun(); + .given("newEmailAddress", "norden@test-ag.example.org").thenExpect(HttpStatus.OK); } } @@ -263,8 +254,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldUpdatePersonData() { new ShouldUpdatePersonData(scenarioTest) .given("oldFamilyName", "Matthieu") - .given("newFamilyName", "Matthieu-Zhang") - .doRun(); + .given("newFamilyName", "Matthieu-Zhang").thenExpect(HttpStatus.OK); } } @@ -278,14 +268,14 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("Partner: P-31011 - Michelle Matthieu") @Produces("Debitor: D-3101100 - Michelle Matthieu") void shouldCreateSelfDebitorForPartnerWithIdenticalContactData() { + // TODO.impl: could be assigned automatically but is not yet new CreateSelfDebitorForPartnerWithIdenticalContactData(scenarioTest) .given("partnerNumber", "P-31011") .given("debitorNumberSuffix", "00") // TODO.impl: could be assigned automatically but is not yet .given("billable", true) .given("vatBusiness", false) .given("vatReverseCharge", false) - .given("defaultPrefix", "mim") - .doRun() + .given("defaultPrefix", "mim").thenExpect(HttpStatus.OK) .keep(); } @@ -294,6 +284,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("Partner: P-31010 - Test AG") @Produces("Debitor: D-3101000 - Test AG - main debitor") void shouldCreateSelfDebitorForPartnerWithDistinctContactData() { + // TODO.impl: could be assigned automaticallybut is not yet new CreateSelfDebitorForPartner(scenarioTest) .given("partnerPersonTradeName", "Test AG") .given("billingContactCaption", "Test AG - billing department") @@ -304,8 +295,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("vatCountryCode", "DE") .given("vatBusiness", true) .given("vatReverseCharge", false) - .given("defaultPrefix", "tst") - .doRun() + .given("defaultPrefix", "tst").thenExpect(HttpStatus.OK) .keep(); } @@ -324,8 +314,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("vatCountryCode", "DE") .given("vatBusiness", true) .given("vatReverseCharge", false) - .given("defaultPrefix", "tsx") - .doRun() + .given("defaultPrefix", "tsx").thenExpect(HttpStatus.OK) .keep(); } @@ -344,8 +333,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("vatCountryCode", "DE") .given("vatBusiness", true) .given("vatReverseCharge", false) - .given("defaultPrefix", "tsy") - .doRun() + .given("defaultPrefix", "tsy").thenExpect(HttpStatus.OK) .keep(); } @@ -356,8 +344,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldDeleteDebitor() { new DeleteDebitor(scenarioTest) .given("partnerNumber", "P-31020") - .given("debitorSuffix", "02") - .doRun(); + .given("debitorSuffix", "02").thenExpect(HttpStatus.OK); } @Test @@ -367,8 +354,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldNotDeleteDefaultDebitor() { new DontDeleteDefaultDebitor(scenarioTest) .given("partnerNumber", "P-31010") - .given("debitorSuffix", "00") - .doRun(); + .given("debitorSuffix", "00").thenExpect(HttpStatus.OK); } } @@ -382,6 +368,9 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("Debitor: D-3101000 - Test AG - main debitor") @Produces("SEPA-Mandate: Test AG") void shouldCreateSepaMandateForDebitor() { + // existing debitor + // new sepa-mandate + // new bank-account new CreateSepaMandateForDebitor(scenarioTest) // existing debitor .given("debitorNumber", "D-3101000") @@ -394,8 +383,7 @@ class HsOfficeScenarioTests extends ScenarioTest { // new bank-account .given("bankAccountHolder", "Test AG - debit bank account") .given("bankAccountIBAN", "DE02701500000000594937") - .given("bankAccountBIC", "SSKMDEMM") - .doRun() + .given("bankAccountBIC", "SSKMDEMM").thenExpect(HttpStatus.OK) .keep(); } @@ -405,8 +393,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldInvalidateSepaMandateForDebitor() { new InvalidateSepaMandateForDebitor(scenarioTest) .given("bankAccountIBAN", "DE02701500000000594937") - .given("mandateValidTo", "2025-09-30") - .doRun(); + .given("mandateValidTo", "2025-09-30").thenExpect(HttpStatus.OK); } @Test @@ -414,8 +401,7 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("SEPA-Mandate: Test AG") void shouldFinallyDeleteSepaMandateForDebitor() { new FinallyDeleteSepaMandateForDebitor(scenarioTest) - .given("bankAccountIBAN", "DE02701500000000594937") - .doRun(); + .given("bankAccountIBAN", "DE02701500000000594937").thenExpect(HttpStatus.OK); } } @@ -433,8 +419,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("partnerName", "Test AG") .given("validFrom", "2020-10-15") .given("newStatus", "ACTIVE") - .given("membershipFeeBillable", "true") - .doRun() + .given("membershipFeeBillable", "true").thenExpect(HttpStatus.OK) .keep(); } @@ -446,8 +431,7 @@ class HsOfficeScenarioTests extends ScenarioTest { new CancelMembership(scenarioTest) .given("memberNumber", "M-3101000") .given("validTo", "2023-12-31") - .given("newStatus", "CANCELLED") - .doRun() + .given("newStatus", "CANCELLED").thenExpect(HttpStatus.OK) .keep(); } @@ -461,8 +445,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("memberNumberSuffix", "01") .given("validFrom", "2025-02-24") .given("newStatus", "ACTIVE") - .given("membershipFeeBillable", "true") - .doRun() + .given("membershipFeeBillable", "true").thenExpect(HttpStatus.OK) .keep(); } } @@ -482,8 +465,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "sign 2024-01-15") .given("shareCount", 100) .given("comment", "Signing the Membership") - .given("transactionDate", "2024-01-15") - .doRun(); + .given("transactionDate", "2024-01-15").thenExpect(HttpStatus.OK); } @Test @@ -493,8 +475,7 @@ class HsOfficeScenarioTests extends ScenarioTest { new CreateCoopSharesRevertTransaction(scenarioTest) .given("memberNumber", "M-3101000") .given("comment", "reverting some incorrect transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); + .given("dateOfIncorrectTransaction", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -507,8 +488,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "cancel 2024-01-15") .given("sharesToCancel", 8) .given("comment", "Cancelling 8 Shares") - .given("transactionDate", "2024-02-15") - .doRun(); + .given("transactionDate", "2024-02-15").thenExpect(HttpStatus.OK); } } @@ -527,8 +507,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "sign 2024-01-15") .given("assetValue", 100 * 64) .given("comment", "disposal for initial shares") - .given("transactionDate", "2024-01-15") - .doRun(); + .given("transactionDate", "2024-01-15").thenExpect(HttpStatus.OK); } @Test @@ -538,8 +517,7 @@ class HsOfficeScenarioTests extends ScenarioTest { new CreateCoopAssetsRevertSimpleTransaction(scenarioTest) .given("memberNumber", "M-3101000") .given("comment", "reverting some incorrect transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); + .given("dateOfIncorrectTransaction", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -552,8 +530,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "cancel 2024-01-15") .given("valueToDisburse", 8 * 64) .given("comment", "disbursal according to shares cancellation") - .given("transactionDate", "2024-02-15") - .doRun(); + .given("transactionDate", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -567,8 +544,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "transfer 2024-12-31") .given("valueToTransfer", 2 * 64) .given("comment", "transfer assets from M-3101000 to M-4303000") - .given("transactionDate", "2024-12-31") - .doRun(); + .given("transactionDate", "2024-12-31").thenExpect(HttpStatus.OK); } @Test @@ -580,8 +556,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("adoptingMemberNumber", "M-4303000") .given("transferredValue", 2 * 64) .given("comment", "reverting some incorrect transfer transaction") - .given("dateOfIncorrectTransaction", "2024-02-15") - .doRun(); + .given("dateOfIncorrectTransaction", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -593,8 +568,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "cancel 2024-01-15") .given("valueToClear", 2 * 64) .given("comment", "clearing according to members debt") - .given("transactionDate", "2024-02-15") - .doRun(); + .given("transactionDate", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -606,8 +580,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "cancel 2024-01-15") .given("valueLost", 2 * 64) .given("comment", "assign balance sheet loss") - .given("transactionDate", "2024-02-15") - .doRun(); + .given("transactionDate", "2024-02-15").thenExpect(HttpStatus.OK); } @Test @@ -619,8 +592,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("reference", "cancel 2024-01-15") .given("valueForLimitation", 2 * 64) .given("comment", "adjust coop ") - .given("transactionDate", "2024-02-15") - .doRun(); + .given("transactionDate", "2024-02-15").thenExpect(HttpStatus.OK); } } @@ -634,14 +606,14 @@ class HsOfficeScenarioTests extends ScenarioTest { @Requires("Person: Test AG") @Produces("Subscription: Michael Miller to operations-announce") void shouldSubscribeNewPersonAndContactToMailinglist() { + // TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists? new SubscribeNewPersonAndContactToMailinglist(scenarioTest) // TODO.spec: do we need the personType? or is an operational contact always a natural person? what about distribution lists? .given("partnerPersonTradeName", "Test AG") .given("subscriberFamilyName", "Miller") .given("subscriberGivenName", "Michael") .given("subscriberEMailAddress", "michael.miller@example.org") - .given("mailingList", "operations-announce") - .doRun() + .given("mailingList", "operations-announce").thenExpect(HttpStatus.OK) .keep(); } @@ -655,8 +627,7 @@ class HsOfficeScenarioTests extends ScenarioTest { .given("subscriberFamilyName", "Miller") .given("subscriberGivenName", "Michael") .given("subscriberEMailAddress", "michael.miller@example.org") - .given("mailingList", "operations-discussion") - .doRun() + .given("mailingList", "operations-discussion").thenExpect(HttpStatus.OK) .keep(); } @@ -666,8 +637,7 @@ class HsOfficeScenarioTests extends ScenarioTest { void shouldUnsubscribePersonAndContactFromMailinglist() { new UnsubscribeFromMailinglist(scenarioTest) .given("mailingList", "operations-announce") - .given("subscriberEMailAddress", "michael.miller@example.org") - .doRun(); + .given("subscriberEMailAddress", "michael.miller@example.org").thenExpect(HttpStatus.OK); } } @@ -694,8 +664,7 @@ class HsOfficeScenarioTests extends ScenarioTest { "country": "Germany" """) .given("communityOfHeirsOfficePhoneNumber", "+49 40 666666") - .given("communityOfHeirsEmailAddress", "lena.stadland@example.org") - .doRun(); + .given("communityOfHeirsEmailAddress", "lena.stadland@example.org").thenExpect(HttpStatus.OK); } } } 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 501a2a6d..54c6acc2 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 @@ -6,6 +6,7 @@ import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class AddPhoneNumberToContactData extends UseCase { @@ -19,14 +20,14 @@ public class AddPhoneNumberToContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + () -> httpGet(asGlobalAgent(), "/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(""" + return withTitle("Patch the Additional Phone-Number into the Contact", () -> + httpPatch(asGlobalAgent(), "/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "phoneNumbers": { ${phoneNumberKeyToAdd}: ${phoneNumberToAdd} @@ -35,15 +36,13 @@ public class AddPhoneNumberToContactData extends UseCase.HttpResponse response) { verify( "Verify if the New Phone Number Got Added", - () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), 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 5f05b6a8..c0b771c2 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 @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class AmendContactData extends UseCase { @@ -17,14 +18,14 @@ public class AmendContactData extends UseCase { protected HttpResponse run() { obtain("partnerContactUuid", () -> - httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + httpGet(asGlobalAgent(), "/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 New Phone Number Into the Contact", () -> - httpPatch("/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" + return withTitle("Patch the New Phone Number Into the Contact", () -> + httpPatch(asGlobalAgent(), "/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "caption": ${newContactCaption???}, "postalAddress": { @@ -40,7 +41,5 @@ public class AmendContactData extends UseCase { """)) .expecting(HttpStatus.OK) ); - - return null; } } 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 03a8e07a..7bd10898 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 @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class RemovePhoneNumberFromContactData extends UseCase { @@ -18,14 +19,14 @@ public class RemovePhoneNumberFromContactData extends UseCase httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + () -> httpGet(asGlobalAgent(), "/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(""" + return withTitle("Patch the Additional Phone-Number into the Contact", () -> + httpPatch(asGlobalAgent(), "/api/hs/office/contacts/%{partnerContactUuid}", usingJsonBody(""" { "phoneNumbers": { ${phoneNumberKeyToRemove}: NULL @@ -34,15 +35,13 @@ public class RemovePhoneNumberFromContactData extends UseCase.HttpResponse response) { verify( "Verify if the New Phone Number Got Added", - () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + () -> httpGet(asGlobalAgent(), "/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 d7a0569a..3aee79bd 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 @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -17,14 +18,14 @@ public class ReplaceContactData extends UseCase { protected HttpResponse run() { obtain("partnerRelationUuid", () -> - httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + httpGet(asGlobalAgent(), "/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(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": ${newContactCaption}, "postalAddress": { @@ -44,23 +45,21 @@ public class ReplaceContactData extends UseCase { "(currently `firm`, `name`, `co`, `street`, `zipcode`, `city`, `country`) " + "its values might not appear in external systems."); - withTitle("Replace the Contact-Reference in the Partner-Relation", () -> - httpPatch("/api/hs/office/relations/%{partnerRelationUuid}", usingJsonBody(""" + return withTitle("Replace the Contact-Reference in the Partner-Relation", () -> + httpPatch(asGlobalAgent(), "/api/hs/office/relations/%{partnerRelationUuid}", usingJsonBody(""" { "contact.uuid": ${Contact: %{newContactCaption}} } """)) .expecting(OK) ); - - return null; } @Override protected void verify(final UseCase.HttpResponse response) { verify( "Verify if the Contact-Relation Got Replaced in the Partner-Relation", - () -> httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].contact.caption").contains("%{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 f05a9e47..9aede0be 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 @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.office.scenarios.person.CreatePerson; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -23,14 +24,14 @@ public class CreateExternalDebitorForPartner extends UseCase - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/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." ); obtain("BankAccount: Billing GmbH - refund bank account", () -> - httpPost("/api/hs/office/bankaccounts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/bankaccounts", usingJsonBody(""" { "holder": "Billing GmbH - refund bank account", "iban": "DE02120300000000202051", @@ -41,7 +42,7 @@ public class CreateExternalDebitorForPartner extends UseCase - httpPost("/api/hs/office/contacts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": "Billing GmbH, billing for Test AG", "emailAddresses": { @@ -52,7 +53,7 @@ public class CreateExternalDebitorForPartner extends UseCase - httpGet("/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=PARTNER&personData=" + uriEncoded("%{partnerPersonTradeName}")) .expecting(OK).expecting(JSON), response -> 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." ); obtain("BankAccount: Test AG - refund bank account", () -> - httpPost("/api/hs/office/bankaccounts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/bankaccounts", usingJsonBody(""" { "holder": "Test AG - refund bank account", "iban": "DE88100900001234567892", @@ -34,7 +35,7 @@ public class CreateSelfDebitorForPartner extends UseCase - httpPost("/api/hs/office/contacts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": ${billingContactCaption}, "emailAddresses": { @@ -45,7 +46,7 @@ public class CreateSelfDebitorForPartner extends UseCase - httpGet("/api/hs/office/partners/" + uriEncoded("%{partnerNumber}")) + httpGet(asGlobalAgent(), "/api/hs/office/partners/" + uriEncoded("%{partnerNumber}")) .reportWithResponse().expecting(HttpStatus.OK).expecting(JSON) .extractUuidAlias("partnerRel.holder.uuid", "partnerPersonUuid") .extractUuidAlias("partnerRel.contact.uuid", "partnerContactUuid") ); - return httpPost("/api/hs/office/debitors", usingJsonBody(""" + return httpPost(asGlobalAgent(), "/api/hs/office/debitors", usingJsonBody(""" { "debitorRel": { "anchor.uuid": ${partnerPersonUuid}, 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 62ae5b54..b064856c 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 @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -17,13 +18,13 @@ public class CreateSepaMandateForDebitor extends UseCase - httpGet("/api/hs/office/debitors/%{debitorNumber}") + httpGet(asGlobalAgent(), "/api/hs/office/debitors/%{debitorNumber}") .expecting(OK).expecting(JSON), response -> response.getFromBody("uuid") ); obtain("BankAccount: Test AG - debit bank account", () -> - httpPost("/api/hs/office/bankaccounts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/bankaccounts", usingJsonBody(""" { "holder": ${bankAccountHolder}, "iban": ${bankAccountIBAN}, @@ -33,7 +34,7 @@ public class CreateSepaMandateForDebitor extends UseCase { public DeleteDebitor(final ScenarioTest testSuite) { @@ -24,10 +26,9 @@ public class DeleteDebitor extends UseCase { @Override protected HttpResponse run() { - withTitle("Delete the Debitor using its UUID", () -> - httpDelete("/api/hs/office/debitors/&{Debitor: Test AG - delete debitor}") + return withTitle("Delete the Debitor using its UUID", () -> + httpDelete(asGlobalAgent(), "/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/DontDeleteDefaultDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DontDeleteDefaultDebitor.java index 00d7fa79..87a35a4e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DontDeleteDefaultDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/DontDeleteDefaultDebitor.java @@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; + public class DontDeleteDefaultDebitor extends UseCase { public DontDeleteDefaultDebitor(final ScenarioTest testSuite) { @@ -12,7 +14,7 @@ public class DontDeleteDefaultDebitor extends UseCase @Override protected HttpResponse run() { - httpDelete("/api/hs/office/debitors/&{Debitor: Test AG - main debitor}") + httpDelete(asGlobalAgent(), "/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/debitor/FinallyDeleteSepaMandateForDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/debitor/FinallyDeleteSepaMandateForDebitor.java index 7dff8d6a..70c409b6 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 @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class FinallyDeleteSepaMandateForDebitor extends UseCase { @@ -17,14 +18,15 @@ public class FinallyDeleteSepaMandateForDebitor extends UseCase - httpGet("/api/hs/office/sepamandates?iban=&{bankAccountIBAN}") + httpGet(asGlobalAgent(), "/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. - return withTitle("Delete the SEPA-Mandate by its UUID", () -> httpDelete("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}") + return withTitle("Delete the SEPA-Mandate by its UUID", () -> + httpDelete(asGlobalAgent(), "/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 136244f4..adc48a80 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 @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class InvalidateSepaMandateForDebitor extends UseCase { @@ -16,14 +17,14 @@ public class InvalidateSepaMandateForDebitor extends UseCase - httpGet("/api/hs/office/sepamandates?iban=&{bankAccountIBAN}") + httpGet(asGlobalAgent(), "/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 withTitle("Patch the End of the Mandate into the SEPA-Mandate", () -> - httpPatch("/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}", usingJsonBody(""" + httpPatch(asGlobalAgent(), "/api/hs/office/sepamandates/&{SEPA-Mandate: %{bankAccountIBAN}}", usingJsonBody(""" { "validTo": ${mandateValidTo} } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CancelMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CancelMembership.java index 5f3c3196..3701607a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CancelMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CancelMembership.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class CancelMembership extends UseCase { @@ -18,12 +19,12 @@ public class CancelMembership extends UseCase { protected HttpResponse run() { obtain("Membership: %{memberNumber}", () -> - httpGet("/api/hs/office/memberships/%{memberNumber}"), + httpGet(asGlobalAgent(), "/api/hs/office/memberships/%{memberNumber}"), response -> response.getFromBody("uuid") ); return withTitle("Patch the New Status Into the Membership", () -> - httpPatch("/api/hs/office/memberships/%{Membership: %{memberNumber}}", usingJsonBody(""" + httpPatch(asGlobalAgent(), "/api/hs/office/memberships/%{Membership: %{memberNumber}}", usingJsonBody(""" { "validTo": ${validTo}, "status": ${newStatus} @@ -37,7 +38,7 @@ public class CancelMembership extends UseCase { protected void verify(final UseCase.HttpResponse response) { verify( "Verify That the Membership Got Cancelled", - () -> httpGet("/api/hs/office/memberships/%{Membership: %{memberNumber}}") + () -> httpGet(asGlobalAgent(), "/api/hs/office/memberships/%{Membership: %{memberNumber}}") .expecting(OK).expecting(JSON), path("validTo").contains("%{validTo}"), path("status").contains("CANCELLED") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java index 63541e09..4c4cf9ac 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/CreateMembership.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class CreateMembership extends UseCase { @@ -18,13 +19,13 @@ public class CreateMembership extends UseCase { protected HttpResponse run() { obtain("Partner: %{partnerName}", () -> - httpGet("/api/hs/office/partners?name=&{partnerName}") + httpGet(asGlobalAgent(), "/api/hs/office/partners?name=&{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." ); - return httpPost("/api/hs/office/memberships", usingJsonBody(""" + return httpPost(asGlobalAgent(), "/api/hs/office/memberships", usingJsonBody(""" { "partner.uuid": ${Partner: %{partnerName}}, "memberNumberSuffix": ${%{memberNumberSuffix???}???00}, @@ -40,7 +41,7 @@ public class CreateMembership extends UseCase { protected void verify(final UseCase.HttpResponse response) { verify( "Verify That the Membership Got Created", - () -> httpGet("/api/hs/office/memberships/" + response.getLocationUuid()) + () -> httpGet(asGlobalAgent(), "/api/hs/office/memberships/" + response.getLocationUuid()) .expecting(OK).expecting(JSON), path("validFrom").contains("%{validFrom}"), path("status").contains("ACTIVE") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransferTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransferTransaction.java index a1680682..e59839d3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransferTransaction.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsRevertTransferTransaction.java @@ -7,6 +7,7 @@ import org.springframework.http.HttpStatus; import java.math.BigDecimal; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static net.hostsharing.hsadminng.hs.scenarios.ScenarioTest.resolveTyped; public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsTransaction { @@ -41,7 +42,7 @@ public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsT given("negativeAssetValue", resolveTyped("%{transferredValue}", BigDecimal.class).negate()); verify("Verify Reverted Coop-Assets TRANSFER-Transaction", - () -> httpGet("/api/hs/office/coopassetstransactions/" + revertedAssetTxUuid) + () -> httpGet(asGlobalAgent(), "/api/hs/office/coopassetstransactions/" + revertedAssetTxUuid) .expecting(HttpStatus.OK).expecting(ContentType.JSON), path("assetValue").contains("%{negativeAssetValue}"), path("comment").contains("%{comment}"), @@ -51,7 +52,7 @@ public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsT final var adoptionAssetTxUuid = response.getFromBody("revertedAssetTx.['adoptionAssetTx.uuid']"); verify("Verify Related Coop-Assets ADOPTION-Transaction Also Got Reverted", - () -> httpGet("/api/hs/office/coopassetstransactions/" + adoptionAssetTxUuid) + () -> httpGet(asGlobalAgent(), "/api/hs/office/coopassetstransactions/" + adoptionAssetTxUuid) .expecting(HttpStatus.OK).expecting(ContentType.JSON), path("reversalAssetTx.['transferAssetTx.uuid']").contains(revertedAssetTxUuid.toString()) ); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java index 1052db28..708e22f5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopassets/CreateCoopAssetsTransaction.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public abstract class CreateCoopAssetsTransaction extends UseCase { @@ -18,13 +19,13 @@ public abstract class CreateCoopAssetsTransaction extends UseCase - httpGet("/api/hs/office/memberships/%{memberNumber}") + httpGet(asGlobalAgent(), "/api/hs/office/memberships/%{memberNumber}") .expecting(OK).expecting(JSON), response -> response.getFromBody("uuid") ); return withTitle("Create the Coop-Assets-%{transactionType} Transaction", () -> - httpPost("/api/hs/office/coopassetstransactions", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/coopassetstransactions", usingJsonBody(""" { "membership.uuid": ${membershipUuid}, "transactionType": ${transactionType}, @@ -43,7 +44,7 @@ public abstract class CreateCoopAssetsTransaction extends UseCase httpGet("/api/hs/office/coopassetstransactions/" + response.getLocationUuid()) + () -> httpGet(asGlobalAgent(), "/api/hs/office/coopassetstransactions/" + response.getLocationUuid()) .expecting(HttpStatus.OK).expecting(ContentType.JSON), path("transactionType").contains("%{transactionType}"), path("assetValue").contains("%{assetValue}"), diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java index 6c215b5d..2441219b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/membership/coopshares/CreateCoopSharesTransaction.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public abstract class CreateCoopSharesTransaction extends UseCase { @@ -18,13 +19,13 @@ public abstract class CreateCoopSharesTransaction extends UseCase - httpGet("/api/hs/office/memberships/%{memberNumber}") + httpGet(asGlobalAgent(), "/api/hs/office/memberships/%{memberNumber}") .expecting(OK).expecting(JSON), response -> response.getFromBody("uuid") ); return withTitle("Create the Coop-Shares-%{transactionType} Transaction", () -> - httpPost("/api/hs/office/coopsharestransactions", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/coopsharestransactions", usingJsonBody(""" { "membership.uuid": ${membershipUuid}, "transactionType": ${transactionType}, @@ -42,7 +43,7 @@ public abstract class CreateCoopSharesTransaction extends UseCase httpGet("/api/hs/office/coopsharestransactions/" + response.getLocationUuid()) + () -> httpGet(asGlobalAgent(), "/api/hs/office/coopsharestransactions/" + response.getLocationUuid()) .expecting(HttpStatus.OK).expecting(ContentType.JSON), path("transactionType").contains("%{transactionType}"), path("shareCount").contains("%{shareCount}"), 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 4ea68cba..091d8dcc 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 @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -19,14 +20,14 @@ public class AddOperationsContactToPartner extends UseCase - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/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." ); obtain("Person: %{operationsContactGivenName} %{operationsContactFamilyName}", () -> - httpPost("/api/hs/office/persons", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/persons", usingJsonBody(""" { "personType": "NATURAL_PERSON", "familyName": ${operationsContactFamilyName}, @@ -39,7 +40,7 @@ public class AddOperationsContactToPartner extends UseCase - httpPost("/api/hs/office/contacts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": "%{operationsContactGivenName} %{operationsContactFamilyName}", "phoneNumbers": { @@ -54,7 +55,7 @@ public class AddOperationsContactToPartner extends UseCase.HttpResponse response) { verify( "Verify the New OPERATIONS Relation", - () -> httpGet("/api/hs/office/relations?relationType=OPERATIONS&personData=" + uriEncoded( + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=OPERATIONS&personData=" + uriEncoded( "%{operationsContactFamilyName}")) .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].contact.caption").contains("%{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 41732d3a..66c52756 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 @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -19,14 +20,14 @@ public class AddRepresentativeToPartner extends UseCase - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/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." ); obtain("Person: %{representativeGivenName} %{representativeFamilyName}", () -> - httpPost("/api/hs/office/persons", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/persons", usingJsonBody(""" { "personType": "NATURAL_PERSON", "familyName": ${representativeFamilyName}, @@ -39,7 +40,7 @@ public class AddRepresentativeToPartner extends UseCase - httpPost("/api/hs/office/contacts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": "%{representativeGivenName} %{representativeFamilyName}", "postalAddress": { @@ -57,7 +58,7 @@ public class AddRepresentativeToPartner extends UseCase.HttpResponse response) { verify( "Verify the REPRESENTATIVE Relation Got Removed", - () -> httpGet("/api/hs/office/relations?relationType=REPRESENTATIVE&personData=" + uriEncoded("%{representativeFamilyName}")) + () -> httpGet(asGlobalAgent(), "/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 a1ddccc4..4c67121d 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 @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class CreatePartner extends UseCase { @@ -24,14 +25,14 @@ public class CreatePartner extends UseCase { protected HttpResponse run() { obtain("Person: Hostsharing eG", () -> - httpGet("/api/hs/office/persons?name=Hostsharing+eG") + httpGet(asGlobalAgent(), "/api/hs/office/persons?name=Hostsharing+eG") .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), "Even in production data we expect this query to return just a single result." // TODO.impl: add constraint? ); obtain("Person: %{%{tradeName???}???%{givenName???} %{familyName???}}", () -> - httpPost("/api/hs/office/persons", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/persons", usingJsonBody(""" { "personType": ${personType???}, "tradeName": ${tradeName???}, @@ -43,7 +44,7 @@ public class CreatePartner extends UseCase { ); obtain("Contact: %{contactCaption}", () -> - httpPost("/api/hs/office/contacts", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/contacts", usingJsonBody(""" { "caption": ${contactCaption}, "postalAddress": { @@ -60,7 +61,7 @@ public class CreatePartner extends UseCase { .expecting(HttpStatus.CREATED).expecting(ContentType.JSON) ); - return httpPost("/api/hs/office/partners", usingJsonBody(""" + return httpPost(asGlobalAgent(), "/api/hs/office/partners", usingJsonBody(""" { "partnerNumber": ${partnerNumber}, "partnerRel": { @@ -84,7 +85,7 @@ public class CreatePartner extends UseCase { protected void verify(final UseCase.HttpResponse response) { verify( "Verify the New Partner Relation", - () -> httpGet("/api/hs/office/relations?relationType=PARTNER&contactData=&{contactCaption}") + () -> httpGet(asGlobalAgent(), "/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/partner/DeletePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/DeletePartner.java index df0a0c57..6d7237d0 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 @@ -4,6 +4,8 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import org.springframework.http.HttpStatus; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; + public class DeletePartner extends UseCase { public DeletePartner(final ScenarioTest testSuite) { @@ -18,10 +20,9 @@ public class DeletePartner extends UseCase { @Override protected HttpResponse run() { - withTitle("Delete Partner by its UUID", () -> - httpDelete("/api/hs/office/partners/&{Partner: Delete AG}") + return withTitle("Delete Partner by its UUID", () -> + httpDelete(asGlobalAgent(), "/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/partner/ReplaceDeceasedPartnerWithCommunityOfHeirs.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/ReplaceDeceasedPartnerWithCommunityOfHeirs.java index 34942d47..28367a2b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/ReplaceDeceasedPartnerWithCommunityOfHeirs.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/partner/ReplaceDeceasedPartnerWithCommunityOfHeirs.java @@ -1,10 +1,12 @@ package net.hostsharing.hsadminng.hs.office.scenarios.partner; +import lombok.val; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -18,7 +20,7 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpGet("/api/hs/office/partners/%{partnerNumber}") + () -> httpGet(asGlobalAgent(), "/api/hs/office/partners/%{partnerNumber}") .reportWithResponse().expecting(OK).expecting(JSON), response -> response.getFromBody("uuid"), "Even in production data we expect this query to return just a single result." @@ -30,8 +32,8 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpPatch("/api/hs/office/partners/%{Partner: %{partnerNumber}}", + val result = withTitle("New Partner-Person+Contact: Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}", + () -> httpPatch(asGlobalAgent(), "/api/hs/office/partners/%{Partner: %{partnerNumber}}", usingJsonBody(""" { "partnerRel": { @@ -68,7 +70,7 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpPost("/api/hs/office/relations", + () -> httpPost(asGlobalAgent(), "/api/hs/office/relations", usingJsonBody(""" { "type": "REPRESENTATIVE", @@ -88,14 +90,14 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase.HttpResponse response) { verify( "Verify the Updated Partner", - () -> httpGet("/api/hs/office/partners/%{partnerNumber}") + () -> httpGet(asGlobalAgent(), "/api/hs/office/partners/%{partnerNumber}") .expecting(OK).expecting(JSON).expectObject(), path("partnerRel.holder.tradeName").contains( "Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}"), @@ -106,7 +108,7 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpGet( + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=EX_PARTNER&personUuid=%{Person: %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}}") .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].anchor.tradeName").contains( @@ -115,7 +117,7 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpGet( + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=REPRESENTATIVE&personUuid=%{Person: Erbengemeinschaft %{givenNameOfDeceasedPerson} %{familyNameOfDeceasedPerson}}") .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].anchor.tradeName").contains( @@ -128,7 +130,7 @@ public class ReplaceDeceasedPartnerWithCommunityOfHeirs extends UseCase httpGet( + () -> httpGet(asGlobalAgent(), "/api/hs/office/debitors?partnerNumber=%{partnerNumber}") .expecting(OK).expecting(JSON).expectArrayElements(1), path("[0].debitorRel.anchor.tradeName").contains( 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 39e39068..3430b3ac 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 @@ -5,6 +5,8 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import org.springframework.http.HttpStatus; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; + public class CreatePerson extends UseCase { public CreatePerson(final ScenarioTest testSuite, final String resultAlias) { @@ -15,7 +17,7 @@ public class CreatePerson extends UseCase { protected HttpResponse run() { return withTitle("Create the Person", () -> - httpPost("/api/hs/office/persons", usingJsonBody(""" + httpPost(asGlobalAgent(), "/api/hs/office/persons", usingJsonBody(""" { "personType": ${personType}, "tradeName": ${tradeName} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/ShouldUpdatePersonData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/ShouldUpdatePersonData.java index 1d092936..65a85df0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/ShouldUpdatePersonData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/person/ShouldUpdatePersonData.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.OK; public class ShouldUpdatePersonData extends UseCase { @@ -18,29 +19,28 @@ public class ShouldUpdatePersonData extends UseCase { obtain( "personUuid", - () -> httpGet("/api/hs/office/persons?name=" + uriEncoded("%{oldFamilyName}")) + () -> httpGet(asGlobalAgent(), "/api/hs/office/persons?name=" + uriEncoded("%{oldFamilyName}")) .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." ); - withTitle("Patch the Additional Phone-Number into the Person", () -> - httpPatch("/api/hs/office/persons/%{personUuid}", usingJsonBody(""" + return withTitle("Patch the Additional Phone-Number into the Person", () -> + httpPatch( + asGlobalAgent(), "/api/hs/office/persons/%{personUuid}", usingJsonBody(""" { "familyName": ${newFamilyName} } """)) .expecting(HttpStatus.OK) ); - - return null; } @Override protected void verify(final UseCase.HttpResponse response) { verify( "Verify that the Family Name Got Amended", - () -> httpGet("/api/hs/office/persons/%{personUuid}") + () -> httpGet(asGlobalAgent(), "/api/hs/office/persons/%{personUuid}") .expecting(OK).expecting(JSON), path("familyName").contains("%{newFamilyName}") ); 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 905c3a72..dac1e168 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.scenarios.ScenarioTest; import net.hostsharing.hsadminng.hs.scenarios.UseCase; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; @@ -19,7 +20,7 @@ public class RemoveOperationsContactFromPartner extends UseCase - httpGet("/api/hs/office/relations?relationType=OPERATIONS&name=" + uriEncoded( + httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=OPERATIONS&name=" + uriEncoded( "%{operationsContactPerson}")) .expecting(OK).expecting(JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), @@ -27,7 +28,7 @@ public class RemoveOperationsContactFromPartner extends UseCase - httpDelete("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") + httpDelete(asGlobalAgent(), "/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") .expecting(NO_CONTENT) ); } @@ -36,7 +37,7 @@ public class RemoveOperationsContactFromPartner extends UseCase.HttpResponse response) { verify( "Verify the New OPERATIONS Relation", - () -> httpGet("/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") + () -> httpGet(asGlobalAgent(), "/api/hs/office/relations/&{Operations-Contact: %{operationsContactPerson}}") .expecting(NOT_FOUND) ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeExistingPersonAndContactToMailinglist.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeExistingPersonAndContactToMailinglist.java index f018c454..654c78ca 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeExistingPersonAndContactToMailinglist.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeExistingPersonAndContactToMailinglist.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import org.springframework.http.HttpStatus; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -19,7 +20,7 @@ public class SubscribeExistingPersonAndContactToMailinglist extends UseCase - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/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." @@ -27,20 +28,20 @@ public class SubscribeExistingPersonAndContactToMailinglist extends UseCase - httpGet("/api/hs/office/persons?name=%{subscriberFamilyName}") + httpGet(asGlobalAgent(), "/api/hs/office/persons?name=%{subscriberFamilyName}") .expecting(HttpStatus.OK).expecting(ContentType.JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), "In real scenarios there are most likely multiple results and you have to choose the right one." ); obtain("Contact: %{subscriberEMailAddress}", () -> - httpGet("/api/hs/office/contacts?emailAddress=%{subscriberEMailAddress}") + httpGet(asGlobalAgent(), "/api/hs/office/contacts?emailAddress=%{subscriberEMailAddress}") .expecting(HttpStatus.OK).expecting(ContentType.JSON), response -> response.expectArrayElements(1).getFromBody("[0].uuid"), "In real scenarios there are most likely multiple results and you have to choose the right one." ); - return httpPost("/api/hs/office/relations", usingJsonBody(""" + return httpPost(asGlobalAgent(), "/api/hs/office/relations", usingJsonBody(""" { "type": "SUBSCRIBER", "mark": ${mailingList}, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeNewPersonAndContactToMailinglist.java b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeNewPersonAndContactToMailinglist.java index 5638e440..6aa8cd37 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeNewPersonAndContactToMailinglist.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/subscription/SubscribeNewPersonAndContactToMailinglist.java @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -17,13 +18,13 @@ public class SubscribeNewPersonAndContactToMailinglist extends UseCase - httpGet("/api/hs/office/persons?name=" + uriEncoded("%{partnerPersonTradeName}")) + httpGet(asGlobalAgent(), "/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." ); - return httpPost("/api/hs/office/relations", usingJsonBody(""" + return httpPost(asGlobalAgent(), "/api/hs/office/relations", usingJsonBody(""" { "type": "SUBSCRIBER", "mark": ${mailingList}, 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 4a6b34cb..fdc7daa5 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 @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.scenarios.UseCase; import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest; import static io.restassured.http.ContentType.JSON; +import static net.hostsharing.hsadminng.hs.scenarios.FakeLoginUser.asGlobalAgent; import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.OK; @@ -17,7 +18,7 @@ public class UnsubscribeFromMailinglist extends UseCase - httpGet("/api/hs/office/relations?relationType=SUBSCRIBER" + + httpGet(asGlobalAgent(), "/api/hs/office/relations?relationType=SUBSCRIBER" + "&mark=" + uriEncoded("%{mailingList}") + "&contactData=" + uriEncoded("%{subscriberEMailAddress}")) .expecting(OK).expecting(JSON), @@ -26,7 +27,7 @@ public class UnsubscribeFromMailinglist extends UseCase - httpDelete("/api/hs/office/relations/&{Subscription: %{subscriberEMailAddress}}") + httpDelete(asGlobalAgent(), "/api/hs/office/relations/&{Subscription: %{subscriberEMailAddress}}") .expecting(NO_CONTENT) ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/FakeLoginUser.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/FakeLoginUser.java new file mode 100644 index 00000000..3e5cd145 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/FakeLoginUser.java @@ -0,0 +1,24 @@ +package net.hostsharing.hsadminng.hs.scenarios; + +import lombok.AllArgsConstructor; +import net.hostsharing.hsadminng.config.JwtFakeBearer; + +@AllArgsConstructor +public class FakeLoginUser { + + final static String GLOBAL_AGENT = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented + + private String name; + + public static FakeLoginUser as(final String name) { + return new FakeLoginUser(name); + } + + public static FakeLoginUser asGlobalAgent() { + return new FakeLoginUser(GLOBAL_AGENT); + } + + public String bearer() { + return JwtFakeBearer.bearer(name); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/Requires.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/Requires.java index d0a3ed5a..3ffb4318 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/Requires.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/Requires.java @@ -9,5 +9,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target(METHOD) @Retention(RUNTIME) public @interface Requires { - String value(); + String[] value(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/ScenarioTest.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/ScenarioTest.java index c4f763a5..62221379 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/ScenarioTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/ScenarioTest.java @@ -31,7 +31,9 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.Stack; import java.util.UUID; import java.util.stream.Collectors; @@ -62,8 +64,6 @@ import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(IgnoreOnFailureExtension.class) public abstract class ScenarioTest extends ContextBasedTest { - final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented - private final Stack currentTestMethodProduces = new Stack<>(); protected ScenarioTest scenarioTest = this; @@ -116,20 +116,17 @@ public abstract class ScenarioTest extends ContextBasedTest { @SneakyThrows private void callRequiredProducers(final Method currentTestMethod) { - final var testMethodRequires = Optional.of(currentTestMethod) + final var testMethodRequires = Stream.of(currentTestMethod) .map(m -> m.getAnnotation(Requires.class)) - .map(Requires::value) - .orElse(null); - if (testMethodRequires != null) { + .filter(Objects::nonNull) + .flatMap(annotation -> Stream.of(annotation.value())) + .collect(Collectors.toSet()); + if (!testMethodRequires.isEmpty()) { for (Method potentialProducerMethod : getPotentialProducerMethods()) { final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class); final var testMethodProduces = producedAliases(producesAnnot); - // @formatter:off - if ( // that method can produce something required - testMethodProduces.contains(testMethodRequires) && - - // and it does not produce anything we already have (would cause errors) - SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty() + if ( thatMethodProducesSomethingRequired(testMethodProduces, testMethodRequires) && + thatMethodDoesNotProduceAnythingWeAlreadyHave(testMethodProduces) ) { assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod); @@ -140,15 +137,30 @@ public abstract class ScenarioTest extends ContextBasedTest { // and finally we call the producer method invokeProducerMethod(this, potentialProducerMethod); } - // @formatter:on } - assertThat(knowVariables().containsKey(testMethodRequires)) + assertThat(haveIntersection(knowVariables().keySet(), testMethodRequires)) .as("no @Producer for @Required(\"" + testMethodRequires + "\") found") .isTrue(); } } + private static boolean haveIntersection(final Set set1, final Set set2) { + return !SetUtils.intersection(set1, set2).isEmpty(); + } + + private static boolean areDisjunct(final Set set1, final Set set2) { + return !haveIntersection(set1, set2); + } + + private static boolean thatMethodProducesSomethingRequired(final Set testMethodProduces, final Set testMethodRequires) { + return haveIntersection(testMethodProduces, testMethodRequires); + } + + private static boolean thatMethodDoesNotProduceAnythingWeAlreadyHave(final Set testMethodProduces) { + return areDisjunct(testMethodProduces, knowVariables().keySet()); + } + private void keepProducesAlias(final Method currentTestMethod) { final var producesAnnot = currentTestMethod.getAnnotation(Produces.class); if (producesAnnot != null) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java index 3da3238b..701e2942 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/scenarios/UseCase.java @@ -37,7 +37,6 @@ import static java.net.URLEncoder.encode; import static java.util.stream.Collectors.joining; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS; import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS; -import static net.hostsharing.hsadminng.config.JwtFakeBearer.bearer; import static net.hostsharing.hsadminng.test.DebuggerDetection.isDebuggerAttached; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -76,7 +75,7 @@ public abstract class UseCase> { requirements.put(alias, useCaseFactory); } - public final HttpResponse doRun() { + public final HttpResponse thenExpect(final HttpStatus expectedStatus) { if (introduction != null) { testReport.printPara(introduction); } @@ -95,8 +94,11 @@ public abstract class UseCase> { } }) ); - final var response = run(); - verify(response); + final var response = run(expectedStatus); + assertThat(response).as("use case implementation must return main response, never null").isNotNull(); + if (!response.status.isError()) { + verify(response); + } keepInProduceAlias(response); resetProperties(); @@ -104,7 +106,14 @@ public abstract class UseCase> { return response; } - protected abstract HttpResponse run(); + // this method is called by the test framework, override, but do not call from subclass + protected HttpResponse run(final HttpStatus expectedStatus) { + assertThat(expectedStatus).as("legacy signature only defined for HttpStatus.OK").isEqualTo(HttpStatus.OK); + return run(); + }; + + // legacy signature for backwards compatibility, only called by above method + protected HttpResponse run() {return null;} protected void verify(final HttpResponse response) { } @@ -171,20 +180,22 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpGet(final String uriPathWithPlaceholders) { + public final HttpResponse httpGet(final FakeLoginUser loginUser, final String uriPathWithPlaceholders) { return httpGet(uriPathWithPlaceholders, - req -> req.header("Authorization", bearer(ScenarioTest.RUN_AS_USER))); + req -> req.header("Authorization", loginUser.bearer())); } @SneakyThrows - public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { + public final HttpResponse httpPost( + final FakeLoginUser loginUser, final String uriPathWithPlaceholders, + final JsonTemplate bodyJsonTemplate) { final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .POST(BodyPublishers.ofString(requestBody)) .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") - .header("Authorization", bearer(ScenarioTest.RUN_AS_USER)) + .header("Authorization", loginUser.bearer()) .timeout(seconds(HTTP_TIMEOUT_SECONDS)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -192,14 +203,17 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) { + public final HttpResponse httpPatch( + final FakeLoginUser loginUser, final String uriPathWithPlaceholders, + final JsonTemplate bodyJsonTemplate + ) { final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var requestBody = bodyJsonTemplate.resolvePlaceholders(); final var request = HttpRequest.newBuilder() .method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody)) .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") - .header("Authorization", bearer(ScenarioTest.RUN_AS_USER)) + .header("Authorization", loginUser.bearer()) .timeout(seconds(HTTP_TIMEOUT_SECONDS)) .build(); final var response = client.send(request, BodyHandlers.ofString()); @@ -207,13 +221,13 @@ public abstract class UseCase> { } @SneakyThrows - public final HttpResponse httpDelete(final String uriPathWithPlaceholders) { + public final HttpResponse httpDelete(final FakeLoginUser loginUser, final String uriPathWithPlaceholders) { final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders, DROP_COMMENTS); final var request = HttpRequest.newBuilder() .DELETE() .uri(new URI("http://localhost:" + testSuite.port + uriPath)) .header("Content-Type", "application/json") - .header("Authorization", bearer(ScenarioTest.RUN_AS_USER)) + .header("Authorization", loginUser.bearer()) .timeout(seconds(HTTP_TIMEOUT_SECONDS)) .build(); final var response = client.send(request, BodyHandlers.ofString());