Add conditional workflow for test-poc (#31406)

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2024-07-19 12:11:17 +02:00 committed by GitHub
parent 64c6dc00c0
commit 7216967465
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 274 additions and 226 deletions

View file

@ -22,6 +22,9 @@ outputs:
ci-webauthn: ci-webauthn:
description: Should "ci.yml" execute (WebAuthn) description: Should "ci.yml" execute (WebAuthn)
value: ${{ steps.changes.outputs.ci-webauthn }} value: ${{ steps.changes.outputs.ci-webauthn }}
ci-test-poc:
description: Should "ci.yml" execute (Test PoC)
value: ${{ steps.changes.outputs.ci-test-poc }}
operator: operator:
description: Should "operator-ci.yml" execute description: Should "operator-ci.yml" execute
value: ${{ steps.changes.outputs.operator }} value: ${{ steps.changes.outputs.operator }}

View file

@ -48,3 +48,5 @@ js/libs/ui-shared/ ci ci-webauthn
themes/ codeql-themes themes/ codeql-themes
testsuite::database-suite ci-store testsuite::database-suite ci-store
test-poc/ ci ci-test-poc

View file

@ -34,6 +34,7 @@ jobs:
ci-sssd: ${{ steps.conditional.outputs.ci-sssd }} ci-sssd: ${{ steps.conditional.outputs.ci-sssd }}
ci-webauthn: ${{ steps.conditional.outputs.ci-webauthn }} ci-webauthn: ${{ steps.conditional.outputs.ci-webauthn }}
ci-store-matrix: ${{ steps.conditional-stores.outputs.matrix }} ci-store-matrix: ${{ steps.conditional-stores.outputs.matrix }}
ci-test-poc: ${{ steps.conditional.outputs.ci-test-poc }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -863,6 +864,26 @@ jobs:
with: with:
job-id: migration-tests-${{ matrix.old-version }}-${{ matrix.database }} job-id: migration-tests-${{ matrix.old-version }}-${{ matrix.database }}
test-poc:
name: Test PoC
runs-on: ubuntu-latest
if: needs.conditional.outputs.ci-test-poc == 'true'
needs:
- conditional
- build
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- id: integration-test-setup
name: Integration test setup
uses: ./.github/actions/integration-test-setup
- name: Run tests
run: |
cd test-poc
mvn clean install
check: check:
name: Status Check - Keycloak CI name: Status Check - Keycloak CI
if: always() if: always()
@ -886,6 +907,7 @@ jobs:
- sssd-unit-tests - sssd-unit-tests
- migration-tests - migration-tests
- external-infinispan-tests - external-infinispan-tests
- test-poc
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View file

@ -7,13 +7,13 @@ import java.util.Optional;
public class NoAdminUserKeycloakTestServerConfig implements KeycloakTestServerConfig { public class NoAdminUserKeycloakTestServerConfig implements KeycloakTestServerConfig {
@Override @Override
public Optional<String> adminUserName() { public String adminUserName() {
return Optional.empty(); return null;
} }
@Override @Override
public Optional<String> adminUserPassword() { public String adminUserPassword() {
return Optional.empty(); return null;
} }
} }

View file

@ -18,18 +18,8 @@ public class PagesTest {
@Test @Test
public void testLoginFromWelcome() { public void testLoginFromWelcome() {
welcomePage.navigateTo(); welcomePage.navigateTo();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
loginPage.fillLogin("admin", "admin"); loginPage.fillLogin("admin", "admin");
loginPage.submit(); loginPage.submit();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }
} }

View file

@ -2,9 +2,8 @@ package org.keycloak.test.framework.admin;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
import org.keycloak.test.framework.TestAdminClient; import org.keycloak.test.framework.TestAdminClient;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.server.KeycloakTestServer; import org.keycloak.test.framework.server.KeycloakTestServer;
@ -22,25 +21,24 @@ public class KeycloakAdminClientSupplier implements Supplier<Keycloak, TestAdmin
} }
@Override @Override
public InstanceWrapper<Keycloak, TestAdminClient> getValue(Registry registry, TestAdminClient annotation) { public Keycloak getValue(InstanceContext<Keycloak, TestAdminClient> instanceContext) {
InstanceWrapper<Keycloak, TestAdminClient> wrapper = new InstanceWrapper<>(this, annotation); KeycloakTestServer testServer = instanceContext.getDependency(KeycloakTestServer.class);
return Keycloak.getInstance(testServer.getBaseUrl(), "master", "admin", "admin", "admin-cli");
KeycloakTestServer testServer = registry.getDependency(KeycloakTestServer.class, wrapper);
Keycloak keycloak = Keycloak.getInstance(testServer.getBaseUrl(), "master", "admin", "admin", "admin-cli");
wrapper.setValue(keycloak, LifeCycle.GLOBAL);
return wrapper;
} }
@Override @Override
public boolean compatible(InstanceWrapper<Keycloak, TestAdminClient> a, RequestedInstance<Keycloak, TestAdminClient> b) { public LifeCycle getDefaultLifecycle() {
return LifeCycle.GLOBAL;
}
@Override
public boolean compatible(InstanceContext<Keycloak, TestAdminClient> a, RequestedInstance<Keycloak, TestAdminClient> b) {
return true; return true;
} }
@Override @Override
public void close(Keycloak keycloak) { public void close(InstanceContext<Keycloak, TestAdminClient> instanceContext) {
keycloak.close(); instanceContext.getValue().close();
} }
} }

