credentials.totpSecret as array and update credentials scenario test (#186)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/186 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
@@ -9,7 +9,7 @@ classDiagram
|
||||
|
||||
class Credentials{
|
||||
+totpSecret: text
|
||||
+telephonePassword: text
|
||||
+phonePassword: text
|
||||
+emailAdress: text
|
||||
+smsNumber: text
|
||||
-active: bool [r/w]
|
||||
|
@@ -8,6 +8,7 @@ import java.util.function.BiConsumer;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ContextResource;
|
||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.api.CredentialsApi;
|
||||
@@ -67,12 +68,12 @@ public class HsCredentialsController implements CredentialsApi {
|
||||
final UUID credentialsUuid) {
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var credentials = credentialsRepo.findByUuid(credentialsUuid);
|
||||
if (credentials.isEmpty()) {
|
||||
final var credentialsEntity = credentialsRepo.findByUuid(credentialsUuid);
|
||||
if (credentialsEntity.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
final var result = mapper.map(
|
||||
credentials.get(), CredentialsResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
credentialsEntity.get(), CredentialsResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ public class HsCredentialsController implements CredentialsApi {
|
||||
mapper.map(person, HsOfficePersonResource.class)
|
||||
)
|
||||
);
|
||||
resource.setContexts(mapper.mapList(entity.getLoginContexts().stream().toList(), ContextResource.class));
|
||||
};
|
||||
|
||||
final BiConsumer<CredentialsInsertResource, HsCredentialsEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
|
@@ -11,6 +11,7 @@ import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class HsCredentialsEntity implements BaseEntity<HsCredentialsEntity>, Str
|
||||
protected static Stringify<HsCredentialsEntity> stringify = stringify(HsCredentialsEntity.class, "credentials")
|
||||
.withProp(HsCredentialsEntity::isActive)
|
||||
.withProp(HsCredentialsEntity::getEmailAddress)
|
||||
.withProp(HsCredentialsEntity::getTotpSecret)
|
||||
.withProp(HsCredentialsEntity::getTotpSecrets)
|
||||
.withProp(HsCredentialsEntity::getPhonePassword)
|
||||
.withProp(HsCredentialsEntity::getSmsNumber)
|
||||
.quotedValues(false);
|
||||
@@ -66,7 +67,7 @@ public class HsCredentialsEntity implements BaseEntity<HsCredentialsEntity>, Str
|
||||
private String onboardingToken;
|
||||
|
||||
@Column
|
||||
private String totpSecret;
|
||||
private List<String> totpSecrets;
|
||||
|
||||
@Column
|
||||
private String phonePassword;
|
||||
@@ -106,4 +107,5 @@ public class HsCredentialsEntity implements BaseEntity<HsCredentialsEntity>, Str
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.accounts.generated.api.v1.model.CredentialsPatc
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class HsCredentialsEntityPatcher implements EntityPatcher<CredentialsPatchResource> {
|
||||
|
||||
@@ -22,8 +23,8 @@ public class HsCredentialsEntityPatcher implements EntityPatcher<CredentialsPatc
|
||||
}
|
||||
OptionalFromJson.of(resource.getEmailAddress())
|
||||
.ifPresent(entity::setEmailAddress);
|
||||
OptionalFromJson.of(resource.getTotpSecret())
|
||||
.ifPresent(entity::setTotpSecret);
|
||||
Optional.ofNullable(resource.getTotpSecrets())
|
||||
.ifPresent(entity::setTotpSecrets);
|
||||
OptionalFromJson.of(resource.getSmsNumber())
|
||||
.ifPresent(entity::setSmsNumber);
|
||||
OptionalFromJson.of(resource.getPhonePassword())
|
||||
|
@@ -14,9 +14,11 @@ components:
|
||||
nickname:
|
||||
type: string
|
||||
pattern: '^[a-z][a-z0-9]{1,8}-[a-z0-9]{1,10}$' # TODO.spec: pattern for login nickname
|
||||
totpSecret:
|
||||
type: string
|
||||
telephonePassword:
|
||||
totpSecrets:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
phonePassword:
|
||||
type: string
|
||||
emailAddress:
|
||||
type: string
|
||||
@@ -46,9 +48,10 @@ components:
|
||||
CredentialsPatch:
|
||||
type: object
|
||||
properties:
|
||||
totpSecret:
|
||||
type: string
|
||||
nullable: true
|
||||
totpSecrets:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
phonePassword:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -75,9 +78,11 @@ components:
|
||||
nickname:
|
||||
type: string
|
||||
pattern: '^[a-z][a-z0-9]{1,8}-[a-z0-9]{1,10}$' # TODO.spec: pattern for login nickname
|
||||
totpSecret:
|
||||
type: string
|
||||
telephonePassword:
|
||||
totpSecrets:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
phonePassword:
|
||||
type: string
|
||||
emailAddress:
|
||||
type: string
|
||||
|
@@ -18,7 +18,7 @@ create table hs_accounts.credentials
|
||||
global_gid int unique, -- w/o
|
||||
onboarding_token text, -- w/o, but can be set to null to invalidate
|
||||
|
||||
totp_secret text,
|
||||
totp_secrets text[],
|
||||
phone_password text,
|
||||
email_address text,
|
||||
sms_number text
|
||||
|
@@ -51,9 +51,9 @@ begin
|
||||
-- call rbac.grantRoleToRole(hs_accounts.context_REFERRER(context_MATRIX_internal), rbac.global_ADMIN());
|
||||
|
||||
-- Add test credentials (linking to assumed rbac.subject UUIDs)
|
||||
INSERT INTO hs_accounts.credentials (uuid, version, person_uuid, active, global_uid, global_gid, onboarding_token, totp_secret, phone_password, email_address, sms_number) VALUES
|
||||
( superuserAlexSubjectUuid, 0, personAlexUuid, true, 1001, 1001, 'token-abc', 'otp-secret-1', 'phone-pw-1', 'alex@example.com', '111-222-3333'),
|
||||
( superuserFranSubjectUuid, 0, personFranUuid, true, 1002, 1002, 'token-def', 'otp-secret-2', 'phone-pw-2', 'fran@example.com', '444-555-6666');
|
||||
INSERT INTO hs_accounts.credentials (uuid, version, person_uuid, active, global_uid, global_gid, onboarding_token, totp_secrets, phone_password, email_address, sms_number) VALUES
|
||||
( superuserAlexSubjectUuid, 0, personAlexUuid, true, 1001, 1001, 'token-abc', ARRAY['otp-secret-1a', 'otp-secret-1b'], 'phone-pw-1', 'alex@example.com', '111-222-3333'),
|
||||
( superuserFranSubjectUuid, 0, personFranUuid, true, 1002, 1002, 'token-def', ARRAY['otp-secret-2'], 'phone-pw-2', 'fran@example.com', '444-555-6666');
|
||||
|
||||
-- Map credentials to contexts
|
||||
INSERT INTO hs_accounts.context_mapping (credentials_uuid, context_uuid) VALUES
|
||||
|
@@ -33,13 +33,13 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
|
||||
private static final Boolean INITIAL_ACTIVE = true;
|
||||
private static final String INITIAL_EMAIL_ADDRESS = "initial@example.com";
|
||||
private static final String INITIAL_TOTP_SECRET = "initial_2fa";
|
||||
private static final List<String> INITIAL_TOTP_SECRETS = List.of("initial_2fa");
|
||||
private static final String INITIAL_SMS_NUMBER = "initial_sms";
|
||||
private static final String INITIAL_PHONE_PASSWORD = "initial_phone_pw";
|
||||
|
||||
private static final Boolean PATCHED_ACTIVE = false;
|
||||
private static final String PATCHED_EMAIL_ADDRESS = "patched@example.com";
|
||||
private static final String PATCHED_TOTP_SECRET = "patched_2fa";
|
||||
private static final List<String> PATCHED_TOTP_SECRETS = List.of("patched_2fa");
|
||||
private static final String PATCHED_SMS_NUMBER = "patched_sms";
|
||||
private static final String PATCHED_PHONE_PASSWORD = "patched_phone_pw";
|
||||
|
||||
@@ -102,7 +102,7 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
entity.setUuid(INITIAL_CREDENTIALS_UUID);
|
||||
entity.setActive(INITIAL_ACTIVE);
|
||||
entity.setEmailAddress(INITIAL_EMAIL_ADDRESS);
|
||||
entity.setTotpSecret(INITIAL_TOTP_SECRET);
|
||||
entity.setTotpSecrets(INITIAL_TOTP_SECRETS);
|
||||
entity.setSmsNumber(INITIAL_SMS_NUMBER);
|
||||
entity.setPhonePassword(INITIAL_PHONE_PASSWORD);
|
||||
// Ensure loginContexts is a mutable set for the patcher
|
||||
@@ -137,12 +137,13 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
PATCHED_EMAIL_ADDRESS,
|
||||
HsCredentialsEntity::setEmailAddress,
|
||||
PATCHED_EMAIL_ADDRESS),
|
||||
new JsonNullableProperty<>(
|
||||
new SimpleProperty<>(
|
||||
"totpSecret",
|
||||
CredentialsPatchResource::setTotpSecret,
|
||||
PATCHED_TOTP_SECRET,
|
||||
HsCredentialsEntity::setTotpSecret,
|
||||
PATCHED_TOTP_SECRET),
|
||||
CredentialsPatchResource::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS,
|
||||
HsCredentialsEntity::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"smsNumber",
|
||||
CredentialsPatchResource::setSmsNumber,
|
||||
|
@@ -0,0 +1,38 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ContextResource;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public abstract class BaseCredentialsUseCase<T extends UseCase<?>> extends UseCase<T> {
|
||||
|
||||
public BaseCredentialsUseCase(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected ContextResource[] fetchContextResourcesByDescriptorPairs(final String descriptPairsVarName) {
|
||||
final var requestedContexts = ScenarioTest.getTypedVariable("contexts", Pair[].class);
|
||||
final var existingContextsJson = withTitle("Fetch Available Account Contexts", () ->
|
||||
httpGet("/api/hs/accounts/contexts").expecting(OK).expecting(JSON)
|
||||
).getResponse().body();
|
||||
final var existingContexts = objectMapper.readValue(existingContextsJson, ContextResource[].class);
|
||||
return Arrays.stream(requestedContexts)
|
||||
.map(pair -> Arrays.stream(existingContexts)
|
||||
.filter(context -> context.getType().equals(pair.getLeft())
|
||||
&& context.getQualifier().equals(pair.getRight()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"No matching context found for type=" + pair.getLeft()
|
||||
+ " and qualifier=" + pair.getRight()))
|
||||
)
|
||||
.toArray(ContextResource[]::new);
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ import org.springframework.http.HttpStatus;
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public class CreateCredentials extends UseCase<CreateCredentials> {
|
||||
public class CreateCredentials extends BaseCredentialsUseCase<CreateCredentials> {
|
||||
|
||||
public CreateCredentials(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
@@ -26,9 +26,8 @@ public class CreateCredentials extends UseCase<CreateCredentials> {
|
||||
"In real situations we have more precise measures to find the related person."
|
||||
);
|
||||
|
||||
|
||||
obtain("CredentialsContexts", () ->
|
||||
httpGet("/api/hs/accounts/contexts").expecting(OK).expecting(JSON)
|
||||
given("resolvedContexts",
|
||||
fetchContextResourcesByDescriptorPairs("contexts")
|
||||
);
|
||||
|
||||
return obtain("newCredentials", () ->
|
||||
@@ -37,12 +36,14 @@ public class CreateCredentials extends UseCase<CreateCredentials> {
|
||||
"person.uuid": ${Person: %{personGivenName} %{personFamilyName}},
|
||||
"nickname": ${nickname},
|
||||
"active": %{active},
|
||||
"totpSecrets": @{totpSecrets},
|
||||
"emailAddress": ${emailAddress},
|
||||
"telephonePassword": ${telephonePassword},
|
||||
"phonePassword": ${phonePassword},
|
||||
"smsNumber": ${smsNumber},
|
||||
"onboardingToken": ${onboardingToken},
|
||||
"globalUid": %{globalUid},
|
||||
"globalGid": %{globalGid},
|
||||
"contexts": @{contexts}
|
||||
"contexts": @{resolvedContexts}
|
||||
}
|
||||
"""))
|
||||
.expecting(HttpStatus.CREATED).expecting(ContentType.JSON)
|
||||
@@ -57,7 +58,9 @@ public class CreateCredentials extends UseCase<CreateCredentials> {
|
||||
.expecting(OK).expecting(JSON),
|
||||
path("uuid").contains("%{newCredentials}"),
|
||||
path("nickname").contains("%{nickname}"),
|
||||
path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}")
|
||||
path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}"),
|
||||
path("totpSecrets").contains("@{totpSecrets}"),
|
||||
path("onboardingToken").contains("%{onboardingToken}")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -4,10 +4,12 @@ import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||
import net.hostsharing.hsadminng.config.DisableSecurityConfig;
|
||||
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.mapper.Array;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import net.hostsharing.hsadminng.test.IgnoreOnFailureExtension;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.ClassOrderer;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
@@ -22,8 +24,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Tag("scenarioTest")
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
@@ -45,6 +45,7 @@ class CredentialsScenarioTests extends ScenarioTest {
|
||||
protected void beforeScenario(final TestInfo testInfo) {
|
||||
super.beforeScenario(testInfo);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Order(10)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@@ -62,22 +63,38 @@ class CredentialsScenarioTests extends ScenarioTest {
|
||||
.given("nickname", "firby-susan")
|
||||
// initial credentials
|
||||
.given("active", true)
|
||||
.given("totpSecrets", Array.of("initialSecret"))
|
||||
.given("emailAddress", "susan.firby@example.com")
|
||||
.given("telephonePassword", "securePass123")
|
||||
.given("phonePassword", "securePass123")
|
||||
.given("smsNumber", "+49123456789")
|
||||
.given("globalUid", 21011)
|
||||
.given("globalGid", 21011)
|
||||
.given("contexts", Array.of(
|
||||
Map.ofEntries(
|
||||
// a hardcoded context from test-data
|
||||
// TODO.impl: the uuid should be determined within CreateCredentials just by (HSDAMIN,prod)
|
||||
Map.entry("uuid", "11111111-1111-1111-1111-111111111111"),
|
||||
Map.entry("type", "HSADMIN"),
|
||||
Map.entry("qualifier", "prod")
|
||||
)
|
||||
Pair.of("HSADMIN", "prod")
|
||||
))
|
||||
.given("onboardingToken", "fake-unboarding-token")
|
||||
.doRun()
|
||||
.keep();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1020)
|
||||
@Requires("Credentials@hsadmin: firby-susan")
|
||||
void shouldUpdateCredentials() {
|
||||
new UpdateCredentials(scenarioTest)
|
||||
// the credentials to update
|
||||
.given("credentialsUuid", "%{Credentials@hsadmin: firby-susan}")
|
||||
// updated credentials
|
||||
.given("active", false)
|
||||
.given("totpSecrets", Array.of("initialSecret", "additionalSecret"))
|
||||
.given("emailAddress", "susan.firby@example.org")
|
||||
.given("phonePassword", "securePass987")
|
||||
.given("smsNumber", "+49987654321")
|
||||
.given("contexts", Array.of(
|
||||
Pair.of("HSADMIN", "prod"),
|
||||
Pair.of("SSH", "internal")
|
||||
))
|
||||
.doRun();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,56 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts.scenarios;
|
||||
|
||||
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 static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public class UpdateCredentials extends BaseCredentialsUseCase<UpdateCredentials> {
|
||||
|
||||
public UpdateCredentials(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
introduction("A set of credentials contains the login data for an RBAC subject.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
|
||||
given("resolvedContexts",
|
||||
fetchContextResourcesByDescriptorPairs("contexts")
|
||||
);
|
||||
|
||||
withTitle("Patch the Changes to the existing Credentials", () ->
|
||||
httpPatch("/api/hs/accounts/credentials/%{credentialsUuid}", usingJsonBody("""
|
||||
{
|
||||
"active": %{active},
|
||||
"totpSecrets": @{totpSecrets},
|
||||
"emailAddress": ${emailAddress},
|
||||
"phonePassword": ${phonePassword},
|
||||
"smsNumber": ${smsNumber},
|
||||
"contexts": @{resolvedContexts}
|
||||
}
|
||||
"""))
|
||||
.reportWithResponse().expecting(HttpStatus.OK).expecting(ContentType.JSON)
|
||||
.extractValue("nickname", "nickname")
|
||||
.extractValue("totpSecrets", "totpSecrets")
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verify(final UseCase<UpdateCredentials>.HttpResponse response) {
|
||||
verify(
|
||||
"Verify the Patched Credentials",
|
||||
() -> httpGet("/api/hs/accounts/credentials/%{credentialsUuid}")
|
||||
.expecting(OK).expecting(JSON),
|
||||
path("uuid").contains("%{newCredentials}"),
|
||||
path("nickname").contains("%{nickname}"),
|
||||
path("totpSecrets").contains("%{totpSecrets}")
|
||||
);
|
||||
}
|
||||
}
|
@@ -18,12 +18,13 @@ public class PathAssertion {
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Consumer<UseCase.HttpResponse> contains(final String resolvableValue) {
|
||||
final var resolvedValue = ScenarioTest.resolve(resolvableValue, DROP_COMMENTS);
|
||||
return response -> {
|
||||
try {
|
||||
response.path(path).isEqualTo(ScenarioTest.resolve(resolvableValue, DROP_COMMENTS));
|
||||
response.path(path).isEqualTo(resolvedValue);
|
||||
} catch (final AssertionError e) {
|
||||
// without this, the error message is often lacking important context
|
||||
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvableValue + "\")`" );
|
||||
fail(e.getMessage() + " in `path(\"" + path + "\").contains(\"" + resolvedValue + "\")`" );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -66,13 +66,13 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
keepProducesAlias(currentTestMethod);
|
||||
});
|
||||
testReport.createTestLogMarkdownFile(testInfo);
|
||||
} catch (Exception exc) {
|
||||
} catch (final Exception exc) {
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterScenario(final TestInfo testInfo) { // final TestInfo testInfo
|
||||
void afterScenario(final TestInfo testInfo) {
|
||||
verifyProduceDeclaration(testInfo);
|
||||
|
||||
properties.clear();
|
||||
@@ -191,7 +191,7 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
properties.remove(propName);
|
||||
}
|
||||
|
||||
static Map<String, Object> knowVariables() {
|
||||
public static Map<String, Object> knowVariables() {
|
||||
final var map = new LinkedHashMap<String, Object>();
|
||||
map.putAll(ScenarioTest.aliases);
|
||||
map.putAll(ScenarioTest.properties);
|
||||
@@ -223,4 +223,13 @@ public abstract class ScenarioTest extends ContextBasedTest {
|
||||
return (T) resolvedValue;
|
||||
}
|
||||
|
||||
public static <T> T getTypedVariable(final String varName, final Class<T> expectedValueClass) {
|
||||
final var value = knowVariables().get(varName);
|
||||
if (value != null && !expectedValueClass.isAssignableFrom(value.getClass())) {
|
||||
throw new IllegalArgumentException("variable '" + varName + "'" +
|
||||
" expected to be of type " + expectedValueClass + " " +
|
||||
" but got " + value.getClass());
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.scenarios;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
@@ -17,6 +19,7 @@ import static net.hostsharing.hsadminng.hs.scenarios.TemplateResolver.Resolver.D
|
||||
public class TemplateResolver {
|
||||
|
||||
public static final String JSON_NULL_VALUE_TO_KEEP = "NULL";
|
||||
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
public enum Resolver {
|
||||
DROP_COMMENTS, // deletes comments ('#{whatever}' -> '')
|
||||
@@ -230,6 +233,7 @@ public class TemplateResolver {
|
||||
};
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String jsonObject(final Object value) {
|
||||
return switch (value) {
|
||||
case null -> "null";
|
||||
@@ -237,7 +241,7 @@ public class TemplateResolver {
|
||||
.map(entry -> "\"" + entry.getKey() + "\": " + jsonQuoted(entry.getValue()))
|
||||
.collect(Collectors.joining(", ")) + "}";
|
||||
case String string -> "{" + string.replace("\n", " ") + "}";
|
||||
default -> throw new IllegalArgumentException("can not format " + value.getClass() + " (" + value + ") as JSON object");
|
||||
default -> OBJECT_MAPPER.writeValueAsString(value);
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ public abstract class UseCase<T extends UseCase<?>> {
|
||||
|
||||
private static final HttpClient client = HttpClient.newHttpClient();
|
||||
private static final int HTTP_TIMEOUT_SECONDS = 20; // FIXME: configurable in environment
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
protected final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
protected final ScenarioTest testSuite;
|
||||
private final TestReport testReport;
|
||||
|
Reference in New Issue
Block a user