JUnit 5 test framework PoC (#29517)
Closes #29516 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
2683c0a7d1
commit
568a5cb678
36 changed files with 1131 additions and 3 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -76,7 +76,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
SEP=""
|
SEP=""
|
||||||
PROJECTS=""
|
PROJECTS=""
|
||||||
for i in `find -name '*Test.java' -type f | egrep -v './(testsuite|quarkus|docs)/' | sed 's|/src/test/java/.*||' | sort | uniq | sed 's|./||'`; do
|
for i in `find -name '*Test.java' -type f | egrep -v './(testsuite|quarkus|docs|test-poc)/' | sed 's|/src/test/java/.*||' | sort | uniq | sed 's|./||'`; do
|
||||||
PROJECTS="$PROJECTS$SEP$i"
|
PROJECTS="$PROJECTS$SEP$i"
|
||||||
SEP=","
|
SEP=","
|
||||||
done
|
done
|
||||||
|
|
7
pom.xml
7
pom.xml
|
@ -1758,6 +1758,13 @@
|
||||||
</modules>
|
</modules>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>test-poc</id>
|
||||||
|
<modules>
|
||||||
|
<module>test-poc</module>
|
||||||
|
</modules>
|
||||||
|
</profile>
|
||||||
|
|
||||||
<!-- Profile to activate EAP8 Adapters Build -->
|
<!-- Profile to activate EAP8 Adapters Build -->
|
||||||
<profile>
|
<profile>
|
||||||
<id>eap8-adapters</id>
|
<id>eap8-adapters</id>
|
||||||
|
|
|
@ -41,7 +41,9 @@ import static org.keycloak.quarkus.runtime.Environment.isImportExportMode;
|
||||||
public class QuarkusKeycloakApplication extends KeycloakApplication {
|
public class QuarkusKeycloakApplication extends KeycloakApplication {
|
||||||
|
|
||||||
private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN";
|
private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN";
|
||||||
|
private static final String KEYCLOAK_ADMIN_PROP_VAR = "keycloakAdmin";
|
||||||
private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD";
|
private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD";
|
||||||
|
private static final String KEYCLOAK_ADMIN_PASSWORD_PROP_VAR = "keycloakAdminPassword";
|
||||||
|
|
||||||
void onStartupEvent(@Observes StartupEvent event) {
|
void onStartupEvent(@Observes StartupEvent event) {
|
||||||
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
|
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
|
||||||
|
@ -69,8 +71,8 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAdminUser() {
|
private void createAdminUser() {
|
||||||
String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR);
|
String adminUserName = getEnvOrProp(KEYCLOAK_ADMIN_ENV_VAR, KEYCLOAK_ADMIN_PROP_VAR);
|
||||||
String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR);
|
String adminPassword = getEnvOrProp(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR, KEYCLOAK_ADMIN_PASSWORD_PROP_VAR);
|
||||||
|
|
||||||
if ((adminUserName == null || adminUserName.trim().length() == 0)
|
if ((adminUserName == null || adminUserName.trim().length() == 0)
|
||||||
|| (adminPassword == null || adminPassword.trim().length() == 0)) {
|
|| (adminPassword == null || adminPassword.trim().length() == 0)) {
|
||||||
|
@ -88,4 +90,9 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getEnvOrProp(String envKey, String propKey) {
|
||||||
|
String value = System.getenv(envKey);
|
||||||
|
return value != null ? value : System.getProperty(propKey);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
66
test-poc/base/pom.xml
Executable file
66
test-poc/base/pom.xml
Executable file
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ and other contributors as indicated by the @author tags.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-test-parent</artifactId>
|
||||||
|
<groupId>org.keycloak.test</groupId>
|
||||||
|
<version>999.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-tests-base</artifactId>
|
||||||
|
<name>Keycloak Base Tests</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>Example tests to demonstrate new testing framework</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak.test</groupId>
|
||||||
|
<artifactId>keycloak-test-junit5-framework</artifactId>
|
||||||
|
<version>999.0.0-SNAPSHOT</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.platform</groupId>
|
||||||
|
<artifactId>junit-platform-suite</artifactId>
|
||||||
|
<version>1.10.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logmanager</groupId>
|
||||||
|
<artifactId>jboss-logmanager</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.keycloak.test.base;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.representations.info.FeatureRepresentation;
|
||||||
|
import org.keycloak.test.framework.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.test.framework.TestAdminClient;
|
||||||
|
import org.keycloak.test.framework.server.KeycloakTestServerConfig;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@KeycloakIntegrationTest(config = CustomConfigTest.CustomServerConfig.class)
|
||||||
|
public class CustomConfigTest {
|
||||||
|
|
||||||
|
@TestAdminClient
|
||||||
|
Keycloak adminClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateEmailFeatureEnabled() {
|
||||||
|
Optional<FeatureRepresentation> updateEmailFeature = adminClient.serverInfo().getInfo().getFeatures().stream().filter(f -> f.getName().equals(Profile.Feature.UPDATE_EMAIL.name())).findFirst();
|
||||||
|
Assertions.assertTrue(updateEmailFeature.isPresent());
|
||||||
|
Assertions.assertTrue(updateEmailFeature.get().isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CustomServerConfig implements KeycloakTestServerConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> features() {
|
||||||
|
return Set.of("update-email");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.keycloak.test.base;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.test.framework.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.test.framework.TestAdminClient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@KeycloakIntegrationTest
|
||||||
|
public class DefaultConfig1Test {
|
||||||
|
|
||||||
|
@TestAdminClient
|
||||||
|
Keycloak adminClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminClient() {
|
||||||
|
List<RealmRepresentation> realms = adminClient.realms().findAll();
|
||||||
|
Assertions.assertFalse(realms.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.keycloak.test.base;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.test.framework.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.test.framework.TestAdminClient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@KeycloakIntegrationTest
|
||||||
|
public class DefaultConfig2Test {
|
||||||
|
|
||||||
|
@TestAdminClient
|
||||||
|
Keycloak adminClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminClient() {
|
||||||
|
List<RealmRepresentation> realms = adminClient.realms().findAll();
|
||||||
|
Assertions.assertFalse(realms.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.keycloak.test.base;
|
||||||
|
|
||||||
|
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.ClientRepresentation;
|
||||||
|
import org.keycloak.test.framework.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.test.framework.TestClient;
|
||||||
|
import org.keycloak.test.framework.TestRealm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@KeycloakIntegrationTest
|
||||||
|
public class ManagedResourcesTest {
|
||||||
|
|
||||||
|
@TestRealm
|
||||||
|
RealmResource realmResource;
|
||||||
|
|
||||||
|
@TestClient
|
||||||
|
ClientResource clientResource;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatedRealm() {
|
||||||
|
Assertions.assertEquals("ManagedResourcesTest", realmResource.toRepresentation().getRealm());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatedClient() {
|
||||||
|
Assertions.assertEquals("ManagedResourcesTest", clientResource.toRepresentation().getClientId());
|
||||||
|
|
||||||
|
List<ClientRepresentation> clients = realmResource.clients().findByClientId("ManagedResourcesTest");
|
||||||
|
Assertions.assertEquals(1, clients.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
test-poc/base/src/test/resources/logging.properties
Normal file
15
test-poc/base/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
|
49
test-poc/framework/pom.xml
Executable file
49
test-poc/framework/pom.xml
Executable file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ and other contributors as indicated by the @author tags.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-test-parent</artifactId>
|
||||||
|
<groupId>org.keycloak.test</groupId>
|
||||||
|
<version>999.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-test-junit5-framework</artifactId>
|
||||||
|
<name>Keycloak JUnit 5 testing framework</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>PoC JUnit 5 testing framework for Keycloak</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-admin-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-junit5</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.keycloak.test.framework;
|
||||||
|
|
||||||
|
import org.keycloak.test.framework.server.DefaultKeycloakTestServerConfig;
|
||||||
|
import org.keycloak.test.framework.server.KeycloakTestServerConfig;
|
||||||
|
|
||||||
|
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.TYPE)
|
||||||
|
public @interface KeycloakIntegrationTest {
|
||||||
|
|
||||||
|
Class<? extends KeycloakTestServerConfig> config() default DefaultKeycloakTestServerConfig.class;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.keycloak.test.framework;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||||
|
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||||
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
import org.keycloak.test.framework.injection.Registry;
|
||||||
|
|
||||||
|
public class KeycloakIntegrationTestExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeAll(ExtensionContext context) {
|
||||||
|
if (isExtensionEnabled(context)) {
|
||||||
|
getRegistry(context).beforeAll(context.getRequiredTestClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeEach(ExtensionContext context) {
|
||||||
|
if (isExtensionEnabled(context)) {
|
||||||
|
getRegistry(context).beforeEach(context.getRequiredTestInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterAll(ExtensionContext context) {
|
||||||
|
if (isExtensionEnabled(context)) {
|
||||||
|
getRegistry(context).afterAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExtensionEnabled(ExtensionContext context) {
|
||||||
|
return context.getRequiredTestClass().isAnnotationPresent(KeycloakIntegrationTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Registry getRegistry(ExtensionContext context) {
|
||||||
|
ExtensionContext.Store store = getStore(context);
|
||||||
|
Registry registry = (Registry) store.getOrComputeIfAbsent(Registry.class, r -> new Registry());
|
||||||
|
registry.setCurrentContext(context);
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtensionContext.Store getStore(ExtensionContext context) {
|
||||||
|
while (context.getParent().isPresent()) {
|
||||||
|
context = context.getParent().get();
|
||||||
|
}
|
||||||
|
return context.getStore(ExtensionContext.Namespace.create(getClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.keycloak.test.framework;
|
||||||
|
|
||||||
|
|
||||||
|
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 TestAdminClient {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.keycloak.test.framework;
|
||||||
|
|
||||||
|
import org.keycloak.test.framework.realm.ClientConfig;
|
||||||
|
import org.keycloak.test.framework.realm.DefaultClientConfig;
|
||||||
|
|
||||||
|
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 TestClient {
|
||||||
|
|
||||||
|
Class<? extends ClientConfig> config() default DefaultClientConfig.class;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.keycloak.test.framework;
|
||||||
|
|
||||||
|
import org.keycloak.test.framework.realm.DefaultRealmConfig;
|
||||||
|
import org.keycloak.test.framework.realm.RealmConfig;
|
||||||
|
|
||||||
|
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 TestRealm {
|
||||||
|
|
||||||
|
Class<? extends RealmConfig> config() default DefaultRealmConfig.class;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.keycloak.test.framework.admin;
|
||||||
|
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.test.framework.TestAdminClient;
|
||||||
|
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.Supplier;
|
||||||
|
import org.keycloak.test.framework.server.KeycloakTestServer;
|
||||||
|
|
||||||
|
public class KeycloakAdminClientSupplier implements Supplier<Keycloak, TestAdminClient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<TestAdminClient> getAnnotationClass() {
|
||||||
|
return TestAdminClient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Keycloak> getValueType() {
|
||||||
|
return Keycloak.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceWrapper<Keycloak, TestAdminClient> getValue(Registry registry, TestAdminClient annotation) {
|
||||||
|
InstanceWrapper<Keycloak, TestAdminClient> wrapper = new InstanceWrapper<>(this, annotation);
|
||||||
|
|
||||||
|
KeycloakTestServer testServer = registry.getDependency(KeycloakTestServer.class, wrapper);
|
||||||
|
|
||||||
|
Keycloak keycloak = Keycloak.getInstance(testServer.getBaseUrl(), "master", "admin", "admin", "admin-cli");
|
||||||
|
wrapper.setValue(keycloak);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LifeCycle getLifeCycle() {
|
||||||
|
return LifeCycle.GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compatible(InstanceWrapper<Keycloak, TestAdminClient> a, InstanceWrapper<Keycloak, TestAdminClient> b) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close(Keycloak keycloak) {
|
||||||
|
keycloak.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.keycloak.test.framework.injection;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class InstanceWrapper<T, A extends Annotation> {
|
||||||
|
|
||||||
|
private final Supplier<T, A> supplier;
|
||||||
|
private final A annotation;
|
||||||
|
private final Set<InstanceWrapper<T, A>> dependencies = new HashSet<>();
|
||||||
|
private T value;
|
||||||
|
private final Map<String, Object> notes = new HashMap<>();
|
||||||
|
|
||||||
|
public InstanceWrapper(Supplier<T, A> supplier, A annotation) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceWrapper(Supplier<T, A> supplier, A annotation, T value) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<T, A> getSupplier() {
|
||||||
|
return supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public A getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<InstanceWrapper<T, A>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerDependency(InstanceWrapper<T, A> instanceWrapper) {
|
||||||
|
dependencies.add(instanceWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNote(String key, Object value) {
|
||||||
|
notes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <N> N getNote(String key, Class<N> type) {
|
||||||
|
return (N) notes.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.keycloak.test.framework.injection;
|
||||||
|
|
||||||
|
public enum LifeCycle {
|
||||||
|
|
||||||
|
GLOBAL,
|
||||||
|
CLASS
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
package org.keycloak.test.framework.injection;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
public class Registry {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(Registry.class);
|
||||||
|
|
||||||
|
private ExtensionContext currentContext;
|
||||||
|
private final List<Supplier<?, ?>> suppliers = new LinkedList<>();
|
||||||
|
private final List<InstanceWrapper<?, ?>> deployedInstances = new LinkedList<>();
|
||||||
|
private final List<InstanceWrapper<?, ?>> requestedInstances = new LinkedList<>();
|
||||||
|
|
||||||
|
public Registry() {
|
||||||
|
loadSuppliers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionContext getCurrentContext() {
|
||||||
|
return currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentContext(ExtensionContext currentContext) {
|
||||||
|
this.currentContext = currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getDependency(Class<T> typeClass, InstanceWrapper dependent) {
|
||||||
|
InstanceWrapper dependency = getDeployedInstance(typeClass);
|
||||||
|
if (dependency != null) {
|
||||||
|
dependency.registerDependency(dependent);
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Injecting existing dependency {0} into {1}",
|
||||||
|
dependency.getSupplier().getClass().getSimpleName(),
|
||||||
|
dependent.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) dependency.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency = getRequestedInstance(typeClass);
|
||||||
|
if (dependency != null) {
|
||||||
|
dependency = dependency.getSupplier().getValue(this, dependency.getAnnotation());
|
||||||
|
dependency.registerDependency(dependent);
|
||||||
|
deployedInstances.add(dependency);
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Injecting requested dependency {0} into {1}",
|
||||||
|
dependency.getSupplier().getClass().getSimpleName(),
|
||||||
|
dependent.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) dependency.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Supplier<?, ?>> supplied = suppliers.stream().filter(s -> s.getValueType().equals(typeClass)).findFirst();
|
||||||
|
if (supplied.isPresent()) {
|
||||||
|
Supplier<?, ?> supplier = supplied.get();
|
||||||
|
dependency = supplier.getValue(this, null);
|
||||||
|
deployedInstances.add(dependency);
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Injecting un-configured dependency {0} into {1}",
|
||||||
|
dependency.getSupplier().getClass().getSimpleName(),
|
||||||
|
dependent.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) dependency.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Dependency not found: " + typeClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beforeAll(Class testClass) {
|
||||||
|
InstanceWrapper requestedServerInstance = createInstanceWrapper(testClass.getAnnotations());
|
||||||
|
requestedInstances.add(requestedServerInstance);
|
||||||
|
|
||||||
|
for (Field f : testClass.getDeclaredFields()) {
|
||||||
|
InstanceWrapper instanceWrapper = createInstanceWrapper(f.getAnnotations());
|
||||||
|
if (instanceWrapper != null) {
|
||||||
|
requestedInstances.add(instanceWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Requested suppliers: {0}",
|
||||||
|
requestedInstances.stream().map(r -> r.getSupplier().getClass().getSimpleName()).collect(Collectors.joining(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<InstanceWrapper<?, ?>> itr = requestedInstances.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
InstanceWrapper<?, ?> requestedInstance = itr.next();
|
||||||
|
InstanceWrapper deployedInstance = getDeployedInstance(requestedInstance.getSupplier());
|
||||||
|
if (deployedInstance != null) {
|
||||||
|
if (deployedInstance.getSupplier().compatible(deployedInstance, requestedInstance)) {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Reusing compatible: {0}",
|
||||||
|
deployedInstance.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
itr.remove();
|
||||||
|
} else {
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Destroying non-compatible: {0}",
|
||||||
|
deployedInstance.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(deployedInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = requestedInstances.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
InstanceWrapper requestedInstance = itr.next();
|
||||||
|
|
||||||
|
InstanceWrapper instance = requestedInstance.getSupplier().getValue(this, requestedInstance.getAnnotation());
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Created instance: {0}",
|
||||||
|
requestedInstance.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
deployedInstances.add(instance);
|
||||||
|
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beforeEach(Object testInstance) {
|
||||||
|
for (Field f : testInstance.getClass().getDeclaredFields()) {
|
||||||
|
InstanceWrapper<?, ?> instance = getDeployedInstance(f.getAnnotations());
|
||||||
|
try {
|
||||||
|
f.setAccessible(true);
|
||||||
|
f.set(testInstance, instance.getValue());
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterAll() {
|
||||||
|
List<InstanceWrapper<?, ?>> destroy = deployedInstances.stream().filter(i -> i.getSupplier().getLifeCycle().equals(LifeCycle.CLASS)).toList();
|
||||||
|
destroy.forEach(this::destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstanceWrapper<?, ?> createInstanceWrapper(Annotation[] annotations) {
|
||||||
|
for (Annotation a : annotations) {
|
||||||
|
for (Supplier s : suppliers) {
|
||||||
|
if (s.getAnnotationClass().equals(a.annotationType())) {
|
||||||
|
return new InstanceWrapper(s, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstanceWrapper<?, ?> getDeployedInstance(Annotation[] annotations) {
|
||||||
|
for (Annotation a : annotations) {
|
||||||
|
for (InstanceWrapper<?, ?> i : deployedInstances) {
|
||||||
|
if (i.getSupplier().getAnnotationClass().equals(a.annotationType())) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void destroy(InstanceWrapper instanceWrapper) {
|
||||||
|
boolean removed = deployedInstances.remove(instanceWrapper);
|
||||||
|
if (removed) {
|
||||||
|
Set<InstanceWrapper> dependencies = instanceWrapper.getDependencies();
|
||||||
|
dependencies.forEach(this::destroy);
|
||||||
|
instanceWrapper.getSupplier().close(instanceWrapper.getValue());
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Closed instance: {0}",
|
||||||
|
instanceWrapper.getSupplier().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstanceWrapper getDeployedInstance(Supplier supplier) {
|
||||||
|
return deployedInstances.stream().filter(i -> i.getSupplier().equals(supplier)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSuppliers() {
|
||||||
|
ServiceLoader.load(Supplier.class).iterator().forEachRemaining(suppliers::add);
|
||||||
|
|
||||||
|
if (LOGGER.isTraceEnabled()) {
|
||||||
|
LOGGER.tracev("Suppliers: {0}", suppliers.stream().map(s -> s.getClass().getSimpleName()).collect(Collectors.joining(", ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstanceWrapper getDeployedInstance(Class typeClass) {
|
||||||
|
return deployedInstances.stream().filter(i -> i.getSupplier().getValueType().equals(typeClass)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InstanceWrapper getRequestedInstance(Class typeClass) {
|
||||||
|
return requestedInstances.stream().filter(i -> i.getSupplier().getValueType().equals(typeClass)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.keycloak.test.framework.injection;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
public interface Supplier<T, S extends Annotation> {
|
||||||
|
|
||||||
|
Class<S> getAnnotationClass();
|
||||||
|
|
||||||
|
Class<T> getValueType();
|
||||||
|
|
||||||
|
InstanceWrapper<T, S> getValue(Registry registry, S annotation);
|
||||||
|
|
||||||
|
LifeCycle getLifeCycle();
|
||||||
|
|
||||||
|
boolean compatible(InstanceWrapper<T, S> a, InstanceWrapper<T, S> b);
|
||||||
|
|
||||||
|
default void close(T instance) {}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.keycloak.test.framework.injection;
|
||||||
|
|
||||||
|
public class SupplierHelpers {
|
||||||
|
|
||||||
|
public static <T> T getInstance(Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredConstructor().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
|
||||||
|
public interface ClientConfig {
|
||||||
|
|
||||||
|
ClientRepresentation getRepresentation();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.test.framework.TestClient;
|
||||||
|
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.Supplier;
|
||||||
|
import org.keycloak.test.framework.injection.SupplierHelpers;
|
||||||
|
|
||||||
|
public class ClientSupplier implements Supplier<ClientResource, TestClient> {
|
||||||
|
|
||||||
|
private static final String CLIENT_UUID_KEY = "clientUuid";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<TestClient> getAnnotationClass() {
|
||||||
|
return TestClient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ClientResource> getValueType() {
|
||||||
|
return ClientResource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceWrapper<ClientResource, TestClient> getValue(Registry registry, TestClient annotation) {
|
||||||
|
InstanceWrapper<ClientResource, TestClient> wrapper = new InstanceWrapper<>(this, annotation);
|
||||||
|
|
||||||
|
RealmResource realm = registry.getDependency(RealmResource.class, wrapper);
|
||||||
|
|
||||||
|
ClientConfig config = SupplierHelpers.getInstance(annotation.config());
|
||||||
|
ClientRepresentation clientRepresentation = config.getRepresentation();
|
||||||
|
|
||||||
|
if (clientRepresentation.getClientId() == null) {
|
||||||
|
clientRepresentation.setClientId(registry.getCurrentContext().getRequiredTestClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Response response = realm.clients().create(clientRepresentation);
|
||||||
|
|
||||||
|
String path = response.getLocation().getPath();
|
||||||
|
String clientId = path.substring(path.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
wrapper.addNote(CLIENT_UUID_KEY, clientId);
|
||||||
|
|
||||||
|
ClientResource clientResource = realm.clients().get(clientId);
|
||||||
|
wrapper.setValue(clientResource);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LifeCycle getLifeCycle() {
|
||||||
|
return LifeCycle.CLASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compatible(InstanceWrapper<ClientResource, TestClient> a, InstanceWrapper<ClientResource, TestClient> b) {
|
||||||
|
return a.getAnnotation().config().equals(b.getAnnotation().config()) &&
|
||||||
|
a.getNote(CLIENT_UUID_KEY, String.class).equals(b.getNote(CLIENT_UUID_KEY, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close(ClientResource client) {
|
||||||
|
client.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
|
||||||
|
public class DefaultClientConfig implements ClientConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientRepresentation getRepresentation() {
|
||||||
|
return new ClientRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
|
||||||
|
public class DefaultRealmConfig implements RealmConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RealmRepresentation getRepresentation() {
|
||||||
|
return new RealmRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
|
||||||
|
public interface RealmConfig {
|
||||||
|
|
||||||
|
RealmRepresentation getRepresentation();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package org.keycloak.test.framework.realm;
|
||||||
|
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.test.framework.TestRealm;
|
||||||
|
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.Supplier;
|
||||||
|
import org.keycloak.test.framework.injection.SupplierHelpers;
|
||||||
|
|
||||||
|
public class RealmSupplier implements Supplier<RealmResource, TestRealm> {
|
||||||
|
|
||||||
|
private static final String REALM_NAME_KEY = "realmName";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<TestRealm> getAnnotationClass() {
|
||||||
|
return TestRealm.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<RealmResource> getValueType() {
|
||||||
|
return RealmResource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceWrapper<RealmResource, TestRealm> getValue(Registry registry, TestRealm annotation) {
|
||||||
|
InstanceWrapper<RealmResource, TestRealm> wrapper = new InstanceWrapper<>(this, annotation);
|
||||||
|
|
||||||
|
Keycloak adminClient = registry.getDependency(Keycloak.class, wrapper);
|
||||||
|
|
||||||
|
RealmConfig config = SupplierHelpers.getInstance(annotation.config());
|
||||||
|
RealmRepresentation realmRepresentation = config.getRepresentation();
|
||||||
|
|
||||||
|
if (realmRepresentation.getRealm() == null) {
|
||||||
|
realmRepresentation.setRealm(registry.getCurrentContext().getRequiredTestClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
String realmName = realmRepresentation.getRealm();
|
||||||
|
wrapper.addNote(REALM_NAME_KEY, realmName);
|
||||||
|
|
||||||
|
adminClient.realms().create(realmRepresentation);
|
||||||
|
|
||||||
|
RealmResource realmResource = adminClient.realm(realmRepresentation.getRealm());
|
||||||
|
wrapper.setValue(realmResource);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LifeCycle getLifeCycle() {
|
||||||
|
return LifeCycle.CLASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compatible(InstanceWrapper<RealmResource, TestRealm> a, InstanceWrapper<RealmResource, TestRealm> b) {
|
||||||
|
return a.getAnnotation().config().equals(b.getAnnotation().config()) &&
|
||||||
|
a.getNote(REALM_NAME_KEY, String.class).equals(b.getNote(REALM_NAME_KEY, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close(RealmResource realm) {
|
||||||
|
realm.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
public class DefaultKeycloakTestServerConfig implements KeycloakTestServerConfig {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
import org.keycloak.Keycloak;
|
||||||
|
import org.keycloak.common.Version;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
|
||||||
|
|
||||||
|
private Keycloak keycloak;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(KeycloakTestServerConfig serverConfig) {
|
||||||
|
System.setProperty("keycloakAdmin", "admin");
|
||||||
|
System.setProperty("keycloakAdminPassword", "admin");
|
||||||
|
|
||||||
|
List<String> rawOptions = new LinkedList<>();
|
||||||
|
rawOptions.add("start-dev");
|
||||||
|
// rawOptions.add("--db=dev-mem"); // TODO With dev-mem there's an issue as the H2 DB isn't stopped when restarting embedded server
|
||||||
|
rawOptions.add("--cache=local");
|
||||||
|
|
||||||
|
if (!serverConfig.features().isEmpty()) {
|
||||||
|
rawOptions.add("--features=" + String.join(",", serverConfig.features()));
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
|
||||||
|
|
||||||
|
keycloak = Keycloak.builder()
|
||||||
|
.setVersion(Version.VERSION)
|
||||||
|
.start(rawOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
keycloak.stop();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return "http://localhost:8080";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
public interface KeycloakTestServer {
|
||||||
|
|
||||||
|
void start(KeycloakTestServerConfig serverConfig);
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
String getBaseUrl();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface KeycloakTestServerConfig {
|
||||||
|
|
||||||
|
default Map<String, String> options() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Set<String> features() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
import org.keycloak.test.framework.KeycloakIntegrationTest;
|
||||||
|
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.Supplier;
|
||||||
|
import org.keycloak.test.framework.injection.SupplierHelpers;
|
||||||
|
|
||||||
|
public class KeycloakTestServerSupplier implements Supplier<KeycloakTestServer, KeycloakIntegrationTest> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<KeycloakTestServer> getValueType() {
|
||||||
|
return KeycloakTestServer.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<KeycloakIntegrationTest> getAnnotationClass() {
|
||||||
|
return KeycloakIntegrationTest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> getValue(Registry registry, KeycloakIntegrationTest annotation) {
|
||||||
|
KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config());
|
||||||
|
|
||||||
|
// RemoteKeycloakTestServer keycloakTestServer = new RemoteKeycloakTestServer();
|
||||||
|
EmbeddedKeycloakTestServer keycloakTestServer = new EmbeddedKeycloakTestServer();
|
||||||
|
|
||||||
|
keycloakTestServer.start(serverConfig);
|
||||||
|
|
||||||
|
return new InstanceWrapper<>(this, annotation, keycloakTestServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LifeCycle getLifeCycle() {
|
||||||
|
return LifeCycle.GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean compatible(InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> a, InstanceWrapper<KeycloakTestServer, KeycloakIntegrationTest> b) {
|
||||||
|
return a.getAnnotation().config().equals(b.getAnnotation().config());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close(KeycloakTestServer remoteKeycloakTestServer) {
|
||||||
|
remoteKeycloakTestServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.keycloak.test.framework.server;
|
||||||
|
|
||||||
|
public class RemoteKeycloakTestServer implements KeycloakTestServer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(KeycloakTestServerConfig serverConfig) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return "http://localhost:8080";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.keycloak.test.framework.KeycloakIntegrationTestExtension
|
|
@ -0,0 +1,4 @@
|
||||||
|
org.keycloak.test.framework.admin.KeycloakAdminClientSupplier
|
||||||
|
org.keycloak.test.framework.server.KeycloakTestServerSupplier
|
||||||
|
org.keycloak.test.framework.realm.RealmSupplier
|
||||||
|
org.keycloak.test.framework.realm.ClientSupplier
|
46
test-poc/pom.xml
Executable file
46
test-poc/pom.xml
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ and other contributors as indicated by the @author tags.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>999.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-test-parent</artifactId>
|
||||||
|
<groupId>org.keycloak.test</groupId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Keycloak Test Parent</name>
|
||||||
|
<description>Keycloak Test Parent</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>base</module>
|
||||||
|
<module>framework</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
Loading…
Reference in a new issue