KEYCLOAK-6541 base changes

This commit is contained in:
vramik 2018-04-18 13:31:04 +02:00 committed by Pavel Drozd
parent f293ab86c3
commit 6a07a7ed2c
28 changed files with 493 additions and 139 deletions

View file

@ -77,18 +77,15 @@ TODO: Add info about Wildfly logging
## Run adapter tests
### Undertow
mvn -f testsuite/integration-arquillian/tests/base/pom.xml \
-Dtest=org.keycloak.testsuite.adapter.**.*Test
### Wildfly
# Prepare servers
mvn -f testsuite/integration-arquillian/servers/pom.xml clean install \
-Pauth-server-wildfly \
-Papp-server-wildfly
# Run tests
mvn -f testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml \
mvn -f testsuite/integration-arquillian/pom.xml \
clean install \
-Pauth-server-wildfly \
-Papp-server-wildfly

View file

@ -46,16 +46,14 @@ ifconfig lo multicast
Lifecycle of application server is always tied to a particular TestClass.
Each *adapter* test class is annotated by `@AppServerContainer("app-server-*")` annotation
that links it to a particular Arquillian container in `arquillian.xml`.
The `AppServerTestEnricher` then ensures the server is started during `BeforeClass` event and stopped during `AfterClass` event for that particular test class.
In case the `@AppServerContainer` annotation has no value it's assumed that the application container
is the same as the auth server container - a "relative" adapter test scenario.
Each *adapter* test class is annotated by one or more `@AppServerContainer("app-server-*")` annotations
that links it to a particular Arquillian container.
The `AppServerTestEnricher` then ensures the corresponding server is started during `BeforeClass` event and stopped during `AfterClass` event for that particular test class.
The app-servers with installed Keycloak adapter are prepared in `servers/app-server` submodules, activated by `-Papp-server-MODULE`.
The app-servers with installed Keycloak adapter are prepared in `servers/app-server` submodules, activated by `-Papp-server-MODULE` or `-Dapp.server=MODULE`
[More details.](servers/app-server/README.md)
The corresponding adapter test modules are in `tests/other/adapters` submodules, and are activated by the same profiles.
NOTE: Some corresponding adapter test modules are in `tests/other/adapters` submodules, and are activated by the same profiles. It will be tranferred into base testsuite.
## SuiteContext and TestContext
@ -104,13 +102,6 @@ The other test modules depend on this module.
Tests for Keycloak Admin Console are located in a separate module `tests/other/console`
and are **disabled** by default. Can be enabled by `-Pconsole-ui-tests`.
### Adapter Tests
Adapter tests are located in submodules of the `tests/other/adapters` module.
They are **disabled** by default; they can be enabled by corresponding profiles.
Multiple profiles can be enabled for a single test execution.
#### Types of adapter tests
1. Using *custom test servlets*
@ -173,7 +164,7 @@ integration-arquillian
└──other (common settings for all test modules dependent on base)
├──adapters (common settings for all adapter test modules)
├──adapters (common settings for all adapter test modules - will be moved into base)
│ ├──jboss
│ ├──tomcat
│ └──karaf

View file

@ -41,15 +41,15 @@
<app.server.java.home>${java.home}</app.server.java.home>
<!--component versions-->
<arquillian-core.version>1.1.13.Final</arquillian-core.version>
<selenium.version>3.5.3</selenium.version>
<arquillian-drone.version>2.4.2</arquillian-drone.version>
<arquillian-graphene.version>2.3.1</arquillian-graphene.version>
<!--to update arquillian-core to 1.3.0.Final or higher see https://issues.jboss.org/browse/ARQ-2181 -->
<arquillian-core.version>1.2.1.Final</arquillian-core.version>
<selenium.version>3.11.0</selenium.version>
<arquillian-drone.version>2.5.1</arquillian-drone.version>
<arquillian-graphene.version>2.3.2</arquillian-graphene.version>
<arquillian-wildfly-container.version>2.1.0.Final</arquillian-wildfly-container.version>
<arquillian-wls-container.version>1.0.1.Final</arquillian-wls-container.version>
<arquillian-container-karaf.version>2.2.0.Final</arquillian-container-karaf.version>
<arquillian-infinispan-container.version>1.2.0.Beta2</arquillian-infinispan-container.version>
<version.shrinkwrap.resolvers>2.2.6</version.shrinkwrap.resolvers>
<undertow-embedded.version>1.0.0.Alpha2</undertow-embedded.version>
<version.org.wildfly.extras.creaper>1.6.1</version.org.wildfly.extras.creaper>
<testcontainers.version>1.5.1</testcontainers.version>

View file

