mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-28 04:13:22 -04:00
Test framework validations and error messages (#45869)
Closes #38163 Signed-off-by: Simon Vacek <simonvacky@email.cz>
This commit is contained in:
parent
9a32b5e2c4
commit
20e78e468d
5 changed files with 89 additions and 26 deletions
|
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.testframework;
|
||||
|
||||
public class FatalTestClassException extends RuntimeException {
|
||||
|
||||
public FatalTestClassException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package org.keycloak.testframework;
|
||||
|
||||
public class TestFrameworkException extends RuntimeException {
|
||||
|
||||
public TestFrameworkException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import org.keycloak.admin.client.Keycloak;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testframework.TestFrameworkException;
|
||||
import org.keycloak.testframework.FatalTestClassException;
|
||||
import org.keycloak.testframework.annotations.InjectAdminClient;
|
||||
import org.keycloak.testframework.config.Config;
|
||||
import org.keycloak.testframework.injection.DependenciesBuilder;
|
||||
|
|
@ -49,20 +49,20 @@ public class AdminClientSupplier implements Supplier<Keycloak, InjectAdminClient
|
|||
String userId = !annotation.user().isEmpty() ? annotation.user() : null;
|
||||
|
||||
if (clientId == null) {
|
||||
throw new TestFrameworkException("Client is required when using managed realm mode");
|
||||
throw new FatalTestClassException("Client is required when using admin client in managed realm mode");
|
||||
}
|
||||
|
||||
RealmRepresentation realmRep = managedRealm.getCreatedRepresentation();
|
||||
ClientRepresentation clientRep = realmRep.getClients().stream()
|
||||
.filter(c -> c.getClientId().equals(annotation.client()))
|
||||
.findFirst().orElseThrow(() -> new TestFrameworkException("Client " + annotation.client() + " not found in managed realm"));
|
||||
.findFirst().orElseThrow(() -> new FatalTestClassException("Client with clientId=\"" + annotation.client() + "\" not found in realm with ref=\"" + annotation.realmRef() + "\""));
|
||||
|
||||
adminBuilder.clientId(clientId).clientSecret(clientRep.getSecret());
|
||||
|
||||
if (userId != null) {
|
||||
UserRepresentation userRep = realmRep.getUsers().stream()
|
||||
.filter(u -> u.getUsername().equals(annotation.user()))
|
||||
.findFirst().orElseThrow(() -> new TestFrameworkException("User " + annotation.user() + " not found in managed realm"));
|
||||
.findFirst().orElseThrow(() -> new FatalTestClassException("User with username=\"" + annotation.user() + "\" not found in realm with ref=\"" + annotation.realmRef() + "\""));
|
||||
String password = ManagedUser.getPassword(userRep);
|
||||
adminBuilder.username(userRep.getUsername()).password(password);
|
||||
adminBuilder.grantType(OAuth2Constants.PASSWORD);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.testframework.FatalTestClassException;
|
||||
import org.keycloak.testframework.TestFrameworkExecutor;
|
||||
import org.keycloak.testframework.annotations.TestCleanup;
|
||||
import org.keycloak.testframework.annotations.TestSetup;
|
||||
|
|
@ -20,6 +21,7 @@ import org.keycloak.testframework.injection.predicates.RequestedInstancePredicat
|
|||
import org.keycloak.testframework.injection.predicates.TestFrameworkExecutorPredicates;
|
||||
import org.keycloak.testframework.server.KeycloakServer;
|
||||
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.InvocationInterceptor;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
|
|
@ -34,6 +36,7 @@ public class Registry implements AutoCloseable {
|
|||
private final Extensions extensions;
|
||||
private final List<InstanceContext<?, ?>> deployedInstances = new LinkedList<>();
|
||||
private final List<RequestedInstance<?, ?>> requestedInstances = new LinkedList<>();
|
||||
private FatalTestClassException fatalTestClassException;
|
||||
|
||||
private Object currentTestInstance;
|
||||
|
||||
|
|
@ -118,18 +121,32 @@ public class Registry implements AutoCloseable {
|
|||
}
|
||||
|
||||
public void beforeEach(Object testInstance, Method testMethod) {
|
||||
findRequestedInstances(testInstance, testMethod);
|
||||
destroyIncompatibleInstances();
|
||||
matchDeployedInstancesWithRequestedInstances();
|
||||
deployRequestedInstances();
|
||||
invokeBeforeEachOnSuppliers();
|
||||
injectFields(testInstance);
|
||||
|
||||
if (currentTestInstance == null || testInstance.getClass() != currentTestInstance.getClass()) {
|
||||
executeSetup(testInstance, TestSetup.class);
|
||||
if (fatalTestClassException != null) {
|
||||
skipTestMethod();
|
||||
}
|
||||
|
||||
currentTestInstance = testInstance;
|
||||
try {
|
||||
findRequestedInstances(testInstance, testMethod);
|
||||
destroyIncompatibleInstances();
|
||||
matchDeployedInstancesWithRequestedInstances();
|
||||
deployRequestedInstances();
|
||||
invokeBeforeEachOnSuppliers();
|
||||
injectFields(testInstance);
|
||||
|
||||
if (currentTestInstance == null || testInstance.getClass() != currentTestInstance.getClass()) {
|
||||
executeSetup(testInstance, TestSetup.class);
|
||||
}
|
||||
|
||||
currentTestInstance = testInstance;
|
||||
} catch (FatalTestClassException e) {
|
||||
requestedInstances.clear();
|
||||
fatalTestClassException = e;
|
||||
skipTestMethod();
|
||||
}
|
||||
}
|
||||
|
||||
private void skipTestMethod() {
|
||||
Assumptions.abort("Skipping test method due to fatal test class error");
|
||||
}
|
||||
|
||||
public void intercept(InvocationInterceptor.Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext) throws Throwable {
|
||||
|
|
@ -262,11 +279,20 @@ public class Registry implements AutoCloseable {
|
|||
}
|
||||
|
||||
public void afterAll() {
|
||||
executeSetup(currentTestInstance, TestCleanup.class);
|
||||
FatalTestClassException exception = fatalTestClassException;
|
||||
fatalTestClassException = null;
|
||||
|
||||
if (exception == null) {
|
||||
executeSetup(currentTestInstance, TestCleanup.class);
|
||||
}
|
||||
|
||||
logger.logAfterAll();
|
||||
List<InstanceContext<?, ?>> destroy = deployedInstances.stream().filter(InstanceContextPredicates.hasLifeCycle(LifeCycle.CLASS)).toList();
|
||||
destroy.forEach(this::destroy);
|
||||
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public void afterEach() {
|
||||
|
|
@ -302,6 +328,9 @@ public class Registry implements AutoCloseable {
|
|||
for (Annotation annotation : annotations) {
|
||||
Supplier<?, ?> supplier = extensions.findSupplierByAnnotation(annotation);
|
||||
if (supplier != null) {
|
||||
if (!supplier.getValueType().isAssignableFrom(valueType)) {
|
||||
throw typeMismatch(annotation.annotationType(), supplier.getValueType(), valueType);
|
||||
}
|
||||
return new RequestedInstance(supplier, annotation, valueType);
|
||||
}
|
||||
}
|
||||
|
|
@ -381,6 +410,18 @@ public class Registry implements AutoCloseable {
|
|||
return extensions.getTestFrameworkExecutors().stream().filter(TestFrameworkExecutorPredicates.shouldExecute(testMethod)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private FatalTestClassException typeMismatch(
|
||||
Class<? extends Annotation> annotation,
|
||||
Class<?> expectedType,
|
||||
Class<?> providedType) {
|
||||
return new FatalTestClassException(
|
||||
String.format("@%s requires %s (or its subclass) but field has type %s",
|
||||
annotation.getSimpleName(),
|
||||
expectedType.getName(),
|
||||
providedType.getName())
|
||||
);
|
||||
}
|
||||
|
||||
private static class RequestedInstanceComparator implements Comparator<RequestedInstance> {
|
||||
|
||||
static final RequestedInstanceComparator INSTANCE = new RequestedInstanceComparator();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.keycloak.testframework.injection;
|
|||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.testframework.FatalTestClassException;
|
||||
import org.keycloak.testframework.annotations.TestCleanup;
|
||||
import org.keycloak.testframework.annotations.TestSetup;
|
||||
import org.keycloak.testframework.config.Config;
|
||||
|
|
@ -20,6 +21,7 @@ import org.hamcrest.Matchers;
|
|||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.TestAbortedException;
|
||||
|
||||
public class RegistryTest {
|
||||
|
||||
|
|
@ -239,7 +241,7 @@ public class RegistryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplRef() {
|
||||
public void testMultipleRef() {
|
||||
MultipleRefTest refTest = new MultipleRefTest();
|
||||
runBeforeEach(refTest);
|
||||
|
||||
|
|
@ -327,6 +329,21 @@ public class RegistryTest {
|
|||
Assertions.assertNotEquals(child1, test.child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationValueTypeMismatch() {
|
||||
AnnotationValueTypeMismatchTest test = new AnnotationValueTypeMismatchTest();
|
||||
|
||||
Assertions.assertThrows(
|
||||
TestAbortedException.class,
|
||||
() -> runBeforeEach(test)
|
||||
);
|
||||
|
||||
Assertions.assertThrows(
|
||||
FatalTestClassException.class,
|
||||
() -> registry.afterAll()
|
||||
);
|
||||
}
|
||||
|
||||
private <T extends AbstractTest> void runBeforeEach(T testInstance) {
|
||||
try {
|
||||
Method testMethod = testInstance.getClass().getMethod("test");
|
||||
|
|
@ -419,7 +436,7 @@ public class RegistryTest {
|
|||
MockParentValue parent;
|
||||
|
||||
@MockChildAnnotation
|
||||
MockParentValue child;
|
||||
MockChildValue child;
|
||||
|
||||
@TestSetup
|
||||
public void setup() {
|
||||
|
|
@ -440,4 +457,9 @@ public class RegistryTest {
|
|||
|
||||
}
|
||||
|
||||
public static final class AnnotationValueTypeMismatchTest extends AbstractTest {
|
||||
@MockParentAnnotation
|
||||
MockChildValue child;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue