Quarkus Tests on Windows

Tested using Windows 10 locally

Closes #10926
This commit is contained in:
Dominik Guhr 2022-04-07 10:35:27 +02:00 committed by Bruno Oliveira da Silva
parent eab2dff979
commit 1162952432
10 changed files with 217 additions and 54 deletions

View file

@ -20,6 +20,8 @@ package org.keycloak.it.junit5.extension;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import static org.testcontainers.shaded.org.hamcrest.MatcherAssert.assertThat;
import static org.testcontainers.shaded.org.hamcrest.Matchers.containsString;
import java.util.List; import java.util.List;
@ -40,6 +42,11 @@ public interface CLIResult extends LaunchResult {
return outputStream; return outputStream;
} }
@Override
public String getErrorOutput() {
return String.join("\n", errStream).replace("\r","");
}
@Override @Override
public List<String> getErrorStream() { public List<String> getErrorStream() {
return errStream; return errStream;
@ -82,7 +89,7 @@ public interface CLIResult extends LaunchResult {
} }
default void assertMessage(String message) { default void assertMessage(String message) {
assertTrue(getOutput().contains(message)); assertThat(getOutput(), containsString(message));
} }
default void assertBuild() { default void assertBuild() {
@ -112,7 +119,7 @@ public interface CLIResult extends LaunchResult {
default void assertJsonLogDefaultsApplied() throws JsonProcessingException { default void assertJsonLogDefaultsApplied() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
String[] splittedOutput = getOutput().split(System.lineSeparator()); String[] splittedOutput = getOutput().split("\n");
int counter = 0; int counter = 0;

View file

@ -1,13 +1,14 @@
package org.keycloak.it.utils; package org.keycloak.it.utils;
import org.keycloak.quarkus.runtime.Environment;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE;
public interface KeycloakDistribution { public interface KeycloakDistribution {
String SCRIPT_CMD = Environment.isWindows() ? "kc.bat" : "kc.sh";
String SCRIPT_CMD_INVOKABLE = Environment.isWindows() ? SCRIPT_CMD : "./"+SCRIPT_CMD;
void start(List<String> arguments); void start(List<String> arguments);
void stop(); void stop();
@ -23,21 +24,7 @@ public interface KeycloakDistribution {
boolean isManualStop(); boolean isManualStop();
default String[] getCliArgs(List<String> arguments) { default String[] getCliArgs(List<String> arguments) {
List<String> commands = new ArrayList<>(); throw new RuntimeException("Not implemented");
commands.add("./kc.sh");
if (this.isDebug()) {
commands.add("--debug");
}
if (!this.isManualStop()) {
commands.add("-D" + LAUNCH_MODE + "=test");
}
commands.addAll(arguments);
return commands.toArray(new String[0]);
} }
default void setManualStop(boolean manualStop) { default void setManualStop(boolean manualStop) {

View file

@ -35,11 +35,12 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutorService; import java.util.concurrent.*;
import java.util.concurrent.Executors; import java.util.stream.Collectors;
import java.util.concurrent.TimeUnit; import java.util.stream.Stream;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@ -52,6 +53,9 @@ import io.quarkus.fs.util.ZipUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.keycloak.common.Version; import org.keycloak.common.Version;
import org.keycloak.quarkus.runtime.Environment;
import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE;
public final class RawKeycloakDistribution implements KeycloakDistribution { public final class RawKeycloakDistribution implements KeycloakDistribution {
@ -104,10 +108,26 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
public void stop() { public void stop() {
if (isRunning()) { if (isRunning()) {
try { try {
if (Environment.isWindows()) {
// On Windows, we're executing kc.bat in a runtime as "keycloak",
// so tha java process is an actual child process
// we have to kill first.
killChildProcessesOnWindows(false);
}
keycloak.destroy(); keycloak.destroy();
keycloak.waitFor(10, TimeUnit.SECONDS); keycloak.waitFor(10, TimeUnit.SECONDS);
exitCode = keycloak.exitValue(); exitCode = keycloak.exitValue();
} catch (Exception cause) { } catch (Exception cause) {
if (Environment.isWindows()) {
try {
killChildProcessesOnWindows(true);
} catch (Exception e) {
throw new RuntimeException("Failed to stop the server", e);
}
}
keycloak.destroyForcibly(); keycloak.destroyForcibly();
throw new RuntimeException("Failed to stop the server", cause); throw new RuntimeException("Failed to stop the server", cause);
} }
@ -116,32 +136,68 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
shutdownOutputExecutor(); shutdownOutputExecutor();
} }
private void killChildProcessesOnWindows(boolean isForced) {
for (ProcessHandle childProcessHandle : keycloak.children().collect(Collectors.toList())) {
CompletableFuture<ProcessHandle> onExit = childProcessHandle.onExit();
if (isForced) {
childProcessHandle.destroyForcibly();
} else {
childProcessHandle.destroy();
}
//for whatever reason windows doesnt wait for the termination,
// and parent process returns immediately with exitCode 1 but is not exited, leading to
// "failed to start the distribution" bc files that should be deleted
// are used by another process, so we need this here.
onExit.join();
}
}
@Override @Override
public List<String> getOutputStream() { public List<String> getOutputStream() {
return outputStream; return outputStream;
} }
@Override @Override
public List<String> getErrorStream() { public List<String> getErrorStream() {
return errorStream; return errorStream;
} }
@Override @Override
public int getExitCode() { public int getExitCode() {
return exitCode; return exitCode;
} }
@Override @Override
public boolean isDebug() { return this.debug; } public boolean isDebug() { return this.debug; }
@Override @Override
public boolean isManualStop() { return this.manualStop; } public boolean isManualStop() { return this.manualStop; }
@Override @Override
public String[] getCliArgs(List<String> arguments) { public String[] getCliArgs(List<String> arguments) {
List<String> allArgs = new ArrayList<>();
if (Environment.isWindows()) {
allArgs.add(distPath.resolve("bin") + File.separator + SCRIPT_CMD_INVOKABLE);
} else {
allArgs.add(SCRIPT_CMD_INVOKABLE);
}
if (this.isDebug()) {
allArgs.add("--debug");
}
if (!this.isManualStop()) {
allArgs.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.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")); this.httpPort = Integer.parseInt(arguments.stream().filter(arg -> arg.startsWith("--http-port")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("8080"));
List<String> args = new ArrayList<>();
args.add("-Dkc.home.dir=" + distPath + File.separator);
args.addAll(arguments);
return KeycloakDistribution.super.getCliArgs(args); allArgs.add("-Dkc.home.dir=" + distPath + File.separator);
allArgs.addAll(arguments);
return allArgs.toArray(String[]::new);
} }
private void waitForReadiness() throws MalformedURLException { private void waitForReadiness() throws MalformedURLException {
@ -256,32 +312,56 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
try { try {
Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests"); Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests");
distRootPath.toFile().mkdirs(); distRootPath.toFile().mkdirs();
File distFile = new File("../../dist/target/keycloak-" + Version.VERSION_KEYCLOAK + ".zip");
File distFile = new File("../../dist/" + File.separator + "target" + File.separator + "keycloak-" + Version.VERSION_KEYCLOAK + ".zip");
if (!distFile.exists()) { if (!distFile.exists()) {
throw new RuntimeException("Distribution archive " + distFile.getAbsolutePath() +" doesn't exists"); throw new RuntimeException("Distribution archive " + distFile.getAbsolutePath() +" doesn't exists");
} }
distRootPath.toFile().mkdirs(); distRootPath.toFile().mkdirs();
String distDirName = distFile.getName().replace("keycloak-server-x-dist", "keycloak.x"); String distDirName = distFile.getName();
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.'))); Path dPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
if (!inited || (reCreate || !dPath.toFile().exists())) {
if (!Environment.isWindows()) {
FileUtils.deleteDirectory(dPath.toFile());
} else {
deleteTempFilesOnWindows(dPath);
}
if (!inited || (reCreate || !distPath.toFile().exists())) {
FileUtils.deleteDirectory(distPath.toFile());
ZipUtils.unzip(distFile.toPath(), distRootPath); ZipUtils.unzip(distFile.toPath(), distRootPath);
} }
// make sure kc.sh is executable // make sure script is executable
if (!distPath.resolve("bin").resolve("kc.sh").toFile().setExecutable(true)) { if (!dPath.resolve("bin").resolve(SCRIPT_CMD).toFile().setExecutable(true)) {
throw new RuntimeException("Cannot set kc.sh executable"); throw new RuntimeException("Cannot set " + SCRIPT_CMD + " executable");
} }
inited = true; inited = true;
return distPath; return dPath;
} catch (Exception cause) { } catch (Exception cause) {
throw new RuntimeException("Failed to prepare distribution", cause); throw new RuntimeException("Failed to prepare distribution", cause);
} }
} }
private void deleteTempFilesOnWindows(Path dPath) {
if (Files.exists(dPath)) {
try (Stream<Path> walk = Files.walk(dPath)) {
walk.sorted(Comparator.reverseOrder())
.forEach(s -> {
try {
Files.delete(s);
} catch (IOException e) {
throw new RuntimeException("Could not delete temp directory for distribution", e);
}
});
} catch (IOException e) {
throw new RuntimeException("Could not traverse temp directory for distribution to delete files", e);
}
}
}
private void readOutput() { private void readOutput() {
try ( try (
BufferedReader outStream = new BufferedReader(new InputStreamReader(keycloak.getInputStream())); BufferedReader outStream = new BufferedReader(new InputStreamReader(keycloak.getInputStream()));

View file

@ -18,13 +18,16 @@
package org.keycloak.it.cli; package org.keycloak.it.cli;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest; import org.keycloak.it.junit5.extension.CLITest;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.it.utils.KeycloakDistribution;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@CLITest @CLITest
public class OptionValidationTest { public class OptionValidationTest {
@ -32,43 +35,49 @@ public class OptionValidationTest {
@Test @Test
@Launch({"build", "--db"}) @Launch({"build", "--db"})
public void failMissingOptionValue(LaunchResult result) { public void failMissingOptionValue(LaunchResult result) {
assertTrue(result.getErrorOutput().contains("Missing required value for option '--db' (vendor). Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres")); CLIResult cliResult = (CLIResult) result;
assertThat(cliResult.getErrorOutput(), containsString("Missing required value for option '--db' (vendor). Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
} }
@Test @Test
@Launch({"build", "--db", "foo", "bar"}) @Launch({"build", "--db", "foo", "bar"})
public void failMultipleOptionValue(LaunchResult result) { public void failMultipleOptionValue(LaunchResult result) {
assertTrue(result.getErrorOutput().contains("Option '--db' expects a single value (vendor) Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres")); CLIResult cliResult = (CLIResult) result;
assertThat(cliResult.getErrorOutput(), containsString("Option '--db' expects a single value (vendor) Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"));
} }
@Test @Test
@Launch({"build", "--nosuch"}) @Launch({"build", "--nosuch"})
public void failUnknownOption(LaunchResult result) { public void failUnknownOption(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertEquals("Unknown option: '--nosuch'\n" + assertEquals("Unknown option: '--nosuch'\n" +
"Try 'kc.sh build --help' for more information on the available options.", result.getErrorOutput()); "Try '" + KeycloakDistribution.SCRIPT_CMD + " build --help' for more information on the available options.", cliResult.getErrorOutput());
} }
@Test @Test
@Launch({"start", "--db-pasword mytestpw"}) @Launch({"start", "--db-pasword mytestpw"})
public void failUnknownOptionWhitespaceSeparatorNotShowingValue(LaunchResult result) { public void failUnknownOptionWhitespaceSeparatorNotShowingValue(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertEquals("Unknown option: '--db-pasword'\n" + assertEquals("Unknown option: '--db-pasword'\n" +
"Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" + "Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" +
"Try 'kc.sh start --help' for more information on the available options.", result.getErrorOutput()); "Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
} }
@Test @Test
@Launch({"start", "--db-pasword=mytestpw"}) @Launch({"start", "--db-pasword=mytestpw"})
public void failUnknownOptionEqualsSeparatorNotShowingValue(LaunchResult result) { public void failUnknownOptionEqualsSeparatorNotShowingValue(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertEquals("Unknown option: '--db-pasword'\n" + assertEquals("Unknown option: '--db-pasword'\n" +
"Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" + "Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" +
"Try 'kc.sh start --help' for more information on the available options.", result.getErrorOutput()); "Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
} }
@Test @Test
@Launch({"start", "--db-username=foobar","--db-pasword=mytestpw", "--foobar=barfoo"}) @Launch({"start", "--db-username=foobar","--db-pasword=mytestpw", "--foobar=barfoo"})
public void failWithFirstOptionOnMultipleUnknownOptions(LaunchResult result) { public void failWithFirstOptionOnMultipleUnknownOptions(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertEquals("Unknown option: '--db-pasword'\n" + assertEquals("Unknown option: '--db-pasword'\n" +
"Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" + "Possible solutions: --db-username, --db-url-host, --db-pool-min-size, --db-password, --db-url-properties, --db-url-database, --db-schema, --db-pool-max-size, --db-pool-initial-size, --db-url, --db-url-port\n" +
"Try 'kc.sh start --help' for more information on the available options.", result.getErrorOutput()); "Try '" + KeycloakDistribution.SCRIPT_CMD + " start --help' for more information on the available options.", cliResult.getErrorOutput());
} }
} }

View file

@ -26,6 +26,7 @@ import org.keycloak.it.junit5.extension.DistributionTest;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.it.utils.KeycloakDistribution;
@DistributionTest @DistributionTest
class BuildCommandDistTest { class BuildCommandDistTest {
@ -39,7 +40,7 @@ class BuildCommandDistTest {
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string."); () -> "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("Server configuration updated and persisted. Run the following command to review the configuration:"),
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string."); () -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
assertTrue(result.getOutput().contains("kc.sh show-config"), assertTrue(result.getOutput().contains(KeycloakDistribution.SCRIPT_CMD + " show-config"),
() -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string."); () -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string.");
} }

View file

@ -22,6 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.BeforeStartDistribution;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionTest;
@ -69,12 +71,23 @@ public class ClusterConfigDistTest {
} }
@Test @Test
@Launch({ "start", "--auto-build", "--http-enabled=true", "--hostname-strict false" }) @EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start", "--auto-build", "--log-level=info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true", "--hostname-strict=false" })
void testStartDefaultsToClustering(LaunchResult result) { void testStartDefaultsToClustering(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertStarted(); cliResult.assertStarted();
cliResult.assertClusteredCache(); cliResult.assertClusteredCache();
assertTrue(cliResult.getOutput().contains("org.jgroups.protocols.UDP")); assertTrue(cliResult.getOutput().contains("JGroups protocol stack: UDP"));
}
@Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell behaviour on Windows.")
@Launch({ "start", "--auto-build", "--log-level=\"info,org.infinispan.remoting.transport.jgroups.JGroupsTransport:debug","--http-enabled=true\"", "--hostname-strict=false" })
void testWinStartDefaultsToClustering(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStarted();
cliResult.assertClusteredCache();
assertTrue(cliResult.getOutput().contains("JGroups protocol stack: UDP"));
} }
@Test @Test

View file

@ -7,6 +7,8 @@ import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
@ -56,6 +58,7 @@ public class FeaturesDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({StartDev.NAME, "--features=token-exchange,admin-fine-grained-authz"}) @Launch({StartDev.NAME, "--features=token-exchange,admin-fine-grained-authz"})
public void testEnableMultipleFeatures(LaunchResult result) { public void testEnableMultipleFeatures(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -66,6 +69,18 @@ public class FeaturesDistTest {
assertFalse(cliResult.getOutput().contains("declarative-user-profile")); assertFalse(cliResult.getOutput().contains("declarative-user-profile"));
} }
@Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({StartDev.NAME, "--features=\"token-exchange,admin-fine-grained-authz\""})
public void testWinEnableMultipleFeatures(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStartedDevMode();
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
containsString("Preview feature enabled: admin_fine_grained_authz"),
containsString("Preview feature enabled: token_exchange")));
assertFalse(cliResult.getOutput().contains("declarative-user-profile"));
}
private void assertPreviewFeaturesEnabled(CLIResult result) { private void assertPreviewFeaturesEnabled(CLIResult result) {
assertThat(result.getOutput(), CoreMatchers.allOf( assertThat(result.getOutput(), CoreMatchers.allOf(
containsString("Preview feature enabled: admin_fine_grained_authz"), containsString("Preview feature enabled: admin_fine_grained_authz"),

View file

@ -22,6 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
@ -60,6 +62,7 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=off,org.keycloak:debug,org.infinispan:info" }) @Launch({ "start-dev", "--log-level=off,org.keycloak:debug,org.infinispan:info" })
void testRootAndCategoryLevels(LaunchResult result) { void testRootAndCategoryLevels(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -69,6 +72,17 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=\"off,org.keycloak:debug,org.infinispan:info\"" })
void testWinRootAndCategoryLevels(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertFalse(cliResult.getOutput().contains("INFO [io.quarkus"));
assertTrue(cliResult.getOutput().contains("DEBUG [org.keycloak"));
assertTrue(cliResult.getOutput().contains("INFO [org.infinispan.CONTAINER]"));
}
@Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=off,org.keycloak:warn,debug" }) @Launch({ "start-dev", "--log-level=off,org.keycloak:warn,debug" })
void testSetLastRootLevelIfMultipleSet(LaunchResult result) { void testSetLastRootLevelIfMultipleSet(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -78,6 +92,17 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=\"off,org.keycloak:warn,debug\"" })
void testWinSetLastRootLevelIfMultipleSet(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertTrue(cliResult.getOutput().contains("DEBUG [org.hibernate"));
assertFalse(cliResult.getOutput().contains("INFO [org.keycloak"));
cliResult.assertStartedDevMode();
}
@Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-console-format=\"%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{1.}] %s%e%n\"" }) @Launch({ "start-dev", "--log-console-format=\"%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{1.}] %s%e%n\"" })
void testSetLogFormat(LaunchResult result) { void testSetLogFormat(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -94,6 +119,7 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=off,org.keycloak:debug,org.infinispan:info", "--log-console-output=json" }) @Launch({ "start-dev", "--log-level=off,org.keycloak:debug,org.infinispan:info", "--log-console-output=json" })
void testLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) { void testLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
@ -103,6 +129,17 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log-level=\"off,org.keycloak:debug,org.infinispan:info\"", "--log-console-output=json" })
void testWinLogLevelSettingsAppliedWhenJsonEnabled(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertFalse(cliResult.getOutput().contains("\"loggerName\":\"io.quarkus\",\"level\":\"INFO\")"));
assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory\",\"level\":\"DEBUG\""));
assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.infinispan.CONTAINER\",\"level\":\"INFO\""));
}
@Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log=console,file"}) @Launch({ "start-dev", "--log=console,file"})
void testKeycloakLogFileCreated(RawDistRootPath path) { void testKeycloakLogFileCreated(RawDistRootPath path) {
Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH); Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH);
@ -111,6 +148,16 @@ public class LoggingDistTest {
} }
@Test @Test
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log=\"console,file\""})
void testWinKeycloakLogFileCreated(RawDistRootPath path) {
Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH);
File logFile = new File(logFilePath.toString());
assertTrue(logFile.isFile(), "Log file does not exist!");
}
@Test
@EnabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "different shell escaping behaviour on Windows.")
@Launch({ "start-dev", "--log=console,file", "--log-file-format=\"%d{HH:mm:ss} %-5p [%c{1.}] (%t) %s%e%n\""}) @Launch({ "start-dev", "--log=console,file", "--log-file-format=\"%d{HH:mm:ss} %-5p [%c{1.}] (%t) %s%e%n\""})
void testFileLoggingHasDifferentFormat(RawDistRootPath path) throws IOException { void testFileLoggingHasDifferentFormat(RawDistRootPath path) throws IOException {
Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH); Path logFilePath = Paths.get(path.getDistRootPath() + File.separator + LoggingPropertyMappers.DEFAULT_LOG_PATH);

View file

@ -30,6 +30,7 @@ import org.keycloak.it.junit5.extension.RawDistOnly;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.it.utils.KeycloakDistribution;
@DistributionTest(reInstall = DistributionTest.ReInstall.NEVER) @DistributionTest(reInstall = DistributionTest.ReInstall.NEVER)
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@ -44,9 +45,9 @@ public class StartAutoBuildDistTest {
cliResult.assertMessage("Changes detected in configuration. Updating the server image."); cliResult.assertMessage("Changes detected in configuration. Updating the server image.");
cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait."); cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait.");
cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:"); cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:");
cliResult.assertMessage("kc.sh show-config"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config");
cliResult.assertMessage("Next time you run the server, just run:"); cliResult.assertMessage("Next time you run the server, just run:");
cliResult.assertMessage("kc.sh start --http-enabled=true --hostname-strict=false"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start --http-enabled=true --hostname-strict=false");
assertFalse(cliResult.getOutput().contains("--cache")); assertFalse(cliResult.getOutput().contains("--cache"));
cliResult.assertStarted(); cliResult.assertStarted();
} }

View file

@ -17,6 +17,8 @@
package org.keycloak.it.cli.dist; package org.keycloak.it.cli.dist;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -28,16 +30,17 @@ import org.keycloak.it.junit5.extension.DistributionTest;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
import org.keycloak.it.utils.KeycloakDistribution;
@DistributionTest @DistributionTest
public class StartCommandDistTest extends StartCommandTest { public class StartCommandDistTest extends StartCommandTest {
@Test @Test
@Launch({ "-pf=dev", "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false" }) @Launch({ "--profile=dev", "start", "--auto-build", "--http-enabled=true", "--hostname-strict=false" })
void failIfAutoBuildUsingDevProfile(LaunchResult result) { void failIfAutoBuildUsingDevProfile(LaunchResult result) {
assertTrue(result.getErrorOutput().contains("You can not 'start' the server in development mode. Please re-build the server first, using 'kc.sh build' for the default production mode."), CLIResult cliResult = (CLIResult) result;
() -> "The Output:\n" + result.getErrorOutput() + "doesn't contains the expected string."); assertThat(cliResult.getErrorOutput(), containsString("You can not 'start' the server in development mode. Please re-build the server first, using 'kc.sh build' for the default production mode."));
assertEquals(4, result.getErrorStream().size()); assertEquals(4, cliResult.getErrorStream().size());
} }
@Test @Test
@ -54,9 +57,9 @@ public class StartCommandDistTest extends StartCommandTest {
cliResult.assertMessage("Changes detected in configuration. Updating the server image."); cliResult.assertMessage("Changes detected in configuration. Updating the server image.");
cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait."); cliResult.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait.");
cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:"); cliResult.assertMessage("Server configuration updated and persisted. Run the following command to review the configuration:");
cliResult.assertMessage("kc.sh show-config"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " show-config");
cliResult.assertMessage("Next time you run the server, just run:"); cliResult.assertMessage("Next time you run the server, just run:");
cliResult.assertMessage("kc.sh start --http-enabled=true --hostname-strict=false"); cliResult.assertMessage(KeycloakDistribution.SCRIPT_CMD + " start --http-enabled=true --hostname-strict=false");
assertFalse(cliResult.getOutput().contains("--cache")); assertFalse(cliResult.getOutput().contains("--cache"));
cliResult.assertStarted(); cliResult.assertStarted();
} }