Testsuite PoC - Injection framework support for multiple instances (#31293)

* Testsuite PoC - Injection framework support for multiple instances

Closes #30613

Signed-off-by: Simon Vacek <simonvacky@email.cz>

* Add unit tests for multiple instances

Signed-off-by: stianst <stianst@gmail.com>

* Remove check for ref redefinitions

Signed-off-by: Simon Vacek <simonvacky@email.cz>

---------

Signed-off-by: Simon Vacek <simonvacky@email.cz>
Signed-off-by: stianst <stianst@gmail.com>
Co-authored-by: stianst <stianst@gmail.com>
This commit is contained in:
Šimon Vacek 2024-07-23 12:34:54 +02:00 committed by GitHub
parent b5597c6293
commit 6de396d537
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 156 additions and 21 deletions

View file

@ -28,17 +28,17 @@ public class GlobalManagedResourcesTest {
@Test @Test
public void testCreatedRealm() { public void testCreatedRealm() {
Assertions.assertEquals(DefaultRealmConfig.class.getSimpleName(), realmResource.toRepresentation().getRealm()); Assertions.assertEquals("default", realmResource.toRepresentation().getRealm());
} }
@Test @Test
public void testCreatedClient() { public void testCreatedClient() {
Assertions.assertEquals(DefaultClientConfig.class.getSimpleName(), clientResource.toRepresentation().getClientId()); Assertions.assertEquals("default", clientResource.toRepresentation().getClientId());
} }
@Test @Test
public void testCreatedUser() { public void testCreatedUser() {
Assertions.assertEquals(DefaultUserConfig.class.getSimpleName().toLowerCase(), userResource.toRepresentation().getUsername()); Assertions.assertEquals("default", userResource.toRepresentation().getUsername());
} }
} }

View file

@ -23,14 +23,14 @@ public class ManagedResources2Test {
@Test @Test
public void testCreatedRealm() { public void testCreatedRealm() {
Assertions.assertEquals(ManagedResources2Test.class.getSimpleName(), realmResource.toRepresentation().getRealm()); Assertions.assertEquals("default", realmResource.toRepresentation().getRealm());
} }
@Test @Test
public void testCreatedClient() { public void testCreatedClient() {
Assertions.assertEquals(ManagedResources2Test.class.getSimpleName(), clientResource.toRepresentation().getClientId()); Assertions.assertEquals("default", clientResource.toRepresentation().getClientId());
List<ClientRepresentation> clients = realmResource.clients().findByClientId(ManagedResources2Test.class.getSimpleName()); List<ClientRepresentation> clients = realmResource.clients().findByClientId("default");
Assertions.assertEquals(1, clients.size()); Assertions.assertEquals(1, clients.size());
} }

View file

@ -28,20 +28,20 @@ public class ManagedResourcesTest {
@Test @Test
public void testCreatedRealm() { public void testCreatedRealm() {
Assertions.assertEquals(ManagedResourcesTest.class.getSimpleName(), realmResource.toRepresentation().getRealm()); Assertions.assertEquals("default", realmResource.toRepresentation().getRealm());
} }
@Test @Test
public void testCreatedClient() { public void testCreatedClient() {
Assertions.assertEquals(ManagedResourcesTest.class.getSimpleName(), clientResource.toRepresentation().getClientId()); Assertions.assertEquals("default", clientResource.toRepresentation().getClientId());
List<ClientRepresentation> clients = realmResource.clients().findByClientId(ManagedResourcesTest.class.getSimpleName()); List<ClientRepresentation> clients = realmResource.clients().findByClientId("default");
Assertions.assertEquals(1, clients.size()); Assertions.assertEquals(1, clients.size());
} }
@Test @Test
public void testCreatedUser() { public void testCreatedUser() {
Assertions.assertEquals(ManagedResourcesTest.class.getSimpleName().toLowerCase(), userResource.toRepresentation().getUsername()); Assertions.assertEquals("default", userResource.toRepresentation().getUsername());
} }
} }

