[KEYCLOAK-19311] Distx IT tests improvements (#8930)
* Testcontainers support to Dist.X testsuite
This commit is contained in:
parent
0ada9bf280
commit
79931fd607
9 changed files with 394 additions and 419 deletions
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
|
@ -5,6 +5,11 @@ on: [push, pull_request]
|
||||||
env:
|
env:
|
||||||
DEFAULT_JDK_VERSION: 11
|
DEFAULT_JDK_VERSION: 11
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
# Only run once for latest commit per ref and cancel other (previous) runs.
|
||||||
|
group: ci-keycloak-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
|
@ -76,7 +81,7 @@ jobs:
|
||||||
name: keycloak-artifacts.zip
|
name: keycloak-artifacts.zip
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: |
|
run: |
|
||||||
if ! mvn install -nsu -B -DskipTestsuite -DskipExamples -f pom.xml; then
|
if ! mvn install -nsu -B -DskipTestsuite -DskipQuarkus -DskipExamples -f pom.xml; then
|
||||||
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-unit-tests.zip -@
|
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-unit-tests.zip -@
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -403,7 +408,11 @@ jobs:
|
||||||
java-version: ${{ env.DEFAULT_JDK_VERSION }}
|
java-version: ${{ env.DEFAULT_JDK_VERSION }}
|
||||||
- name: Update maven settings
|
- name: Update maven settings
|
||||||
run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/
|
run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/
|
||||||
- name: Run Quarkus Tests
|
|
||||||
|
- name: Prepare the local distribution archives
|
||||||
|
run: mvn clean install -DskipTests -Pdistribution
|
||||||
|
|
||||||
|
- name: Run Quarkus Integration Tests
|
||||||
run: |
|
run: |
|
||||||
mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh
|
mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh
|
||||||
TEST_RESULT=${PIPESTATUS[0]}
|
TEST_RESULT=${PIPESTATUS[0]}
|
||||||
|
|
|
@ -61,20 +61,23 @@ public interface CLIResult extends LaunchResult {
|
||||||
boolean isDistribution();
|
boolean isDistribution();
|
||||||
|
|
||||||
default void assertStarted() {
|
default void assertStarted() {
|
||||||
assertTrue(getOutput().contains("Listening on:"));
|
assertTrue(getOutput().contains("Listening on:"), () -> "The standard output:\n" + getOutput() + "does include \"Listening on:\"");
|
||||||
assertNotDevMode();
|
assertNotDevMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void assertNotDevMode() {
|
default void assertNotDevMode() {
|
||||||
assertFalse(getOutput().contains("Running the server in dev mode."));
|
assertFalse(getOutput().contains("Running the server in dev mode."),
|
||||||
|
() -> "The standard output:\n" + getOutput() + "does include the Start Dev output");
|
||||||
}
|
}
|
||||||
|
|
||||||
default void assertStartedDevMode() {
|
default void assertStartedDevMode() {
|
||||||
assertTrue(getOutput().contains("Running the server in dev mode."));
|
assertTrue(getOutput().contains("Running the server in dev mode."),
|
||||||
|
() -> "The standard output:\n" + getOutput() + "doesn't include the Start Dev output");
|
||||||
}
|
}
|
||||||
|
|
||||||
default void assertError(String msg) {
|
default void assertError(String msg) {
|
||||||
assertTrue(getErrorOutput().contains(msg));
|
assertTrue(getErrorOutput().contains(msg),
|
||||||
|
() -> "The Error Output:\n " + getErrorOutput() + "\ndoesn't contains " + msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
default void assertHelp(String command) {
|
default void assertHelp(String command) {
|
||||||
|
@ -99,7 +102,8 @@ public interface CLIResult extends LaunchResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
// not very reliable, we should be comparing the output with some static reference to the help message.
|
// not very reliable, we should be comparing the output with some static reference to the help message.
|
||||||
assertTrue(getOutput().equals(outStream.toString().trim()));
|
assertTrue(getOutput().trim().equals(outStream.toString().trim()),
|
||||||
|
() -> "The Output:\n " + getOutput() + "\ndoesnt't contains " + outStream.toString().trim());
|
||||||
} catch (IOException cause) {
|
} catch (IOException cause) {
|
||||||
throw new RuntimeException("Failed to assert help", cause);
|
throw new RuntimeException("Failed to assert help", cause);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ import java.util.List;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
import org.junit.jupiter.api.extension.ParameterContext;
|
import org.junit.jupiter.api.extension.ParameterContext;
|
||||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||||
import org.keycloak.it.junit5.extension.DistributionTest.ReInstall;
|
|
||||||
import org.keycloak.it.utils.KeycloakDistribution;
|
import org.keycloak.it.utils.KeycloakDistribution;
|
||||||
|
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||||
import org.keycloak.quarkus.runtime.Environment;
|
import org.keycloak.quarkus.runtime.Environment;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||||
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
import org.keycloak.quarkus.runtime.cli.command.StartDev;
|
||||||
|
@ -65,7 +65,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||||
|
|
||||||
if (distConfig != null) {
|
if (distConfig != null) {
|
||||||
if (distConfig.keepAlive()) {
|
if (distConfig.keepAlive()) {
|
||||||
dist.stopIfRunning();
|
dist.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,17 +76,17 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||||
public void afterAll(ExtensionContext context) throws Exception {
|
public void afterAll(ExtensionContext context) throws Exception {
|
||||||
if (dist != null) {
|
if (dist != null) {
|
||||||
// just to make sure the server is stopped after all tests
|
// just to make sure the server is stopped after all tests
|
||||||
dist.stopIfRunning();
|
dist.stop();
|
||||||
}
|
}
|
||||||
super.afterAll(context);
|
super.afterAll(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeycloakDistribution createDistribution(DistributionTest config) {
|
private KeycloakDistribution createDistribution(DistributionTest config) {
|
||||||
KeycloakDistribution distribution = new KeycloakDistribution();
|
KeycloakDistribution distribution = new RawKeycloakDistribution(
|
||||||
|
config.debug(),
|
||||||
distribution.setReCreate(!ReInstall.NEVER.equals(config.reInstall()));
|
config.keepAlive(),
|
||||||
distribution.setDebug(config.debug());
|
!DistributionTest.ReInstall.NEVER.equals(config.reInstall())
|
||||||
distribution.setManualStop(config.keepAlive());
|
);
|
||||||
|
|
||||||
return distribution;
|
return distribution;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,337 +1,41 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 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.it.utils;
|
package org.keycloak.it.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE;
|
import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
public interface KeycloakDistribution {
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.eclipse.aether.artifact.Artifact;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import io.quarkus.bootstrap.util.ZipUtils;
|
void start(List<String> arguments);
|
||||||
|
|
||||||
public final class KeycloakDistribution {
|
void stop();
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(KeycloakDistribution.class);
|
List<String> getOutputStream();
|
||||||
|
|
||||||
private Process keycloak;
|
List<String> getErrorStream();
|
||||||
private int exitCode = -1;
|
|
||||||
private final Path distPath;
|
|
||||||
private final List<String> outputStream = new ArrayList<>();
|
|
||||||
private final List<String> errorStream = new ArrayList<>();
|
|
||||||
private boolean reCreate;
|
|
||||||
private boolean manualStop;
|
|
||||||
private String relativePath;
|
|
||||||
private int httpPort;
|
|
||||||
private boolean debug;
|
|
||||||
private ExecutorService outputExecutor;
|
|
||||||
|
|
||||||
public <T> KeycloakDistribution() {
|
int getExitCode();
|
||||||
distPath = prepareDistribution();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start(List<String> arguments) {
|
boolean isDebug();
|
||||||
reset();
|
|
||||||
if (manualStop && isRunning()) {
|
|
||||||
throw new IllegalStateException("Server already running. You should manually stop the server before starting it again.");
|
|
||||||
}
|
|
||||||
stopIfRunning();
|
|
||||||
try {
|
|
||||||
startServer(arguments);
|
|
||||||
if (manualStop) {
|
|
||||||
asyncReadOutput();
|
|
||||||
waitForReadiness();
|
|
||||||
} else {
|
|
||||||
readOutput();
|
|
||||||
}
|
|
||||||
} catch (Exception cause) {
|
|
||||||
stopIfRunning();
|
|
||||||
throw new RuntimeException("Failed to start the server", cause);
|
|
||||||
} finally {
|
|
||||||
if (!manualStop) {
|
|
||||||
stopIfRunning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopIfRunning() {
|
boolean isManualStop();
|
||||||
if (isRunning()) {
|
|
||||||
try {
|
|
||||||
keycloak.destroy();
|
|
||||||
keycloak.waitFor(10, TimeUnit.SECONDS);
|
|
||||||
exitCode = keycloak.exitValue();
|
|
||||||
} catch (Exception cause) {
|
|
||||||
keycloak.destroyForcibly();
|
|
||||||
throw new RuntimeException("Failed to stop the server", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdownOutputExecutor();
|
default String[] getCliArgs(List<String> arguments) {
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getOutputStream() {
|
|
||||||
return outputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getErrorStream() {
|
|
||||||
return errorStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExitCode() {
|
|
||||||
return exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReCreate(boolean reCreate) {
|
|
||||||
this.reCreate = reCreate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebug(boolean debug) {
|
|
||||||
this.debug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setManualStop(boolean manualStop) {
|
|
||||||
this.manualStop = manualStop;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getCliArgs(List<String> arguments) {
|
|
||||||
List<String> commands = new ArrayList<>();
|
List<String> commands = new ArrayList<>();
|
||||||
|
|
||||||
commands.add("./kc.sh");
|
commands.add("./kc.sh");
|
||||||
|
|
||||||
if (debug) {
|
if (this.isDebug()) {
|
||||||
commands.add("--debug");
|
commands.add("--debug");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manualStop) {
|
if (!this.isManualStop()) {
|
||||||
commands.add("-D" + LAUNCH_MODE + "=test");
|
commands.add("-D" + LAUNCH_MODE + "=test");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.relativePath = arguments.stream().filter(arg -> arg.startsWith("--http-relative-path")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("/");
|
|
||||||
this.httpPort = Integer.parseInt(arguments.stream().filter(arg -> arg.startsWith("--http-port")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("8080"));
|
|
||||||
|
|
||||||
commands.addAll(arguments);
|
commands.addAll(arguments);
|
||||||
|
|
||||||
return commands.toArray(new String[0]);
|
return commands.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForReadiness() throws MalformedURLException {
|
|
||||||
URL contextRoot = new URL("http://localhost:" + httpPort + ("/" + relativePath + "/realms/master/").replace("//", "/"));
|
|
||||||
HttpURLConnection connection = null;
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (System.currentTimeMillis() - startTime > getStartTimeout()) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Timeout [" + getStartTimeout() + "] while waiting for Quarkus server");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// wait before checking for opening a new connection
|
|
||||||
Thread.sleep(1000);
|
|
||||||
if ("https".equals(contextRoot.getProtocol())) {
|
|
||||||
HttpsURLConnection httpsConnection = (HttpsURLConnection) (connection = (HttpURLConnection) contextRoot.openConnection());
|
|
||||||
httpsConnection.setSSLSocketFactory(createInsecureSslSocketFactory());
|
|
||||||
httpsConnection.setHostnameVerifier(createInsecureHostnameVerifier());
|
|
||||||
} else {
|
|
||||||
connection = (HttpURLConnection) contextRoot.openConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.setReadTimeout((int) getStartTimeout());
|
|
||||||
connection.setConnectTimeout((int) getStartTimeout());
|
|
||||||
connection.connect();
|
|
||||||
|
|
||||||
if (connection.getResponseCode() == 200) {
|
|
||||||
LOGGER.infof("Keycloak is ready at %s", contextRoot);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
} finally {
|
|
||||||
if (connection != null) {
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getStartTimeout() {
|
|
||||||
return TimeUnit.SECONDS.toMillis(120);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HostnameVerifier createInsecureHostnameVerifier() {
|
|
||||||
return new HostnameVerifier() {
|
|
||||||
@Override
|
|
||||||
public boolean verify(String s, SSLSession sslSession) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private SSLSocketFactory createInsecureSslSocketFactory() throws IOException {
|
|
||||||
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
|
|
||||||
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
|
|
||||||
SSLContext sslContext;
|
|
||||||
SSLSocketFactory socketFactory;
|
|
||||||
|
|
||||||
try {
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
|
||||||
socketFactory = sslContext.getSocketFactory();
|
|
||||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
|
||||||
throw new IOException("Can't create unsecure trust manager");
|
|
||||||
}
|
|
||||||
return socketFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isRunning() {
|
|
||||||
return keycloak != null && keycloak.isAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void asyncReadOutput() {
|
|
||||||
shutdownOutputExecutor();
|
|
||||||
outputExecutor = Executors.newSingleThreadExecutor();
|
|
||||||
outputExecutor.execute(this::readOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shutdownOutputExecutor() {
|
|
||||||
if (outputExecutor != null) {
|
|
||||||
outputExecutor.shutdown();
|
|
||||||
try {
|
|
||||||
outputExecutor.awaitTermination(30, TimeUnit.SECONDS);
|
|
||||||
} catch (InterruptedException cause) {
|
|
||||||
throw new RuntimeException("Failed to terminate output executor", cause);
|
|
||||||
} finally {
|
|
||||||
outputExecutor = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reset() {
|
|
||||||
outputStream.clear();
|
|
||||||
errorStream.clear();
|
|
||||||
exitCode = -1;
|
|
||||||
keycloak = null;
|
|
||||||
shutdownOutputExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path prepareDistribution() {
|
|
||||||
try {
|
|
||||||
Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests");
|
|
||||||
distRootPath.toFile().mkdirs();
|
|
||||||
File distFile = Maven.resolveArtifact("org.keycloak", "keycloak-server-x-dist", "zip")
|
|
||||||
.map(Artifact::getFile)
|
|
||||||
.orElseThrow(new Supplier<RuntimeException>() {
|
|
||||||
@Override
|
|
||||||
public RuntimeException get() {
|
|
||||||
return new RuntimeException("Could not obtain distribution artifact");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
String distDirName = distFile.getName().replace("keycloak-server-x-dist", "keycloak.x");
|
|
||||||
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
|
|
||||||
|
|
||||||
if (reCreate || !distPath.toFile().exists()) {
|
|
||||||
distPath.toFile().delete();
|
|
||||||
ZipUtils.unzip(distFile.toPath(), distRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure kc.sh is executable
|
|
||||||
distPath.resolve("bin").resolve("kc.sh").toFile().setExecutable(true);
|
|
||||||
|
|
||||||
return distPath;
|
|
||||||
} catch (Exception cause) {
|
|
||||||
throw new RuntimeException("Failed to prepare distribution", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readOutput() {
|
|
||||||
try (
|
|
||||||
BufferedReader outStream = new BufferedReader(new InputStreamReader(keycloak.getInputStream()));
|
|
||||||
BufferedReader errStream = new BufferedReader(new InputStreamReader(keycloak.getErrorStream()));
|
|
||||||
) {
|
|
||||||
while (keycloak.isAlive()) {
|
|
||||||
readStream(outStream, outputStream);
|
|
||||||
readStream(errStream, errorStream);
|
|
||||||
}
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
throw new RuntimeException("Failed to read server output", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readStream(BufferedReader reader, List<String> stream) throws IOException {
|
|
||||||
String line;
|
|
||||||
|
|
||||||
while (reader.ready() && (line = reader.readLine()) != null) {
|
|
||||||
stream.add(line);
|
|
||||||
System.out.println(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The server is configured to redirect errors to output stream. This adds a limitation when checking whether a
|
|
||||||
* message arrived via error stream.
|
|
||||||
*
|
|
||||||
* @param arguments the list of arguments to run the server
|
|
||||||
* @throws Exception if something bad happens
|
|
||||||
*/
|
|
||||||
private void startServer(List<String> arguments) throws Exception {
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(getCliArgs(arguments));
|
|
||||||
ProcessBuilder builder = pb.directory(distPath.resolve("bin").toFile());
|
|
||||||
|
|
||||||
builder.environment().put("KEYCLOAK_ADMIN", "admin");
|
|
||||||
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
|
|
||||||
|
|
||||||
FileUtils.deleteDirectory(distPath.resolve("data").toFile());
|
|
||||||
|
|
||||||
keycloak = builder.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 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.it.utils;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.eclipse.aether.artifact.Artifact;
|
|
||||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
|
||||||
import org.eclipse.aether.resolution.ArtifactResult;
|
|
||||||
|
|
||||||
import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
|
|
||||||
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
|
|
||||||
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
|
|
||||||
import io.quarkus.bootstrap.utils.BuildToolHelper;
|
|
||||||
|
|
||||||
public final class Maven {
|
|
||||||
|
|
||||||
private static Path resolveProjectDir() {
|
|
||||||
try {
|
|
||||||
String classFilePath = KeycloakDistribution.class.getName().replace(".", "/") + ".class";
|
|
||||||
URL classFileResource = Thread.currentThread().getContextClassLoader().getResource(classFilePath);
|
|
||||||
String classPath = classFileResource.getPath();
|
|
||||||
classPath = classPath.substring(0, classPath.length() - classFilePath.length());
|
|
||||||
URL newResource = new URL(classFileResource.getProtocol(), classFileResource.getHost(), classFileResource.getPort(),
|
|
||||||
classPath);
|
|
||||||
|
|
||||||
return BuildToolHelper.getProjectDir(Paths.get(newResource.toURI()));
|
|
||||||
} catch (Exception cause) {
|
|
||||||
throw new RuntimeException("Failed to resolve project dir", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Optional<Artifact> resolveArtifact(String groupId, String artifactId) {
|
|
||||||
return resolveArtifact(groupId, artifactId, "jar");
|
|
||||||
}
|
|
||||||
|
|
||||||
static Optional<Artifact> resolveArtifact(String groupId, String artifactId, String extension) {
|
|
||||||
BootstrapMavenContext mvnCtx = createBootstrapContext();
|
|
||||||
LocalProject project = mvnCtx.getCurrentProject();
|
|
||||||
|
|
||||||
try {
|
|
||||||
MavenArtifactResolver mvnResolver = new MavenArtifactResolver(mvnCtx);
|
|
||||||
ArtifactResult resolve = mvnResolver.resolve(new DefaultArtifact(groupId, artifactId, extension,
|
|
||||||
project.getVersion()));
|
|
||||||
|
|
||||||
if (resolve.isResolved()) {
|
|
||||||
return Optional.of(resolve.getArtifact());
|
|
||||||
}
|
|
||||||
} catch (Exception cause) {
|
|
||||||
throw new RuntimeException("Failed to resolve project artifact [" + groupId + ":" + artifactId + ":" + project.getVersion() + ":" + extension, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BootstrapMavenContext createBootstrapContext() {
|
|
||||||
try {
|
|
||||||
return new BootstrapMavenContext(BootstrapMavenContext.config().setCurrentProject(resolveProjectDir().toString()));
|
|
||||||
} catch (Exception cause) {
|
|
||||||
throw new RuntimeException("Failed to create maven boorstrap context", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,318 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.it.utils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import io.quarkus.bootstrap.util.ZipUtils;
|
||||||
|
import org.keycloak.common.Version;
|
||||||
|
|
||||||
|
public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(RawKeycloakDistribution.class);
|
||||||
|
|
||||||
|
private Process keycloak;
|
||||||
|
private int exitCode = -1;
|
||||||
|
private final Path distPath;
|
||||||
|
private final List<String> outputStream = new ArrayList<>();
|
||||||
|
private final List<String> errorStream = new ArrayList<>();
|
||||||
|
private boolean manualStop;
|
||||||
|
private String relativePath;
|
||||||
|
private int httpPort;
|
||||||
|
private boolean debug;
|
||||||
|
private boolean reCreate;
|
||||||
|
private ExecutorService outputExecutor;
|
||||||
|
|
||||||
|
public RawKeycloakDistribution(boolean debug, boolean manualStop, boolean reCreate) {
|
||||||
|
this.debug = debug;
|
||||||
|
this.manualStop = manualStop;
|
||||||
|
this.reCreate = reCreate;
|
||||||
|
this.distPath = prepareDistribution();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(List<String> arguments) {
|
||||||
|
reset();
|
||||||
|
if (manualStop && isRunning()) {
|
||||||
|
throw new IllegalStateException("Server already running. You should manually stop the server before starting it again.");
|
||||||
|
}
|
||||||
|
stop();
|
||||||
|
try {
|
||||||
|
startServer(arguments);
|
||||||
|
if (manualStop) {
|
||||||
|
asyncReadOutput();
|
||||||
|
waitForReadiness();
|
||||||
|
} else {
|
||||||
|
readOutput();
|
||||||
|
}
|
||||||
|
} catch (Exception cause) {
|
||||||
|
stop();
|
||||||
|
throw new RuntimeException("Failed to start the server", cause);
|
||||||
|
} finally {
|
||||||
|
if (!manualStop) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
if (isRunning()) {
|
||||||
|
try {
|
||||||
|
keycloak.destroy();
|
||||||
|
keycloak.waitFor(10, TimeUnit.SECONDS);
|
||||||
|
exitCode = keycloak.exitValue();
|
||||||
|
} catch (Exception cause) {
|
||||||
|
keycloak.destroyForcibly();
|
||||||
|
throw new RuntimeException("Failed to stop the server", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownOutputExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getOutputStream() {
|
||||||
|
return outputStream;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<String> getErrorStream() {
|
||||||
|
return errorStream;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int getExitCode() {
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isDebug() { return this.debug; }
|
||||||
|
@Override
|
||||||
|
public boolean isManualStop() { return this.manualStop; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCliArgs(List<String> arguments) {
|
||||||
|
this.relativePath = arguments.stream().filter(arg -> arg.startsWith("--http-relative-path")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("/");
|
||||||
|
this.httpPort = Integer.parseInt(arguments.stream().filter(arg -> arg.startsWith("--http-port")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("8080"));
|
||||||
|
|
||||||
|
return KeycloakDistribution.super.getCliArgs(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForReadiness() throws MalformedURLException {
|
||||||
|
URL contextRoot = new URL("http://localhost:" + httpPort + ("/" + relativePath + "/realms/master/").replace("//", "/"));
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (System.currentTimeMillis() - startTime > getStartTimeout()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Timeout [" + getStartTimeout() + "] while waiting for Quarkus server");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// wait before checking for opening a new connection
|
||||||
|
Thread.sleep(1000);
|
||||||
|
if ("https".equals(contextRoot.getProtocol())) {
|
||||||
|
HttpsURLConnection httpsConnection = (HttpsURLConnection) (connection = (HttpURLConnection) contextRoot.openConnection());
|
||||||
|
httpsConnection.setSSLSocketFactory(createInsecureSslSocketFactory());
|
||||||
|
httpsConnection.setHostnameVerifier(createInsecureHostnameVerifier());
|
||||||
|
} else {
|
||||||
|
connection = (HttpURLConnection) contextRoot.openConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.setReadTimeout((int) getStartTimeout());
|
||||||
|
connection.setConnectTimeout((int) getStartTimeout());
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
if (connection.getResponseCode() == 200) {
|
||||||
|
LOGGER.infof("Keycloak is ready at %s", contextRoot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
} finally {
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getStartTimeout() {
|
||||||
|
return TimeUnit.SECONDS.toMillis(120);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HostnameVerifier createInsecureHostnameVerifier() {
|
||||||
|
return new HostnameVerifier() {
|
||||||
|
@Override
|
||||||
|
public boolean verify(String s, SSLSession sslSession) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLSocketFactory createInsecureSslSocketFactory() throws IOException {
|
||||||
|
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
|
||||||
|
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
SSLContext sslContext;
|
||||||
|
SSLSocketFactory socketFactory;
|
||||||
|
|
||||||
|
try {
|
||||||
|
sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||||
|
socketFactory = sslContext.getSocketFactory();
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
|
throw new IOException("Can't create unsecure trust manager");
|
||||||
|
}
|
||||||
|
return socketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRunning() {
|
||||||
|
return keycloak != null && keycloak.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void asyncReadOutput() {
|
||||||
|
shutdownOutputExecutor();
|
||||||
|
outputExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
outputExecutor.execute(this::readOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownOutputExecutor() {
|
||||||
|
if (outputExecutor != null) {
|
||||||
|
outputExecutor.shutdown();
|
||||||
|
try {
|
||||||
|
outputExecutor.awaitTermination(30, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException cause) {
|
||||||
|
throw new RuntimeException("Failed to terminate output executor", cause);
|
||||||
|
} finally {
|
||||||
|
outputExecutor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
outputStream.clear();
|
||||||
|
errorStream.clear();
|
||||||
|
exitCode = -1;
|
||||||
|
keycloak = null;
|
||||||
|
shutdownOutputExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path prepareDistribution() {
|
||||||
|
try {
|
||||||
|
Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests");
|
||||||
|
distRootPath.toFile().mkdirs();
|
||||||
|
File distFile = new File("../../../distribution/server-x-dist/target/keycloak.x-" + Version.VERSION_KEYCLOAK + ".zip");
|
||||||
|
if (!distFile.exists()) {
|
||||||
|
throw new RuntimeException("Distribution archive " + distFile.getAbsolutePath() +" doesn't exists");
|
||||||
|
}
|
||||||
|
distRootPath.toFile().mkdirs();
|
||||||
|
String distDirName = distFile.getName().replace("keycloak-server-x-dist", "keycloak.x");
|
||||||
|
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
|
||||||
|
|
||||||
|
if (reCreate || !distPath.toFile().exists()) {
|
||||||
|
distPath.toFile().delete();
|
||||||
|
ZipUtils.unzip(distFile.toPath(), distRootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure kc.sh is executable
|
||||||
|
if (!distPath.resolve("bin").resolve("kc.sh").toFile().setExecutable(true)) {
|
||||||
|
throw new RuntimeException("Cannot set kc.sh executable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return distPath;
|
||||||
|
} catch (Exception cause) {
|
||||||
|
throw new RuntimeException("Failed to prepare distribution", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readOutput() {
|
||||||
|
try (
|
||||||
|
BufferedReader outStream = new BufferedReader(new InputStreamReader(keycloak.getInputStream()));
|
||||||
|
BufferedReader errStream = new BufferedReader(new InputStreamReader(keycloak.getErrorStream()));
|
||||||
|
) {
|
||||||
|
while (keycloak.isAlive()) {
|
||||||
|
readStream(outStream, outputStream);
|
||||||
|
readStream(errStream, errorStream);
|
||||||
|
}
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
throw new RuntimeException("Failed to read server output", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readStream(BufferedReader reader, List<String> stream) throws IOException {
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while (reader.ready() && (line = reader.readLine()) != null) {
|
||||||
|
stream.add(line);
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server is configured to redirect errors to output stream. This adds a limitation when checking whether a
|
||||||
|
* message arrived via error stream.
|
||||||
|
*
|
||||||
|
* @param arguments the list of arguments to run the server
|
||||||
|
* @throws Exception if something bad happens
|
||||||
|
*/
|
||||||
|
private void startServer(List<String> arguments) throws Exception {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(getCliArgs(arguments));
|
||||||
|
ProcessBuilder builder = pb.directory(distPath.resolve("bin").toFile());
|
||||||
|
|
||||||
|
builder.environment().put("KEYCLOAK_ADMIN", "admin");
|
||||||
|
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
|
||||||
|
|
||||||
|
FileUtils.deleteDirectory(distPath.resolve("data").toFile());
|
||||||
|
|
||||||
|
keycloak = builder.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,19 +32,22 @@ public class StartCommandTest {
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "start", "--hostname-strict=false" })
|
@Launch({ "start", "--hostname-strict=false" })
|
||||||
void failNoTls(LaunchResult result) {
|
void failNoTls(LaunchResult result) {
|
||||||
assertTrue(result.getOutput().contains("Key material not provided to setup HTTPS"));
|
assertTrue(result.getOutput().contains("Key material not provided to setup HTTPS"),
|
||||||
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "start", "--http-enabled=true" })
|
@Launch({ "start", "--http-enabled=true" })
|
||||||
void failNoHostnameNotSet(LaunchResult result) {
|
void failNoHostnameNotSet(LaunchResult result) {
|
||||||
assertTrue(result.getOutput().contains("ERROR: Strict hostname resolution configured but no hostname was set"));
|
assertTrue(result.getOutput().contains("ERROR: Strict hostname resolution configured but no hostname was set"),
|
||||||
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "--profile=dev", "start" })
|
@Launch({ "--profile=dev", "start" })
|
||||||
void failUsingDevProfile(LaunchResult result) {
|
void failUsingDevProfile(LaunchResult result) {
|
||||||
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'start' the server using the 'dev' configuration profile. Please re-build the server first, using 'kc.sh build' for the default production profile, or using 'kc.sh build --profile=<profile>' with a profile more suitable for production."));
|
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'start' the server using the 'dev' configuration profile. Please re-build the server first, using 'kc.sh build' for the default production profile, or using 'kc.sh build --profile=<profile>' with a profile more suitable for production."),
|
||||||
|
() -> "The Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -31,17 +31,24 @@ class BuildCommandDistTest {
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "build" })
|
@Launch({ "build" })
|
||||||
void resetConfig(LaunchResult result) {
|
void resetConfig(LaunchResult result) {
|
||||||
assertTrue(result.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."));
|
assertTrue(result.getOutput().contains("Updating the configuration and installing your custom providers, if any. Please wait."),
|
||||||
assertTrue(result.getOutput().contains("Quarkus augmentation completed"));
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
assertTrue(result.getOutput().contains("Server configuration updated and persisted. Run the following command to review the configuration:"));
|
assertTrue(result.getOutput().contains("Quarkus augmentation completed"),
|
||||||
assertTrue(result.getOutput().contains("kc.sh show-config"));
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
|
assertTrue(result.getOutput().contains("Server configuration updated and persisted. Run the following command to review the configuration:"),
|
||||||
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
|
assertTrue(result.getOutput().contains("kc.sh show-config"),
|
||||||
|
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Launch({ "--profile=dev", "build" })
|
@Launch({ "--profile=dev", "build" })
|
||||||
void failIfDevProfile(LaunchResult result) {
|
void failIfDevProfile(LaunchResult result) {
|
||||||
assertTrue(result.getErrorOutput().contains("ERROR: Failed to run 'build' command."));
|
assertTrue(result.getErrorOutput().contains("ERROR: Failed to run 'build' command."),
|
||||||
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'build' the server using the 'dev' configuration profile. Please re-build the server first, using 'kc.sh build' for the default production profile, or using 'kc.sh build --profile=<profile>' with a profile more suitable for production."));
|
() -> "The Error Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||||
assertTrue(result.getErrorOutput().contains("For more details run the same command passing the '--verbose' option. Also you can use '--help' to see the details about the usage of the particular command."));
|
assertTrue(result.getErrorOutput().contains("ERROR: You can not 'build' the server using the 'dev' configuration profile. Please re-build the server first, using 'kc.sh build' for the default production profile, or using 'kc.sh build --profile=<profile>' with a profile more suitable for production."),
|
||||||
|
() -> "The Error Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||||
|
assertTrue(result.getErrorOutput().contains("For more details run the same command passing the '--verbose' option. Also you can use '--help' to see the details about the usage of the particular command."),
|
||||||
|
() -> "The Error Output:\n" + result.getErrorOutput() + "doesn't contains the expected string.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,18 @@
|
||||||
<artifactId>keycloak-quarkus-test-parent</artifactId>
|
<artifactId>keycloak-quarkus-test-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>noIntegrations</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>!skipQuarkus</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
<modules>
|
<modules>
|
||||||
<module>integration</module>
|
<module>integration</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
Loading…
Reference in a new issue