Add initial tests for the framework registry (#31433)
Closes #31432 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
6afc8c3e04
commit
02e3339762
12 changed files with 367 additions and 6 deletions
|
@ -35,7 +35,7 @@ public class KeycloakIntegrationTestExtension implements BeforeEachCallback, Aft
|
|||
|
||||
private Registry getRegistry(ExtensionContext context) {
|
||||
ExtensionContext.Store store = getStore(context);
|
||||
Registry registry = (Registry) store.getOrComputeIfAbsent(Registry.class, r -> new Registry());
|
||||
Registry registry = (Registry) store.getOrComputeIfAbsent(Registry.class, r -> createRegistry());
|
||||
registry.setCurrentContext(context);
|
||||
return registry;
|
||||
}
|
||||
|
@ -47,4 +47,10 @@ public class KeycloakIntegrationTestExtension implements BeforeEachCallback, Aft
|
|||
return context.getStore(ExtensionContext.Namespace.create(getClass()));
|
||||
}
|
||||
|
||||
private Registry createRegistry() {
|
||||
Registry registry = new Registry();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(registry::close));
|
||||
return registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ public class Registry {
|
|||
|
||||
public Registry() {
|
||||
loadSuppliers();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onShutdown));
|
||||
}
|
||||
|
||||
public ExtensionContext getCurrentContext() {
|
||||
|
@ -99,7 +98,9 @@ public class Registry {
|
|||
private void findRequestedInstances(Object testInstance) {
|
||||
Class testClass = testInstance.getClass();
|
||||
RequestedInstance requestedServerInstance = createRequestedInstance(testClass.getAnnotations(), null);
|
||||
requestedInstances.add(requestedServerInstance);
|
||||
if (requestedServerInstance != null) {
|
||||
requestedInstances.add(requestedServerInstance);
|
||||
}
|
||||
|
||||
for (Field f : testClass.getDeclaredFields()) {
|
||||
RequestedInstance requestedInstance = createRequestedInstance(f.getAnnotations(), f.getType());
|
||||
|
@ -182,9 +183,9 @@ public class Registry {
|
|||
destroy.forEach(this::destroy);
|
||||
}
|
||||
|
||||
public void onShutdown() {
|
||||
LOGGER.trace("Closing instances with global lifecycle");
|
||||
List<InstanceContext<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.GLOBAL)).toList();
|
||||
public void close() {
|
||||
LOGGER.trace("Closing all instances");
|
||||
List<InstanceContext<?, ?>> destroy = deployedInstances.stream().toList();
|
||||
destroy.forEach(this::destroy);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
package org.keycloak.test.framework.injection;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.test.framework.injection.mocks.MockChildAnnotation;
|
||||
import org.keycloak.test.framework.injection.mocks.MockChildSupplier;
|
||||
import org.keycloak.test.framework.injection.mocks.MockChildValue;
|
||||
import org.keycloak.test.framework.injection.mocks.MockInstances;
|
||||
import org.keycloak.test.framework.injection.mocks.MockParentAnnotation;
|
||||
import org.keycloak.test.framework.injection.mocks.MockParentSupplier;
|
||||
import org.keycloak.test.framework.injection.mocks.MockParentValue;
|
||||
|
||||
public class RegistryTest {
|
||||
|
||||
private Registry registry;
|
||||
|
||||
@BeforeEach
|
||||
public void resetMocks() {
|
||||
MockInstances.reset();
|
||||
MockParentSupplier.reset();
|
||||
MockChildSupplier.reset();
|
||||
registry = new Registry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobalLifeCycle() {
|
||||
MockParentSupplier.DEFAULT_LIFECYCLE = LifeCycle.GLOBAL;
|
||||
ParentTest test = new ParentTest();
|
||||
|
||||
registry.beforeEach(test);
|
||||
MockParentValue value1 = test.parent;
|
||||
assertRunning(value1);
|
||||
|
||||
registry.afterEach();
|
||||
registry.afterAll();
|
||||
assertRunning(value1);
|
||||
|
||||
ParentTest test2 = new ParentTest();
|
||||
registry.beforeEach(test2);
|
||||
MockParentValue value2 = test2.parent;
|
||||
|
||||
Assertions.assertSame(value1, value2);
|
||||
|
||||
registry.close();
|
||||
|
||||
assertClosed(value1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassLifeCycle() {
|
||||
ParentTest test = new ParentTest();
|
||||
|
||||
registry.beforeEach(test);
|
||||
MockParentValue value1 = test.parent;
|
||||
assertRunning(value1);
|
||||
|
||||
registry.afterEach();
|
||||
assertRunning(value1);
|
||||
|
||||
registry.beforeEach(test);
|
||||
MockParentValue value2 = test.parent;
|
||||
Assertions.assertSame(value1, value2);
|
||||
|
||||
registry.afterEach();
|
||||
assertRunning(value1);
|
||||
|
||||
registry.afterAll();
|
||||
assertClosed(value1);
|
||||
|
||||
ParentTest test2 = new ParentTest();
|
||||
registry.beforeEach(test2);
|
||||
MockParentValue value3 = test2.parent;
|
||||
|
||||
Assertions.assertNotSame(value1, value3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodLifeCycle() {
|
||||
MockParentSupplier.DEFAULT_LIFECYCLE = LifeCycle.METHOD;
|
||||
|
||||
ParentTest test = new ParentTest();
|
||||
|
||||
registry.beforeEach(test);
|
||||
MockParentValue value1 = test.parent;
|
||||
assertRunning(value1);
|
||||
|
||||
registry.afterEach();
|
||||
assertClosed(value1);
|
||||
|
||||
registry.beforeEach(test);
|
||||
MockParentValue value2 = test.parent;
|
||||
Assertions.assertNotSame(value1, value2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLifeCycleBetweenDependencies() {
|
||||
MockParentSupplier.DEFAULT_LIFECYCLE = LifeCycle.METHOD;
|
||||
MockChildSupplier.DEFAULT_LIFECYCLE = LifeCycle.GLOBAL;
|
||||
|
||||
ParentAndChildTest parentAndChildTest = new ParentAndChildTest();
|
||||
MockChildValue child1 = parentAndChildTest.child;
|
||||
|
||||
registry.beforeEach(parentAndChildTest);
|
||||
assertRunning(parentAndChildTest.parent, parentAndChildTest.child);
|
||||
Assertions.assertSame(parentAndChildTest.parent, parentAndChildTest.child.getParent());
|
||||
|
||||
registry.afterEach();
|
||||
assertClosed(parentAndChildTest.parent, parentAndChildTest.child);
|
||||
|
||||
registry.beforeEach(parentAndChildTest);
|
||||
Assertions.assertNotSame(child1, parentAndChildTest.child);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDependencyCreatedOnDemand() {
|
||||
ChildTest childTest = new ChildTest();
|
||||
|
||||
registry.beforeEach(childTest);
|
||||
assertRunning(childTest.child, childTest.child.getParent());
|
||||
}
|
||||
|
||||
public static void assertRunning(Object... values) {
|
||||
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasItems(values));
|
||||
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasSize(values.length));
|
||||
}
|
||||
|
||||
public static void assertClosed(Object... values) {
|
||||
MatcherAssert.assertThat(MockInstances.CLOSED_INSTANCES, Matchers.hasItems(values));
|
||||
MatcherAssert.assertThat(MockInstances.CLOSED_INSTANCES, Matchers.hasSize(values.length));
|
||||
}
|
||||
|
||||
public static final class ParentAndChildTest {
|
||||
@MockParentAnnotation
|
||||
MockParentValue parent;
|
||||
|
||||
@MockChildAnnotation
|
||||
MockChildValue child;
|
||||
}
|
||||
|
||||
public static final class ParentTest {
|
||||
@MockParentAnnotation
|
||||
MockParentValue parent;
|
||||
}
|
||||
|
||||
public static final class ChildTest {
|
||||
@MockChildAnnotation
|
||||
MockChildValue child;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface MockChildAnnotation {
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
import org.keycloak.test.framework.injection.InstanceContext;
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
import org.keycloak.test.framework.injection.RequestedInstance;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
|
||||
public class MockChildSupplier implements Supplier<MockChildValue, MockChildAnnotation> {
|
||||
|
||||
public static LifeCycle DEFAULT_LIFECYCLE = LifeCycle.CLASS;
|
||||
|
||||
public static void reset() {
|
||||
DEFAULT_LIFECYCLE = LifeCycle.CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MockChildAnnotation> getAnnotationClass() {
|
||||
return MockChildAnnotation.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MockChildValue> getValueType() {
|
||||
return MockChildValue.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockChildValue getValue(InstanceContext<MockChildValue, MockChildAnnotation> instanceContext) {
|
||||
MockParentValue mockParentValue = instanceContext.getDependency(MockParentValue.class);
|
||||
return new MockChildValue(mockParentValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<MockChildValue, MockChildAnnotation> a, RequestedInstance<MockChildValue, MockChildAnnotation> b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(InstanceContext<MockChildValue, MockChildAnnotation> instanceContext) {
|
||||
instanceContext.getValue().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifeCycle getDefaultLifecycle() {
|
||||
return DEFAULT_LIFECYCLE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
public class MockChildValue {
|
||||
|
||||
private final MockParentValue parent;
|
||||
|
||||
public MockChildValue(MockParentValue parent) {
|
||||
MockInstances.INSTANCES.add(this);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public MockParentValue getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (parent.isClosed()) {
|
||||
throw new RuntimeException("Parent is closed!");
|
||||
}
|
||||
|
||||
boolean removed = MockInstances.INSTANCES.remove(this);
|
||||
if (!removed) {
|
||||
throw new RuntimeException("Instance already removed");
|
||||
} else {
|
||||
MockInstances.CLOSED_INSTANCES.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
import io.vertx.core.impl.ConcurrentHashSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class MockInstances {
|
||||
|
||||
public static final Set<Object> INSTANCES = new ConcurrentHashSet<>();
|
||||
public static final Set<Object> CLOSED_INSTANCES = new ConcurrentHashSet<>();
|
||||
|
||||
public static void reset() {
|
||||
INSTANCES.clear();
|
||||
CLOSED_INSTANCES.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface MockParentAnnotation {
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
import org.keycloak.test.framework.injection.InstanceContext;
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
import org.keycloak.test.framework.injection.RequestedInstance;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
|
||||
public class MockParentSupplier implements Supplier<MockParentValue, MockParentAnnotation> {
|
||||
|
||||
public static LifeCycle DEFAULT_LIFECYCLE = LifeCycle.CLASS;
|
||||
|
||||
public static void reset() {
|
||||
DEFAULT_LIFECYCLE = LifeCycle.CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MockParentAnnotation> getAnnotationClass() {
|
||||
return MockParentAnnotation.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<MockParentValue> getValueType() {
|
||||
return MockParentValue.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockParentValue getValue(InstanceContext<MockParentValue, MockParentAnnotation> instanceContext) {
|
||||
return new MockParentValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<MockParentValue, MockParentAnnotation> a, RequestedInstance<MockParentValue, MockParentAnnotation> b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(InstanceContext<MockParentValue, MockParentAnnotation> instanceContext) {
|
||||
instanceContext.getValue().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifeCycle getDefaultLifecycle() {
|
||||
return DEFAULT_LIFECYCLE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.keycloak.test.framework.injection.mocks;
|
||||
|
||||
public class MockParentValue {
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
public MockParentValue() {
|
||||
MockInstances.INSTANCES.add(this);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
boolean removed = MockInstances.INSTANCES.remove(this);
|
||||
if (!removed) {
|
||||
throw new RuntimeException("Instance already removed");
|
||||
} else {
|
||||
MockInstances.CLOSED_INSTANCES.add(this);
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.keycloak.test.framework.injection.mocks.MockParentSupplier
|
||||
org.keycloak.test.framework.injection.mocks.MockChildSupplier
|
15
test-poc/framework/src/test/resources/logging.properties
Normal file
15
test-poc/framework/src/test/resources/logging.properties
Normal file
|
@ -0,0 +1,15 @@
|
|||
loggers=org.keycloak.test
|
||||
logger.org.keycloak.test.level=TRACE
|
||||
|
||||
logger.handlers=CONSOLE
|
||||
|
||||
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
|
||||
handler.CONSOLE.properties=autoFlush
|
||||
handler.CONSOLE.level=ERROR
|
||||
handler.CONSOLE.autoFlush=true
|
||||
handler.CONSOLE.formatter=PATTERN
|
||||
|
||||
# The log format pattern for both logs
|
||||
formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
|
||||
formatter.PATTERN.properties=pattern
|
||||
formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p %t [%c] %m%n
|
Loading…
Reference in a new issue