View file

@ -0,0 +1,52 @@
package org.keycloak.test.examples;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
import org.keycloak.test.framework.annotations.TestClient;
import org.keycloak.test.framework.annotations.TestRealm;
import org.keycloak.test.framework.realm.RealmConfig;
@KeycloakIntegrationTest
public class MultipleInstancesTest {
@TestRealm
RealmResource realm1;
@TestRealm
RealmResource realm2;
@TestRealm(ref = "another", config = CustomRealmConfig.class)
RealmResource realm3;
@TestClient(ref = "client1")
ClientResource client;
@TestClient
ClientResource client2;
@Test
public void testMultipleInstances() {
Assertions.assertEquals("default", realm1.toRepresentation().getRealm());
Assertions.assertEquals("default", realm2.toRepresentation().getRealm());
Assertions.assertEquals(realm1, realm2);
Assertions.assertEquals("another", realm3.toRepresentation().getRealm());
Assertions.assertEquals("client1", client.toRepresentation().getClientId());
Assertions.assertEquals("default", client2.toRepresentation().getClientId());
}
public static class CustomRealmConfig implements RealmConfig {
@Override
public RealmRepresentation getRepresentation() {
return new RealmRepresentation();
}
}
}

View file

@ -15,6 +15,8 @@ public @interface TestClient {
Class<? extends ClientConfig> config() default DefaultClientConfig.class; Class<? extends ClientConfig> config() default DefaultClientConfig.class;
String ref() default "default";
LifeCycle lifecycle() default LifeCycle.CLASS; LifeCycle lifecycle() default LifeCycle.CLASS;
} }

View file

@ -17,4 +17,5 @@ public @interface TestRealm {
LifeCycle lifecycle() default LifeCycle.CLASS; LifeCycle lifecycle() default LifeCycle.CLASS;
String ref() default "default";
} }

View file

@ -17,4 +17,5 @@ public @interface TestUser {
LifeCycle lifecycle() default LifeCycle.CLASS; LifeCycle lifecycle() default LifeCycle.CLASS;
String ref() default "default";
} }

View file

