TP-20241126-znuny-view-customer_company #129
5
Jenkinsfile
vendored
5
Jenkinsfile
vendored
@ -76,7 +76,10 @@ pipeline {
|
||||
sh '''
|
||||
./gradlew convertMarkdownToHtml
|
||||
'''
|
||||
archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true
|
||||
archiveArtifacts artifacts:
|
||||
'build/doc/scenarios/*.html, ' +
|
||||
'build/reports/dependency-license/dependencies-without-allowed-license.json',
|
||||
allowEmptyArchive: true
|
||||
|
||||
// cleanup workspace
|
||||
cleanWs()
|
||||
|
22
README.md
22
README.md
@ -63,33 +63,37 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
|
||||
source .aliases # creates some comfortable bash aliases, e.g. 'gw'='./gradlew'
|
||||
gw # initially downloads the configured Gradle version into the project
|
||||
|
||||
gw test # compiles and runs unit- and integration-tests
|
||||
gw test # compiles and runs unit- and integration-tests - takes >10min even on a fast machine
|
||||
gw scenarioTests # compiles and scenario-tests - takes ~1min on a decent machine
|
||||
|
||||
# if the container has not been built yet, run this:
|
||||
pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432
|
||||
# if the container has been built already, run this:
|
||||
# if the container has been built already and you want to keep the data, run this:
|
||||
pg-sql-start
|
||||
|
||||
gw bootRun # compiles and runs the application on localhost:8080
|
||||
|
||||
# the following command should reply with "pong":
|
||||
curl http://localhost:8080/api/ping
|
||||
curl -f http://localhost:8080/api/ping
|
||||
|
||||
# the following command should return a JSON array with just all customers:
|
||||
curl \
|
||||
curl -f\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' \
|
||||
http://localhost:8080/api/test/customers
|
||||
http://localhost:8080/api/test/customers \
|
||||
| jq # just if `jq` is installed, to prettyprint the output
|
||||
|
||||
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
|
||||
curl \
|
||||
curl -f\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' -H 'assumed-roles: rbactest.customer#yyy:ADMIN' \
|
||||
http://localhost:8080/api/test/packages
|
||||
http://localhost:8080/api/test/packages \
|
||||
| jq
|
||||
|
||||
# add a new customer
|
||||
curl \
|
||||
curl -f\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
|
||||
-d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
|
||||
-X POST http://localhost:8080/api/test/customers
|
||||
-X POST http://localhost:8080/api/test/customers \
|
||||
| jq
|
||||
|
||||
If you wonder who 'superuser-alex@hostsharing.net' and 'superuser-fran@hostsharing.net' are and where the data comes from:
|
||||
Mike and Sven are just example global admin accounts as part of the example data which is automatically inserted in Testcontainers and Development environments.
|
||||
|
@ -58,6 +58,8 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2'
|
||||
implementation 'org.springdoc:springdoc-openapi:2.6.0'
|
||||
implementation 'org.postgresql:postgresql:42.7.4'
|
||||
|
@ -43,6 +43,11 @@
|
||||
|
||||
{ "moduleLicense": "WTFPL" },
|
||||
|
||||
{
|
||||
"moduleLicense": "Public Domain, per Creative Commons CC0",
|
||||
"moduleVersion": "2.0.3"
|
||||
},
|
||||
|
||||
{
|
||||
"moduleLicense": null,
|
||||
"#moduleLicense": "Apache License 2.0, see https://github.com/springdoc/springdoc-openapi/blob/main/LICENSE",
|
||||
|
@ -0,0 +1,26 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
@Profile("!test")
|
||||
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/api/**").permitAll() // TODO.impl: implement authentication
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -27,11 +27,13 @@ import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.REVERSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.TRANSFER;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.CLEARING;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DEPOSIT;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.DISBURSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.LOSS;
|
||||
import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeCoopAssetsTransactionTypeResource.TRANSFER;
|
||||
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||
|
||||
@RestController
|
||||
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
||||
@ -66,7 +68,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
fromValueDate,
|
||||
toValueDate);
|
||||
|
||||
final var resources = mapper.mapList(entities, HsOfficeCoopAssetsTransactionResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
final var resources = mapper.mapList(
|
||||
entities,
|
||||
HsOfficeCoopAssetsTransactionResource.class,
|
||||
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resources);
|
||||
}
|
||||
|
||||
@ -106,7 +111,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
if (result.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeCoopAssetsTransactionResource.class));
|
||||
final var resource = mapper.map(
|
||||
result.get(),
|
||||
HsOfficeCoopAssetsTransactionResource.class,
|
||||
ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resource);
|
||||
|
||||
}
|
||||
|
||||
@ -131,7 +140,8 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
private static void validateCreditTransaction(
|
||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody,
|
||||
final ArrayList<String> violations) {
|
||||
if (List.of(DISBURSAL, TRANSFER, CLEARING, LOSS).contains(requestBody.getTransactionType())
|
||||
if (List.of(DISBURSAL, HsOfficeCoopAssetsTransactionTypeResource.TRANSFER, CLEARING, LOSS)
|
||||
.contains(requestBody.getTransactionType())
|
||||
&& requestBody.getAssetValue().signum() > 0) {
|
||||
violations.add("for %s, assetValue must be negative but is \"%.2f\"".formatted(
|
||||
requestBody.getTransactionType(), requestBody.getAssetValue()));
|
||||
@ -147,57 +157,108 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
}
|
||||
}
|
||||
|
||||
// TODO.refa: this logic needs to get extracted to a service
|
||||
final BiConsumer<HsOfficeCoopAssetsTransactionEntity, HsOfficeCoopAssetsTransactionResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||
resource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.setMembershipMemberNumber(entity.getMembership().getTaggedMemberNumber());
|
||||
|
||||
if (entity.getReversalAssetTx() != null) {
|
||||
resource.getReversalAssetTx().setRevertedAssetTxUuid(entity.getUuid());
|
||||
resource.getReversalAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.getReversalAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getReversalAssetTx(), reversalAssetTxResource -> {
|
||||
reversalAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
reversalAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
reversalAssetTxResource.setRevertedAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getAdoptionAssetTx(), adoptionAssetTx ->
|
||||
reversalAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||
withNonNull(
|
||||
entity.getTransferAssetTx(), transferAssetTxResource ->
|
||||
reversalAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getRevertedAssetTx() != null) {
|
||||
resource.getRevertedAssetTx().setReversalAssetTxUuid(entity.getUuid());
|
||||
resource.getRevertedAssetTx().setMembershipUuid(entity.getMembership().getUuid());
|
||||
resource.getRevertedAssetTx().setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getRevertedAssetTx(), revertAssetTxResource -> {
|
||||
revertAssetTxResource.setMembershipUuid(entity.getMembership().getUuid());
|
||||
revertAssetTxResource.setMembershipMemberNumber(entity.getTaggedMemberNumber());
|
||||
revertAssetTxResource.setReversalAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getRevertedAssetTx().getAdoptionAssetTx(), adoptionAssetTx ->
|
||||
revertAssetTxResource.setAdoptionAssetTxUuid(adoptionAssetTx.getUuid()));
|
||||
withNonNull(
|
||||
entity.getRevertedAssetTx().getTransferAssetTx(), transferAssetTxResource ->
|
||||
revertAssetTxResource.setTransferAssetTxUuid(transferAssetTxResource.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getAdoptionAssetTx() != null) {
|
||||
resource.getAdoptionAssetTx().setTransferAssetTxUuid(entity.getUuid());
|
||||
resource.getAdoptionAssetTx().setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
||||
resource.getAdoptionAssetTx().setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
||||
}
|
||||
withNonNull(
|
||||
resource.getAdoptionAssetTx(), adoptionAssetTxResource -> {
|
||||
adoptionAssetTxResource.setMembershipUuid(entity.getAdoptionAssetTx().getMembership().getUuid());
|
||||
adoptionAssetTxResource.setMembershipMemberNumber(entity.getAdoptionAssetTx().getTaggedMemberNumber());
|
||||
adoptionAssetTxResource.setTransferAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getAdoptionAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||
adoptionAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||
});
|
||||
|
||||
if (entity.getTransferAssetTx() != null) {
|
||||
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
resource.getTransferAssetTx(), transferAssetTxResource -> {
|
||||
resource.getTransferAssetTx().setMembershipUuid(entity.getTransferAssetTx().getMembership().getUuid());
|
||||
resource.getTransferAssetTx().setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
||||
}
|
||||
resource.getTransferAssetTx()
|
||||
.setMembershipMemberNumber(entity.getTransferAssetTx().getTaggedMemberNumber());
|
||||
resource.getTransferAssetTx().setAdoptionAssetTxUuid(entity.getUuid());
|
||||
withNonNull(
|
||||
entity.getTransferAssetTx().getReversalAssetTx(), reversalAssetTx ->
|
||||
transferAssetTxResource.setReversalAssetTxUuid(reversalAssetTx.getUuid()));
|
||||
});
|
||||
};
|
||||
|
||||
// TODO.refa: this logic needs to get extracted to a service
|
||||
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
|
||||
if (resource.getMembershipUuid() != null) {
|
||||
final HsOfficeMembershipEntity membership = ofNullable(emw.find(HsOfficeMembershipEntity.class, resource.getMembershipUuid()))
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] membership.uuid %s not found".formatted(
|
||||
final HsOfficeMembershipEntity membership = ofNullable(emw.find(
|
||||
HsOfficeMembershipEntity.class,
|
||||
resource.getMembershipUuid()))
|
||||
.orElseThrow(() -> new EntityNotFoundException("membership.uuid %s not found".formatted(
|
||||
resource.getMembershipUuid())));
|
||||
entity.setMembership(membership);
|
||||
}
|
||||
if (resource.getRevertedAssetTxUuid() != null) {
|
||||
|
||||
if (entity.getTransactionType() == REVERSAL) {
|
||||
if (resource.getRevertedAssetTxUuid() == null) {
|
||||
throw new ValidationException("REVERSAL asset transaction requires revertedAssetTx.uuid");
|
||||
}
|
||||
final var revertedAssetTx = coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedEntityUuid %s not found".formatted(
|
||||
.orElseThrow(() -> new EntityNotFoundException("revertedAssetTx.uuid %s not found".formatted(
|
||||
resource.getRevertedAssetTxUuid())));
|
||||
revertedAssetTx.setReversalAssetTx(entity);
|
||||
entity.setRevertedAssetTx(revertedAssetTx);
|
||||
if (resource.getAssetValue().negate().compareTo(revertedAssetTx.getAssetValue()) != 0) {
|
||||
throw new ValidationException("given assetValue=" + resource.getAssetValue() +
|
||||
" but must be negative value from reverted asset tx: " + revertedAssetTx.getAssetValue());
|
||||
}
|
||||
|
||||
if (revertedAssetTx.getTransactionType() == TRANSFER) {
|
||||
final var adoptionAssetTx = revertedAssetTx.getAdoptionAssetTx();
|
||||
final var adoptionReversalAssetTx = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.transactionType(REVERSAL)
|
||||
.membership(adoptionAssetTx.getMembership())
|
||||
.revertedAssetTx(adoptionAssetTx)
|
||||
.assetValue(adoptionAssetTx.getAssetValue().negate())
|
||||
.comment(resource.getComment())
|
||||
.reference(resource.getReference())
|
||||
.valueDate(resource.getValueDate())
|
||||
.build();
|
||||
adoptionAssetTx.setReversalAssetTx(adoptionReversalAssetTx);
|
||||
adoptionReversalAssetTx.setRevertedAssetTx(adoptionAssetTx);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER) {
|
||||
final var adoptingMembership = determineAdoptingMembership(resource);
|
||||
if (adoptingMembership != null) {
|
||||
final var adoptingAssetTx = coopAssetsTransactionRepo.save(createAdoptingAssetTx(entity, adoptingMembership));
|
||||
if ( entity.getMembership() == adoptingMembership) {
|
||||
throw new ValidationException("transferring and adopting membership must be different, but both are " +
|
||||
adoptingMembership.getTaggedMemberNumber());
|
||||
}
|
||||
final var adoptingAssetTx = createAdoptingAssetTx(entity, adoptingMembership);
|
||||
entity.setAdoptionAssetTx(adoptingAssetTx);
|
||||
}
|
||||
};
|
||||
@ -206,11 +267,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
final var adoptingMembershipUuid = resource.getAdoptingMembershipUuid();
|
||||
final var adoptingMembershipMemberNumber = resource.getAdoptingMembershipMemberNumber();
|
||||
if (adoptingMembershipUuid != null && adoptingMembershipMemberNumber != null) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new ValidationException(
|
||||
// @formatter:off
|
||||
resource.getTransactionType() == TRANSFER
|
||||
? "[400] either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
||||
: "[400] adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
||||
resource.getTransactionType() == HsOfficeCoopAssetsTransactionTypeResource.TRANSFER
|
||||
? "either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"
|
||||
: "adoptingMembership.uuid and adoptingMembership.memberNumber must not be given for transactionType="
|
||||
+ resource.getTransactionType());
|
||||
// @formatter:on
|
||||
}
|
||||
@ -232,13 +293,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
+ "' not found or not accessible");
|
||||
}
|
||||
|
||||
if (resource.getTransactionType() == TRANSFER) {
|
||||
throw new ValidationException(
|
||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType="
|
||||
+ TRANSFER);
|
||||
}
|
||||
|
||||
return null;
|
||||
+ HsOfficeCoopAssetsTransactionTypeResource.TRANSFER);
|
||||
}
|
||||
|
||||
private HsOfficeCoopAssetsTransactionEntity createAdoptingAssetTx(
|
||||
|
@ -98,21 +98,21 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
||||
private String comment;
|
||||
|
||||
// Optionally, the UUID of the corresponding transaction for a reversal transaction.
|
||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
||||
@OneToOne
|
||||
@JoinColumn(name = "revertedassettxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
||||
|
||||
// and the other way around
|
||||
@OneToOne(mappedBy = "revertedAssetTx")
|
||||
@OneToOne(mappedBy = "revertedAssetTx", cascade = CascadeType.PERSIST)
|
||||
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
||||
|
||||
// Optionally, the UUID of the corresponding transaction for a transfer transaction.
|
||||
@OneToOne(cascade = CascadeType.PERSIST) // TODO.impl: can probably be removed after office data migration
|
||||
@OneToOne(cascade = CascadeType.PERSIST)
|
||||
@JoinColumn(name = "assetadoptiontxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity adoptionAssetTx;
|
||||
|
||||
// and the other way around
|
||||
@OneToOne(mappedBy = "adoptionAssetTx")
|
||||
@OneToOne(mappedBy = "adoptionAssetTx", cascade = CascadeType.PERSIST)
|
||||
private HsOfficeCoopAssetsTransactionEntity transferAssetTx;
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,11 @@
|
||||
package net.hostsharing.hsadminng.lambda;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class WithNonNull {
|
||||
public static <T> void withNonNull(final T target, final Consumer<T> code) {
|
||||
if (target != null ) {
|
||||
code.accept(target);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
server:
|
||||
port : 8080
|
||||
|
||||
management:
|
||||
server:
|
||||
port: 8081
|
||||
address: 127.0.0.1
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: info, health, metrics
|
||||
|
||||
spring:
|
||||
|
||||
datasource:
|
||||
|
@ -46,15 +46,15 @@ create or replace function rbac.hasGlobalAdminRole()
|
||||
stable -- leakproof
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentSubjectOrAssumedRolesUuids text;
|
||||
assumedRoles text;
|
||||
begin
|
||||
begin
|
||||
currentSubjectOrAssumedRolesUuids := current_setting('hsadminng.currentSubjectOrAssumedRolesUuids');
|
||||
assumedRoles := current_setting('hsadminng.assumedRoles');
|
||||
exception
|
||||
when others then
|
||||
currentSubjectOrAssumedRolesUuids := null;
|
||||
assumedRoles := null;
|
||||
end;
|
||||
return currentSubjectOrAssumedRolesUuids is null or length(currentSubjectOrAssumedRolesUuids) = 0;
|
||||
return TRIM(COALESCE(assumedRoles, '')) = '' and rbac.isGlobalAdmin();
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
@ -35,21 +35,41 @@ create table if not exists hs_office.coopassettx
|
||||
--changeset michael.hoennig:hs-office-coopassets-BUSINESS-RULES endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
alter table hs_office.coopassettx
|
||||
add constraint reversal_asset_tx_must_have_reverted_asset_tx
|
||||
check (transactionType <> 'REVERSAL' or revertedAssetTxUuid is not null);
|
||||
-- Not as CHECK constraints because those cannot be deferrable,
|
||||
-- but we need these constraints deferrable because the rows are linked to each other.
|
||||
|
||||
alter table hs_office.coopassettx
|
||||
add constraint non_reversal_asset_tx_must_not_have_reverted_asset_tx
|
||||
check (transactionType = 'REVERSAL' or revertedAssetTxUuid is null or transactionType = 'REVERSAL');
|
||||
CREATE OR REPLACE FUNCTION validate_transaction_type()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- REVERSAL transactions must have revertedAssetTxUuid
|
||||
IF NEW.transactionType = 'REVERSAL' AND NEW.revertedAssetTxUuid IS NULL THEN
|
||||
RAISE EXCEPTION 'REVERSAL transactions must have revertedAssetTxUuid';
|
||||
END IF;
|
||||
|
||||
alter table hs_office.coopassettx
|
||||
add constraint transfer_asset_tx_must_have_adopted_asset_tx
|
||||
check (transactionType <> 'TRANSFER' or assetAdoptionTxUuid is not null);
|
||||
-- Non-REVERSAL transactions must not have revertedAssetTxUuid
|
||||
IF NEW.transactionType != 'REVERSAL' AND NEW.revertedAssetTxUuid IS NOT NULL THEN
|
||||
RAISE EXCEPTION 'Non-REVERSAL transactions must not have revertedAssetTxUuid';
|
||||
END IF;
|
||||
|
||||
-- TRANSFER transactions must have assetAdoptionTxUuid
|
||||
IF NEW.transactionType = 'TRANSFER' AND NEW.assetAdoptionTxUuid IS NULL THEN
|
||||
RAISE EXCEPTION 'TRANSFER transactions must have assetAdoptionTxUuid';
|
||||
END IF;
|
||||
|
||||
-- Non-TRANSFER transactions must not have assetAdoptionTxUuid
|
||||
IF NEW.transactionType != 'TRANSFER' AND NEW.assetAdoptionTxUuid IS NOT NULL THEN
|
||||
RAISE EXCEPTION 'Non-TRANSFER transactions must not have assetAdoptionTxUuid';
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Attach the trigger to the table
|
||||
CREATE TRIGGER enforce_transaction_constraints
|
||||
AFTER INSERT OR UPDATE ON hs_office.coopassettx
|
||||
FOR EACH ROW EXECUTE FUNCTION validate_transaction_type();
|
||||
|
||||
alter table hs_office.coopassettx
|
||||
add constraint non_transfer_asset_tx_must_not_have_adopted_asset_tx
|
||||
check (transactionType = 'TRANSFER' or assetAdoptionTxUuid is null);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
|
@ -68,6 +68,7 @@ public class ArchitectureTest {
|
||||
"..hs.hosting.asset",
|
||||
"..hs.hosting.asset.validators",
|
||||
"..hs.hosting.asset.factories",
|
||||
"..hs.scenarios",
|
||||
"..errors",
|
||||
"..mapper",
|
||||
"..ping",
|
||||
@ -160,9 +161,11 @@ public class ArchitectureTest {
|
||||
.that().resideInAPackage("..hs.office.(*)..")
|
||||
.should().onlyBeAccessed().byClassesThat()
|
||||
.resideInAnyPackage(
|
||||
"..hs.office.(*)..",
|
||||
"..hs.office.(*)..",
|
||||
"..hs.booking.(*)..",
|
||||
"..hs.hosting.(*)..",
|
||||
"..hs.scenarios",
|
||||
"..hs.migration",
|
||||
"..rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest
|
||||
);
|
||||
|
@ -0,0 +1,60 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@TestPropertySource(properties = {"management.port=0", "server.port=0"})
|
||||
// IMPORTANT: To test prod config, do not use test profile!
|
||||
class WebSecurityConfigIntegrationTest {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int serverPort;
|
||||
|
||||
@Value("${local.management.port}")
|
||||
private int managementPort;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldSupportPingEndpoint() {
|
||||
final var result = this.restTemplate.getForEntity(
|
||||
"http://localhost:" + this.serverPort + "/api/ping", String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(result.getBody()).startsWith("pong");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportActuatorEndpoint() {
|
||||
final var result = this.restTemplate.getForEntity(
|
||||
"http://localhost:" + this.managementPort + "/actuator", Map.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportHealthEndpoint() {
|
||||
final var result = this.restTemplate.getForEntity(
|
||||
"http://localhost:" + this.managementPort + "/actuator/health", Map.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(result.getBody().get("status")).isEqualTo("UP");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportMetricsEndpoint() {
|
||||
final var result = this.restTemplate.getForEntity(
|
||||
"http://localhost:" + this.managementPort + "/actuator/metrics", Map.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealRepository;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.Dns;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.ClassOrderer;
|
||||
@ -24,6 +25,7 @@ import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@ -42,8 +44,9 @@ import static org.hamcrest.Matchers.matchesRegex;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
@TestClassOrder(ClassOrderer.OrderAnnotation.class) // fail early on fetching problems
|
||||
class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealRepository;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -38,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsBookingItemController.class)
|
||||
@Import({StrictMapper.class, JsonObjectMapperConfiguration.class})
|
||||
@Import({StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class})
|
||||
@RunWith(SpringRunner.class)
|
||||
class HsBookingItemControllerRestTest {
|
||||
|
||||
|
@ -6,11 +6,13 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -23,8 +25,9 @@ import static org.hamcrest.Matchers.matchesRegex;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsBookingProjectControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -14,6 +14,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.ClassOrderer;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -23,6 +24,7 @@ import org.junit.jupiter.api.TestClassOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -43,8 +45,9 @@ import static org.hamcrest.Matchers.matchesRegex;
|
||||
@Transactional
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@TestClassOrder(ClassOrderer.OrderAnnotation.class) // fail early on fetching problems
|
||||
class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -11,6 +11,7 @@ import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
||||
import net.hostsharing.hsadminng.mapper.Array;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@ -52,7 +53,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsHostingAssetController.class)
|
||||
@Import({ StandardMapper.class, JsonObjectMapperConfiguration.class})
|
||||
@Import({ StandardMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class })
|
||||
@RunWith(SpringRunner.class)
|
||||
public class HsHostingAssetControllerRestTest {
|
||||
|
||||
|
@ -3,16 +3,19 @@ package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||
import io.restassured.RestAssured;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
class HsHostingAssetPropsControllerAcceptanceTest {
|
||||
|
||||
@LocalServerPort
|
||||
|
@ -880,7 +880,6 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
|
||||
});
|
||||
|
||||
|
||||
coopAssets.entrySet().forEach(entry -> {
|
||||
final var legacyId = entry.getKey();
|
||||
final var assetTransaction = entry.getValue();
|
||||
|
@ -6,12 +6,14 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -26,8 +28,9 @@ import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeBankAccountControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -2,12 +2,15 @@ package net.hostsharing.hsadminng.hs.office.bankaccount;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
@ -16,6 +19,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsOfficeBankAccountController.class)
|
||||
@Import(DisableSecurityConfig.class)
|
||||
@ActiveProfiles("test")
|
||||
class HsOfficeBankAccountControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
|
@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@ -15,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -33,8 +35,9 @@ import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -31,8 +33,9 @@ import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -8,8 +8,10 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import net.hostsharing.hsadminng.test.TestUuidGenerator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -18,18 +20,27 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.ADOPTION;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DEPOSIT;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.DISBURSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.REVERSAL;
|
||||
import static net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType.TRANSFER;
|
||||
import static net.hostsharing.hsadminng.rbac.test.JsonBuilder.jsonObject;
|
||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assumptions.assumeThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@ -38,11 +49,18 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsOfficeCoopAssetsTransactionController.class)
|
||||
@Import({ StrictMapper.class, JsonObjectMapperConfiguration.class })
|
||||
@Import({ StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class })
|
||||
@ActiveProfiles("test")
|
||||
@RunWith(SpringRunner.class)
|
||||
class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
private static final UUID UNAVAILABLE_MEMBERSHIP_UUID = TestUuidGenerator.use(0);
|
||||
// If you need to run just a single test-case in this data-driven test-method, set SINGLE_TEST_CASE_EXECUTION to true!
|
||||
// There is a test which fails if single test-case execution active to avoid merging this to master.
|
||||
private static final boolean SINGLE_TEST_CASE_EXECUTION = false;
|
||||
|
||||
private static final int DYNAMIC_UUID_START_INDEX = 13;
|
||||
|
||||
private static final UUID UNAVAILABLE_UUID = TestUuidGenerator.use(0);
|
||||
private static final String UNAVAILABLE_MEMBER_NUMBER = "M-1234699";
|
||||
|
||||
private static final UUID ORIGIN_MEMBERSHIP_UUID = TestUuidGenerator.use(1);
|
||||
@ -65,9 +83,11 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
.memberNumberSuffix(suffixOf(AVAILABLE_TARGET_MEMBER_NUMBER))
|
||||
.build();
|
||||
|
||||
// the following refs might change if impl changes
|
||||
private static final UUID NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.ref(4);
|
||||
private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(5);
|
||||
// The following refs depend on the implementation of the respective implementation and might change if it changes.
|
||||
// The same TestUuidGenerator.ref(#) does NOT mean the UUIDs refer to the same entity,
|
||||
// its rather coincidence because different test-cases have different execution paths in the production code.
|
||||
private static final UUID NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.ref(DYNAMIC_UUID_START_INDEX);
|
||||
private static final UUID NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.ref(DYNAMIC_UUID_START_INDEX);
|
||||
|
||||
private static final UUID SOME_EXISTING_LOSS_ASSET_TX_UUID = TestUuidGenerator.use(3);
|
||||
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_LOSS_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
@ -80,6 +100,402 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
.valueDate(LocalDate.parse("2024-10-15"))
|
||||
.build();
|
||||
|
||||
private static final UUID SOME_EXISTING_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.use(4);
|
||||
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_EXISTING_TRANSFER_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.TRANSFER)
|
||||
.assetValue(BigDecimal.valueOf(-256))
|
||||
.reference("some transfer asset tx ref")
|
||||
.comment("some transfer asset tx comment")
|
||||
.valueDate(LocalDate.parse("2024-10-15"))
|
||||
.build();
|
||||
|
||||
private static final UUID SOME_EXISTING_ADOPTION_ASSET_TX_UUID = TestUuidGenerator.use(5);
|
||||
public final HsOfficeCoopAssetsTransactionEntity SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_EXISTING_ADOPTION_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(HsOfficeCoopAssetsTransactionType.TRANSFER)
|
||||
.assetValue(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY.getAssetValue().negate())
|
||||
.reference("some adoption asset tx ref")
|
||||
.comment("some adoption asset tx comment")
|
||||
.valueDate(LocalDate.parse("2024-10-15"))
|
||||
.transferAssetTx(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY)
|
||||
.build();
|
||||
{
|
||||
SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY.setAdoptionAssetTx(SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY);
|
||||
}
|
||||
|
||||
private final static UUID SOME_REVERTED_DISBURSAL_ASSET_TX_UUID = TestUuidGenerator.use(7);
|
||||
private final static UUID SOME_DISBURSAL_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(8);
|
||||
private final HsOfficeCoopAssetsTransactionEntity SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_REVERTED_DISBURSAL_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(DISBURSAL)
|
||||
.assetValue(BigDecimal.valueOf(-128.00))
|
||||
.valueDate(LocalDate.parse("2024-10-15"))
|
||||
.reference("some disbursal")
|
||||
.comment("some disbursal to get reverted")
|
||||
.reversalAssetTx(
|
||||
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_DISBURSAL_REVERSAL_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(REVERSAL)
|
||||
.assetValue(BigDecimal.valueOf(128.00))
|
||||
.valueDate(LocalDate.parse("2024-10-20"))
|
||||
.reference("some reversal")
|
||||
.comment("some reversal of a disbursal asset tx")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
{
|
||||
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY.getReversalAssetTx().setRevertedAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||
}
|
||||
|
||||
private final static UUID SOME_REVERTED_TRANSFER_ASSET_TX_UUID = TestUuidGenerator.use(9);
|
||||
private final static UUID SOME_TRANSFER_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(10);
|
||||
private final static UUID SOME_REVERTED_ADOPTION_ASSET_TX_UUID = TestUuidGenerator.use(11);
|
||||
private final static UUID SOME_ADOPTION_REVERSAL_ASSET_TX_UUID = TestUuidGenerator.use(12);
|
||||
final HsOfficeCoopAssetsTransactionEntity SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY = HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_REVERTED_TRANSFER_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(TRANSFER)
|
||||
.assetValue(BigDecimal.valueOf(-1024))
|
||||
.valueDate(LocalDate.parse("2024-11-10"))
|
||||
.reference("some transfer")
|
||||
.comment("some transfer to get reverted")
|
||||
.adoptionAssetTx(
|
||||
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_REVERTED_ADOPTION_ASSET_TX_UUID)
|
||||
.membership(AVAILABLE_MEMBER_ENTITY)
|
||||
.transactionType(ADOPTION)
|
||||
.assetValue(BigDecimal.valueOf(1024))
|
||||
.valueDate(LocalDate.parse("2024-11-10"))
|
||||
.reference("related adoption")
|
||||
.comment("some reversal of a transfer asset tx")
|
||||
.reversalAssetTx(
|
||||
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_ADOPTION_REVERSAL_ASSET_TX_UUID)
|
||||
.membership(AVAILABLE_MEMBER_ENTITY)
|
||||
.transactionType(REVERSAL)
|
||||
.assetValue(BigDecimal.valueOf(1024))
|
||||
.valueDate(LocalDate.parse("2024-11-11"))
|
||||
.reference("some reversal")
|
||||
.comment("some adoption asset tx reversal")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.reversalAssetTx(
|
||||
HsOfficeCoopAssetsTransactionEntity.builder()
|
||||
.uuid(SOME_TRANSFER_REVERSAL_ASSET_TX_UUID)
|
||||
.membership(ORIGIN_TARGET_MEMBER_ENTITY)
|
||||
.transactionType(REVERSAL)
|
||||
.assetValue(BigDecimal.valueOf(1024))
|
||||
.valueDate(LocalDate.parse("2024-11-11"))
|
||||
.reference("some transfer")
|
||||
.comment("some transfer asset tx reversal")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
{
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx()
|
||||
.setTransferAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getReversalAssetTx()
|
||||
.setRevertedAssetTx(SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY);
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx().getReversalAssetTx()
|
||||
.setRevertedAssetTx(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx());
|
||||
}
|
||||
|
||||
private static final String EXPECTED_RESULT_FROM_GET_SINGLE = """
|
||||
{
|
||||
"uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": -1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "some transfer",
|
||||
"comment": "some transfer to get reverted",
|
||||
"adoptionAssetTx": {
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "ADOPTION",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "related adoption",
|
||||
"comment": "some reversal of a transfer asset tx",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
private static final String EXPECTED_RESULT_FROM_GET_LIST = """
|
||||
[
|
||||
{
|
||||
"uuid": "33333333-3333-3333-3333-333333333333",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "LOSS",
|
||||
"assetValue": -64,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some loss asset tx ref",
|
||||
"comment": "some loss asset tx comment",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": null
|
||||
},
|
||||
{
|
||||
"uuid": "44444444-4444-4444-4444-444444444444",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": -256,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some transfer asset tx ref",
|
||||
"comment": "some transfer asset tx comment",
|
||||
"adoptionAssetTx": {
|
||||
"uuid": "55555555-5555-5555-5555-555555555555",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": 256,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some adoption asset tx ref",
|
||||
"comment": "some adoption asset tx comment",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": "44444444-4444-4444-4444-444444444444",
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": null
|
||||
},
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": null
|
||||
},
|
||||
{
|
||||
"uuid": "55555555-5555-5555-5555-555555555555",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": 256,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some adoption asset tx ref",
|
||||
"comment": "some adoption asset tx comment",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": {
|
||||
"uuid": "44444444-4444-4444-4444-444444444444",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": -256,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some transfer asset tx ref",
|
||||
"comment": "some transfer asset tx comment",
|
||||
"adoptionAssetTx.uuid": "55555555-5555-5555-5555-555555555555",
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": null
|
||||
},
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": null
|
||||
},
|
||||
{
|
||||
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "DISBURSAL",
|
||||
"assetValue": -128.0,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some disbursal",
|
||||
"comment": "some disbursal to get reverted",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": {
|
||||
"uuid": "88888888-8888-8888-8888-888888888888",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 128.0,
|
||||
"valueDate": "2024-10-20",
|
||||
"reference": "some reversal",
|
||||
"comment": "some reversal of a disbursal asset tx",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"reversalAssetTx.uuid": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "88888888-8888-8888-8888-888888888888",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 128.0,
|
||||
"valueDate": "2024-10-20",
|
||||
"reference": "some reversal",
|
||||
"comment": "some reversal of a disbursal asset tx",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": {
|
||||
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "DISBURSAL",
|
||||
"assetValue": -128.0,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some disbursal",
|
||||
"comment": "some disbursal to get reverted",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "88888888-8888-8888-8888-888888888888"
|
||||
},
|
||||
"reversalAssetTx": null
|
||||
},
|
||||
{
|
||||
"uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": -1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "some transfer",
|
||||
"comment": "some transfer to get reverted",
|
||||
"adoptionAssetTx": {
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "ADOPTION",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "related adoption",
|
||||
"comment": "some reversal of a transfer asset tx",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"
|
||||
},
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": {
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-11",
|
||||
"reference": "some transfer",
|
||||
"comment": "some transfer asset tx reversal",
|
||||
"adoptionAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"reversalAssetTx.uuid": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "ADOPTION",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "related adoption",
|
||||
"comment": "some reversal of a transfer asset tx",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": {
|
||||
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "DISBURSAL",
|
||||
"assetValue": -128.0,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some disbursal",
|
||||
"comment": "some disbursal to get reverted",
|
||||
"adoptionAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "88888888-8888-8888-8888-888888888888"
|
||||
},
|
||||
"revertedAssetTx": null,
|
||||
"reversalAssetTx": {
|
||||
"uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-11",
|
||||
"reference": "some reversal",
|
||||
"comment": "some adoption asset tx reversal",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"revertedAssetTx.uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"reversalAssetTx.uuid": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-11",
|
||||
"reference": "some transfer",
|
||||
"comment": "some transfer asset tx reversal",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": {
|
||||
"uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"membership.uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"membership.memberNumber": "M-1111100",
|
||||
"transactionType": "DISBURSAL",
|
||||
"assetValue": -128.0,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some disbursal",
|
||||
"comment": "some disbursal to get reverted",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
"reversalAssetTx": null
|
||||
},
|
||||
{
|
||||
"uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-11",
|
||||
"reference": "some reversal",
|
||||
"comment": "some adoption asset tx reversal",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": {
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
|
||||
"membership.uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"membership.memberNumber": "M-1234500",
|
||||
"transactionType": "ADOPTION",
|
||||
"assetValue": 1024,
|
||||
"valueDate": "2024-11-10",
|
||||
"reference": "related adoption",
|
||||
"comment": "some reversal of a transfer asset tx",
|
||||
"adoptionAssetTx.uuid": null,
|
||||
"transferAssetTx.uuid": "77777777-7777-7777-7777-777777777777",
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc"
|
||||
},
|
||||
"reversalAssetTx": null
|
||||
}
|
||||
]
|
||||
""";
|
||||
|
||||
@Autowired
|
||||
MockMvc mockMvc;
|
||||
|
||||
@ -117,6 +533,44 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
requestBody -> requestBody.without("membership.uuid"),
|
||||
"[membershipUuid must not be null but is \"null\"]"), // TODO.impl: should be membership.uuid, Spring validation-problem?
|
||||
|
||||
MEMBERSHIP_UUID_NOT_FOUND_OR_NOT_ACCESSIBLE(
|
||||
requestBody -> requestBody.with("membership.uuid", UNAVAILABLE_UUID),
|
||||
"membership.uuid " + UNAVAILABLE_UUID + " not found"),
|
||||
|
||||
MEMBERSHIP_UUID_AND_MEMBER_NUMBER_MUST_NOT_BE_GIVEN_BOTH(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", TRANSFER.name())
|
||||
.with("assetValue", "-128.00")
|
||||
.with("adoptingMembership.uuid", UNAVAILABLE_UUID)
|
||||
.with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER),
|
||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber can be given, not both"),
|
||||
|
||||
MEMBERSHIP_UUID_OR_MEMBER_NUMBER_MUST_BE_GIVEN(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", "-128.00"),
|
||||
"either adoptingMembership.uuid or adoptingMembership.memberNumber must be given for transactionType=TRANSFER"),
|
||||
|
||||
REVERSAL_ASSET_TRANSACTION_REQUIRES_REVERTED_ASSET_TX_UUID(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", "-128.00"),
|
||||
"REVERSAL asset transaction requires revertedAssetTx.uuid"),
|
||||
|
||||
REVERSAL_ASSET_TRANSACTION_REQUIRES_AVAILABLE_REVERTED_ASSET_TX_UUID(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", "-128.00")
|
||||
.with("revertedAssetTx.uuid", UNAVAILABLE_UUID),
|
||||
"revertedAssetTx.uuid " + UNAVAILABLE_UUID + " not found"),
|
||||
|
||||
REVERSAL_ASSET_TRANSACTION_MUST_NEGATE_VALUE_OF_REVERTED_ASSET_TX(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", "128.00")
|
||||
.with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID),
|
||||
"given assetValue=128.00 but must be negative value from reverted asset tx: -64"),
|
||||
|
||||
TRANSACTION_TYPE_MISSING(
|
||||
requestBody -> requestBody.without("transactionType"),
|
||||
"[transactionType must not be null but is \"null\"]"),
|
||||
@ -127,35 +581,40 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
ASSETS_VALUE_FOR_DEPOSIT_MUST_BE_POSITIVE(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "DEPOSIT")
|
||||
.with("transactionType", DEPOSIT)
|
||||
.with("assetValue", -64.00),
|
||||
"[for DEPOSIT, assetValue must be positive but is \"-64.00\"]"),
|
||||
|
||||
ASSETS_VALUE_FOR_DISBURSAL_MUST_BE_NEGATIVE(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "DISBURSAL")
|
||||
.with("transactionType", DISBURSAL)
|
||||
.with("assetValue", 64.00),
|
||||
"[for DISBURSAL, assetValue must be negative but is \"64.00\"]"),
|
||||
|
||||
//TODO: other transaction types
|
||||
ADOPTING_MEMBERSHIP_MUST_NOT_BE_THE_SAME(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", -64.00)
|
||||
.with("adoptingMembership.uuid", ORIGIN_MEMBERSHIP_UUID),
|
||||
"transferring and adopting membership must be different, but both are M-1111100"),
|
||||
|
||||
ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "TRANSFER")
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", -64.00)
|
||||
.with("adoptingMembership.memberNumber", UNAVAILABLE_MEMBER_NUMBER),
|
||||
"adoptingMembership.memberNumber='M-1234699' not found or not accessible"),
|
||||
|
||||
ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "TRANSFER")
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", -64.00)
|
||||
.with("adoptingMembership.uuid", UNAVAILABLE_MEMBERSHIP_UUID.toString()),
|
||||
"adoptingMembership.uuid='" + UNAVAILABLE_MEMBERSHIP_UUID + "' not found or not accessible"),
|
||||
.with("adoptingMembership.uuid", UNAVAILABLE_UUID),
|
||||
"adoptingMembership.uuid='" + UNAVAILABLE_UUID + "' not found or not accessible"),
|
||||
|
||||
ASSETS_VALUE_MUST_NOT_BE_NULL(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "REVERSAL")
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", 0.00),
|
||||
"[assetValue must not be 0 but is \"0.00\"]"),
|
||||
|
||||
@ -190,8 +649,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
@EnumSource(BadRequestTestCases.class)
|
||||
void respondWithBadRequest(final BadRequestTestCases testCase) throws Exception {
|
||||
// HOWTO: run just a single test-case in a data-driven test-method
|
||||
// org.assertj.core.api.Assumptions.assumeThat(
|
||||
// testCase == ADOPTING_MEMBERSHIP_NUMBER_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
|
||||
// - set SINGLE_TEST_CASE_EXECUTION to true - see above
|
||||
// - select the test case enum value you want to run
|
||||
assumeThat(!SINGLE_TEST_CASE_EXECUTION ||
|
||||
testCase == BadRequestTestCases.ADOPTING_MEMBERSHIP_MUST_NOT_BE_THE_SAME).isTrue();
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
@ -202,9 +663,9 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage)))
|
||||
.andExpect(jsonPath("statusCode", is(400)))
|
||||
.andExpect(jsonPath("statusPhrase", is("Bad Request")))
|
||||
.andExpect(jsonPath("message", is("ERROR: [400] " + testCase.expectedErrorMessage)))
|
||||
.andExpect(status().is4xxClientError());
|
||||
}
|
||||
|
||||
@ -212,28 +673,38 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
REVERTING_SIMPLE_ASSET_TRANSACTION(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "REVERSAL")
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", "64.00")
|
||||
.with("valueDate", "2024-10-15")
|
||||
.with("reference", "reversal ref")
|
||||
.with("comment", "reversal comment")
|
||||
.with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString()),
|
||||
Expected.REVERT_RESPONSE),
|
||||
.with("reference", "reversal of loss ref")
|
||||
.with("comment", "reversal of loss asset tx comment")
|
||||
.with("revertedAssetTx.uuid", SOME_EXISTING_LOSS_ASSET_TX_UUID),
|
||||
Expected.REVERT_LOSS_RESPONSE),
|
||||
|
||||
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_NUMBER(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "TRANSFER")
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", -64.00)
|
||||
.with("adoptingMembership.memberNumber", AVAILABLE_TARGET_MEMBER_NUMBER),
|
||||
Expected.TRANSFER_RESPONSE),
|
||||
|
||||
TRANSFER_TO_GIVEN_AVAILABLE_MEMBERSHIP_UUID(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", "TRANSFER")
|
||||
.with("transactionType", TRANSFER)
|
||||
.with("assetValue", -64.00)
|
||||
.with("membership.uuid", ORIGIN_MEMBERSHIP_UUID.toString())
|
||||
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString()),
|
||||
Expected.TRANSFER_RESPONSE);
|
||||
.with("membership.uuid", ORIGIN_MEMBERSHIP_UUID)
|
||||
.with("adoptingMembership.uuid", AVAILABLE_TARGET_MEMBERSHIP_UUID),
|
||||
Expected.TRANSFER_RESPONSE),
|
||||
|
||||
REVERTING_TRANSFER_ASSET_TRANSACTION_IMPLICITLY_REVERTS_ADOPTING_ASSET_TRANSACTION(
|
||||
requestBody -> requestBody
|
||||
.with("transactionType", REVERSAL)
|
||||
.with("assetValue", "256.00")
|
||||
.with("valueDate", "2024-10-15")
|
||||
.with("reference", "reversal of transfer ref")
|
||||
.with("comment", "reversal of transfer asset tx comment")
|
||||
.with("revertedAssetTx.uuid", SOME_EXISTING_TRANSFER_ASSET_TX_UUID),
|
||||
Expected.REVERT_TRANSFER_RESPONSE);
|
||||
|
||||
private final Function<JsonBuilder, JsonBuilder> givenBodyTransformation;
|
||||
private final String expectedResponseBody;
|
||||
@ -251,7 +722,7 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
private static class Expected {
|
||||
|
||||
public static final String REVERT_RESPONSE = """
|
||||
public static final String REVERT_LOSS_RESPONSE = """
|
||||
{
|
||||
"uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
|
||||
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
|
||||
@ -259,9 +730,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 64.00,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "reversal ref",
|
||||
"comment": "reversal comment",
|
||||
"reference": "reversal of loss ref",
|
||||
"comment": "reversal of loss asset tx comment",
|
||||
"adoptionAssetTx": null,
|
||||
"reversalAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": {
|
||||
"uuid": "%{SOME_EXISTING_LOSS_ASSET_TX_UUID}",
|
||||
@ -279,7 +751,9 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.replace("%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}", NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString())
|
||||
.replace(
|
||||
"%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
|
||||
NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
|
||||
.replace("%{SOME_EXISTING_LOSS_ASSET_TX_UUID}", SOME_EXISTING_LOSS_ASSET_TX_UUID.toString());
|
||||
@ -303,20 +777,57 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
"reversalAssetTx": null
|
||||
}
|
||||
"""
|
||||
.replace("%{NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID}", NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID.toString())
|
||||
.replace(
|
||||
"%{NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID}",
|
||||
NEW_EXPLICITLY_CREATED_TRANSFER_ASSET_TX_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
|
||||
.replace("%{AVAILABLE_MEMBERSHIP_UUID}", AVAILABLE_TARGET_MEMBERSHIP_UUID.toString())
|
||||
.replace("%{AVAILABLE_TARGET_MEMBER_NUMBER}", AVAILABLE_TARGET_MEMBER_NUMBER);
|
||||
|
||||
public static final String REVERT_TRANSFER_RESPONSE = """
|
||||
{
|
||||
"uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
|
||||
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
|
||||
"membership.memberNumber": "%{ORIGIN_MEMBER_NUMBER}",
|
||||
"transactionType": "REVERSAL",
|
||||
"assetValue": 256.00,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "reversal of transfer ref",
|
||||
"comment": "reversal of transfer asset tx comment",
|
||||
"adoptionAssetTx": null,
|
||||
"transferAssetTx": null,
|
||||
"revertedAssetTx": {
|
||||
"uuid": "%{SOME_EXISTING_TRANSFER_ASSET_TX_UUID}",
|
||||
"membership.uuid": "%{ORIGIN_MEMBERSHIP_UUID}",
|
||||
"membership.memberNumber": "%{ORIGIN_MEMBER_NUMBER}",
|
||||
"transactionType": "TRANSFER",
|
||||
"assetValue": -256.00,
|
||||
"valueDate": "2024-10-15",
|
||||
"reference": "some transfer asset tx ref",
|
||||
"comment": "some transfer asset tx comment",
|
||||
"adoptionAssetTx.uuid": "%{SOME_EXISTING_ADOPTION_ASSET_TX_UUID}",
|
||||
"transferAssetTx.uuid": null,
|
||||
"revertedAssetTx.uuid": null,
|
||||
"reversalAssetTx.uuid": "%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.replace(
|
||||
"%{NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID}",
|
||||
NEW_EXPLICITLY_CREATED_REVERSAL_ASSET_TX_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBERSHIP_UUID}", ORIGIN_MEMBERSHIP_UUID.toString())
|
||||
.replace("%{ORIGIN_MEMBER_NUMBER}", ORIGIN_MEMBER_NUMBER)
|
||||
.replace("%{SOME_EXISTING_TRANSFER_ASSET_TX_UUID}", SOME_EXISTING_TRANSFER_ASSET_TX_UUID.toString())
|
||||
.replace("%{SOME_EXISTING_ADOPTION_ASSET_TX_UUID}", SOME_EXISTING_ADOPTION_ASSET_TX_UUID.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(SuccessfullyCreatedTestCases.class)
|
||||
void respondWithSuccessfullyCreated(final SuccessfullyCreatedTestCases testCase) throws Exception {
|
||||
// uncomment, if you need to run just a single test-case in this data-driven test-method
|
||||
// org.assertj.core.api.Assumptions.assumeThat(
|
||||
// testCase == ADOPTING_MEMBERSHIP_UUID_FOR_TRANSFER_MUST_BE_GIVEN_AND_AVAILABLE).isTrue();
|
||||
assumeThat(!SINGLE_TEST_CASE_EXECUTION ||
|
||||
testCase == SuccessfullyCreatedTestCases.REVERTING_TRANSFER_ASSET_TRANSACTION_IMPLICITLY_REVERTS_ADOPTING_ASSET_TRANSACTION).isTrue();
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
@ -331,12 +842,77 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
.andExpect(jsonPath("$", lenientlyEquals(testCase.expectedResponseBody)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleGeneratesProperJsonForAvailableUuid() throws Exception {
|
||||
// given
|
||||
when(coopAssetsTransactionRepo.findByUuid(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getUuid()))
|
||||
.thenReturn(Optional.of(SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/office/coopassetstransactions/" + SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getUuid())
|
||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath("$", lenientlyEquals(EXPECTED_RESULT_FROM_GET_SINGLE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleGeneratesNotFoundForUnavailableUuid() throws Exception {
|
||||
// given
|
||||
when(coopAssetsTransactionRepo.findByUuid(UNAVAILABLE_UUID)).thenReturn(Optional.empty());
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/office/coopassetstransactions/" + UNAVAILABLE_UUID)
|
||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getListGeneratesProperJson() throws Exception {
|
||||
// given
|
||||
when(coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(null, null, null))
|
||||
.thenReturn(List.of(
|
||||
SOME_EXISTING_LOSS_ASSET_TX_ENTITY,
|
||||
SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY,
|
||||
SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY,
|
||||
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY,
|
||||
SOME_REVERTED_DISBURSAL_ASSET_TX_ENTITY.getReversalAssetTx(),
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY,
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx(),
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getReversalAssetTx(),
|
||||
SOME_REVERTED_TRANSFER_ASSET_TX_ENTITY.getAdoptionAssetTx().getReversalAssetTx()
|
||||
));
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/office/coopassetstransactions")
|
||||
.header("current-subject", "superuser-alex@hostsharing.net")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
|
||||
// then
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath("$", lenientlyEquals(EXPECTED_RESULT_FROM_GET_LIST)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleTestCaseExecutionMustBeDisabled() {
|
||||
assertThat(SINGLE_TEST_CASE_EXECUTION).isFalse();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void initMocks() {
|
||||
TestUuidGenerator.start(4);
|
||||
TestUuidGenerator.start(DYNAMIC_UUID_START_INDEX);
|
||||
|
||||
when(emw.find(eq(HsOfficeMembershipEntity.class), eq(ORIGIN_MEMBERSHIP_UUID))).thenReturn(ORIGIN_TARGET_MEMBER_ENTITY);
|
||||
when(emw.find(eq(HsOfficeMembershipEntity.class), eq(AVAILABLE_TARGET_MEMBERSHIP_UUID))).thenReturn(AVAILABLE_MEMBER_ENTITY);
|
||||
when(emw.find(eq(HsOfficeMembershipEntity.class), eq(AVAILABLE_TARGET_MEMBERSHIP_UUID))).thenReturn(
|
||||
AVAILABLE_MEMBER_ENTITY);
|
||||
|
||||
final var availableMemberNumber = Integer.valueOf(AVAILABLE_TARGET_MEMBER_NUMBER.substring("M-".length()));
|
||||
when(membershipRepo.findMembershipByMemberNumber(eq(availableMemberNumber))).thenReturn(AVAILABLE_MEMBER_ENTITY);
|
||||
@ -346,6 +922,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
|
||||
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_LOSS_ASSET_TX_UUID))
|
||||
.thenReturn(Optional.of(SOME_EXISTING_LOSS_ASSET_TX_ENTITY));
|
||||
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_TRANSFER_ASSET_TX_UUID))
|
||||
.thenReturn(Optional.of(SOME_EXISTING_TRANSFER_ASSET_TX_ENTITY));
|
||||
when(coopAssetsTransactionRepo.findByUuid(SOME_EXISTING_ADOPTION_ASSET_TX_UUID))
|
||||
.thenReturn(Optional.of(SOME_EXISTING_ADOPTION_ASSET_TX_ENTITY));
|
||||
when(coopAssetsTransactionRepo.save(any(HsOfficeCoopAssetsTransactionEntity.class)))
|
||||
.thenAnswer(invocation -> {
|
||||
final var entity = (HsOfficeCoopAssetsTransactionEntity) invocation.getArgument(0);
|
||||
@ -358,10 +938,10 @@ class HsOfficeCoopAssetsTransactionControllerRestTest {
|
||||
}
|
||||
|
||||
private int partnerNumberOf(final String memberNumber) {
|
||||
return Integer.parseInt(memberNumber.substring("M-".length(), memberNumber.length()-2));
|
||||
return Integer.parseInt(memberNumber.substring("M-".length(), memberNumber.length() - 2));
|
||||
}
|
||||
|
||||
private String suffixOf(final String memberNumber) {
|
||||
return memberNumber.substring("M-".length()+5);
|
||||
return memberNumber.substring("M-".length() + 5);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -28,7 +30,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {HsadminNgApplication.class, JpaAttempt.class})
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = {HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class})
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -3,12 +3,15 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.rbac.test.JsonBuilder;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
@ -21,6 +24,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsOfficeCoopSharesTransactionController.class)
|
||||
@Import(DisableSecurityConfig.class)
|
||||
@ActiveProfiles("test")
|
||||
class HsOfficeCoopSharesTransactionControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
|
@ -12,6 +12,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -19,6 +20,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -33,8 +35,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -15,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -31,8 +33,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransact
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@ -14,6 +15,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
@ -27,7 +29,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsOfficeMembershipController.class)
|
||||
@Import(StandardMapper.class)
|
||||
@Import({StandardMapper.class, DisableSecurityConfig.class})
|
||||
@ActiveProfiles("test")
|
||||
public class HsOfficeMembershipControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
|
@ -13,10 +13,12 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealReposito
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -29,8 +31,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
private static final UUID GIVEN_NON_EXISTING_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||
|
@ -7,6 +7,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -16,6 +17,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
@ -36,7 +38,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(HsOfficePartnerController.class)
|
||||
@Import(StandardMapper.class)
|
||||
@Import({StandardMapper.class, DisableSecurityConfig.class})
|
||||
@ActiveProfiles("test")
|
||||
class HsOfficePartnerControllerRestTest {
|
||||
|
||||
static final UUID GIVEN_MANDANTE_UUID = UUID.randomUUID();
|
||||
|
@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -13,6 +14,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -26,8 +28,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
class HsOfficePersonControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
@LocalServerPort
|
||||
|
@ -9,11 +9,13 @@ import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -26,8 +28,9 @@ import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -16,7 +16,8 @@ import net.hostsharing.hsadminng.hs.office.scenarios.membership.CancelMembership
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDepositTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsDisbursalTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertSimpleTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsRevertTransferTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets.CreateCoopAssetsTransferTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesCancellationTransaction;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares.CreateCoopSharesRevertTransaction;
|
||||
@ -29,23 +30,29 @@ import net.hostsharing.hsadminng.hs.office.scenarios.person.ShouldUpdatePersonDa
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.RemoveOperationsContactFromPartner;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.SubscribeToMailinglist;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.subscription.UnsubscribeFromMailinglist;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.Produces;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.Requires;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.IgnoreOnFailure;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension;
|
||||
import org.junit.jupiter.api.ClassOrderer;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestClassOrder;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
@Tag("scenarioTest")
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class },
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class },
|
||||
properties = {
|
||||
"spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///scenariosTC}",
|
||||
"spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}",
|
||||
@ -53,21 +60,27 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
"hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}"
|
||||
}
|
||||
)
|
||||
@DirtiesContext
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@ActiveProfiles("test")
|
||||
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
|
||||
@ExtendWith(IgnoreOnFailureExtension.class)
|
||||
class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Nested
|
||||
@Order(10)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class PartnerScenarios {
|
||||
|
||||
@Test
|
||||
@Order(1010)
|
||||
@Produces(explicitly = "Partner: P-31010 - Test AG", implicitly = { "Person: Test AG", "Contact: Test AG - Hamburg" })
|
||||
void shouldCreateLegalPersonAsPartner() {
|
||||
new CreatePartner(this)
|
||||
new CreatePartner(scenarioTest)
|
||||
.given("partnerNumber", "P-31010")
|
||||
.given("personType", "LEGAL_PERSON")
|
||||
.given("tradeName", "Test AG")
|
||||
.given("contactCaption", "Test AG - Hamburg")
|
||||
.given("postalAddress", """
|
||||
.given(
|
||||
"postalAddress", """
|
||||
"firm": "Test AG",
|
||||
"street": "Shanghai-Allee 1",
|
||||
"zipcode": "20123",
|
||||
@ -85,13 +98,14 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Produces(explicitly = "Partner: P-31011 - Michelle Matthieu",
|
||||
implicitly = { "Person: Michelle Matthieu", "Contact: Michelle Matthieu" })
|
||||
void shouldCreateNaturalPersonAsPartner() {
|
||||
new CreatePartner(this)
|
||||
new CreatePartner(scenarioTest)
|
||||
.given("partnerNumber", "P-31011")
|
||||
.given("personType", "NATURAL_PERSON")
|
||||
.given("givenName", "Michelle")
|
||||
.given("familyName", "Matthieu")
|
||||
.given("contactCaption", "Michelle Matthieu")
|
||||
.given("postalAddress", """
|
||||
.given(
|
||||
"postalAddress", """
|
||||
"name": "Michelle Matthieu",
|
||||
"street": "An der Wandse 34",
|
||||
"zipcode": "22123",
|
||||
@ -109,11 +123,12 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Person: Test AG")
|
||||
@Produces("Representative: Tracy Trust for Test AG")
|
||||
void shouldAddRepresentativeToPartner() {
|
||||
new AddRepresentativeToPartner(this)
|
||||
new AddRepresentativeToPartner(scenarioTest)
|
||||
.given("partnerPersonTradeName", "Test AG")
|
||||
.given("representativeFamilyName", "Trust")
|
||||
.given("representativeGivenName", "Tracy")
|
||||
.given("representativePostalAddress", """
|
||||
.given(
|
||||
"representativePostalAddress", """
|
||||
"name": "Michelle Matthieu",
|
||||
"street": "An der Alster 100",
|
||||
"zipcode": "20000",
|
||||
@ -131,7 +146,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Person: Test AG")
|
||||
@Produces("Operations-Contact: Dennis Krause for Test AG")
|
||||
void shouldAddOperationsContactToPartner() {
|
||||
new AddOperationsContactToPartner(this)
|
||||
new AddOperationsContactToPartner(scenarioTest)
|
||||
.given("partnerPersonTradeName", "Test AG")
|
||||
.given("operationsContactFamilyName", "Krause")
|
||||
.given("operationsContactGivenName", "Dennis")
|
||||
@ -145,7 +160,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(1039)
|
||||
@Requires("Operations-Contact: Dennis Krause for Test AG")
|
||||
void shouldRemoveOperationsContactFromPartner() {
|
||||
new RemoveOperationsContactFromPartner(this)
|
||||
new RemoveOperationsContactFromPartner(scenarioTest)
|
||||
.given("operationsContactPerson", "Dennis Krause")
|
||||
.doRun();
|
||||
}
|
||||
@ -153,16 +168,22 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Test
|
||||
@Order(1090)
|
||||
void shouldDeletePartner() {
|
||||
new DeletePartner(this)
|
||||
new DeletePartner(scenarioTest)
|
||||
.given("partnerNumber", "P-31020")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(11)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class PartnerContactScenarios {
|
||||
|
||||
@Test
|
||||
@Order(1100)
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldAmendContactData() {
|
||||
new AmendContactData(this)
|
||||
new AmendContactData(scenarioTest)
|
||||
.given("partnerName", "Matthieu")
|
||||
.given("newEmailAddress", "michelle@matthieu.example.org")
|
||||
.doRun();
|
||||
@ -172,7 +193,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(1101)
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldAddPhoneNumberToContactData() {
|
||||
new AddPhoneNumberToContactData(this)
|
||||
new AddPhoneNumberToContactData(scenarioTest)
|
||||
.given("partnerName", "Matthieu")
|
||||
.given("phoneNumberKeyToAdd", "mobile")
|
||||
.given("phoneNumberToAdd", "+49 152 1234567")
|
||||
@ -183,7 +204,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(1102)
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldRemovePhoneNumberFromContactData() {
|
||||
new RemovePhoneNumberFromContactData(this)
|
||||
new RemovePhoneNumberFromContactData(scenarioTest)
|
||||
.given("partnerName", "Matthieu")
|
||||
.given("phoneNumberKeyToRemove", "office")
|
||||
.doRun();
|
||||
@ -193,7 +214,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(1103)
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
void shouldReplaceContactData() {
|
||||
new ReplaceContactData(this)
|
||||
new ReplaceContactData(scenarioTest)
|
||||
.given("partnerName", "Test AG")
|
||||
.given("newContactCaption", "Test AG - China")
|
||||
.given("newPostalAddress", """
|
||||
@ -210,23 +231,34 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
.given("newEmailAddress", "norden@test-ag.example.org")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(12)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class PartnerPersonScenarios {
|
||||
|
||||
@Test
|
||||
@Order(1201)
|
||||
@Requires("Partner: P-31011 - Michelle Matthieu")
|
||||
void shouldUpdatePersonData() {
|
||||
new ShouldUpdatePersonData(this)
|
||||
new ShouldUpdatePersonData(scenarioTest)
|
||||
.given("oldFamilyName", "Matthieu")
|
||||
.given("newFamilyName", "Matthieu-Zhang")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(20)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class DebitorScenarios {
|
||||
@Test
|
||||
@Order(2010)
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
@Produces("Debitor: D-3101000 - Test AG - main debitor")
|
||||
void shouldCreateSelfDebitorForPartner() {
|
||||
new CreateSelfDebitorForPartner(this)
|
||||
new CreateSelfDebitorForPartner(scenarioTest)
|
||||
.given("partnerPersonTradeName", "Test AG")
|
||||
.given("billingContactCaption", "Test AG - billing department")
|
||||
.given("billingContactEmailAddress", "billing@test-ag.example.org")
|
||||
@ -246,7 +278,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Person: Test AG")
|
||||
@Produces("Debitor: D-3101001 - Test AG - main debitor")
|
||||
void shouldCreateExternalDebitorForPartner() {
|
||||
new CreateExternalDebitorForPartner(this)
|
||||
new CreateExternalDebitorForPartner(scenarioTest)
|
||||
.given("partnerPersonTradeName", "Test AG")
|
||||
.given("billingContactCaption", "Billing GmbH - billing department")
|
||||
.given("billingContactEmailAddress", "billing@test-ag.example.org")
|
||||
@ -266,7 +298,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Person: Test AG")
|
||||
@Produces(explicitly = "Debitor: D-3101000 - Test AG - delete debitor", permanent = false)
|
||||
void shouldDeleteDebitor() {
|
||||
new DeleteDebitor(this)
|
||||
new DeleteDebitor(scenarioTest)
|
||||
.given("partnerNumber", "P-31020")
|
||||
.given("debitorSuffix", "02")
|
||||
.doRun();
|
||||
@ -277,18 +309,24 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||
@Disabled("see TODO.spec in DontDeleteDefaultDebitor")
|
||||
void shouldNotDeleteDefaultDebitor() {
|
||||
new DontDeleteDefaultDebitor(this)
|
||||
new DontDeleteDefaultDebitor(scenarioTest)
|
||||
.given("partnerNumber", "P-31010")
|
||||
.given("debitorSuffix", "00")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(31)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class SepaMandateScenarios {
|
||||
|
||||
@Test
|
||||
@Order(3100)
|
||||
@Requires("Debitor: D-3101000 - Test AG - main debitor")
|
||||
@Produces("SEPA-Mandate: Test AG")
|
||||
void shouldCreateSepaMandateForDebitor() {
|
||||
new CreateSepaMandateForDebitor(this)
|
||||
new CreateSepaMandateForDebitor(scenarioTest)
|
||||
// existing debitor
|
||||
.given("debitorNumber", "D-3101000")
|
||||
|
||||
@ -309,7 +347,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(3108)
|
||||
@Requires("SEPA-Mandate: Test AG")
|
||||
void shouldInvalidateSepaMandateForDebitor() {
|
||||
new InvalidateSepaMandateForDebitor(this)
|
||||
new InvalidateSepaMandateForDebitor(scenarioTest)
|
||||
.given("bankAccountIBAN", "DE02701500000000594937")
|
||||
.given("mandateValidUntil", "2025-09-30")
|
||||
.doRun();
|
||||
@ -319,17 +357,23 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(3109)
|
||||
@Requires("SEPA-Mandate: Test AG")
|
||||
void shouldFinallyDeleteSepaMandateForDebitor() {
|
||||
new FinallyDeleteSepaMandateForDebitor(this)
|
||||
new FinallyDeleteSepaMandateForDebitor(scenarioTest)
|
||||
.given("bankAccountIBAN", "DE02701500000000594937")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(40)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class MembershipScenarios {
|
||||
|
||||
@Test
|
||||
@Order(4000)
|
||||
@Requires("Partner: P-31010 - Test AG")
|
||||
@Produces("Membership: M-3101000 - Test AG")
|
||||
void shouldCreateMembershipForPartner() {
|
||||
new CreateMembership(this)
|
||||
new CreateMembership(scenarioTest)
|
||||
.given("partnerName", "Test AG")
|
||||
.given("validFrom", "2024-10-15")
|
||||
.given("newStatus", "ACTIVE")
|
||||
@ -338,12 +382,29 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
.keep();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4090)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldCancelMembershipOfPartner() {
|
||||
new CancelMembership(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("validTo", "2025-12-30")
|
||||
.given("newStatus", "CANCELLED")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(42)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class CoopSharesScenarios {
|
||||
|
||||
@Test
|
||||
@Order(4201)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
@Produces("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction")
|
||||
void shouldSubscribeCoopShares() {
|
||||
new CreateCoopSharesSubscriptionTransaction(this)
|
||||
new CreateCoopSharesSubscriptionTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("reference", "sign 2024-01-15")
|
||||
.given("shareCount", 100)
|
||||
@ -356,7 +417,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(4202)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldRevertCoopSharesSubscription() {
|
||||
new CreateCoopSharesRevertTransaction(this)
|
||||
new CreateCoopSharesRevertTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("comment", "reverting some incorrect transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
@ -368,7 +429,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Requires("Coop-Shares M-3101000 - Test AG - SUBSCRIPTION Transaction")
|
||||
@Produces("Coop-Shares M-3101000 - Test AG - CANCELLATION Transaction")
|
||||
void shouldCancelCoopSharesSubscription() {
|
||||
new CreateCoopSharesCancellationTransaction(this)
|
||||
new CreateCoopSharesCancellationTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("reference", "cancel 2024-01-15")
|
||||
.given("sharesToCancel", 8)
|
||||
@ -376,13 +437,19 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
.given("transactionDate", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(43)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class CoopAssetsScenarios {
|
||||
|
||||
@Test
|
||||
@Order(4301)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
@Produces("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
@Produces("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
void shouldSubscribeCoopAssets() {
|
||||
new CreateCoopAssetsDepositTransaction(this)
|
||||
new CreateCoopAssetsDepositTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("reference", "sign 2024-01-15")
|
||||
.given("assetValue", 100 * 64)
|
||||
@ -395,7 +462,7 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(4302)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldRevertCoopAssetsSubscription() {
|
||||
new CreateCoopAssetsRevertTransaction(this)
|
||||
new CreateCoopAssetsRevertSimpleTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("comment", "reverting some incorrect transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
@ -404,10 +471,10 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(4303)
|
||||
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
@Produces("Coop-Assets M-3101000 - Test AG - DISBURSAL Transaction")
|
||||
@Requires("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
@Produces("Coop-Assets: M-3101000 - Test AG - DISBURSAL Transaction")
|
||||
void shouldDisburseCoopAssets() {
|
||||
new CreateCoopAssetsDisbursalTransaction(this)
|
||||
new CreateCoopAssetsDisbursalTransaction(scenarioTest)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("reference", "cancel 2024-01-15")
|
||||
.given("valueToDisburse", 8 * 64)
|
||||
@ -418,14 +485,14 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(4304)
|
||||
@Requires("Coop-Assets M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
@Produces("Coop-Assets M-3101000 - Test AG - TRANSFER Transaction")
|
||||
@Requires("Coop-Assets: M-3101000 - Test AG - DEPOSIT Transaction")
|
||||
@Produces(explicitly = "Coop-Assets: M-3101000 - Test AG - TRANSFER Transaction", implicitly = "Membership: M-4303000 - New AG")
|
||||
void shouldTransferCoopAssets() {
|
||||
new CreateCoopAssetsTransferTransaction(this)
|
||||
new CreateCoopAssetsTransferTransaction(scenarioTest)
|
||||
.given("transferringMemberNumber", "M-3101000")
|
||||
.given("adoptingMemberNumber", "M-4303000")
|
||||
.given("reference", "transfer 2024-12-31")
|
||||
.given("valueToDisburse", 2 * 64)
|
||||
.given("valueToTransfer", 2 * 64)
|
||||
.given("comment", "transfer assets from M-3101000 to M-4303000")
|
||||
.given("transactionDate", "2024-12-31")
|
||||
.doRun();
|
||||
@ -433,33 +500,29 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
|
||||
@Test
|
||||
@Order(4305)
|
||||
@Requires("Coop-Assets M-3101000 - Test AG - TRANSFER Transaction")
|
||||
@IgnoreOnFailure("TODO.impl: reverting transfers is not implemented yet")
|
||||
void shouldRevertCoopAssetsTransfer() {
|
||||
new CreateCoopAssetsRevertTransaction(this)
|
||||
.given("memberNumber", "M-3101000")
|
||||
@Requires("Coop-Assets: M-3101000 - Test AG - TRANSFER Transaction")
|
||||
void shouldRevertCoopAssetsTransferIncludingRelatedAssetAdoption() {
|
||||
new CreateCoopAssetsRevertTransferTransaction(scenarioTest)
|
||||
.given("transferringMemberNumber", "M-3101000")
|
||||
.given("adoptingMemberNumber", "M-4303000")
|
||||
.given("transferredValue", 2 * 64)
|
||||
.given("comment", "reverting some incorrect transfer transaction")
|
||||
.given("dateOfIncorrectTransaction", "2024-02-15")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4900)
|
||||
@Requires("Membership: M-3101000 - Test AG")
|
||||
void shouldCancelMembershipOfPartner() {
|
||||
new CancelMembership(this)
|
||||
.given("memberNumber", "M-3101000")
|
||||
.given("validTo", "2025-12-30")
|
||||
.given("newStatus", "CANCELLED")
|
||||
.doRun();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(50)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class SubscriptionScenarios {
|
||||
|
||||
@Test
|
||||
@Order(5000)
|
||||
@Requires("Person: Test AG")
|
||||
@Produces("Subscription: Michael Miller to operations-announce")
|
||||
void shouldSubscribeNewPersonAndContactToMailinglist() {
|
||||
new SubscribeToMailinglist(this)
|
||||
new SubscribeToMailinglist(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")
|
||||
@ -474,9 +537,10 @@ class HsOfficeScenarioTests extends ScenarioTest {
|
||||
@Order(5001)
|
||||
@Requires("Subscription: Michael Miller to operations-announce")
|
||||
void shouldUnsubscribeNewPersonAndContactToMailinglist() {
|
||||
new UnsubscribeFromMailinglist(this)
|
||||
new UnsubscribeFromMailinglist(scenarioTest)
|
||||
.given("mailingList", "operations-announce")
|
||||
.given("subscriberEMailAddress", "michael.miller@example.org")
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Target(METHOD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Produces {
|
||||
String value() default ""; // same as explicitly, makes it possible to omit the property name
|
||||
String explicitly() default ""; // same as value
|
||||
String[] implicitly() default {};
|
||||
boolean permanent() default true; // false means that the object gets deleted again in the process
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver;
|
||||
import net.hostsharing.hsadminng.lambda.Reducer;
|
||||
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import org.apache.commons.collections4.SetUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public abstract class ScenarioTest extends ContextBasedTest {
|
||||
|
||||
final static String RUN_AS_USER = "superuser-alex@hostsharing.net"; // TODO.test: use global:AGENT when implemented
|
||||
|
||||
record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ObjectUtils.toString(uuid);
|
||||
}
|
||||
|
||||
}
|
||||
private final static Map<String, Alias<?>> aliases = new HashMap<>();
|
||||
|
||||
private final static Map<String, Object> properties = new HashMap<>();
|
||||
public final TestReport testReport = new TestReport(aliases);
|
||||
|
||||
@LocalServerPort
|
||||
Integer port;
|
||||
|
||||
@Autowired
|
||||
HsOfficePersonRepository personRepo;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@SneakyThrows
|
||||
@BeforeEach
|
||||
void init(final TestInfo testInfo) {
|
||||
createHostsharingPerson();
|
||||
try {
|
||||
testInfo.getTestMethod().ifPresent(this::callRequiredProducers);
|
||||
testReport.createTestLogMarkdownFile(testInfo);
|
||||
} catch (Exception exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() { // final TestInfo testInfo
|
||||
properties.clear();
|
||||
testReport.close();
|
||||
}
|
||||
|
||||
private void createHostsharingPerson() {
|
||||
jpaAttempt.transacted(() ->
|
||||
{
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
aliases.put(
|
||||
"Person: Hostsharing eG",
|
||||
new Alias<>(
|
||||
null,
|
||||
personRepo.findPersonByOptionalNameLike("Hostsharing eG")
|
||||
.stream()
|
||||
.map(HsOfficePersonEntity::getUuid)
|
||||
.reduce(Reducer::toSingleElement).orElseThrow())
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void callRequiredProducers(final Method currentTestMethod) {
|
||||
final var testMethodRequired = Optional.of(currentTestMethod)
|
||||
.map(m -> m.getAnnotation(Requires.class))
|
||||
.map(Requires::value)
|
||||
.orElse(null);
|
||||
if (testMethodRequired != null) {
|
||||
for (Method potentialProducerMethod : getClass().getDeclaredMethods()) {
|
||||
final var producesAnnot = potentialProducerMethod.getAnnotation(Produces.class);
|
||||
if (producesAnnot != null) {
|
||||
final var testMethodProduces = allOf(
|
||||
producesAnnot.value(),
|
||||
producesAnnot.explicitly(),
|
||||
producesAnnot.implicitly());
|
||||
// @formatter:off
|
||||
if ( // that method can produce something required
|
||||
testMethodProduces.contains(testMethodRequired) &&
|
||||
|
||||
// and it does not produce anything we already have (would cause errors)
|
||||
SetUtils.intersection(testMethodProduces, knowVariables().keySet()).isEmpty()
|
||||
) {
|
||||
assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod);
|
||||
|
||||
// then we recursively produce the pre-requisites of the producer method
|
||||
callRequiredProducers(potentialProducerMethod);
|
||||
|
||||
// and finally we call the producer method
|
||||
potentialProducerMethod.invoke(this);
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
|
||||
final var all = new HashSet<String>();
|
||||
if (!value.isEmpty()) {
|
||||
all.add(value);
|
||||
}
|
||||
if (!explicitly.isEmpty()) {
|
||||
all.add(explicitly);
|
||||
}
|
||||
all.addAll(asList(implicitly));
|
||||
return all;
|
||||
}
|
||||
|
||||
static boolean containsAlias(final String alias) {
|
||||
return aliases.containsKey(alias);
|
||||
}
|
||||
|
||||
static UUID uuid(final String nameWithPlaceholders) {
|
||||
final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS);
|
||||
final UUID alias = ofNullable(knowVariables().get(resolvedName)).filter(v -> v instanceof UUID).map(UUID.class::cast).orElse(null);
|
||||
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
|
||||
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
|
||||
).isNotNull();
|
||||
return alias;
|
||||
}
|
||||
|
||||
static void putAlias(final String name, final Alias<?> value) {
|
||||
aliases.put(name, value);
|
||||
}
|
||||
|
||||
static void putProperty(final String name, final Object value) {
|
||||
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
|
||||
}
|
||||
|
||||
static Map<String, Object> knowVariables() {
|
||||
final var map = new LinkedHashMap<String, Object>();
|
||||
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
|
||||
map.putAll(ScenarioTest.properties);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String resolve(final String text, final Resolver resolver) {
|
||||
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(resolver);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public static Object resolveTyped(final String text) {
|
||||
final var resolved = resolve(text, DROP_COMMENTS);
|
||||
try {
|
||||
return UUID.fromString(resolved);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// ignore and just use the String value
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.contact;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.contact;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.contact;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.contact;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.person.CreatePerson;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class DeleteDebitor extends UseCase<DeleteDebitor> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class DontDeleteDefaultDebitor extends UseCase<DontDeleteDefaultDebitor> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.debitor;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopAssetsDepositTransaction extends CreateCoopAssetsTransaction {
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopAssetsDisbursalTransaction extends CreateCoopAssetsTransaction {
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransaction {
|
||||
public class CreateCoopAssetsRevertSimpleTransaction extends CreateCoopAssetsTransaction {
|
||||
|
||||
public CreateCoopAssetsRevertTransaction(final ScenarioTest testSuite) {
|
||||
public CreateCoopAssetsRevertSimpleTransaction(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
requires("CoopAssets-Transaction with incorrect assetValue", alias ->
|
||||
new CreateCoopAssetsDepositTransaction(testSuite)
|
||||
.given("memberNumber", "%{memberNumber}")
|
||||
.given("reference", "sign %{dateOfIncorrectTransaction}") // same as relatedAssetTx
|
||||
.given("reference", "sign %{dateOfIncorrectTransaction}") // same text as relatedAssetTx
|
||||
.given("assetValue", 10)
|
||||
.given("comment", "coop-assets deposit transaction with wrong asset value")
|
||||
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||
@ -21,7 +21,9 @@ public class CreateCoopAssetsRevertTransaction extends CreateCoopAssetsTransacti
|
||||
protected HttpResponse run() {
|
||||
given("transactionType", "REVERSAL");
|
||||
given("assetValue", -10);
|
||||
given("reference", "sign %{dateOfIncorrectTransaction}"); // same text as relatedAssetTx
|
||||
given("revertedAssetTx", uuid("CoopAssets-Transaction with incorrect assetValue"));
|
||||
given("transactionDate", "%{dateOfIncorrectTransaction}");
|
||||
return super.run();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.ScenarioTest.resolveTyped;
|
||||
|
||||
public class CreateCoopAssetsRevertTransferTransaction extends CreateCoopAssetsTransaction {
|
||||
|
||||
public CreateCoopAssetsRevertTransferTransaction(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
requires("Accidental CoopAssets-TRANSFER-Transaction", alias ->
|
||||
new CreateCoopAssetsTransferTransaction(testSuite)
|
||||
.given("reference", "transfer %{dateOfIncorrectTransaction}")
|
||||
.given("valueToTransfer", "%{transferredValue}")
|
||||
.given("comment", "accidental transfer of assets from %{transferringMemberNumber} to %{adoptingMemberNumber}")
|
||||
.given("transactionDate", "%{dateOfIncorrectTransaction}")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
given("transactionType", "REVERSAL");
|
||||
given("assetValue", "%{transferredValue}");
|
||||
given("reference", "sign %{dateOfIncorrectTransaction}"); // same text as relatedAssetTx
|
||||
given("revertedAssetTx", uuid("Accidental CoopAssets-TRANSFER-Transaction"));
|
||||
given("transactionDate", "%{dateOfIncorrectTransaction}");
|
||||
return super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verify(final UseCase<CreateCoopAssetsTransaction>.HttpResponse response) {
|
||||
super.verify(response);
|
||||
|
||||
final var revertedAssetTxUuid = response.getFromBody("revertedAssetTx.uuid");
|
||||
given("negativeAssetValue", resolveTyped("%{transferredValue}", BigDecimal.class).negate());
|
||||
|
||||
verify("Verify Reverted Coop-Assets TRANSFER-Transaction",
|
||||
() -> httpGet("/api/hs/office/coopassetstransactions/" + revertedAssetTxUuid)
|
||||
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
|
||||
path("assetValue").contains("%{negativeAssetValue}"),
|
||||
path("comment").contains("%{comment}"),
|
||||
path("valueDate").contains("%{transactionDate}")
|
||||
);
|
||||
|
||||
final var adoptionAssetTxUuid = response.getFromBody("revertedAssetTx.['adoptionAssetTx.uuid']");
|
||||
|
||||
verify("Verify Related Coop-Assets ADOPTION-Transaction Also Got Reverted",
|
||||
() -> httpGet("/api/hs/office/coopassetstransactions/" + adoptionAssetTxUuid)
|
||||
.expecting(HttpStatus.OK).expecting(ContentType.JSON),
|
||||
path("reversalAssetTx.['transferAssetTx.uuid']").contains(revertedAssetTxUuid.toString())
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopassets;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.membership.CreateMembership;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.partner.CreatePartner;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
|
||||
public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransaction {
|
||||
|
||||
@ -19,8 +19,8 @@ public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransac
|
||||
.given("emailAddress", "board-of-directors@new-ag.example.org")
|
||||
);
|
||||
|
||||
requires("Membership: New AG", alias -> new CreateMembership(testSuite)
|
||||
.given("partnerNumber", toPartnerNumber("%{adoptingMemberNumber}"))
|
||||
requires("Membership: %{adoptingMemberNumber} - New AG", alias -> new CreateMembership(testSuite)
|
||||
.given("memberNumber", toPartnerNumber("%{adoptingMemberNumber}"))
|
||||
.given("partnerName", "New AG")
|
||||
.given("validFrom", "2024-11-15")
|
||||
.given("newStatus", "ACTIVE")
|
||||
@ -34,8 +34,7 @@ public class CreateCoopAssetsTransferTransaction extends CreateCoopAssetsTransac
|
||||
|
||||
given("memberNumber", "%{transferringMemberNumber}");
|
||||
given("transactionType", "TRANSFER");
|
||||
given("assetValue", "-%{valueToDisburse}");
|
||||
given("assetValue", "-%{valueToDisburse}");
|
||||
given("assetValue", "-%{valueToTransfer}");
|
||||
return super.run();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesCancellationTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesRevertTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
public class CreateCoopSharesSubscriptionTransaction extends CreateCoopSharesTransaction {
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.membership.coopshares;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.partner;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.partner;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.partner;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.partner;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class DeletePartner extends UseCase<DeletePartner> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.person;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class CreatePerson extends UseCase<CreatePerson> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.person;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
|
||||
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios.subscription;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.NO_CONTENT;
|
||||
|
@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountReposi
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -15,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -30,8 +32,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
|
||||
public final class JsonOptional<V> {
|
||||
|
||||
private final boolean jsonValueGiven;
|
||||
private final V jsonValue;
|
||||
|
||||
private JsonOptional() {
|
||||
this.jsonValueGiven = false;
|
||||
this.jsonValue = null;
|
||||
}
|
||||
|
||||
private JsonOptional(final V jsonValue) {
|
||||
this.jsonValueGiven = true;
|
||||
this.jsonValue = jsonValue;
|
||||
}
|
||||
|
||||
public static <T> JsonOptional<T> ofValue(final T value) {
|
||||
return new JsonOptional<>(value);
|
||||
}
|
||||
|
||||
public static <T> JsonOptional<T> notGiven() {
|
||||
return new JsonOptional<>();
|
||||
}
|
||||
|
||||
public V given() {
|
||||
if (!jsonValueGiven) {
|
||||
throw new IllegalStateException("JSON value was not given");
|
||||
}
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
public String givenAsString() {
|
||||
if (jsonValue instanceof Double doubleValue) {
|
||||
if (doubleValue % 1 == 0) {
|
||||
return String.valueOf(doubleValue.intValue()); // avoid trailing ".0"
|
||||
} else {
|
||||
return doubleValue.toString();
|
||||
}
|
||||
}
|
||||
return jsonValue == null ? null : jsonValue.toString();
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.scenarios.UseCase.HttpResponse;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase.HttpResponse;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class PathAssertion {
|
||||
@ -19,7 +19,7 @@ public class PathAssertion {
|
||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
||||
return response -> {
|
||||
try {
|
||||
response.path(path).map(this::asString).contains(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
||||
response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
||||
} catch (final AssertionError e) {
|
||||
// without this, the error message is often lacking important context
|
||||
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
|
||||
@ -37,15 +37,4 @@ public class PathAssertion {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String asString(final Object value) {
|
||||
if (value instanceof Double doubleValue) {
|
||||
if (doubleValue % 1 == 0) {
|
||||
return String.valueOf(doubleValue.intValue()); // avoid trailing ".0"
|
||||
} else {
|
||||
return doubleValue.toString();
|
||||
}
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@Target(METHOD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Produces {
|
||||
String value() default ""; // same as explicitly, makes it possible to omit the property name
|
||||
String explicitly() default ""; // same as value
|
||||
String[] implicitly() default {};
|
||||
boolean permanent() default true; // false means that the object gets deleted again in the process
|
||||
|
||||
@UtilityClass
|
||||
final class Aggregator {
|
||||
|
||||
public static Set<String> producedAliases(final Produces producesAnnot) {
|
||||
return allOf(
|
||||
producesAnnot.value(),
|
||||
producesAnnot.explicitly(),
|
||||
producesAnnot.implicitly());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Set<String> allOf(final String value, final String explicitly, final String[] implicitly) {
|
||||
final var all = new HashSet<String>();
|
||||
if (!value.isEmpty()) {
|
||||
all.add(value);
|
||||
}
|
||||
if (!explicitly.isEmpty()) {
|
||||
all.add(explicitly);
|
||||
}
|
||||
all.addAll(asList(implicitly));
|
||||
return all;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
@ -0,0 +1,257 @@
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
|
||||
import net.hostsharing.hsadminng.lambda.Reducer;
|
||||
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver;
|
||||
import org.apache.commons.collections4.SetUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.Produces.Aggregator.producedAliases;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
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<String> currentTestMethodProduces = new Stack<>();
|
||||
|
||||
protected ScenarioTest scenarioTest = this;
|
||||
|
||||
Optional<String> takeProducedAlias() {
|
||||
if (currentTestMethodProduces.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(currentTestMethodProduces.pop());
|
||||
}
|
||||
|
||||
record Alias<T extends UseCase<T>>(Class<T> useCase, UUID uuid) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toString(uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final static Map<String, Alias<?>> aliases = new HashMap<>();
|
||||
|
||||
private final static Map<String, Object> properties = new HashMap<>();
|
||||
public final TestReport testReport = new TestReport(aliases);
|
||||
|
||||
@LocalServerPort
|
||||
Integer port;
|
||||
|
||||
@Autowired
|
||||
HsOfficePersonRepository personRepo;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
|
||||
@SneakyThrows
|
||||
@BeforeEach
|
||||
void beforeScenario(final TestInfo testInfo) {
|
||||
createHostsharingPerson();
|
||||
try {
|
||||
testInfo.getTestMethod().ifPresent(currentTestMethod -> {
|
||||
callRequiredProducers(currentTestMethod);
|
||||
keepProducesAlias(currentTestMethod);
|
||||
});
|
||||
testReport.createTestLogMarkdownFile(testInfo);
|
||||
} catch (Exception exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterScenario(final TestInfo testInfo) { // final TestInfo testInfo
|
||||
testInfo.getTestMethod() .ifPresent(currentTestMethod -> {
|
||||
// FIXME: extract to method
|
||||
final var producesAnnot = currentTestMethod.getAnnotation(Produces.class);
|
||||
if (producesAnnot != null && producesAnnot.permanent()) {
|
||||
final var testMethodProduces = producedAliases(producesAnnot);
|
||||
testMethodProduces.forEach(declaredAlias ->
|
||||
assertThat(knowVariables().containsKey(declaredAlias))
|
||||
.as("@Producer method " + currentTestMethod.getName() +
|
||||
" did declare but not produce \"" + declaredAlias + "\"")
|
||||
.isTrue() );
|
||||
}
|
||||
});
|
||||
|
||||
properties.clear();
|
||||
testReport.close();
|
||||
}
|
||||
|
||||
private void createHostsharingPerson() {
|
||||
jpaAttempt.transacted(() ->
|
||||
{
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
aliases.put(
|
||||
"Person: Hostsharing eG",
|
||||
new Alias<>(
|
||||
null,
|
||||
personRepo.findPersonByOptionalNameLike("Hostsharing eG")
|
||||
.stream()
|
||||
.map(HsOfficePersonEntity::getUuid)
|
||||
.reduce(Reducer::toSingleElement).orElseThrow())
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void callRequiredProducers(final Method currentTestMethod) {
|
||||
final var testMethodRequires = Optional.of(currentTestMethod)
|
||||
.map(m -> m.getAnnotation(Requires.class))
|
||||
.map(Requires::value)
|
||||
.orElse(null);
|
||||
if (testMethodRequires != null) {
|
||||
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()
|
||||
) {
|
||||
assertThat(producesAnnot.permanent()).as("cannot depend on non-permanent producer: " + potentialProducerMethod);
|
||||
|
||||
// then we recursively produce the pre-requisites of the producer method
|
||||
callRequiredProducers(potentialProducerMethod);
|
||||
keepProducesAlias(currentTestMethod);
|
||||
|
||||
// and finally we call the producer method
|
||||
invokeProducerMethod(this, potentialProducerMethod);
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
assertThat(knowVariables().containsKey(testMethodRequires))
|
||||
.as("no @Producer for @Required(\"" + testMethodRequires + "\") found")
|
||||
.isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
private void keepProducesAlias(final Method currentTestMethod) {
|
||||
final var producesAnnot = currentTestMethod.getAnnotation(Produces.class);
|
||||
if (producesAnnot != null) {
|
||||
final var producesAlias = isNotBlank(producesAnnot.value()) ? producesAnnot.value() : producesAnnot.explicitly();
|
||||
assertThat(producesAlias)
|
||||
.as(currentTestMethod.getName() + " must define either value or explicit for @Produces")
|
||||
.isNotNull();
|
||||
this.currentTestMethodProduces.push(producesAlias);
|
||||
}
|
||||
}
|
||||
|
||||
private Method @NotNull [] getPotentialProducerMethods() {
|
||||
final var methodsDeclaredInOuterTestClass = stream(getClass().getDeclaredMethods())
|
||||
.filter(m -> m.getAnnotation(Produces.class) != null)
|
||||
.toArray(Method[]::new);
|
||||
final var methodsDeclaredInInnerTestClasses = stream(getClass().getDeclaredClasses())
|
||||
.map(Class::getDeclaredMethods).flatMap(Stream::of)
|
||||
.filter(m -> m.getAnnotation(Produces.class) != null)
|
||||
.toArray(Method[]::new);
|
||||
return ArrayUtils.addAll(methodsDeclaredInOuterTestClass, methodsDeclaredInInnerTestClasses);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void invokeProducerMethod(final ScenarioTest scenarioTest, final Method producerMethod) {
|
||||
producerMethod.setAccessible(true);
|
||||
if (producerMethod.getDeclaringClass() == scenarioTest.getClass()) {
|
||||
producerMethod.invoke(this);
|
||||
} else {
|
||||
final var innerClassConstructor = producerMethod.getDeclaringClass()
|
||||
.getDeclaredConstructor(scenarioTest.getClass());
|
||||
innerClassConstructor.setAccessible(true);
|
||||
final var inner = innerClassConstructor.newInstance(this);
|
||||
producerMethod.invoke(inner);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean containsAlias(final String alias) {
|
||||
return aliases.containsKey(alias);
|
||||
}
|
||||
|
||||
static UUID uuid(final String nameWithPlaceholders) {
|
||||
final var resolvedName = resolve(nameWithPlaceholders, DROP_COMMENTS);
|
||||
final UUID alias = ofNullable(knowVariables().get(resolvedName)).filter(v -> v instanceof UUID)
|
||||
.map(UUID.class::cast)
|
||||
.orElse(null);
|
||||
assertThat(alias).as("alias '" + resolvedName + "' not found in aliases nor in properties [" +
|
||||
knowVariables().keySet().stream().map(v -> "'" + v + "'").collect(Collectors.joining(", ")) + "]"
|
||||
).isNotNull();
|
||||
return alias;
|
||||
}
|
||||
|
||||
static void putAlias(final String name, final Alias<?> value) {
|
||||
aliases.put(name, value);
|
||||
}
|
||||
|
||||
static void putProperty(final String name, final Object value) {
|
||||
properties.put(name, (value instanceof String string) ? resolveTyped(string) : value);
|
||||
}
|
||||
|
||||
static void removeProperty(final String propName) {
|
||||
properties.remove(propName);
|
||||
}
|
||||
|
||||
static Map<String, Object> knowVariables() {
|
||||
final var map = new LinkedHashMap<String, Object>();
|
||||
ScenarioTest.aliases.forEach((key, value) -> map.put(key, value.uuid()));
|
||||
map.putAll(ScenarioTest.properties);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String resolve(final String text, final Resolver resolver) {
|
||||
final var resolved = new TemplateResolver(text, ScenarioTest.knowVariables()).resolve(resolver);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public static Object resolveTyped(final String resolvableText) {
|
||||
final var resolved = resolve(resolvableText, DROP_COMMENTS);
|
||||
try {
|
||||
return UUID.fromString(resolved);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// ignore and just use the String value
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public static <T> T resolveTyped(final String resolvableText, final Class<T> valueType) {
|
||||
final var resolvedValue = resolve(resolvableText, DROP_COMMENTS);
|
||||
if (valueType == BigDecimal.class) {
|
||||
//noinspection unchecked
|
||||
return (T) new BigDecimal(resolvedValue);
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) resolvedValue;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -10,7 +10,7 @@ import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
|
||||
public class TemplateResolver {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TemplateResolverUnitTest {
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.system.SystemProcess;
|
||||
@ -12,9 +12,12 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.lang.String.join;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestReport {
|
||||
@ -41,9 +44,12 @@ public class TestReport {
|
||||
}
|
||||
|
||||
public void createTestLogMarkdownFile(final TestInfo testInfo) throws IOException {
|
||||
final var testMethodName = testInfo.getTestMethod().map(Method::getName).orElseThrow();
|
||||
final var testMethodName = testInfo.getTestMethod().map(Method::getName)
|
||||
.map(TestReport::chopShouldPrefix)
|
||||
.map(TestReport::splitMixedCaseIntoSeparateWords)
|
||||
.orElseThrow();
|
||||
final var testMethodOrder = testInfo.getTestMethod().map(m -> m.getAnnotation(Order.class).value()).orElseThrow();
|
||||
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + "-" + testMethodName + ".md");
|
||||
markdownReportFile = new File(BUILD_DOC_SCENARIOS, testMethodOrder + ": " + testMethodName + ".md");
|
||||
markdownReport = new PrintWriter(new FileWriter(markdownReportFile));
|
||||
print("## Scenario #" + determineScenarioTitle(testInfo));
|
||||
}
|
||||
@ -119,6 +125,20 @@ public class TestReport {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String chopShouldPrefix(final String text) {
|
||||
return text.replaceAll("^should", "");
|
||||
}
|
||||
|
||||
private static String splitMixedCaseIntoSeparateWords(final String text) {
|
||||
final var WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))");
|
||||
final var matcher = WORD_FINDER.matcher(text);
|
||||
final var words = new ArrayList<String>();
|
||||
while (matcher.find()) {
|
||||
words.add(matcher.group(0));
|
||||
}
|
||||
return join(" ", words);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private String currentGitBranch() {
|
||||
try {
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@ -9,13 +9,14 @@ import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.reflection.AnnotationFinder;
|
||||
import org.apache.commons.collections4.map.LinkedMap;
|
||||
import org.assertj.core.api.OptionalAssert;
|
||||
import org.assertj.core.api.AbstractStringAssert;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
@ -27,15 +28,15 @@ import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.net.URLEncoder.encode;
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.DROP_COMMENTS;
|
||||
import static net.hostsharing.hsadminng.hs.office.scenarios.TemplateResolver.Resolver.KEEP_COMMENTS;
|
||||
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.test.DebuggerDetection.isDebuggerAttached;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
@ -70,10 +71,8 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public final void requires(final String alias, final Function<String, UseCase<?>> useCaseFactory) {
|
||||
if (!ScenarioTest.containsAlias(alias)) {
|
||||
requirements.put(alias, useCaseFactory);
|
||||
}
|
||||
}
|
||||
|
||||
public final HttpResponse doRun() {
|
||||
if (introduction != null) {
|
||||
@ -88,13 +87,18 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
testReport.printLine("");
|
||||
testReport.silent(() ->
|
||||
requirements.forEach((alias, factory) -> {
|
||||
if (!ScenarioTest.containsAlias(alias)) {
|
||||
factory.apply(alias).run().keepAs(alias);
|
||||
final var resolvedAlias = ScenarioTest.resolve(alias, DROP_COMMENTS);
|
||||
if (!ScenarioTest.containsAlias(resolvedAlias)) {
|
||||
factory.apply(resolvedAlias).run().keepAs(resolvedAlias);
|
||||
}
|
||||
})
|
||||
);
|
||||
final var response = run();
|
||||
verify(response);
|
||||
keepInProduceAlias(response);
|
||||
|
||||
resetProperties();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -109,7 +113,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
public final UseCase<T> given(final String propName, final Object propValue) {
|
||||
givenProperties.put(propName, propValue);
|
||||
givenProperties.put(propName, ScenarioTest.resolve(propValue == null ? null : propValue.toString(), TemplateResolver.Resolver.KEEP_COMMENTS));
|
||||
ScenarioTest.putProperty(propName, propValue);
|
||||
return this;
|
||||
}
|
||||
@ -206,7 +210,8 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
return new PathAssertion(path);
|
||||
}
|
||||
|
||||
protected void verify(
|
||||
@SafeVarargs
|
||||
protected final void verify(
|
||||
final String title,
|
||||
final Supplier<UseCase.HttpResponse> http,
|
||||
final Consumer<UseCase.HttpResponse>... assertions) {
|
||||
@ -236,12 +241,24 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
String resolvePlaceholders() {
|
||||
return ScenarioTest.resolve(template, DROP_COMMENTS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void keepInProduceAlias(final HttpResponse response) {
|
||||
final var producedAlias = testSuite.takeProducedAlias();
|
||||
if (response != null) {
|
||||
producedAlias.ifPresent(response::keepAs);
|
||||
}
|
||||
}
|
||||
|
||||
private static Duration seconds(final int secondsIfNoDebuggerAttached) {
|
||||
return isDebuggerAttached() ? Duration.ofHours(1) : Duration.ofSeconds(secondsIfNoDebuggerAttached);
|
||||
}
|
||||
|
||||
private void resetProperties() {
|
||||
givenProperties.forEach((propName, val) -> ScenarioTest.removeProperty(propName));
|
||||
}
|
||||
|
||||
public final class HttpResponse {
|
||||
|
||||
@Getter
|
||||
@ -319,22 +336,25 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String getFromBody(final String path) {
|
||||
return JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS));
|
||||
public <V> V getFromBody(final String path) {
|
||||
final var body = response.body();
|
||||
final var resolvedPath = ScenarioTest.resolve(path, DROP_COMMENTS);
|
||||
return JsonPath.parse(body).read(resolvedPath);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
public <T> Optional<T> getFromBodyAsOptional(final String path) {
|
||||
public <V> JsonOptional<V> getFromBodyAsOptional(final String path) {
|
||||
try {
|
||||
return Optional.ofNullable(JsonPath.parse(response.body()).read(ScenarioTest.resolve(path, DROP_COMMENTS)));
|
||||
return JsonOptional.ofValue(getFromBody(path));
|
||||
} catch (final PathNotFoundException e) {
|
||||
return null; // means the property did not exist at all, not that it was there with value null
|
||||
return JsonOptional.notGiven();
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public <T> OptionalAssert<T> path(final String path) {
|
||||
return assertThat(getFromBodyAsOptional(path));
|
||||
public AbstractStringAssert<?> path(final String path) {
|
||||
return assertThat(getFromBodyAsOptional(path).givenAsString());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@ -396,4 +416,12 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
private String title(String resultAlias) {
|
||||
return getClass().getSimpleName().replaceAll("([a-z])([A-Z]+)", "$1 $2") + " => " + resultAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final var properties = givenProperties.entrySet().stream()
|
||||
.map(e -> "\t" + e.getKey() + "=" + e.getValue())
|
||||
.collect(joining("\n"));
|
||||
return getClass().getSimpleName() + "(\n\t" + properties + "\n)";
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.hs.office.scenarios;
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import static org.assertj.core.api.Assumptions.assumeThat;
|
||||
|
@ -0,0 +1,30 @@
|
||||
package net.hostsharing.hsadminng.lambda;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class WithNonNullUnitTest {
|
||||
|
||||
Boolean didRun = null;
|
||||
|
||||
@Test
|
||||
void withNonNullRunsBodyIfNotNull() {
|
||||
didRun = false;
|
||||
withNonNull("test", nonNullValue -> {
|
||||
assertThat(nonNullValue).isEqualTo("test");
|
||||
didRun = true;
|
||||
} );
|
||||
assertThat(didRun).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withNonNullDoesNotRunBodyIfNull() {
|
||||
didRun = false;
|
||||
withNonNull(null, nonNullValue -> {
|
||||
didRun = true;
|
||||
} );
|
||||
assertThat(didRun).isFalse();
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package net.hostsharing.hsadminng.rbac.context;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.mapper.Array;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -13,6 +13,8 @@ import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -32,6 +34,9 @@ class ContextIntegrationTests {
|
||||
@Autowired
|
||||
private JpaAttempt jpaAttempt;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Test
|
||||
void defineWithoutHttpServletRequestUsesCallStack() {
|
||||
|
||||
@ -43,7 +48,7 @@ class ContextIntegrationTests {
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
void defineWithcurrentSubjectButWithoutAssumedRoles() {
|
||||
void defineWithCurrentSubjectButWithoutAssumedRoles() {
|
||||
// when
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
|
||||
@ -60,7 +65,7 @@ class ContextIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void defineWithoutcurrentSubjectButWithAssumedRoles() {
|
||||
void defineWithoutCurrentSubjectButWithAssumedRoles() {
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() ->
|
||||
context.define(null, "rbactest.package#yyy00:ADMIN")
|
||||
@ -73,7 +78,7 @@ class ContextIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void defineWithUnknowncurrentSubject() {
|
||||
void defineWithUnknownCurrentSubject() {
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() ->
|
||||
context.define("unknown@example.org")
|
||||
@ -87,7 +92,7 @@ class ContextIntegrationTests {
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
void defineWithcurrentSubjectAndAssumedRoles() {
|
||||
void defineWithCurrentSubjectAndAssumedRoles() {
|
||||
// given
|
||||
context.define("superuser-alex@hostsharing.net", "rbactest.customer#xxx:OWNER;rbactest.customer#yyy:OWNER");
|
||||
|
||||
@ -102,7 +107,7 @@ class ContextIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defineContextWithcurrentSubjectAndAssumeInaccessibleRole() {
|
||||
public void defineContextWithCurrentSubjectAndAssumeInaccessibleRole() {
|
||||
// when
|
||||
final var result = jpaAttempt.transacted(() ->
|
||||
context.define("customer-admin@xxx.example.com", "rbactest.package#yyy00:ADMIN")
|
||||
@ -113,4 +118,52 @@ class ContextIntegrationTests {
|
||||
jakarta.persistence.PersistenceException.class,
|
||||
"ERROR: [403] subject customer-admin@xxx.example.com has no permission to assume role rbactest.package#yyy00:ADMIN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasGlobalAdminRoleIsTrueForGlobalAdminWithoutAssumedRole() {
|
||||
|
||||
final var hsGlobalAdminRole = jpaAttempt.transacted(() -> {
|
||||
// given
|
||||
context.define("superuser-alex@hostsharing.net");
|
||||
|
||||
// when
|
||||
return (boolean) em.createNativeQuery("select rbac.hasGlobalAdminRole()").getSingleResult();
|
||||
}
|
||||
);
|
||||
|
||||
// then
|
||||
assertThat(hsGlobalAdminRole.returnedValue()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasGlobalAdminRoleIsTrueForGlobalAdminWithAssumedRole() {
|
||||
final var hsGlobalAdminRole = jpaAttempt.transacted(() -> {
|
||||
// given
|
||||
context.define("superuser-alex@hostsharing.net", "rbactest.package#yyy00:ADMIN");
|
||||
|
||||
// when
|
||||
return (boolean) em.createNativeQuery("select rbac.hasGlobalAdminRole()").getSingleResult();
|
||||
});
|
||||
|
||||
// when
|
||||
|
||||
// then
|
||||
assertThat(hsGlobalAdminRole.returnedValue()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasGlobalAdminRoleIsFalseForNonGlobalAdminWithoutAssumedRole() {
|
||||
|
||||
final var hsGlobalAdminRole = jpaAttempt.transacted(() -> {
|
||||
// given
|
||||
context.define("customer-admin@xxx.example.com");
|
||||
|
||||
// when
|
||||
return (boolean) em.createNativeQuery("select rbac.hasGlobalAdminRole()").getSingleResult();
|
||||
}
|
||||
);
|
||||
|
||||
// then
|
||||
assertThat(hsGlobalAdminRole.returnedValue()).isFalse();
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,14 @@ import net.hostsharing.hsadminng.rbac.role.RbacRoleRepository;
|
||||
import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity;
|
||||
import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -31,8 +33,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional(readOnly = true, propagation = Propagation.NEVER)
|
||||
class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
||||
|
||||
|
@ -4,17 +4,20 @@ import io.restassured.RestAssured;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.subject.RbacSubjectRepository;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class
|
||||
classes = {HsadminNgApplication.class, DisableSecurityConfig.class}
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
class RbacRoleControllerAcceptanceTest {
|
||||
|
||||
@LocalServerPort
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.rbac.role;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -11,6 +12,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
@ -29,7 +31,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(RbacRoleController.class)
|
||||
@Import(StandardMapper.class)
|
||||
@Import({StandardMapper.class, DisableSecurityConfig.class})
|
||||
@ActiveProfiles("test")
|
||||
@RunWith(SpringRunner.class)
|
||||
class RbacRoleControllerRestTest {
|
||||
|
||||
|
@ -5,11 +5,13 @@ import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -19,8 +21,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class RbacSubjectControllerAcceptanceTest {
|
||||
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.rbac.subject;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StandardMapper;
|
||||
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -10,6 +11,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
@ -24,7 +26,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@WebMvcTest(RbacSubjectController.class)
|
||||
@Import(StandardMapper.class)
|
||||
@Import({StandardMapper.class, DisableSecurityConfig.class})
|
||||
@ActiveProfiles("test")
|
||||
@RunWith(SpringRunner.class)
|
||||
class RbacSubjectControllerRestTest {
|
||||
|
||||
|
@ -27,7 +27,7 @@ public class JsonBuilder {
|
||||
* @param value JSON value
|
||||
* @return this JsonBuilder
|
||||
*/
|
||||
public JsonBuilder with(final String key, final String value) {
|
||||
public JsonBuilder with(final String key, final Object value) {
|
||||
try {
|
||||
jsonObject.put(key, value);
|
||||
} catch (JSONException e) {
|
||||
|
@ -5,6 +5,7 @@ import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@ -12,6 +13,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@ -24,8 +26,9 @@ import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class, JpaAttempt.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class TestCustomerControllerAcceptanceTest {
|
||||
|
||||
|
@ -4,6 +4,7 @@ import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.test.DisableSecurityConfig;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -11,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -22,8 +24,9 @@ import static org.hamcrest.Matchers.is;
|
||||
|
||||
@SpringBootTest(
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class
|
||||
classes = { HsadminNgApplication.class, DisableSecurityConfig.class }
|
||||
)
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
class TestPackageControllerAcceptanceTest {
|
||||
|
||||
|
@ -38,27 +38,29 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
|
||||
class FindAllByOptionalNameLike {
|
||||
|
||||
@Test
|
||||
public void globalAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
|
||||
public void globalAdmin_withoutAssumedRole_canViewAllPackagesDueToBypassoOfRecursiveCteRbacQuery() {
|
||||
// given
|
||||
// alex is not just rbac.global-admin but lso the creating user, thus we use fran
|
||||
// alex is not just rbac.global-admin but also the creating user, thus we use fran
|
||||
context.define("superuser-fran@hostsharing.net");
|
||||
|
||||
// when
|
||||
final var result = testPackageRepository.findAllByOptionalNameLike(null);
|
||||
|
||||
// then
|
||||
noPackagesAreReturned(result);
|
||||
|
||||
exactlyThesePackagesAreReturned(result,
|
||||
"xxx00", "xxx01", "xxx02", "yyy00", "yyy01", "yyy02", "zzz00", "zzz01", "zzz02");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
|
||||
given:
|
||||
public void globalAdmin_withAssumedGlobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
|
||||
// given
|
||||
context.define("superuser-alex@hostsharing.net", "rbac.global#global:ADMIN");
|
||||
|
||||
// when
|
||||
final var result = testPackageRepository.findAllByOptionalNameLike(null);
|
||||
|
||||
then:
|
||||
// then
|
||||
noPackagesAreReturned(result);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package net.hostsharing.hsadminng.test;
|
||||
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@TestConfiguration
|
||||
public class DisableSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
return http.build();
|
||||
}
|
||||
}
|
@ -63,6 +63,9 @@ public class TestUuidGenerator {
|
||||
* @return a constant UUID related to the given index
|
||||
*/
|
||||
public static UUID use(final int index) {
|
||||
if (staticallyUsedIndexes.contains(index)) {
|
||||
throw new IllegalArgumentException("index " + index + " already used statically");
|
||||
}
|
||||
staticallyUsedIndexes.add(index);
|
||||
return GIVEN_UUIDS.get(index);
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
|
||||
management:
|
||||
server:
|
||||
port: 8081
|
||||
address: 127.0.0.1
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: info, health, metrics
|
||||
|
||||
spring:
|
||||
sql:
|
||||
init:
|
||||
|
Loading…
Reference in New Issue
Block a user