View file

@ -1,9 +1,8 @@
package org.keycloak.test.framework.database; package org.keycloak.test.framework.database;
import org.keycloak.test.framework.KeycloakTestDatabase; import org.keycloak.test.framework.KeycloakTestDatabase;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
@ -23,17 +22,26 @@ public abstract class AbstractDatabaseSupplier implements Supplier<TestDatabase,
} }
@Override @Override
public InstanceWrapper<TestDatabase, KeycloakTestDatabase> getValue(Registry registry, KeycloakTestDatabase annotation) { public TestDatabase getValue(InstanceContext<TestDatabase, KeycloakTestDatabase> instanceContext) {
TestDatabase testDatabase = getTestDatabase(); TestDatabase testDatabase = getTestDatabase();
testDatabase.start(); testDatabase.start();
return new InstanceWrapper<>(this, annotation, testDatabase, LifeCycle.GLOBAL); return testDatabase;
} }
@Override @Override
public boolean compatible(InstanceWrapper<TestDatabase, KeycloakTestDatabase> a, RequestedInstance<TestDatabase, KeycloakTestDatabase> b) { public boolean compatible(InstanceContext<TestDatabase, KeycloakTestDatabase> a, RequestedInstance<TestDatabase, KeycloakTestDatabase> b) {
return true; return true;
} }
@Override
public LifeCycle getDefaultLifecycle() {
return LifeCycle.GLOBAL;
}
abstract TestDatabase getTestDatabase(); abstract TestDatabase getTestDatabase();
@Override
public void close(InstanceContext<TestDatabase, KeycloakTestDatabase> instanceContext) {
instanceContext.getValue().stop();
}
} }

View file