@ -15,6 +15,7 @@ public class InstanceContext<T, A extends Annotation> {
private T value; private T value;
private Class<? extends T> requestedValueType; private Class<? extends T> requestedValueType;
private LifeCycle lifeCycle; private LifeCycle lifeCycle;
private final String ref;
private final Map<String, Object> notes = new HashMap<>(); private final Map<String, Object> notes = new HashMap<>();
public InstanceContext(Registry registry, Supplier<T, A> supplier, A annotation, Class<? extends T> requestedValueType) { public InstanceContext(Registry registry, Supplier<T, A> supplier, A annotation, Class<? extends T> requestedValueType) {
@ -23,6 +24,7 @@ public class InstanceContext<T, A extends Annotation> {
this.annotation = annotation; this.annotation = annotation;
this.requestedValueType = requestedValueType; this.requestedValueType = requestedValueType;
this.lifeCycle = supplier.getLifeCycle(annotation); this.lifeCycle = supplier.getLifeCycle(annotation);
this.ref = supplier.getRef(annotation);
} }
public <D> D getDependency(Class<D> typeClazz) { public <D> D getDependency(Class<D> typeClazz) {
@ -53,6 +55,10 @@ public class InstanceContext<T, A extends Annotation> {
return lifeCycle; return lifeCycle;
} }
public String getRef() {
return ref;
}
public A getAnnotation() { public A getAnnotation() {
return annotation; return annotation;
} }

View file

@ -14,6 +14,7 @@ import java.util.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public class Registry { public class Registry {
@ -146,13 +147,15 @@ public class Registry {
while (!requestedInstances.isEmpty()) { while (!requestedInstances.isEmpty()) {
RequestedInstance requestedInstance = requestedInstances.remove(0); RequestedInstance requestedInstance = requestedInstances.remove(0);
InstanceContext instance = new InstanceContext(this, requestedInstance.getSupplier(), requestedInstance.getAnnotation(), requestedInstance.getValueType()); if (getDeployedInstance(requestedInstance) == null) {
instance.setValue(requestedInstance.getSupplier().getValue(instance)); InstanceContext instance = new InstanceContext(this, requestedInstance.getSupplier(), requestedInstance.getAnnotation(), requestedInstance.getValueType());
deployedInstances.add(instance); instance.setValue(requestedInstance.getSupplier().getValue(instance));
deployedInstances.add(instance);
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.tracev("Created instance: {0}", LOGGER.tracev("Created instance: {0}",
requestedInstance.getSupplier().getClass().getSimpleName()); requestedInstance.getSupplier().getClass().getSimpleName());
}
} }
} }
} }
@ -205,8 +208,10 @@ public class Registry {
private InstanceContext<?, ?> getDeployedInstance(Class<?> valueType, Annotation[] annotations) { private InstanceContext<?, ?> getDeployedInstance(Class<?> valueType, Annotation[] annotations) {
for (Annotation a : annotations) { for (Annotation a : annotations) {
for (InstanceContext<?, ?> i : deployedInstances) { for (InstanceContext<?, ?> i : deployedInstances) {
Supplier<?, ?> supplier = i.getSupplier(); Supplier supplier = i.getSupplier();
if (supplier.getAnnotationClass().equals(a.annotationType()) && valueType.isAssignableFrom(i.getValue().getClass())) { if (supplier.getAnnotationClass().equals(a.annotationType())
&& valueType.isAssignableFrom(i.getValue().getClass())
&& supplier.getRef(a).equals(i.getRef()) ) {
return i; return i;
} }
} }
@ -229,8 +234,13 @@ public class Registry {
} }
private InstanceContext getDeployedInstance(RequestedInstance requestedInstance) { private InstanceContext getDeployedInstance(RequestedInstance requestedInstance) {
String requestedRef = requestedInstance.getRef();
Class requestedValueType = requestedInstance.getValueType(); Class requestedValueType = requestedInstance.getValueType();
for (InstanceContext<?, ?> i : deployedInstances) { for (InstanceContext<?, ?> i : deployedInstances) {
if(!i.getRef().equals(requestedRef)) {
continue;
}
if (requestedValueType != null) { if (requestedValueType != null) {
if (requestedValueType.isAssignableFrom(i.getValue().getClass())) { if (requestedValueType.isAssignableFrom(i.getValue().getClass())) {
return i; return i;

View file

@ -8,12 +8,14 @@ public class RequestedInstance<T, A extends Annotation> {
private final A annotation; private final A annotation;
private final Class<? extends T> valueType; private final Class<? extends T> valueType;
private final LifeCycle lifeCycle; private final LifeCycle lifeCycle;
private final String ref;
public RequestedInstance(Supplier<T, A> supplier, A annotation, Class<? extends T> valueType) { public RequestedInstance(Supplier<T, A> supplier, A annotation, Class<? extends T> valueType) {
this.supplier = supplier; this.supplier = supplier;
this.annotation = annotation; this.annotation = annotation;
this.valueType = valueType; this.valueType = valueType;
this.lifeCycle = supplier.getLifeCycle(annotation); this.lifeCycle = supplier.getLifeCycle(annotation);
this.ref = supplier.getRef(annotation);
} }
public Supplier<T, A> getSupplier() { public Supplier<T, A> getSupplier() {
@ -31,4 +33,8 @@ public class RequestedInstance<T, A extends Annotation> {
public LifeCycle getLifeCycle() { public LifeCycle getLifeCycle() {
return lifeCycle; return lifeCycle;
} }
public String getRef() {
return ref;
}
} }

View file

@ -27,6 +27,20 @@ public interface Supplier<T, S extends Annotation> {
return getDefaultLifecycle(); return getDefaultLifecycle();
} }
default String getRef(S annotation) {
if (annotation != null) {
Optional<Method> ref = Arrays.stream(annotation.annotationType().getMethods()).filter(m -> m.getName().equals("ref")).findFirst();
if (ref.isPresent()) {
try {
return (String) ref.get().invoke(annotation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return "";
}
default LifeCycle getDefaultLifecycle() { default LifeCycle getDefaultLifecycle() {
return LifeCycle.CLASS; return LifeCycle.CLASS;
} }

View file

@ -33,7 +33,7 @@ public class ClientSupplier implements Supplier<ClientResource, TestClient> {
ClientRepresentation clientRepresentation = config.getRepresentation(); ClientRepresentation clientRepresentation = config.getRepresentation();
if (clientRepresentation.getClientId() == null) { if (clientRepresentation.getClientId() == null) {
String clientId = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName(); String clientId = instanceContext.getRef();
clientRepresentation.setClientId(clientId); clientRepresentation.setClientId(clientId);
} }

View file

@ -32,7 +32,7 @@ public class RealmSupplier implements Supplier<RealmResource, TestRealm> {
RealmRepresentation realmRepresentation = config.getRepresentation(); RealmRepresentation realmRepresentation = config.getRepresentation();
if (realmRepresentation.getRealm() == null) { if (realmRepresentation.getRealm() == null) {
String realmName = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName(); String realmName = instanceContext.getRef();
realmRepresentation.setRealm(realmName); realmRepresentation.setRealm(realmName);
} }

View file

@ -33,7 +33,7 @@ public class UserSupplier implements Supplier<UserResource, TestUser> {
UserRepresentation userRepresentation = config.getRepresentation(); UserRepresentation userRepresentation = config.getRepresentation();
if (userRepresentation.getUsername() == null) { if (userRepresentation.getUsername() == null) {
String username = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName(); String username = instanceContext.getRef();
userRepresentation.setUsername(username); userRepresentation.setUsername(username);
} }

View file

@ -208,6 +208,35 @@ public class RegistryTest {
assertRunning(test.child, test.child.getParent()); assertRunning(test.child, test.child.getParent());
} }
@Test
public void testMultiplRef() {
MultipleRefTest refTest = new MultipleRefTest();
registry.beforeEach(refTest);
MockParentValue def1 = refTest.def;
MockParentValue a1 = refTest.a;
Assertions.assertNotSame(refTest.def, refTest.a);
Assertions.assertNotSame(refTest.def, refTest.b);
Assertions.assertNotSame(refTest.a, refTest.b);
assertRunning(refTest.def, refTest.a, refTest.b);
registry.afterEach();
registry.beforeEach(refTest);
assertRunning(refTest.def, refTest.a, refTest.b);
Assertions.assertSame(def1, refTest.def);
Assertions.assertSame(a1, refTest.a);
registry.afterEach();
assertRunning(refTest.def, refTest.a, refTest.b);
registry.afterAll();
assertClosed(refTest.def, refTest.a, refTest.b);
}
public static void assertRunning(Object... values) { public static void assertRunning(Object... values) {
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasItems(values)); MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasItems(values));
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasSize(values.length)); MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasSize(values.length));
@ -243,4 +272,15 @@ public class RegistryTest {
@MockParentAnnotation @MockParentAnnotation
MockParentValue parent; MockParentValue parent;
} }
public static final class MultipleRefTest {
@MockParentAnnotation()
MockParentValue def;
@MockParentAnnotation(ref = "a")
MockParentValue a;
@MockParentAnnotation(ref = "b")
MockParentValue b;
}
} }

View file

@ -8,4 +8,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
public @interface MockParentAnnotation { public @interface MockParentAnnotation {
String ref() default "";
} }