@ -13,10 +13,9 @@ Submodules are enabled with profiles: `-Papp-server-MODULE`
### Modules
* __`as7` JBossAS 7__
* __`wildfly8` Wildfly 8__
* __`wildfly9` Wildfly 9__
* __`wildfly` Wildfly 10__
* __`wildfly10` Wildfly 10__
* __`wildfly` Wildfly 11__
* __`eap6` EAP 6__ Requires access to EAP product repo, or setting `-Deap6.version` to public EAP 6 Alpha.
* __`eap` EAP 7__ Requires access to EAP product repo.
* __`relative`__ Activate with `-Papp-server-relative`.

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!--
~ Copyright 2018 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 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<parent>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-servers-app-server</artifactId>
<version>4.0.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
<packaging>jar</packaging>
<name>App Server - SPI</name>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.protocol</groupId>
<artifactId>arquillian-protocol-servlet</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,39 @@
/*
* Copyright 2018 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.
*/
package org.keycloak.testsuite.arquillian.container;
import java.util.List;
import org.jboss.shrinkwrap.descriptor.spi.node.Node;
/**
* @author <a href="mailto:vramik@redhat.com">Vlasta Ramik</a>
*/
public interface AppServerContainerSPI {
public static final String APP_SERVER = "app-server";
/**
* @return string name of container
*/
public String getName();
/**
* @return List of available containers or null if there are none
*/
public List<Node> getContainers();
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 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.
*/
package org.keycloak.testsuite.arquillian.container;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import org.jboss.shrinkwrap.descriptor.spi.node.Node;
/**
* @author <a href="mailto:vramik@redhat.com">Vlasta Ramik</a>
*/
public class AppServerContainerService {
private static AppServerContainerService service;
private final ServiceLoader<AppServerContainerSPI> loader;
private AppServerContainerService() {
loader = ServiceLoader.load(AppServerContainerSPI.class);
}
public static synchronized AppServerContainerService getInstance() {
if (service == null) {
service = new AppServerContainerService();
}
return service;
}
public List<Node> getContainers(String appServerName) {
List<Node> containers = null;
try {
Iterator<AppServerContainerSPI> definitions = loader.iterator();
List<AppServerContainerSPI> availableDefinitions = new ArrayList<>();
while (definitions != null && definitions.hasNext()) {
availableDefinitions.add(definitions.next());
}
for (AppServerContainerSPI def : availableDefinitions) {
if (def.getName().equals(appServerName)) {
containers = def.getContainers();
}
}
} catch (ServiceConfigurationError serviceError) {
containers = null;
throw serviceError;
}
return containers;
}
}

View file

@ -36,6 +36,7 @@
</properties>
<modules>
<module>app-server-spi</module>
<module>jboss</module>
<module>karaf</module>
<module>tomcat</module>

View file

@ -45,8 +45,8 @@
<exclude.x509>**/x509/*Test.java</exclude.x509>
<!-- KEYCLOAK-6771 exclude Mutual TLS Holder of Key Token x509 tests by default, enabled by 'ssl' profile -->
<exclude.HoK>**/hok/**/*Test.java</exclude.HoK>
<!-- exclude undertow adapter tests. They can be added by -Dtest=org.keycloak.testsuite.adapter.undertow.**.*Test -->
<exclude.undertow.adapter>**/adapter/undertow/**/*Test.java</exclude.undertow.adapter>
<!-- see include-CORS-tests profile -->
<exclude.cors.tests>**/cors/*Test.java</exclude.cors.tests>
</properties>
<dependencies>
@ -121,6 +121,11 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
@ -162,6 +167,7 @@
<exclude>${exclude.crossdc}</exclude>
<exclude>${exclude.undertow.adapter}</exclude>
<exclude>${exclude.x509}</exclude>
<exclude>${exclude.cors.tests}</exclude>
<exclude>${exclude.HoK}</exclude>
</excludes>
</configuration>
@ -330,7 +336,25 @@
</kie.maven.settings>
</properties>
</profile>
<profile>
<id>include-CORS-tests</id>
<!--
If you want to run CORS tests it is necessary to put
127.0.0.1 localhost-auth
127.0.0.1 localhost-db
to your /etc/hosts file
-->
<activation>
<property>
<name>includeCorsTests</name>
</property>
</activation>
<properties>
<exclude.cors.tests>-</exclude.cors.tests>
</properties>
</profile>
</profiles>
</project>

View file

@ -0,0 +1,52 @@
/*
* Copyright 2018 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.
*/
package org.keycloak.testsuite.arquillian;
import java.lang.reflect.Method;
import org.jboss.arquillian.test.spi.execution.ExecutionDecision;
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.logging.Logger;
/**
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
*/
public class AdapterTestExecutionDecider implements TestExecutionDecider {
private final Logger log = Logger.getLogger(AdapterTestExecutionDecider.class);
@Inject private Instance<TestContext> testContextInstance;
@Override
public ExecutionDecision decide(Method method) {
TestContext testContext = testContextInstance.get();
if (!testContext.isAdapterTest()) return ExecutionDecision.execute();
if (testContext.isAdapterContainerEnabled() || testContext.isAdapterContainerEnabledCluster()) {
return ExecutionDecision.execute();
} else {
log.debug("Skipping test: Not enabled by @AppServerContainer annotations.");
return ExecutionDecision.dontExecute("Not enabled by @AppServerContainer annotations.");
}
}
@Override
public int precedence() {
return 1;
}
}

View file

@ -2,13 +2,12 @@ package org.keycloak.testsuite.arquillian;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.InstanceProducer;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.test.spi.annotation.ClassScoped;
import org.jboss.arquillian.test.spi.event.suite.BeforeClass;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainers;
import org.wildfly.extras.creaper.core.ManagementClient;
import org.wildfly.extras.creaper.core.online.ManagementProtocol;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
@ -17,6 +16,10 @@ import org.wildfly.extras.creaper.core.online.OnlineOptions;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.keycloak.testsuite.arquillian.AuthServerTestEnricher.getAuthServerContextRoot;
@ -28,21 +31,25 @@ public class AppServerTestEnricher {
protected final Logger log = Logger.getLogger(this.getClass());
@Inject
@ClassScoped
private InstanceProducer<TestContext> testContextProducer;
public static final String APP_SERVER_PREFIX = "app-server-";
public static final String CURRENT_APP_SERVER = System.getProperty("app.server", "undertow");
@Inject private Instance<ContainerController> containerConrollerInstance;
@Inject private Instance<TestContext> testContextInstance;
private TestContext testContext;
public static String getAppServerQualifier(Class testClass) {
Class<? extends AuthServerTestEnricher> annotatedClass = getNearestSuperclassWithAnnotation(testClass, AppServerContainer.class);
public static List<String> getAppServerQualifiers(Class testClass) {
Class<?> annotatedClass = getNearestSuperclassWithAppServerAnnotation(testClass);
String appServerQ = (annotatedClass == null ? null
: annotatedClass.getAnnotation(AppServerContainer.class).value());
if (annotatedClass == null) return null; // no @AppServerContainer annotation --> no adapter test
return annotatedClass == null ? null // no @AppServerContainer annotation --> no adapter test
: (appServerQ == null || appServerQ.isEmpty() // @AppServerContainer annotation present but qualifier not set --> relative adapter test
? AuthServerTestEnricher.AUTH_SERVER_CONTAINER // app server == auth server
: appServerQ);
AppServerContainer[] appServerContainers = annotatedClass.getAnnotationsByType(AppServerContainer.class);
List<String> appServerQualifiers = new ArrayList<>();
for (AppServerContainer appServerContainer : appServerContainers) {
appServerQualifiers.add(appServerContainer.value());
}
return appServerQualifiers;
}
public static String getAppServerContextRoot() {
@ -62,11 +69,32 @@ public class AppServerTestEnricher {
}
public void updateTestContextWithAppServerInfo(@Observes(precedence = 1) BeforeClass event) {
testContext = testContextProducer.get();
String appServerQualifier = getAppServerQualifier(testContext.getTestClass());
testContext = testContextInstance.get();
List<String> appServerQualifiers = getAppServerQualifiers(testContext.getTestClass());
if (appServerQualifiers == null) { // no adapter test
log.info("\n\n" + testContext);
return;
}
String appServerQualifier = null;
for (String qualifier : appServerQualifiers) {
if (qualifier.contains(";")) {// cluster adapter test
final List<String> appServers = Arrays.asList(qualifier.split("\\s*;\\s*"));
List<ContainerInfo> appServerBackendsInfo = testContext.getSuiteContext().getContainers().stream()
.filter(ci -> appServers.contains(ci.getQualifier()))
.map(this::updateWithAppServerInfo)
.collect(Collectors.toList());
testContext.setAppServerBackendsInfo(appServerBackendsInfo);
} else {// non-cluster adapter test
for (ContainerInfo container : testContext.getSuiteContext().getContainers()) {
if (container.getQualifier().equals(appServerQualifier)) {
if (container.getQualifier().equals(qualifier)) {
testContext.setAppServerInfo(updateWithAppServerInfo(container));
appServerQualifier = qualifier;
break;
}
//TODO add warning if there are two or more matching containers.
}
}
}
// validate app server
@ -83,7 +111,7 @@ public class AppServerTestEnricher {
private ContainerInfo updateWithAppServerInfo(ContainerInfo appServerInfo, int clusterPortOffset) {
try {
String appServerContextRootStr = isRelative(testContext.getTestClass())
String appServerContextRootStr = isRelative()
? getAuthServerContextRoot(clusterPortOffset)
: getAppServerContextRoot(clusterPortOffset);
@ -111,11 +139,8 @@ public class AppServerTestEnricher {
return managementClient;
}
@Inject
private Instance<ContainerController> containerConrollerInstance;
public void startAppServer(@Observes(precedence = -1) BeforeClass event) throws MalformedURLException, InterruptedException, IOException {
if (testContext.isAdapterTest() && !testContext.isRelativeAdapterTest()) {
if (testContext.isAdapterContainerEnabled() && !testContext.isRelativeAdapterTest()) {
ContainerController controller = containerConrollerInstance.get();
if (!controller.isStarted(testContext.getAppServerInfo().getQualifier())) {
log.info("Starting app server: " + testContext.getAppServerInfo().getQualifier());
@ -131,39 +156,42 @@ public class AppServerTestEnricher {
* @return testClass or the nearest superclass of testClass annotated with
* annotationClass
*/
public static Class getNearestSuperclassWithAnnotation(Class testClass, Class annotationClass) {
return testClass.isAnnotationPresent(annotationClass) ? testClass
private static Class getNearestSuperclassWithAppServerAnnotation(Class<?> testClass) {
return (testClass.isAnnotationPresent(AppServerContainer.class) || testClass.isAnnotationPresent(AppServerContainers.class)) ? testClass
: (testClass.getSuperclass().equals(Object.class) ? null // stop recursion
: getNearestSuperclassWithAnnotation(testClass.getSuperclass(), annotationClass)); // continue recursion
: getNearestSuperclassWithAppServerAnnotation(testClass.getSuperclass())); // continue recursion
}
public static boolean hasAppServerContainerAnnotation(Class testClass) {
return getNearestSuperclassWithAnnotation(testClass, AppServerContainer.class) != null;
return getNearestSuperclassWithAppServerAnnotation(testClass) != null;
}
public static boolean isRelative(Class testClass) {
return getAppServerQualifier(testClass).equals(AuthServerTestEnricher.AUTH_SERVER_CONTAINER);
public static boolean isUndertowAppServer() {
return CURRENT_APP_SERVER.equals("undertow");
}
public static boolean isWildflyAppServer(Class testClass) {
return getAppServerQualifier(testClass).contains("wildfly");
public static boolean isRelative() {
return CURRENT_APP_SERVER.equals("relative");
}
public static boolean isTomcatAppServer(Class testClass) {
return getAppServerQualifier(testClass).contains("tomcat");
public static boolean isWildflyAppServer() {
return CURRENT_APP_SERVER.equals("wildfly");
}
public static boolean isWASAppServer(Class testClass) {
return getAppServerQualifier(testClass).contains("was");
public static boolean isTomcatAppServer() {
return CURRENT_APP_SERVER.equals("tomcat");
}
public static boolean isWLSAppServer(Class testClass) {
return getAppServerQualifier(testClass).contains("wls");
public static boolean isWASAppServer() {
return CURRENT_APP_SERVER.equals("was");
}
public static boolean isOSGiAppServer(Class testClass) {
String q = getAppServerQualifier(testClass);
return q.contains("karaf") || q.contains("fuse");
public static boolean isWLSAppServer() {
return CURRENT_APP_SERVER.equals("wls");
}
public static boolean isOSGiAppServer() {
return CURRENT_APP_SERVER.contains("karaf") || CURRENT_APP_SERVER.contains("fuse");
}
}

View file

@ -49,6 +49,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.NotFoundException;
import org.jboss.arquillian.core.api.annotation.ApplicationScoped;
/**
*
@ -97,7 +98,7 @@ public class AuthServerTestEnricher {
private SuiteContext suiteContext;
@Inject
@ClassScoped
@ApplicationScoped // needed in AdapterTestExecutionDecider
private InstanceProducer<TestContext> testContextProducer;
@Inject

View file

@ -13,7 +13,7 @@ import java.util.Objects;
*
* @author tkyjovsk
*/
public class ContainerInfo {
public class ContainerInfo implements Comparable<ContainerInfo> {
private URL contextRoot;
private Container arquillianContainer;
@ -116,4 +116,9 @@ public class ContainerInfo {
return Objects.equals(arquillianContainer.getContainerConfiguration().getMode(), "manual");
}
@Override
public int compareTo(ContainerInfo o) {
return this.getQualifier().compareTo(o.getQualifier());
}
}

View file

@ -42,7 +42,6 @@ import org.keycloak.testsuite.util.IOUtil;
import org.keycloak.util.JsonSerialization;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.io.IOException;
@ -107,7 +106,7 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
// } else {
// log.info(testClass.getJavaClass().getSimpleName() + " is not an AdapterTest");
// }
if (isWLSAppServer(testClass.getJavaClass())) {
if (isWLSAppServer()) {
// {
MavenResolverSystem resolver = Maven.resolver();
MavenFormatStage dependencies = resolver
@ -122,7 +121,7 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
.addClass(org.keycloak.testsuite.arquillian.annotation.UseServletFilter.class);
}
if (isWASAppServer(testClass.getJavaClass())) {
if (isWASAppServer()) {
// {
MavenResolverSystem resolver = Maven.resolver();
MavenFormatStage dependencies = resolver
@ -146,7 +145,7 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
}
protected void modifyAdapterConfigs(Archive<?> archive, TestClass testClass) {
boolean relative = isRelative(testClass.getJavaClass());
boolean relative = isRelative();
modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH, relative);
modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_TENANT1, relative);
modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_TENANT2, relative);
@ -260,7 +259,7 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
} catch (Exception ex) {
throw new RuntimeException("Error when processing " + archive.getName(), ex);
}
if (isTomcatAppServer(testClass.getJavaClass())) {
if (isTomcatAppServer()) {
modifyDocElementValue(webXmlDoc, "auth-method", "KEYCLOAK", "BASIC");
}

View file

@ -17,17 +17,20 @@
package org.keycloak.testsuite.arquillian;
import java.util.ArrayList;
import java.util.Objects;
import java.util.List;
import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
import org.jboss.arquillian.container.spi.client.deployment.TargetDescription;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator;
import org.jboss.arquillian.test.spi.TestClass;
import org.jboss.logging.Logger;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.testsuite.arquillian.AppServerTestEnricher;
import java.util.List;
import java.util.Objects;
import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getAppServerQualifier;
import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getAppServerQualifiers;
/**
* Changes target container for all Arquillian deployments based on value of
@ -39,17 +42,33 @@ public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenera
// Will be replaced in runtime by real auth-server-container
public static final String AUTH_SERVER_CURRENT = "auth-server-current";
// Will be replaced in runtime by real app-server-container
public static final String APP_SERVER_CURRENT = "app-server-current";
protected final Logger log = Logger.getLogger(this.getClass());
@Inject
private Instance<TestContext> testContext;
@Override
public List<DeploymentDescription> generate(TestClass testClass) {
TestContext context = testContext.get();
if (context.isAdapterTest() && !context.isAdapterContainerEnabled() && !context.isAdapterContainerEnabledCluster()) {
return new ArrayList<>(); // adapter test will be skipped, no need to genarate dependencies
}
List<DeploymentDescription> deployments = super.generate(testClass);
checkAuthServerTestDeployment(deployments, testClass);
checkTestDeployments(deployments, testClass);
List<String> appServerQualifiers = getAppServerQualifiers(testClass.getJavaClass());
if (appServerQualifiers == null) return deployments; // no adapter test
String appServerQualifier = getAppServerQualifier(
testClass.getJavaClass());
String appServerQualifier = appServerQualifiers.stream()
.filter(q -> q.contains(AppServerTestEnricher.CURRENT_APP_SERVER))
.findAny()
.orElse(null);
if (appServerQualifier.contains(";")) return deployments;
if (appServerQualifier != null && !appServerQualifier.isEmpty()) {
for (DeploymentDescription deployment : deployments) {
@ -65,21 +84,24 @@ public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenera
}
}
}
return deployments;
}
private void checkAuthServerTestDeployment(List<DeploymentDescription> descriptions, TestClass testClass) {
private void checkTestDeployments(List<DeploymentDescription> descriptions, TestClass testClass) {
for (DeploymentDescription deployment : descriptions) {
if (deployment.getTarget() != null) {
String containerQualifier = deployment.getTarget().getName();
if (AUTH_SERVER_CURRENT.equals(containerQualifier)) {
String newAuthServerQualifier = AuthServerTestEnricher.AUTH_SERVER_CONTAINER;
updateAuthServerQualifier(deployment, testClass, newAuthServerQualifier);
updateServerQualifier(deployment, testClass, newAuthServerQualifier);
} else if (containerQualifier.contains(APP_SERVER_CURRENT)) {
String suffix = containerQualifier.split(APP_SERVER_CURRENT)[1];
String newAppServerQualifier = AppServerTestEnricher.APP_SERVER_PREFIX + AppServerTestEnricher.CURRENT_APP_SERVER + "-" + suffix;
updateServerQualifier(deployment, testClass, newAppServerQualifier);
} else {
String newAuthServerQualifier = StringPropertyReplacer.replaceProperties(containerQualifier);
if (!newAuthServerQualifier.equals(containerQualifier)) {
updateAuthServerQualifier(deployment, testClass, newAuthServerQualifier);
String newServerQualifier = StringPropertyReplacer.replaceProperties(containerQualifier);
if (!newServerQualifier.equals(containerQualifier)) {
updateServerQualifier(deployment, testClass, newServerQualifier);
}
}
@ -88,9 +110,9 @@ public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenera
}
}
private void updateAuthServerQualifier(DeploymentDescription deployment, TestClass testClass, String newAuthServerQualifier) {
log.infof("Setting target container for deployment %s.%s: %s", testClass.getName(), deployment.getName(), newAuthServerQualifier);
deployment.setTarget(new TargetDescription(newAuthServerQualifier));
private void updateServerQualifier(DeploymentDescription deployment, TestClass testClass, String newServerQualifier) {
log.infof("Setting target container for deployment %s.%s: %s", testClass.getName(), deployment.getName(), newServerQualifier);
deployment.setTarget(new TargetDescription(newServerQualifier));
}
}

View file

@ -70,7 +70,8 @@ public class KeycloakArquillianExtension implements LoadableExtension {
.observer(AppServerTestEnricher.class)
.observer(H2TestEnricher.class);
builder
.service(TestExecutionDecider.class, MigrationTestExecutionDecider.class);
.service(TestExecutionDecider.class, MigrationTestExecutionDecider.class)
.service(TestExecutionDecider.class, AdapterTestExecutionDecider.class);
builder
.override(ResourceProvider.class, URLResourceProvider.class, URLProvider.class)

View file

@ -17,13 +17,16 @@
package org.keycloak.testsuite.arquillian;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;
import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getAppServerQualifiers;
import org.keycloak.testsuite.client.KeycloakTestingClient;
import org.keycloak.testsuite.util.TestCleanup;
@ -81,12 +84,36 @@ public final class TestContext {
return appServerBackendsInfo;
}
public void setAppServerBackendsInfo(List<ContainerInfo> appServerBackendsInfo) {
Collections.sort(appServerBackendsInfo);
this.appServerBackendsInfo.addAll(appServerBackendsInfo);
}
public Class getTestClass() {
return testClass;
}
public boolean isAdapterTest() {
return appServerInfo != null;
return getAppServerQualifiers(testClass) != null;
}
public boolean isAdapterContainerEnabled() {
if (!isAdapterTest()) return false; //no adapter test
if (appServerInfo == null) return false;
return getAppServerQualifiers(testClass).contains(appServerInfo.getQualifier());
}
public boolean isAdapterContainerEnabledCluster() {
if (!isAdapterTest()) return false; //no adapter test
if (appServerBackendsInfo.isEmpty()) return false; //no adapter clustered test
List<String> appServerQualifiers = getAppServerQualifiers(testClass);
String qualifier = appServerBackendsInfo.stream()
.map(ContainerInfo::getQualifier)
.collect(Collectors.joining(";"));
return appServerQualifiers.contains(qualifier);
}
public boolean isRelativeAdapterTest() {
@ -95,10 +122,6 @@ public final class TestContext {
suiteContext.getAuthServerInfo().getQualifier()); // app server == auth server
}
public boolean isClusteredAdapterTest() {
return isAdapterTest() && !appServerBackendsInfo.isEmpty();
}
public SuiteContext getSuiteContext() {
return suiteContext;
}
@ -106,7 +129,7 @@ public final class TestContext {
@Override
public String toString() {
return "TEST CONTEXT: " + getTestClass().getCanonicalName() + "\n"
+ (isAdapterTest() ? "App server container: " + getAppServerInfo() + "\n" : "");
+ (isAdapterTest() ? "Activated @AppServerContainer(" + getAppServerQualifiers(testClass) + ")\n" : "");
}
public Keycloak getAdminClient() {

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.arquillian.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@ -31,7 +32,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Retention(RUNTIME)
@Target({ElementType.TYPE})
public @interface AppServerContainer
{
String value() default "";
@Repeatable(AppServerContainers.class)
public @interface AppServerContainer {
String value();
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2018 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.
*/
package org.keycloak.testsuite.arquillian.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Holder for @AppServerContainer annotations
*
*/
@Documented
@Retention(RUNTIME)
@Target({ElementType.TYPE})
public @interface AppServerContainers {
AppServerContainer[] value();
}

View file

@ -19,6 +19,8 @@ package org.keycloak.testsuite.arquillian.containers;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.config.descriptor.api.ContainerDef;
import org.jboss.arquillian.config.descriptor.api.GroupDef;
import org.jboss.arquillian.config.descriptor.impl.ContainerDefImpl;
import org.jboss.arquillian.config.descriptor.impl.GroupDefImpl;
import org.jboss.arquillian.container.spi.ContainerRegistry;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.core.api.Injector;
@ -30,12 +32,14 @@ import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.spi.ServiceLoader;
import org.jboss.arquillian.core.spi.Validate;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.descriptor.spi.node.Node;
import org.jboss.shrinkwrap.descriptor.spi.node.NodeDescriptor;
import org.keycloak.testsuite.arquillian.container.AppServerContainerService;
import org.mvel2.MVEL;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.mvel2.MVEL;
import static org.keycloak.testsuite.arquillian.containers.SecurityActions.isClassPresent;
import static org.keycloak.testsuite.arquillian.containers.SecurityActions.loadClass;
@ -50,6 +54,8 @@ import static org.keycloak.testsuite.arquillian.containers.SecurityActions.loadC
public class RegistryCreator {
protected final Logger log = Logger.getLogger(this.getClass());
public static final String ADAPTER_IMPL_CONFIG_STRING = "adapterImplClass";
private static final String ENABLED = "enabled";
@Inject
@ApplicationScoped
@ -74,18 +80,23 @@ public class RegistryCreator {
throw new IllegalStateException("There are not any container adapters on the classpath");
}
createRegistry(event.getContainers(), containers, reg, serviceLoader);
List<ContainerDef> containersDefs = event.getContainers();//arquillian.xml
List<GroupDef> groupDefs = event.getGroups();//arquillian.xml
for (GroupDef group : event.getGroups()) {
createRegistry(group.getGroupContainers(), containers, reg, serviceLoader);
addAppServerContainers(containersDefs, groupDefs);//dynamically loaded containers/groups
createRegistry(containersDefs, reg, serviceLoader);
for (GroupDef group : groupDefs) {
createRegistry(group.getGroupContainers(), reg, serviceLoader);
}
registry.set(reg);
}
private void createRegistry(List<ContainerDef> containerDefs, Collection<DeployableContainer> containers, ContainerRegistry reg, ServiceLoader serviceLoader) {
private void createRegistry(List<ContainerDef> containerDefs, ContainerRegistry reg, ServiceLoader serviceLoader) {
for (ContainerDef container : containerDefs) {
if (isCreatingContainer(container, containers)) {
if (isAdapterImplClassAvailable(container)) {
if (isEnabled(container)) {
log.info("Registering container: " + container.getContainerName());
reg.create(container, serviceLoader);
@ -96,7 +107,20 @@ public class RegistryCreator {
}
}
private static final String ENABLED = "enabled";
private void addAppServerContainers(List<ContainerDef> containerDefs, List<GroupDef> groupDefs) {
Node parent = ((NodeDescriptor)containerDefs.get(0)).getRootNode();
String appServerName = System.getProperty("app.server", "undertow");
List<Node> containers = AppServerContainerService.getInstance().getContainers(appServerName);
for (Node container : containers) {
if (container.getName().equals("container")) {
containerDefs.add(new ContainerDefImpl("arquillian.xml", parent, container));
} else if (container.getName().equals("group")) {
groupDefs.add(new GroupDefImpl("arquillian.xml", parent, container));
}
}
}
private static boolean isEnabled(ContainerDef containerDef) {
Map<String, String> props = containerDef.getContainerProperties();
@ -109,7 +133,7 @@ public class RegistryCreator {
}
@SuppressWarnings("rawtypes")
private boolean isCreatingContainer(ContainerDef containerDef, Collection<DeployableContainer> containers) {
private boolean isAdapterImplClassAvailable(ContainerDef containerDef) {
if (hasAdapterImplClassProperty(containerDef)) {
if (isClassPresent(getAdapterImplClassValue(containerDef))) {
@ -135,7 +159,6 @@ public class RegistryCreator {
public static String getAdapterImplClassValue(ContainerDef containerDef) {
return containerDef.getContainerProperties().get(ADAPTER_IMPL_CONFIG_STRING).trim();
}
public static final String ADAPTER_IMPL_CONFIG_STRING = "adapterImplClass";
@SuppressWarnings("rawtypes")
public static DeployableContainer<?> getContainerAdapter(String adapterImplClass, Collection<DeployableContainer> containers) {

View file

@ -57,7 +57,7 @@ public class MigrationTestExecutionDecider implements TestExecutionDecider {
@Override
public int precedence() {
return 1;
return 2;
}
}

View file

@ -33,7 +33,9 @@ import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.keycloak.testsuite.arquillian.ContainerInfo;
public class URLProvider extends URLResourceProvider {
@ -106,7 +108,13 @@ public class URLProvider extends URLResourceProvider {
return suiteContext.get().getAuthServerInfo().getContextRoot();
}
if (AppServerContext.class.isAssignableFrom(a.annotationType())) {
return testContext.get().getAppServerInfo().getContextRoot();
ContainerInfo appServerInfo = testContext.get().getAppServerInfo();
if (appServerInfo != null) return appServerInfo.getContextRoot();
List<ContainerInfo> appServerBackendsInfo = testContext.get().getAppServerBackendsInfo();
if (appServerBackendsInfo.isEmpty()) throw new IllegalStateException("Both testContext's appServerInfo and appServerBackendsInfo not set.");
return appServerBackendsInfo.get(0).getContextRoot();
}
}

View file

@ -199,7 +199,7 @@ public class IOUtil {
return;
}
log.info("Removing node " + removeNode);
log.trace("Removing node " + removeNode);
parentElement.removeChild(removeElement);
}

View file

@ -16,6 +16,9 @@
*/
package org.keycloak.testsuite.util;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.arquillian.graphene.wait.ElementBuilder;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
@ -25,11 +28,6 @@ import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.keycloak.testsuite.util.DroneUtils.getCurrentDriver;
import static org.openqa.selenium.support.ui.ExpectedConditions.*;
@ -102,7 +100,7 @@ public final class WaitUtils {
// Ensure the URL is "stable", i.e. is not changing anymore; if it'd changing, some redirects are probably still in progress
for (int maxRedirects = 2; maxRedirects > 0; maxRedirects--) {
String currentUrl = driver.getCurrentUrl();
FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(250, TimeUnit.MILLISECONDS);
FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(Duration.ofMillis(250));
try {
wait.until(not(urlToBe(currentUrl)));
}

View file

@ -258,7 +258,7 @@ public abstract class AbstractKeycloakTest {
protected void deleteAllCookiesForRealm(String realmName) {
// masterRealmPage.navigateTo();
driver.navigate().to(oauth.AUTH_SERVER_ROOT + "/realms/" + realmName + "/account"); // Because IE webdriver freezes when loading a JSON page (realm page), we need to use this alternative
driver.navigate().to(OAuthClient.AUTH_SERVER_ROOT + "/realms/" + realmName + "/account"); // Because IE webdriver freezes when loading a JSON page (realm page), we need to use this alternative
log.info("deleting cookies in '" + realmName + "' realm");
driver.manage().deleteAllCookies();
}
@ -319,7 +319,6 @@ public abstract class AbstractKeycloakTest {
log.debug("importing realm: " + realm.getRealm());
try { // TODO - figure out a way how to do this without try-catch
RealmResource realmResource = adminClient.realms().realm(realm.getRealm());
RealmRepresentation rRep = realmResource.toRepresentation();
log.debug("realm already exists on server, re-importing");
realmResource.remove();
} catch (NotFoundException nfe) {

View file

@ -49,10 +49,12 @@ import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* <code>@AppServerContainer</code> is needed for stopping recursion in
* AppServerTestEnricher.getNearestSuperclassWithAnnotation
*
* @author tkyjovsk
*/
@AppServerContainer
@AppServerContainer("")
public abstract class AbstractAdapterTest extends AbstractAuthTest {
@Page

View file

@ -64,7 +64,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getNearestSuperclassWithAnnotation;
import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getNearestSuperclassWithAppServerAnnotation;
import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
@ -301,7 +301,7 @@ public abstract class AbstractSAMLAdapterClusterTest extends AbstractServletsAda
}
private String getAppServerId() {
Class<?> annotatedClass = getNearestSuperclassWithAnnotation(this.getClass(), AppServerContainer.class);
Class<?> annotatedClass = getNearestSuperclassWithAppServerAnnotation(this.getClass());
return (annotatedClass == null ? "<cannot-find-@AppServerContainer>"
: annotatedClass.getAnnotation(AppServerContainer.class).value());

View file

@ -77,10 +77,10 @@ fi
if [ $1 == "crossdc" ]; then
cd testsuite/integration-arquillian
mvn install -B -nsu -Pauth-servers-crossdc-jboss,auth-server-wildfly,cache-server-infinispan -DskipTests
mvn install -B -nsu -Pauth-servers-crossdc-jboss,auth-server-wildfly,cache-server-infinispan,app-server-wildfly -DskipTests
cd tests/base
mvn clean test -B -nsu -Pcache-server-infinispan,auth-servers-crossdc-jboss,auth-server-wildfly -Dtest=*.crossdc.**.* 2>&1 |
mvn clean test -B -nsu -Pcache-server-infinispan,auth-servers-crossdc-jboss,auth-server-wildfly,app-server-wildfly -Dtest=*.crossdc.**.* 2>&1 |
java -cp ../../../utils/target/classes org.keycloak.testsuite.LogTrimmer
BASE_TESTS_STATUS=${PIPESTATUS[0]}