@ -6,30 +6,35 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class InstanceWrapper<T, A extends Annotation> { public class InstanceContext<T, A extends Annotation> {
private final Registry registry;
private final Supplier<T, A> supplier; private final Supplier<T, A> supplier;
private final A annotation; private final A annotation;
private final Set<InstanceWrapper<T, A>> dependencies = new HashSet<>(); private final Set<InstanceContext<T, A>> dependencies = new HashSet<>();
private T value; private T value;
private Class<? extends T> requestedValueType;
private LifeCycle lifeCycle; private LifeCycle lifeCycle;
private final Map<String, Object> notes = new HashMap<>(); private final Map<String, Object> notes = new HashMap<>();
public InstanceWrapper(Supplier<T, A> supplier, A annotation) { public InstanceContext(Registry registry, Supplier<T, A> supplier, A annotation, Class<? extends T> requestedValueType) {
this.registry = registry;
this.supplier = supplier; this.supplier = supplier;
this.annotation = annotation; this.annotation = annotation;
this.requestedValueType = requestedValueType;
this.lifeCycle = supplier.getLifeCycle(annotation);
} }
public InstanceWrapper(Supplier<T, A> supplier, A annotation, T value, LifeCycle lifeCycle) { public <D> D getDependency(Class<D> typeClazz) {
this.supplier = supplier; return registry.getDependency(typeClazz, this);
this.annotation = annotation;
this.value = value;
this.lifeCycle = lifeCycle;
} }
public void setValue(T value, LifeCycle lifeCycle) { public Registry getRegistry() {
return registry;
}
void setValue(T value) {
this.value = value; this.value = value;
this.lifeCycle = lifeCycle;
} }
public Supplier<T, A> getSupplier() { public Supplier<T, A> getSupplier() {
@ -40,6 +45,10 @@ public class InstanceWrapper<T, A extends Annotation> {
return value; return value;
} }
public Class<? extends T> getRequestedValueType() {
return requestedValueType;
}
public LifeCycle getLifeCycle() { public LifeCycle getLifeCycle() {
return lifeCycle; return lifeCycle;
} }
@ -48,12 +57,12 @@ public class InstanceWrapper<T, A extends Annotation> {
return annotation; return annotation;
} }
public Set<InstanceWrapper<T, A>> getDependencies() { public Set<InstanceContext<T, A>> getDependencies() {
return dependencies; return dependencies;
} }
public void registerDependency(InstanceWrapper<T, A> instanceWrapper) { public void registerDependency(InstanceContext<T, A> instanceContext) {
dependencies.add(instanceWrapper); dependencies.add(instanceContext);
} }
public void addNote(String key, Object value) { public void addNote(String key, Object value) {

View file

@ -22,7 +22,7 @@ public class Registry {
private ExtensionContext currentContext; private ExtensionContext currentContext;
private final List<Supplier<?, ?>> suppliers = new LinkedList<>(); private final List<Supplier<?, ?>> suppliers = new LinkedList<>();
private final List<InstanceWrapper<?, ?>> deployedInstances = new LinkedList<>(); private final List<InstanceContext<?, ?>> deployedInstances = new LinkedList<>();
private final List<RequestedInstance<?, ?>> requestedInstances = new LinkedList<>(); private final List<RequestedInstance<?, ?>> requestedInstances = new LinkedList<>();
public Registry() { public Registry() {
@ -38,8 +38,8 @@ public class Registry {
this.currentContext = currentContext; this.currentContext = currentContext;
} }
public <T> T getDependency(Class<T> typeClass, InstanceWrapper dependent) { public <T> T getDependency(Class<T> typeClass, InstanceContext dependent) {
InstanceWrapper dependency = getDeployedInstance(typeClass); InstanceContext dependency = getDeployedInstance(typeClass);
if (dependency != null) { if (dependency != null) {
dependency.registerDependency(dependent); dependency.registerDependency(dependent);
@ -54,7 +54,8 @@ public class Registry {
RequestedInstance requestedDependency = getRequestedInstance(typeClass); RequestedInstance requestedDependency = getRequestedInstance(typeClass);
if (requestedDependency != null) { if (requestedDependency != null) {
dependency = requestedDependency.getSupplier().getValue(this, requestedDependency.getAnnotation(), typeClass); dependency = new InstanceContext<Object, Annotation>(this, requestedDependency.getSupplier(), requestedDependency.getAnnotation(), requestedDependency.getValueType());
dependency.setValue(requestedDependency.getSupplier().getValue(dependency));
dependency.registerDependency(dependent); dependency.registerDependency(dependent);
deployedInstances.add(dependency); deployedInstances.add(dependency);
@ -70,7 +71,10 @@ public class Registry {
Optional<Supplier<?, ?>> supplied = suppliers.stream().filter(s -> s.getValueType().equals(typeClass)).findFirst(); Optional<Supplier<?, ?>> supplied = suppliers.stream().filter(s -> s.getValueType().equals(typeClass)).findFirst();
if (supplied.isPresent()) { if (supplied.isPresent()) {
Supplier<T, ?> supplier = (Supplier<T, ?>) supplied.get(); Supplier<T, ?> supplier = (Supplier<T, ?>) supplied.get();
dependency = supplier.getValue(this, null, typeClass); dependency = new InstanceContext(this, supplier, null, typeClass);
dependency.registerDependency(dependent);
dependency.setValue(supplier.getValue(dependency));
deployedInstances.add(dependency); deployedInstances.add(dependency);
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
@ -114,9 +118,9 @@ public class Registry {
Iterator<RequestedInstance<?, ?>> itr = requestedInstances.iterator(); Iterator<RequestedInstance<?, ?>> itr = requestedInstances.iterator();
while (itr.hasNext()) { while (itr.hasNext()) {
RequestedInstance<?, ?> requestedInstance = itr.next(); RequestedInstance<?, ?> requestedInstance = itr.next();
InstanceWrapper deployedInstance = getDeployedInstance(requestedInstance); InstanceContext deployedInstance = getDeployedInstance(requestedInstance);
if (deployedInstance != null) { if (deployedInstance != null) {
if (deployedInstance.getSupplier().compatible(deployedInstance, requestedInstance)) { if (requestedInstance.getLifeCycle().equals(deployedInstance.getLifeCycle()) && deployedInstance.getSupplier().compatible(deployedInstance, requestedInstance)) {
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.tracev("Reusing compatible: {0}", LOGGER.tracev("Reusing compatible: {0}",
deployedInstance.getSupplier().getClass().getSimpleName()); deployedInstance.getSupplier().getClass().getSimpleName());
@ -140,7 +144,8 @@ public class Registry {
while (itr.hasNext()) { while (itr.hasNext()) {
RequestedInstance requestedInstance = itr.next(); RequestedInstance requestedInstance = itr.next();
InstanceWrapper instance = requestedInstance.getSupplier().getValue(this, requestedInstance.getAnnotation(), requestedInstance.getValueType()); InstanceContext instance = new InstanceContext(this, requestedInstance.getSupplier(), requestedInstance.getAnnotation(), requestedInstance.getValueType());
instance.setValue(requestedInstance.getSupplier().getValue(instance));
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.tracev("Created instance: {0}", LOGGER.tracev("Created instance: {0}",
@ -155,7 +160,7 @@ public class Registry {
private void injectFields(Object testInstance) { private void injectFields(Object testInstance) {
for (Field f : testInstance.getClass().getDeclaredFields()) { for (Field f : testInstance.getClass().getDeclaredFields()) {
InstanceWrapper<?, ?> instance = getDeployedInstance(f.getType(), f.getAnnotations()); InstanceContext<?, ?> instance = getDeployedInstance(f.getType(), f.getAnnotations());
try { try {
f.setAccessible(true); f.setAccessible(true);
f.set(testInstance, instance.getValue()); f.set(testInstance, instance.getValue());
@ -167,19 +172,19 @@ public class Registry {
public void afterAll() { public void afterAll() {
LOGGER.trace("Closing instances with class lifecycle"); LOGGER.trace("Closing instances with class lifecycle");
List<InstanceWrapper<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.CLASS)).toList(); List<InstanceContext<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.CLASS)).toList();
destroy.forEach(this::destroy); destroy.forEach(this::destroy);
} }
public void afterEach() { public void afterEach() {
LOGGER.trace("Closing instances with method lifecycle"); LOGGER.trace("Closing instances with method lifecycle");
List<InstanceWrapper<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.METHOD)).toList(); List<InstanceContext<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.METHOD)).toList();
destroy.forEach(this::destroy); destroy.forEach(this::destroy);
} }
public void onShutdown() { public void onShutdown() {
LOGGER.trace("Closing instances with global lifecycle"); LOGGER.trace("Closing instances with global lifecycle");
List<InstanceWrapper<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.GLOBAL)).toList(); List<InstanceContext<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getLifeCycle().equals(LifeCycle.GLOBAL)).toList();
destroy.forEach(this::destroy); destroy.forEach(this::destroy);
} }
@ -194,9 +199,9 @@ public class Registry {
return null; return null;
} }
private InstanceWrapper<?, ?> getDeployedInstance(Class<?> valueType, Annotation[] annotations) { private InstanceContext<?, ?> getDeployedInstance(Class<?> valueType, Annotation[] annotations) {
for (Annotation a : annotations) { for (Annotation a : annotations) {
for (InstanceWrapper<?, ?> 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())) {
return i; return i;
@ -206,23 +211,23 @@ public class Registry {
return null; return null;
} }
private void destroy(InstanceWrapper instanceWrapper) { private void destroy(InstanceContext instanceContext) {
boolean removed = deployedInstances.remove(instanceWrapper); boolean removed = deployedInstances.remove(instanceContext);
if (removed) { if (removed) {
Set<InstanceWrapper> dependencies = instanceWrapper.getDependencies(); Set<InstanceContext> dependencies = instanceContext.getDependencies();
dependencies.forEach(this::destroy); dependencies.forEach(this::destroy);
instanceWrapper.getSupplier().close(instanceWrapper); instanceContext.getSupplier().close(instanceContext);
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.tracev("Closed instance: {0}", LOGGER.tracev("Closed instance: {0}",
instanceWrapper.getSupplier().getClass().getSimpleName()); instanceContext.getSupplier().getClass().getSimpleName());
} }
} }
} }
private InstanceWrapper getDeployedInstance(RequestedInstance requestedInstance) { private InstanceContext getDeployedInstance(RequestedInstance requestedInstance) {
Class requestedValueType = requestedInstance.getValueType(); Class requestedValueType = requestedInstance.getValueType();
for (InstanceWrapper<?, ?> i : deployedInstances) { for (InstanceContext<?, ?> i : deployedInstances) {
if (requestedValueType != null) { if (requestedValueType != null) {
if (requestedValueType.isAssignableFrom(i.getValue().getClass())) { if (requestedValueType.isAssignableFrom(i.getValue().getClass())) {
return i; return i;
@ -280,7 +285,7 @@ public class Registry {
} }
} }
private InstanceWrapper getDeployedInstance(Class typeClass) { private InstanceContext getDeployedInstance(Class typeClass) {
return deployedInstances.stream().filter(i -> i.getSupplier().getValueType().equals(typeClass)).findFirst().orElse(null); return deployedInstances.stream().filter(i -> i.getSupplier().getValueType().equals(typeClass)).findFirst().orElse(null);
} }

View file

@ -7,11 +7,13 @@ public class RequestedInstance<T, A extends Annotation> {
private final Supplier<T, A> supplier; private final Supplier<T, A> supplier;
private final A annotation; private final A annotation;
private final Class<? extends T> valueType; private final Class<? extends T> valueType;
private final LifeCycle lifeCycle;
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);
} }
public Supplier<T, A> getSupplier() { public Supplier<T, A> getSupplier() {
@ -25,4 +27,8 @@ public class RequestedInstance<T, A extends Annotation> {
public Class<? extends T> getValueType() { public Class<? extends T> getValueType() {
return valueType; return valueType;
} }
public LifeCycle getLifeCycle() {
return lifeCycle;
}
} }

View file

@ -1,6 +1,9 @@
package org.keycloak.test.framework.injection; package org.keycloak.test.framework.injection;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
public interface Supplier<T, S extends Annotation> { public interface Supplier<T, S extends Annotation> {
@ -8,19 +11,29 @@ public interface Supplier<T, S extends Annotation> {
Class<T> getValueType(); Class<T> getValueType();
InstanceWrapper<T, S> getValue(Registry registry, S annotation); T getValue(InstanceContext<T, S> instanceContext);
default InstanceWrapper<T, S> getValue(Registry registry, S annotation, Class<? extends T> valueType) { default LifeCycle getLifeCycle(S annotation) {
return getValue(registry, annotation); if (annotation != null) {
Optional<Method> lifecycle = Arrays.stream(annotation.annotationType().getMethods()).filter(m -> m.getName().equals("lifecycle")).findFirst();
if (lifecycle.isPresent()) {
try {
return (LifeCycle) lifecycle.get().invoke(annotation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return getDefaultLifecycle();
} }
boolean compatible(InstanceWrapper<T, S> a, RequestedInstance<T, S> b); default LifeCycle getDefaultLifecycle() {
return LifeCycle.CLASS;
default void close(T value) {
} }
default void close(InstanceWrapper<T, S> instanceWrapper) { boolean compatible(InstanceContext<T, S> a, RequestedInstance<T, S> b);
close(instanceWrapper.getValue());
default void close(InstanceContext<T, S> instanceContext) {
} }
default String getAlias() { default String getAlias() {

View file

@ -1,8 +1,6 @@
package org.keycloak.test.framework.page; package org.keycloak.test.framework.page;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
@ -21,21 +19,14 @@ public class PageSupplier implements Supplier<AbstractPage, TestPage> {
return AbstractPage.class; return AbstractPage.class;
} }
public InstanceWrapper<AbstractPage, TestPage> getValue(Registry registry, TestPage annotation) { @Override
throw new UnsupportedOperationException(); public AbstractPage getValue(InstanceContext<AbstractPage, TestPage> instanceContext) {
WebDriver webDriver = instanceContext.getDependency(WebDriver.class);
return createPage(webDriver, instanceContext.getRequestedValueType());
} }
@Override @Override
public InstanceWrapper<AbstractPage, TestPage> getValue(Registry registry, TestPage annotation, Class<? extends AbstractPage> valueType) { public boolean compatible(InstanceContext<AbstractPage, TestPage> a, RequestedInstance<AbstractPage, TestPage> b) {
InstanceWrapper<AbstractPage, TestPage> instanceWrapper = new InstanceWrapper<>(this, annotation);
WebDriver webDriver = registry.getDependency(WebDriver.class, instanceWrapper);
AbstractPage page = createPage(webDriver, valueType);
instanceWrapper.setValue(page, LifeCycle.CLASS);
return instanceWrapper;
}
@Override
public boolean compatible(InstanceWrapper<AbstractPage, TestPage> a, RequestedInstance<AbstractPage, TestPage> b) {
return true; return true;
} }

View file

@ -5,9 +5,8 @@ import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.test.framework.TestClient; import org.keycloak.test.framework.TestClient;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
@ -27,17 +26,14 @@ public class ClientSupplier implements Supplier<ClientResource, TestClient> {
} }
@Override @Override
public InstanceWrapper<ClientResource, TestClient> getValue(Registry registry, TestClient annotation) { public ClientResource getValue(InstanceContext<ClientResource, TestClient> instanceContext) {
InstanceWrapper<ClientResource, TestClient> wrapper = new InstanceWrapper<>(this, annotation); RealmResource realm = instanceContext.getDependency(RealmResource.class);
LifeCycle lifecycle = annotation.lifecycle();
RealmResource realm = registry.getDependency(RealmResource.class, wrapper); ClientConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
ClientConfig config = SupplierHelpers.getInstance(annotation.config());
ClientRepresentation clientRepresentation = config.getRepresentation(); ClientRepresentation clientRepresentation = config.getRepresentation();
if (clientRepresentation.getClientId() == null) { if (clientRepresentation.getClientId() == null) {
String clientId = lifecycle.equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : registry.getCurrentContext().getRequiredTestClass().getSimpleName(); String clientId = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName();
clientRepresentation.setClientId(clientId); clientRepresentation.setClientId(clientId);
} }
@ -48,22 +44,19 @@ public class ClientSupplier implements Supplier<ClientResource, TestClient> {
response.close(); response.close();
wrapper.addNote(CLIENT_UUID_KEY, clientId); instanceContext.addNote(CLIENT_UUID_KEY, clientId);
ClientResource clientResource = realm.clients().get(clientId); return realm.clients().get(clientId);
wrapper.setValue(clientResource, lifecycle);
return wrapper;
} }
@Override @Override
public boolean compatible(InstanceWrapper<ClientResource, TestClient> a, RequestedInstance<ClientResource, TestClient> b) { public boolean compatible(InstanceContext<ClientResource, TestClient> a, RequestedInstance<ClientResource, TestClient> b) {
return a.getAnnotation().config().equals(b.getAnnotation().config()); return a.getAnnotation().config().equals(b.getAnnotation().config());
} }
@Override @Override
public void close(ClientResource client) { public void close(InstanceContext<ClientResource, TestClient> instanceContext) {
client.remove(); instanceContext.getValue().remove();
} }
} }

View file

@ -4,9 +4,8 @@ import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.test.framework.TestRealm; import org.keycloak.test.framework.TestRealm;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
@ -26,39 +25,33 @@ public class RealmSupplier implements Supplier<RealmResource, TestRealm> {
} }
@Override @Override
public InstanceWrapper<RealmResource, TestRealm> getValue(Registry registry, TestRealm annotation) { public RealmResource getValue(InstanceContext<RealmResource, TestRealm> instanceContext) {
InstanceWrapper<RealmResource, TestRealm> wrapper = new InstanceWrapper<>(this, annotation); Keycloak adminClient = instanceContext.getDependency(Keycloak.class);
LifeCycle lifecycle = annotation.lifecycle();
Keycloak adminClient = registry.getDependency(Keycloak.class, wrapper); RealmConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
RealmConfig config = SupplierHelpers.getInstance(annotation.config());
RealmRepresentation realmRepresentation = config.getRepresentation(); RealmRepresentation realmRepresentation = config.getRepresentation();
if (realmRepresentation.getRealm() == null) { if (realmRepresentation.getRealm() == null) {
String realmName = lifecycle.equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : registry.getCurrentContext().getRequiredTestClass().getSimpleName(); String realmName = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName();
realmRepresentation.setRealm(realmName); realmRepresentation.setRealm(realmName);
} }
String realmName = realmRepresentation.getRealm(); String realmName = realmRepresentation.getRealm();
wrapper.addNote(REALM_NAME_KEY, realmName); instanceContext.addNote(REALM_NAME_KEY, realmName);
adminClient.realms().create(realmRepresentation); adminClient.realms().create(realmRepresentation);
RealmResource realmResource = adminClient.realm(realmRepresentation.getRealm()); return adminClient.realm(realmRepresentation.getRealm());
wrapper.setValue(realmResource, lifecycle);
return wrapper;
} }
@Override @Override
public boolean compatible(InstanceWrapper<RealmResource, TestRealm> a, RequestedInstance<RealmResource, TestRealm> b) { public boolean compatible(InstanceContext<RealmResource, TestRealm> a, RequestedInstance<RealmResource, TestRealm> b) {
return a.getAnnotation().config().equals(b.getAnnotation().config()); return a.getAnnotation().config().equals(b.getAnnotation().config());
} }
@Override @Override
public void close(RealmResource realm) { public void close(InstanceContext<RealmResource, TestRealm> instanceContext) {
realm.remove(); instanceContext.getValue().remove();
} }
} }

View file

@ -5,9 +5,8 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.test.framework.TestUser; import org.keycloak.test.framework.TestUser;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
@ -27,17 +26,14 @@ public class UserSupplier implements Supplier<UserResource, TestUser> {
} }
@Override @Override
public InstanceWrapper<UserResource, TestUser> getValue(Registry registry, TestUser annotation) { public UserResource getValue(InstanceContext<UserResource, TestUser> instanceContext) {
InstanceWrapper<UserResource, TestUser> wrapper = new InstanceWrapper<>(this, annotation); RealmResource realm = instanceContext.getDependency(RealmResource.class);
LifeCycle lifecycle = annotation.lifecycle();
RealmResource realm = registry.getDependency(RealmResource.class, wrapper); UserConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
UserConfig config = SupplierHelpers.getInstance(annotation.config());
UserRepresentation userRepresentation = config.getRepresentation(); UserRepresentation userRepresentation = config.getRepresentation();
if (userRepresentation.getUsername() == null) { if (userRepresentation.getUsername() == null) {
String username = lifecycle.equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : registry.getCurrentContext().getRequiredTestClass().getSimpleName(); String username = instanceContext.getLifeCycle().equals(LifeCycle.GLOBAL) ? config.getClass().getSimpleName() : instanceContext.getRegistry().getCurrentContext().getRequiredTestClass().getSimpleName();
userRepresentation.setUsername(username); userRepresentation.setUsername(username);
} }
@ -48,22 +44,19 @@ public class UserSupplier implements Supplier<UserResource, TestUser> {
response.close(); response.close();
wrapper.addNote(USER_UUID_KEY, userId); instanceContext.addNote(USER_UUID_KEY, userId);
UserResource userResource = realm.users().get(userId); return realm.users().get(userId);
wrapper.setValue(userResource, lifecycle);
return wrapper;
} }
@Override @Override
public boolean compatible(InstanceWrapper<UserResource, TestUser> a, RequestedInstance<UserResource, TestUser> b) { public boolean compatible(InstanceContext<UserResource, TestUser> a, RequestedInstance<UserResource, TestUser> b) {
return a.getAnnotation().config().equals(b.getAnnotation().config()); return a.getAnnotation().config().equals(b.getAnnotation().config());
} }
@Override @Override
public void close(UserResource user) { public void close(InstanceContext<UserResource, TestUser> instanceContext) {
user.remove(); instanceContext.getValue().remove();
} }
} }

View file

@ -2,9 +2,8 @@ package org.keycloak.test.framework.server;
import org.keycloak.test.framework.KeycloakIntegrationTest; import org.keycloak.test.framework.KeycloakIntegrationTest;
import org.keycloak.test.framework.database.TestDatabase; import org.keycloak.test.framework.database.TestDatabase;
import org.keycloak.test.framework.injection.InstanceWrapper; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
@ -25,13 +24,13 @@ public abstract class AbstractKeycloakTestServerSupplier implements Supplier<Key
} }
@Override @Override
public InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> getValue(Registry registry, KeycloakIntegrationTest annotation) { public KeycloakTestServer getValue(InstanceContext<KeycloakTestServer, KeycloakIntegrationTest> instanceContext) {
KeycloakIntegrationTest annotation = instanceContext.getAnnotation();
KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config()); KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config());
InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> wrapper = new InstanceWrapper<>(this, annotation);
Map<String, String> databaseConfig; Map<String, String> databaseConfig;
if (requiresDatabase()) { if (requiresDatabase()) {
TestDatabase testDatabase = registry.getDependency(TestDatabase.class, wrapper); TestDatabase testDatabase = instanceContext.getDependency(TestDatabase.class);
databaseConfig = testDatabase.getServerConfig(); databaseConfig = testDatabase.getServerConfig();
} else { } else {
databaseConfig = Collections.emptyMap(); databaseConfig = Collections.emptyMap();
@ -39,20 +38,22 @@ public abstract class AbstractKeycloakTestServerSupplier implements Supplier<Key
KeycloakTestServer keycloakTestServer = getServer(); KeycloakTestServer keycloakTestServer = getServer();
keycloakTestServer.start(serverConfig, databaseConfig); keycloakTestServer.start(serverConfig, databaseConfig);
return keycloakTestServer;
wrapper.setValue(keycloakTestServer, LifeCycle.GLOBAL);
return wrapper;
} }
@Override @Override
public boolean compatible(InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> a, RequestedInstance<KeycloakTestServer, KeycloakIntegrationTest> b) { public LifeCycle getDefaultLifecycle() {
return LifeCycle.GLOBAL;
}
@Override
public boolean compatible(InstanceContext<KeycloakTestServer, KeycloakIntegrationTest> a, RequestedInstance<KeycloakTestServer, KeycloakIntegrationTest> b) {
return a.getAnnotation().config().equals(b.getAnnotation().config()); return a.getAnnotation().config().equals(b.getAnnotation().config());
} }
@Override @Override
public void close(KeycloakTestServer keycloakTestServer) { public void close(InstanceContext<KeycloakTestServer, KeycloakIntegrationTest> instanceContext) {
keycloakTestServer.stop(); instanceContext.getValue().stop();
} }
public abstract KeycloakTestServer getServer(); public abstract KeycloakTestServer getServer();

View file

@ -21,8 +21,8 @@ public class DistributionKeycloakTestServer implements KeycloakTestServer {
keycloak = new RawKeycloakDistribution(debug, manualStop, enableTls, reCreate, removeBuildOptionsAfterBuild, requestPort); keycloak = new RawKeycloakDistribution(debug, manualStop, enableTls, reCreate, removeBuildOptionsAfterBuild, requestPort);
// Set environment variables user and password for Keycloak Admin used by Keycloak instance. // Set environment variables user and password for Keycloak Admin used by Keycloak instance.
keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", serverConfig.adminUserName().get()); keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", serverConfig.adminUserName());
keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", serverConfig.adminUserPassword().get()); keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", serverConfig.adminUserPassword());
List<String> rawOptions = new LinkedList<>(); List<String> rawOptions = new LinkedList<>();
rawOptions.add("start-dev"); rawOptions.add("start-dev");

View file

@ -22,8 +22,16 @@ public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
rawOptions.add("--features=" + String.join(",", serverConfig.features())); rawOptions.add("--features=" + String.join(",", serverConfig.features()));
} }
serverConfig.adminUserName().ifPresent(username -> System.setProperty("keycloakAdmin", username)); if (serverConfig.adminUserName() != null) {
serverConfig.adminUserPassword().ifPresent(password -> System.setProperty("keycloakAdminPassword", password)); System.setProperty("keycloakAdmin", serverConfig.adminUserName());
} else {
System.getProperties().remove("keycloakAdmin");
}
if (serverConfig.adminUserPassword() != null) {
System.setProperty("keycloakAdminPassword", serverConfig.adminUserPassword());
} else {
System.getProperties().remove("keycloakAdminPassword");
}
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value)); serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value)); databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value));

View file

@ -15,12 +15,12 @@ public interface KeycloakTestServerConfig {
return Collections.emptySet(); return Collections.emptySet();
} }
default Optional<String> adminUserName() { default String adminUserName() {
return Optional.of("admin"); return "admin";
} }
default Optional<String> adminUserPassword() { default String adminUserPassword() {
return Optional.of("admin"); return "admin";
} }
} }

View file

@ -0,0 +1,52 @@
package org.keycloak.test.framework.webdriver;
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;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.AbstractDriverOptions;
import java.time.Duration;
public abstract class AbstractWebDriverSupplier implements Supplier<WebDriver, TestWebDriver> {
@Override
public Class<TestWebDriver> getAnnotationClass() {
return TestWebDriver.class;
}
@Override
public Class<WebDriver> getValueType() {
return WebDriver.class;
}
@Override
public WebDriver getValue(InstanceContext<WebDriver, TestWebDriver> instanceContext) {
return getWebDriver();
}
@Override
public boolean compatible(InstanceContext<WebDriver, TestWebDriver> a, RequestedInstance<WebDriver, TestWebDriver> b) {
return true;
}
@Override
public LifeCycle getLifeCycle(TestWebDriver annotation) {
return LifeCycle.GLOBAL;
}
@Override
public void close(InstanceContext<WebDriver, TestWebDriver> instanceContext) {
instanceContext.getValue().quit();
}
public abstract WebDriver getWebDriver();
public void setGlobalOptions(AbstractDriverOptions<?> options) {
options.setImplicitWaitTimeout(Duration.ofSeconds(5));
options.setPageLoadStrategy(PageLoadStrategy.NORMAL);
}
}

View file

@ -1,44 +1,27 @@
package org.keycloak.test.framework.webdriver; package org.keycloak.test.framework.webdriver;
import org.keycloak.test.framework.injection.InstanceWrapper;
import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class ChromeWebDriverSupplier implements Supplier<WebDriver, TestWebDriver> { public class ChromeWebDriverSupplier extends AbstractWebDriverSupplier {
@Override
public Class<TestWebDriver> getAnnotationClass() {
return TestWebDriver.class;
}
@Override
public Class<WebDriver> getValueType() {
return WebDriver.class;
}
@Override
public InstanceWrapper<WebDriver, TestWebDriver> getValue(Registry registry, TestWebDriver annotation) {
final var driver = new ChromeDriver();
return new InstanceWrapper<>(this, annotation, driver, LifeCycle.GLOBAL);
}
@Override
public boolean compatible(InstanceWrapper<WebDriver, TestWebDriver> a, RequestedInstance<WebDriver, TestWebDriver> b) {
return true;
}
@Override
public void close(WebDriver instance) {
instance.quit();
}
@Override @Override
public String getAlias() { public String getAlias() {
return "chrome"; return "chrome";
} }
@Override
public WebDriver getWebDriver() {
ChromeOptions options = new ChromeOptions();
setGlobalOptions(options);
options.addArguments(
"--headless",
"--disable-gpu",
"--window-size=1920,1200",
"--ignore-certificate-errors",
"--disable-dev-shm-usage"
);
return new ChromeDriver(options);
}
} }

View file

@ -1,43 +1,21 @@
package org.keycloak.test.framework.webdriver; package org.keycloak.test.framework.webdriver;
import org.keycloak.test.framework.injection.InstanceWrapper;
import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.Registry;
import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
public class FirefoxWebDriverSupplier implements Supplier<WebDriver, TestWebDriver> { public class FirefoxWebDriverSupplier extends AbstractWebDriverSupplier {
@Override
public Class<TestWebDriver> getAnnotationClass() {
return TestWebDriver.class;
}
@Override
public Class<WebDriver> getValueType() {
return WebDriver.class;
}
@Override
public InstanceWrapper<WebDriver, TestWebDriver> getValue(Registry registry, TestWebDriver annotation) {
final var driver = new FirefoxDriver();
return new InstanceWrapper<>(this, annotation, driver, LifeCycle.GLOBAL);
}
@Override
public boolean compatible(InstanceWrapper<WebDriver, TestWebDriver> a, RequestedInstance<WebDriver, TestWebDriver> b) {
return true;
}
@Override
public void close(WebDriver instance) {
instance.quit();
}
@Override @Override
public String getAlias() { public String getAlias() {
return "firefox"; return "firefox";
} }
@Override
public WebDriver getWebDriver() {
FirefoxOptions options = new FirefoxOptions();
setGlobalOptions(options);
options.addArguments("-headless");
return new FirefoxDriver(options);
}
} }