diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java index c61ad766d6c..d02fb55af4e 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Optional; import java.util.Properties; import java.util.concurrent.ForkJoinPool; +import java.util.function.BiConsumer; import java.util.function.Consumer; import jakarta.enterprise.context.ApplicationScoped; @@ -58,6 +59,8 @@ import static org.keycloak.quarkus.runtime.Environment.hasEarlyExitLaunchMode; @ApplicationScoped public class KeycloakMain implements QuarkusApplication { + public static final String KC_SERVER_PRINT_RUNNING = "kc.server.print_running"; + public static final String RUNNING_MESSAGE = "The server is running"; private static AbstractNonServerCommand COMMAND; private static Consumer ERROR_HANDLER; @@ -163,8 +166,8 @@ public class KeycloakMain implements QuarkusApplication { */ @Override public int run(String... args) throws Exception { + QuarkusKeycloakApplication application = Arc.container().instance(QuarkusKeycloakApplication.class).get(); if (COMMAND != null) { - QuarkusKeycloakApplication application = Arc.container().instance(QuarkusKeycloakApplication.class).get(); QuarkusKeycloakSessionFactory sessionFactory = Arc.container().instance(QuarkusKeycloakSessionFactory.class).get(); COMMAND.onStart(application, sessionFactory); } @@ -173,6 +176,15 @@ public class KeycloakMain implements QuarkusApplication { // we should be managing this behavior more dynamically depending on the tests requirements (short/long lived) Quarkus.asyncExit(ApplicationLifecycleManager.getExitCode()); } else { + if (Boolean.getBoolean(KC_SERVER_PRINT_RUNNING)) { + BiConsumer started = (v, t) -> { + if (t == null) { + System.out.println("\n" + RUNNING_MESSAGE); + } + }; + application.getBootstrapFuture().ifPresentOrElse(future -> future.whenComplete(started), + () -> started.accept(null, null)); + } Quarkus.waitForExit(); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java index 68a64383864..48a6df24002 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java @@ -17,6 +17,7 @@ package org.keycloak.quarkus.runtime.integration.jaxrs; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import jakarta.enterprise.event.Observes; @@ -59,6 +60,8 @@ public class QuarkusKeycloakApplication extends KeycloakApplication { private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; private static final Logger logger = Logger.getLogger(QuarkusKeycloakApplication.class); + + private CompletableFuture bootstrapFuture; @Override protected String getDataDir() { @@ -77,12 +80,16 @@ public class QuarkusKeycloakApplication extends KeycloakApplication { startup(); } else { ManagedExecutor executor = Arc.container().instance(ManagedExecutor.class).get(); - CompletableFuture.runAsync(this::startup, executor).exceptionally(cause -> { + bootstrapFuture = CompletableFuture.runAsync(this::startup, executor).exceptionally(cause -> { KeycloakMain.asyncExit(1, cause); return null; }); } } + + public Optional> getBootstrapFuture() { + return Optional.ofNullable(bootstrapFuture); + } void onShutdownEvent(@Observes ShutdownEvent event) { shutdown(); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/AbstractPathDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/AbstractPathDistTest.java index 21067eb74c1..f34a52fdde2 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/AbstractPathDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/AbstractPathDistTest.java @@ -8,8 +8,8 @@ import java.util.function.Consumer; import org.keycloak.it.TestProvider; import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.quarkus.runtime.cli.command.StartDev; @@ -32,13 +32,13 @@ abstract class AbstractPathDistTest { @BeforeStartDistribution(AddCustomScriptsProvider.class) @RawDistOnly(reason = "Testing installation path handling") @Test - void testApplicationBuildAndStart(KeycloakDistribution dist) throws IOException { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testApplicationBuildAndStart(KeycloakRunner runner) throws IOException { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); Path distPath = rawDist.getDistPath(); Path newPath = distPath.getParent().resolve(getSubPath()).resolve(distPath.getFileName()); rawDist.setDistPath(newPath); try { - CLIResult result = dist.run(StartDev.NAME); + CLIResult result = runner.run(StartDev.NAME); result.assertStartedDevMode(); assertThat(result.getOutput(), containsString("Updating the configuration and installing your custom providers, if any. Please wait.")); } finally { @@ -46,11 +46,10 @@ abstract class AbstractPathDistTest { } } - public static final class AddCustomScriptsProvider implements Consumer { + public static final class AddCustomScriptsProvider implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { - RawKeycloakDistribution rawDist = distribution.unwrap(RawKeycloakDistribution.class); - rawDist.copyProvider(new ScriptProviderForTest()); + public void accept(RawKeycloakDistribution distribution) { + distribution.copyProvider(new ScriptProviderForTest()); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BootstrapAdminDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BootstrapAdminDistTest.java index 5b9f5b2b549..7f1ea8ba443 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BootstrapAdminDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BootstrapAdminDistTest.java @@ -19,9 +19,10 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; @@ -87,14 +88,15 @@ public class BootstrapAdminDistTest { @Test @WithEnvVars({"MY_SECRET", "admin123"}) - void createAndUseSericeAccountAdmin(KeycloakDistribution dist) throws Exception { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); - CLIResult result = rawDist.run("bootstrap-admin", "service", "--db=dev-file", "--client-id=admin", "--client-secret:env=MY_SECRET"); + void createAndUseSericeAccountAdmin(KeycloakRunner runner) throws Exception { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + CLIResult result = runner.run("bootstrap-admin", "service", "--db=dev-file", "--client-id=admin", "--client-secret:env=MY_SECRET"); assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput()); - rawDist.setManualStop(true); - rawDist.run("start-dev"); + runner.setStopServer(Mode.MANUAL); + result = runner.run("start-dev"); + result.assertStartedDevMode(); CLIResult adminResult = rawDist.kcadm("get", "clients", "--server", "http://localhost:8080", "--realm", "master", "--client", "admin", "--secret", "admin123"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildAndStartDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildAndStartDistTest.java index cdde270535a..6d6c3efaaa2 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildAndStartDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildAndStartDistTest.java @@ -19,10 +19,11 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; @@ -36,21 +37,20 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand. import static org.junit.jupiter.api.Assertions.assertTrue; -@WithEnvVars({"KC_CACHE", "local"}) // avoid flakey port conflicts @DistributionTest @RawDistOnly(reason = "Containers are immutable") @TestMethodOrder(OrderAnnotation.class) @Tag(DistributionTest.WIN) public class BuildAndStartDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test - void testBuildAndStart(KeycloakDistribution dist) { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testBuildAndStart(KeycloakRunner runner) { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); // start using based on the build options set via CLI - CLIResult cliResult = rawDist.run("build", "--db=dev-file"); + CLIResult cliResult = runner.run("build", "--db=dev-file"); cliResult.assertBuild(); - cliResult = rawDist.run("start", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG); + cliResult = runner.run("start", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG); cliResult.assertNoBuild(); assertTrue(cliResult.getErrorOutput().isBlank()); @@ -59,19 +59,19 @@ public class BuildAndStartDistTest { rawDist.setProperty("hostname-strict", "false"); rawDist.setProperty("http-relative-path", "/auth"); rawDist.setProperty("db", "dev-file"); - cliResult = rawDist.run("build"); + cliResult = runner.run("build"); cliResult.assertBuild(); - cliResult = rawDist.run("start", OPTIMIZED_BUILD_OPTION_LONG); + cliResult = runner.run("start", OPTIMIZED_BUILD_OPTION_LONG); cliResult.assertNoBuild(); assertTrue(cliResult.getErrorOutput().isBlank(), cliResult.getErrorOutput()); // running start without optimized flag should not cause a build - cliResult = rawDist.run("start"); + cliResult = runner.run("start"); cliResult.assertNoBuild(); assertTrue(cliResult.getErrorOutput().isBlank()); // remove the build option from conf file to force a build during start rawDist.removeProperty("http-relative-path"); - cliResult = rawDist.run("start"); + cliResult = runner.run("start"); cliResult.assertBuild(); assertTrue(cliResult.getErrorOutput().isBlank()); } @@ -79,31 +79,31 @@ public class BuildAndStartDistTest { @Test @WithEnvVars({"KEYCLOAK_ADMIN", "oldadmin123", "KEYCLOAK_ADMIN_PASSWORD", "oldadmin123"}) @Launch({"start-dev"}) - void testCreateLegacyAdmin(KeycloakDistribution dist, LaunchResult result) { - assertAdminCreation(dist, result, "oldadmin123", "oldadmin123", "oldadmin123"); + void testCreateLegacyAdmin(KeycloakRunner runner, LaunchResult result) { + assertAdminCreation(runner, result, "oldadmin123", "oldadmin123", "oldadmin123"); } @Test @WithEnvVars({"KC_BOOTSTRAP_ADMIN_USERNAME", "admin123", "KC_BOOTSTRAP_ADMIN_PASSWORD", "admin123"}) @Launch({"start-dev"}) - void testCreateAdmin(KeycloakDistribution dist, LaunchResult result) { - assertAdminCreation(dist, result, "admin123", "admin123", "admin123"); + void testCreateAdmin(KeycloakRunner runner, LaunchResult result) { + assertAdminCreation(runner, result, "admin123", "admin123", "admin123"); } @Test @WithEnvVars({"KC_BOOTSTRAP_ADMIN_USERNAME", "admin123", "KC_BOOTSTRAP_ADMIN_PASSWORD", "admin123"}) @Launch({"start-dev"}) - void testCreateDifferentAdmin(KeycloakDistribution dist, LaunchResult result) { - assertAdminCreation(dist, result, "admin123", "new-admin", "new-admin"); + void testCreateDifferentAdmin(KeycloakRunner runner, LaunchResult result) { + assertAdminCreation(runner, result, "admin123", "new-admin", "new-admin"); } - private void assertAdminCreation(KeycloakDistribution dist, LaunchResult result, String initialUsername, String nextUsername, String password) { + private void assertAdminCreation(KeycloakRunner runner, LaunchResult result, String initialUsername, String nextUsername, String password) { assertTrue(result.getOutput().contains("Created temporary admin user with username " + initialUsername), () -> "The Output:\n" + result.getOutput() + "doesn't contains the expected string."); - dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", nextUsername); - dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", password); - CLIResult cliResult = dist.run("start-dev", "--log-level=org.keycloak.services:debug"); + runner.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", nextUsername); + runner.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", password); + CLIResult cliResult = runner.run("start-dev", "--log-level=org.keycloak.services:debug"); cliResult.assertNoMessage("Added temporary admin user '"); cliResult.assertStartedDevMode(); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java index e90bd0661f5..7187fc753a1 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/BuildCommandDistTest.java @@ -22,9 +22,11 @@ import java.nio.file.Paths; import org.keycloak.config.database.Database; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.WithEnvVars; import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Test; @@ -84,20 +86,21 @@ class BuildCommandDistTest { @Test @RawDistOnly(reason = "Raw is enough and we avoid issues with including custom conf file in the container") - public void testFailInvalidOptionInConf(KeycloakDistribution distribution) { - CLIResult cliResult = distribution.run(CONFIG_FILE_LONG_NAME + "=" + Paths.get("src/test/resources/BuildCommandDistTest/keycloak.conf").toAbsolutePath().normalize(), "build"); + public void testFailInvalidOptionInConf(KeycloakRunner runner) { + CLIResult cliResult = runner.run(CONFIG_FILE_LONG_NAME + "=" + Paths.get("src/test/resources/BuildCommandDistTest/keycloak.conf").toAbsolutePath().normalize(), "build"); cliResult.assertError("Invalid value for option 'kc.db' in keycloak.conf: foo. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"); } @Test @RawDistOnly(reason = "Containers are immutable") - void testDoNotRecordRuntimeOptionsDuringBuild(KeycloakDistribution distribution) { - distribution.setProperty("db-url", "invalid"); - CLIResult cliResult = distribution.run("build"); + void testDoNotRecordRuntimeOptionsDuringBuild(KeycloakRunner runner) { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + rawDist.setProperty("db-url", "invalid"); + CLIResult cliResult = runner.run("build"); cliResult.assertBuild(); - distribution.removeProperty("db-url"); + rawDist.removeProperty("db-url"); - CLIResult result = distribution.run("start", "--hostname=mykeycloak", "--cache=local", "--http-enabled=true", OPTIMIZED_BUILD_OPTION_LONG); + CLIResult result = runner.run("start", "--hostname=mykeycloak", "--cache=local", "--http-enabled=true", OPTIMIZED_BUILD_OPTION_LONG); result.assertStarted(); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CacheEmbeddedMtlsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CacheEmbeddedMtlsDistTest.java index 93bfa9b6dd2..b4715ca9ca8 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CacheEmbeddedMtlsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CacheEmbeddedMtlsDistTest.java @@ -24,21 +24,20 @@ import java.util.Arrays; import org.keycloak.config.CachingOptions; import org.keycloak.config.Option; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.SkipRealmBootstrap; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.junit.jupiter.api.Test; -@DistributionTest -@SkipRealmBootstrap +@DistributionTest(stopServer = Mode.BEFORE_BOOTSTRAP) public class CacheEmbeddedMtlsDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - public void testCacheEmbeddedMtlsDisabled(KeycloakDistribution dist) { + public void testCacheEmbeddedMtlsDisabled(KeycloakRunner runner) { for (var option : Arrays.asList( CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE, CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE, @@ -46,24 +45,24 @@ public class CacheEmbeddedMtlsDistTest { CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD, CachingOptions.CACHE_EMBEDDED_MTLS_ROTATION )) { - var result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=false", "--%s=1".formatted(option.getKey())); + var result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=false", "--%s=1".formatted(option.getKey())); result.assertError("Disabled option: '--%s'. Available only when property 'cache-embedded-mtls-enabled' is enabled".formatted(option.getKey())); } } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - public void testCacheEmbeddedMtlsFileValidation(KeycloakDistribution dist) { - doFileAndPasswordValidation(dist, CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE, CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD); - doFileAndPasswordValidation(dist, CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE, CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD); + public void testCacheEmbeddedMtlsFileValidation(KeycloakRunner runner) { + doFileAndPasswordValidation(runner, CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE, CachingOptions.CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD); + doFileAndPasswordValidation(runner, CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE, CachingOptions.CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - public void testCacheEmbeddedMtlsFileExistsValidation(KeycloakDistribution dist) throws IOException { - var result = dist.run( + public void testCacheEmbeddedMtlsFileExistsValidation(KeycloakRunner runner) throws IOException { + var result = runner.run( "start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", @@ -75,7 +74,7 @@ public class CacheEmbeddedMtlsDistTest { result.assertError("The 'cache-embedded-mtls-key-store-file' file 'keystore.p12' does not exist"); File keystore = Util.createTempFile("key", ".p12"); - result = dist.run( + result = runner.run( "start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", @@ -87,37 +86,37 @@ public class CacheEmbeddedMtlsDistTest { result.assertError("The 'cache-embedded-mtls-trust-store-file' file 'truststore.p12' does not exist"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - public void testCacheEmbeddedMtlsValidation(KeycloakDistribution dist) { + public void testCacheEmbeddedMtlsValidation(KeycloakRunner runner) { var key = CachingOptions.CACHE_EMBEDDED_MTLS_ROTATION.getKey(); // test zero - var result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=0".formatted(key)); + var result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=0".formatted(key)); result.assertError("JGroups MTLS certificate rotation in '%s' option must positive.".formatted(key)); // test negative - result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=-1".formatted(key)); + result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=-1".formatted(key)); result.assertError("JGroups MTLS certificate rotation in '%s' option must positive.".formatted(key)); // test blank - result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=".formatted(key)); + result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=".formatted(key)); result.assertError("Invalid empty value for option '--%s'".formatted(key)); } @Test @RawDistOnly(reason = "Containers are immutable") - public void testCacheEmbeddedMtlsEnabled(KeycloakDistribution dist) { - var result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true"); + public void testCacheEmbeddedMtlsEnabled(KeycloakRunner runner) { + var result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true"); result.assertMessage("JGroups JDBC_PING discovery enabled."); result.assertMessage("JGroups Encryption enabled (mTLS)."); } - private void doFileAndPasswordValidation(KeycloakDistribution dist, Option fileOption, Option passwordOption) { - var result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=file".formatted(fileOption.getKey())); + private void doFileAndPasswordValidation(KeycloakRunner runner, Option fileOption, Option passwordOption) { + var result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=file".formatted(fileOption.getKey())); result.assertError("The option '%s' requires '%s' to be enabled.".formatted(fileOption.getKey(), passwordOption.getKey())); - result = dist.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=secret".formatted(passwordOption.getKey())); + result = runner.run("start-dev", "--cache=ispn", "--cache-embedded-mtls-enabled=true", "--%s=secret".formatted(passwordOption.getKey())); result.assertError("The option '%s' requires '%s' to be enabled.".formatted(passwordOption.getKey(), fileOption.getKey())); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java index feb896bf263..8dda42338f4 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java @@ -25,10 +25,10 @@ import java.util.function.Consumer; import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.SkipRealmBootstrap; -import org.keycloak.it.junit5.extension.Storage; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer.Mode; +import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -45,10 +45,8 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; import org.testcontainers.shaded.com.google.common.io.Files; -@DistributionTest(defaultOptions = {"--db=dev-file", "--http-enabled=true", "--hostname-strict=false"}) +@DistributionTest(localCache = false, stopServer = Mode.BEFORE_BOOTSTRAP, defaultOptions = {"--db=dev-file", "--http-enabled=true", "--hostname-strict=false"}) @RawDistOnly(reason = "Not possible to mount files using docker.") -@Storage(defaultLocalCache = false) -@SkipRealmBootstrap @Tag(DistributionTest.SMOKE) @Tag(DistributionTest.SLOW) @TestMethodOrder(OrderAnnotation.class) @@ -210,19 +208,19 @@ public class ClusterConfigDistTest { } @Test - void testAbsoluteCacheFile(KeycloakDistribution dist, @TempDir Path tempDir) throws Exception { + void testAbsoluteCacheFile(KeycloakRunner runner, @TempDir Path tempDir) throws Exception { File customCacheFile = tempDir.resolve("my-custom-cache.xml").toFile(); File missingCacheFile = tempDir.resolve("my-missing-cache.xml").toFile(); try (InputStream is = ClusterConfigDistTest.class.getResourceAsStream("/cache-ispn-custom-cache.xml")) { Files.write(is.readAllBytes(), customCacheFile); } - LaunchResult result = dist.run("start", "--cache-config-file=" + customCacheFile.getAbsolutePath()); + LaunchResult result = runner.run("start", "--cache-config-file=" + customCacheFile.getAbsolutePath()); Assertions.assertEquals(0, result.exitCode()); MatcherAssert.assertThat(result.getOutput(), Matchers.containsString(WARN_DEFAULT_CACHE_MUTATIONS)); String absolutePath = missingCacheFile.getAbsolutePath(); - result = dist.run("start", "--cache-config-file=" + absolutePath); + result = runner.run("start", "--cache-config-file=" + absolutePath); Assertions.assertEquals(2, result.exitCode()); MatcherAssert.assertThat(result.getErrorOutput(), Matchers.matchesRegex("Cache config file '" + absolutePath + "' does not exist")); } @@ -264,18 +262,18 @@ public class ClusterConfigDistTest { result.assertNoMessage("Ignoring unbounded max-count for cache 'sessions'"); } - public static class ConfigureCacheUsingAsyncEncryption implements Consumer { + public static class ConfigureCacheUsingAsyncEncryption implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/cache-ispn-asym-enc.xml", Path.of("conf", "cache-ispn-asym-enc.xml")); } } - public static class ConfigureCustomCache implements Consumer { + public static class ConfigureCustomCache implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/cache-ispn-custom-cache.xml", Path.of("conf", "cache-ispn-custom-cache.xml")); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigKeepAliveDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigKeepAliveDistTest.java index 86aceddc9db..10a6063c777 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigKeepAliveDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigKeepAliveDistTest.java @@ -27,10 +27,11 @@ import java.util.stream.Stream; import org.keycloak.config.CachingOptions; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.resource.realm.TestRealmResourceTestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import com.google.common.base.CaseFormat; import org.infinispan.commons.dataconversion.MediaType; @@ -51,12 +52,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Ryan Emerson */ -@DistributionTest(keepAlive = true, enableTls = true) +@DistributionTest(stopServer = Mode.MANUAL, enableTls = true) @RawDistOnly(reason = "Containers are immutable") public class ClusterConfigKeepAliveDistTest { @Test @TestProvider(TestRealmResourceTestProvider.class) - void testMaxCountApplied(KeycloakDistribution dist) { + void testMaxCountApplied(KeycloakRunner runner) { int maxCount = 100; Set maxCountCaches = Stream.of(CachingOptions.LOCAL_MAX_COUNT_CACHES, CachingOptions.CLUSTERED_MAX_COUNT_CACHES) .flatMap(Arrays::stream) @@ -67,7 +68,7 @@ public class ClusterConfigKeepAliveDistTest { sb.append(" --").append(CachingOptions.cacheMaxCountProperty(cache)).append("=").append(maxCount); String args = sb.toString(); - dist.run(args.split(" ")); + runner.run(args.split(" ")); for (String cache : maxCountCaches) { Configuration config = getCacheConfiguration(cache); @@ -77,19 +78,19 @@ public class ClusterConfigKeepAliveDistTest { @Test @TestProvider(TestRealmResourceTestProvider.class) - void testNumOwnersWithPersistentSessions(KeycloakDistribution dist) { - doNumOwnerTest(dist, false); + void testNumOwnersWithPersistentSessions(KeycloakRunner runner) { + doNumOwnerTest(runner, false); } @Test @TestProvider(TestRealmResourceTestProvider.class) - void testNumOwnersWithVolatileSessions(KeycloakDistribution dist) { - doNumOwnerTest(dist, true); + void testNumOwnersWithVolatileSessions(KeycloakRunner runner) { + doNumOwnerTest(runner, true); } @Test @TestProvider(TestRealmResourceTestProvider.class) - void testCheckMinimumNumOwners(KeycloakDistribution dist) { + void testCheckMinimumNumOwners(KeycloakRunner runner) { List args = new ArrayList<>(); args.add("start-dev"); args.add("--cache=ispn"); @@ -99,13 +100,13 @@ public class ClusterConfigKeepAliveDistTest { .map(cache -> CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, cache)) .map("--spi-cache-embedded--default--%s-owners=1"::formatted) .forEach(args::add); - dist.run(args); + runner.run(args); // forces the numOwner to 2 to prevent data loss. assertNumOwner(Arrays.stream(CLUSTERED_CACHE_NUM_OWNERS), 2); } - private void doNumOwnerTest(KeycloakDistribution dist, boolean volatileSessions) { + private void doNumOwnerTest(KeycloakRunner runner, boolean volatileSessions) { final int owners = 5; List args = new ArrayList<>(); args.add("start-dev"); @@ -118,7 +119,7 @@ public class ClusterConfigKeepAliveDistTest { .map(cache -> CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, cache)) .map(cache -> "--spi-cache-embedded--default--%s-owners=%s".formatted(cache, owners)) .forEach(args::add); - dist.run(args); + runner.run(args); Stream caches = Arrays.stream(CLUSTERED_CACHE_NUM_OWNERS); if (!volatileSessions) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CustomJpaEntityProviderDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CustomJpaEntityProviderDistTest.java index 91b5eb72d45..4bb8ccb691f 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CustomJpaEntityProviderDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/CustomJpaEntityProviderDistTest.java @@ -19,9 +19,9 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.TestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import com.acme.provider.legacy.jpa.entity.CustomJpaEntityProvider; import io.quarkus.test.junit.main.Launch; @@ -37,13 +37,13 @@ public class CustomJpaEntityProviderDistTest { private static final String MULTIPLE_DATASOURCES_MSG = "Multiple datasources are specified: , client-store, new-user-store, pu-without-dialect-store"; @Test - void dbKindSpecifiedInBuildTime(KeycloakDistribution dist) { - var result = dist.run("build", "--db=dev-file", "--db-kind-new-user-store=dev-mem", "--db-kind-pu-without-dialect-store=dev-mem"); + void dbKindSpecifiedInBuildTime(KeycloakRunner runner) { + var result = runner.run("build", "--db=dev-file", "--db-kind-new-user-store=dev-mem", "--db-kind-pu-without-dialect-store=dev-mem"); result.assertMessage(MULTIPLE_DATASOURCES_MSG); result.assertMessage("You have set DB kind for 'client-store' datasource via a Quarkus property. This approach is deprecated and you should use the Keycloak 'db-kind-client-store' property."); result.assertBuild(); - result = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); + result = runner.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); result.assertNoError("Detected additional named datasources. You need to explicitly set the DB kind for the datasource(s) to properly work as: db-kind-user-store"); result.assertMessage("Datasource 'client-store' was deactivated automatically because its URL is not set"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ExportDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ExportDistTest.java index bac410d23ed..8bd671ae363 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ExportDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ExportDistTest.java @@ -21,8 +21,8 @@ import java.nio.file.Path; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import org.junit.jupiter.api.Tag; @@ -36,48 +36,48 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ExportDistTest { @Test - void testExport(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build"); + void testExport(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build"); - cliResult = dist.run("export", "--realm=master", "--dir=."); + cliResult = runner.run("export", "--realm=master", "--dir=."); cliResult.assertMessage("Export of realm 'master' requested."); cliResult.assertMessage("Export finished successfully"); cliResult.assertNoMessage("Changes detected in configuration"); cliResult.assertNoMessage("Listening on: http"); - cliResult = dist.run("export", "--realm=master"); + cliResult = runner.run("export", "--realm=master"); cliResult.assertError("Must specify either --dir or --file options."); - cliResult = dist.run("export", "--file=master", "--users=skip"); + cliResult = runner.run("export", "--file=master", "--users=skip"); cliResult.assertError("Property '--users' can be used only when exporting to a directory, or value set to 'same_file' when exporting to a file."); - cliResult = dist.run("export", "--file=some-file", "--users=same_file"); + cliResult = runner.run("export", "--file=some-file", "--users=same_file"); cliResult.assertNoError("Property '--users' can be used only when exporting to a directory, or value set to 'same_file' when exporting to a file."); cliResult.assertMessage("Exporting model into file"); - cliResult = dist.run("export", "--dir=some-dir", "--users=skip"); + cliResult = runner.run("export", "--dir=some-dir", "--users=skip"); cliResult.assertMessage("Realm 'master' - data exported"); } @Test - void testExportRealmFGAPEnabled(KeycloakDistribution dist) { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testExportRealmFGAPEnabled(KeycloakRunner runner) { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); Path importDir = rawDist.getDistPath().resolve("data").resolve("import"); assertTrue(importDir.toFile().mkdirs()); - dist.copyOrReplaceFileFromClasspath("/fgap-realm.json", importDir.resolve("fgap-realm.json")); - rawDist.run("start-dev","-v", "--import-realm", "--features=admin-fine-grained-authz:v2"); + rawDist.copyOrReplaceFileFromClasspath("/fgap-realm.json", importDir.resolve("fgap-realm.json")); + runner.run("start-dev","-v", "--import-realm", "--features=admin-fine-grained-authz:v2"); rawDist.stop(); - CLIResult cliResult = rawDist.run("export", "--realm=fgap", "--dir=" + importDir.toAbsolutePath(), "--features=admin-fine-grained-authz:v2"); + CLIResult cliResult = runner.run("export", "--realm=fgap", "--dir=" + importDir.toAbsolutePath(), "--features=admin-fine-grained-authz:v2"); cliResult.assertMessage("Export of realm 'fgap' requested."); cliResult.assertMessage("Export finished successfully"); } @Test - void testExportNonExistent(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build"); + void testExportNonExistent(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build"); - cliResult = dist.run("export", "--realm=non-existent-realm", "--dir=."); + cliResult = runner.run("export", "--realm=non-existent-realm", "--dir=."); cliResult.assertMessage("realm not found by realm name 'non-existent-realm'"); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java index 28b0f667fc7..a359e7afb5d 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java @@ -7,9 +7,9 @@ import java.util.stream.Collectors; import org.keycloak.common.Profile; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.SkipRealmBootstrap; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.quarkus.runtime.cli.command.Build; import org.keycloak.quarkus.runtime.cli.command.Start; import org.keycloak.quarkus.runtime.cli.command.StartDev; @@ -27,11 +27,10 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand. import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@DistributionTest +@DistributionTest(stopServer = Mode.BEFORE_BOOTSTRAP) @RawDistOnly(reason = "Containers are immutable") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Tag(DistributionTest.SMOKE) -@SkipRealmBootstrap public class FeaturesDistTest { private static final String PREVIEW_FEATURES_EXPECTED_LOG = "Preview features enabled: " + Arrays.stream(Profile.Feature.values()) @@ -48,12 +47,12 @@ public class FeaturesDistTest { .collect(Collectors.joining(", ")); @Test - public void testEnableOnBuild(KeycloakDistribution dist) { - CLIResult cliResult = dist.run(Build.NAME, "--db=dev-file", "--features=preview"); + public void testEnableOnBuild(KeycloakRunner runner) { + CLIResult cliResult = runner.run(Build.NAME, "--db=dev-file", "--features=preview"); cliResult.assertBuild(); assertPreviewFeaturesEnabled(cliResult); - cliResult = dist.run(Start.NAME, "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG); + cliResult = runner.run(Start.NAME, "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG); assertPreviewFeaturesEnabled(cliResult); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java index 84f8edb8d41..0d2994ece6e 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java @@ -22,15 +22,16 @@ import java.nio.file.Path; import org.keycloak.crypto.fips.FIPS1402Provider; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -@DistributionTest(keepAlive = true, defaultOptions = { "--db=dev-file", "--features=fips", "--http-enabled=true", "--hostname-strict=false" }) +@DistributionTest(stopServer = Mode.MANUAL, defaultOptions = { "--db=dev-file", "--features=fips", "--http-enabled=true", "--hostname-strict=false" }) @RawDistOnly(reason = "Containers are immutable") @Tag(DistributionTest.SLOW) public class FipsDistTest { @@ -38,9 +39,9 @@ public class FipsDistTest { private static final String BCFIPS_VERSION = "BCFIPS version 2.0102"; @Test - void testFipsNonApprovedMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - CLIResult cliResult = dist.run("start"); + void testFipsNonApprovedMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + CLIResult cliResult = runner.run("start"); cliResult.assertStarted(); // Not shown as FIPS is not a preview anymore cliResult.assertMessageWasShownExactlyNumberOfTimes("Preview features enabled: fips:v1", 0); @@ -49,17 +50,17 @@ public class FipsDistTest { } @Test - void testFipsApprovedMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin"); - dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "admin"); + void testFipsApprovedMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + runner.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin"); + runner.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "admin"); - CLIResult cliResult = dist.run("start", "--fips-mode=strict"); + CLIResult cliResult = runner.run("start", "--fips-mode=strict"); cliResult.assertMessage("password must be at least 112 bits"); cliResult.assertMessage("FIPS1402Provider created: KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + FIPS1402Provider.isSystemFipsEnabled() + ")"); - dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "adminadminadmin"); - cliResult = dist.run("start", "--fips-mode=strict"); + runner.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "adminadminadmin"); + cliResult = runner.run("start", "--fips-mode=strict"); cliResult.assertStarted(); cliResult.assertMessage("Created temporary admin user with username admin"); }); @@ -72,101 +73,101 @@ public class FipsDistTest { } @Test - void testUnsupportedHttpsJksKeyStoreInStrictMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore")); - CLIResult cliResult = dist.run("start", "--fips-mode=strict"); + void testUnsupportedHttpsJksKeyStoreInStrictMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore")); + CLIResult cliResult = runner.run("start", "--fips-mode=strict"); cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); }); } @Test - void testHttpsBcfksKeyStoreInStrictMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore.bcfks", Path.of("conf", "server.keystore")); - CLIResult cliResult = dist.run("start", "--fips-mode=strict", "--https-key-store-password=passwordpassword"); + void testHttpsBcfksKeyStoreInStrictMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/server.keystore.bcfks", Path.of("conf", "server.keystore")); + CLIResult cliResult = runner.run("start", "--fips-mode=strict", "--https-key-store-password=passwordpassword"); cliResult.assertStarted(); }); } @Test - void testHttpsBcfksTrustStoreInStrictMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore.bcfks", Path.of("conf", "server.keystore")); - - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testHttpsBcfksTrustStoreInStrictMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + rawDist.copyOrReplaceFileFromClasspath("/server.keystore.bcfks", Path.of("conf", "server.keystore")); + Path truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.keystore").toAbsolutePath(); // https-trust-store-type should be automatically set to bcfks in fips-mode=strict - CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=strict", "--https-key-store-password=passwordpassword", + CLIResult cliResult = runner.run("--verbose", "start", "--fips-mode=strict", "--https-key-store-password=passwordpassword", "--https-trust-store-file=" + truststorePath, "--https-trust-store-password=passwordpassword"); cliResult.assertStarted(); }); } @Test - void testUnencryptedPkcs12TrustStoreInStrictMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { + void testUnencryptedPkcs12TrustStoreInStrictMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { String truststoreName = "keycloak-truststore.p12"; - dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName)); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + rawDist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName)); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); Path truststorePath = rawDist.getDistPath().resolve("conf").resolve(truststoreName).toAbsolutePath(); - CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=strict", "--truststore-paths=" + truststorePath); + CLIResult cliResult = runner.run("--verbose", "start", "--fips-mode=strict", "--truststore-paths=" + truststorePath); cliResult.assertStarted(); }); } @Test - void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); - CLIResult cliResult = dist.run("start", "--fips-mode=strict", "--https-key-store-password=passwordpassword"); + void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); + CLIResult cliResult = runner.run("start", "--fips-mode=strict", "--https-key-store-password=passwordpassword"); cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); }); } @Test - void testHttpsPkcs12KeyStoreInNonApprovedMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); - CLIResult cliResult = dist.run("start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword"); + void testHttpsPkcs12KeyStoreInNonApprovedMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); + CLIResult cliResult = runner.run("start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword"); cliResult.assertStarted(); }); } @Test - void testHttpsPkcs12TrustStoreInNonApprovedMode(KeycloakDistribution dist) { - runOnFipsEnabledDistribution(dist, () -> { - dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); + void testHttpsPkcs12TrustStoreInNonApprovedMode(KeycloakRunner runner) { + runOnFipsEnabledDistribution(runner, () -> { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + rawDist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); Path truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.keystore").toAbsolutePath(); - CLIResult cliResult = dist.run("--verbose", "start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword", + CLIResult cliResult = runner.run("--verbose", "start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword", "--https-trust-store-file=" + truststorePath, "--https-trust-store-password=passwordpassword"); cliResult.assertMessage("Unable to determine 'https-trust-store-type' automatically. Adjust the file extension or specify the property."); - dist.stop(); + runner.stop(); - dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.p12")); + rawDist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.p12")); - rawDist = dist.unwrap(RawKeycloakDistribution.class); + rawDist = runner.getDistribution(RawKeycloakDistribution.class); truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.p12").toAbsolutePath(); - cliResult = dist.run("--verbose", "start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword", + cliResult = runner.run("--verbose", "start", "--fips-mode=non-strict", "--https-key-store-password=passwordpassword", "--https-trust-store-file=" + truststorePath, "--https-trust-store-password=passwordpassword"); cliResult.assertStarted(); }); } - private void runOnFipsEnabledDistribution(KeycloakDistribution dist, Runnable runnable) { - installBcFips(dist); + private void runOnFipsEnabledDistribution(KeycloakRunner runner, Runnable runnable) { + installBcFips(runner); runnable.run(); } - private void installBcFips(KeycloakDistribution dist) { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + private void installBcFips(KeycloakRunner runner) { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); rawDist.copyProvider("org.bouncycastle", "bc-fips"); rawDist.copyProvider("org.bouncycastle", "bctls-fips"); rawDist.copyProvider("org.bouncycastle", "bcpkix-fips"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java index ac3afdc3f53..f91e538f7e1 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java @@ -23,7 +23,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.KeycloakRunner; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -36,7 +37,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -@DistributionTest(keepAlive = true, +@DistributionTest(stopServer = Mode.MANUAL, requestPort = 9000, containerExposedPorts = {8080, 9000}) @Tag(DistributionTest.SLOW) @@ -44,16 +45,16 @@ public class HealthDistTest { @Test @Launch({ "start-dev" }) - void testHealthEndpointNotEnabled(KeycloakDistribution distribution) { + void testHealthEndpointNotEnabled(KeycloakRunner runner) { assertThrows(IOException.class, () -> when().get("/health"), "Connection refused must be thrown"); - distribution.setRequestPort(8080); + runner.setRequestPort(8080); when().get("/health").then() .statusCode(404); } @Test @Launch({ "start-dev", "--health-enabled=true" }) - void testHealthEndpoint(KeycloakDistribution distribution) { + void testHealthEndpoint(KeycloakRunner runner) { when().get("/health").then() .statusCode(200); when().get("/health/live").then() @@ -67,15 +68,15 @@ public class HealthDistTest { .statusCode(404); // still nothing on main - distribution.setRequestPort(8080); + runner.setRequestPort(8080); when().get("/health/ready").then() .statusCode(404); } @Test @Launch({ "start-dev", "--health-enabled=true", "--http-management-health-enabled=false" }) - void testHealthEndpointOnMain(KeycloakDistribution distribution) { - distribution.setRequestPort(8080); + void testHealthEndpointOnMain(KeycloakRunner runner) { + runner.setRequestPort(8080); when().get("/health/ready").then().statusCode(200); } @@ -95,7 +96,7 @@ public class HealthDistTest { private static final String BOOTSTRAP_COMPLETED = "Bootstrap completed"; @Test - @Launch({ "start", "--health-enabled=true", "--http-enabled=true", "--hostname-strict=false" }) + @Launch({ "start", "--cache=ispn", "--health-enabled=true", "--http-enabled=true", "--hostname-strict=false" }) void testAsyncStartupEnabled(LaunchResult result) { when().get("/health/live").then() .statusCode(200); @@ -140,21 +141,21 @@ public class HealthDistTest { } @Test - void testUsingRelativePath(KeycloakDistribution distribution) { + void testUsingRelativePath(KeycloakRunner runner) { for (String relativePath : List.of("/auth", "/auth/", "auth")) { - distribution.run("start-dev", "--health-enabled=true", "--http-management-relative-path=" + relativePath); + runner.run("start-dev", "--health-enabled=true", "--http-management-relative-path=" + relativePath); if (!relativePath.endsWith("/")) { relativePath = relativePath + "/"; } when().get(relativePath + "health").then().statusCode(200); - distribution.stop(); + runner.stop(); } } @Test - void testMultipleRequests(KeycloakDistribution distribution) throws Exception { + void testMultipleRequests(KeycloakRunner runner) throws Exception { for (String relativePath : List.of("/", "/auth/", "auth")) { - distribution.run("start-dev", "--health-enabled=true", "--http-management-relative-path=" + relativePath); + runner.run("start-dev", "--health-enabled=true", "--http-management-relative-path=" + relativePath); CompletableFuture future = CompletableFuture.completedFuture(null); for (int i = 0; i < 3; i++) { @@ -173,7 +174,7 @@ public class HealthDistTest { future.get(5, TimeUnit.MINUTES); - distribution.stop(); + runner.stop(); } } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java index 4ae8cd4f562..07f2478fa83 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HelpCommandDistTest.java @@ -23,9 +23,9 @@ import java.util.List; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.cli.command.BootstrapAdmin; import org.keycloak.quarkus.runtime.cli.command.BootstrapAdminService; @@ -48,7 +48,7 @@ import org.junit.jupiter.api.Test; import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG; @WithEnvVars({"KEYCLOAK_COMMAND_MODE", "ALL", "KEYCLOAK_HELP_WIDTH", "80"}) -@DistributionTest +@DistributionTest(localCache = false) @RawDistOnly(reason = "Verifying the help message output doesn't need long spin-up of docker dist tests.") @Tag(DistributionTest.WIN) public class HelpCommandDistTest { @@ -188,7 +188,7 @@ public class HelpCommandDistTest { } @Test - public void testHelpDoesNotStartReAugJvm(KeycloakDistribution dist) { + public void testHelpDoesNotStartReAugJvm(KeycloakRunner runner) { for (String helpCmd : List.of("-h", "--help", "--help-all")) { for (String cmd : List.of("", "start", "start-dev", "build")) { String debugOption = "--debug"; @@ -197,7 +197,7 @@ public class HelpCommandDistTest { debugOption = "--debug=8787"; } - CLIResult run = dist.run(debugOption, cmd, helpCmd); + CLIResult run = runner.run(debugOption, cmd, helpCmd); assertSingleJvmStarted(run); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameV2DistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameV2DistTest.java index 1de0de01601..6d45bfa18b9 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameV2DistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameV2DistTest.java @@ -20,7 +20,7 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.SkipRealmBootstrap; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -29,9 +29,8 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@DistributionTest +@DistributionTest(stopServer = Mode.BEFORE_BOOTSTRAP) @RawDistOnly(reason = "Containers are immutable") -@SkipRealmBootstrap public class HostnameV2DistTest { @Test @Launch({"start", "--db=dev-file", "--http-enabled=true"}) diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java index 36dd4f7c278..8df1495417a 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java @@ -26,10 +26,11 @@ import java.util.concurrent.Executors; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.resource.realm.TestRealmResourceTestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; @@ -44,13 +45,13 @@ import static org.hamcrest.MatcherAssert.assertThat; /** * @author Vaclav Muzikar */ -@DistributionTest(keepAlive = true, enableTls = true) +@DistributionTest(stopServer = Mode.MANUAL, enableTls = true) @RawDistOnly(reason = "Containers are immutable") public class HttpDistTest { @Test @TestProvider(TestRealmResourceTestProvider.class) - public void maxQueuedRequestsTest(KeycloakDistribution dist) { - dist.run("start-dev", "--http-max-queued-requests=1", "--http-pool-max-threads=1"); + public void maxQueuedRequestsTest(KeycloakRunner runner) { + runner.run("start-dev", "--http-max-queued-requests=1", "--http-pool-max-threads=1"); ExecutorService executor = Executors.newFixedThreadPool(5); try { @@ -93,24 +94,24 @@ public class HttpDistTest { } @Test - public void httpStoreTypeValidation(KeycloakDistribution dist) { - CLIResult result = dist.run("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false"); + public void httpStoreTypeValidation(KeycloakRunner runner) { + CLIResult result = runner.run("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false"); result.assertExitCode(-1); result.assertMessage("ERROR: Unable to determine 'https-key-store-type' automatically. Adjust the file extension or specify the property"); - result = dist.run("start", "--https-trust-store-file=not-there.ks", "--hostname-strict=false"); + result = runner.run("start", "--https-trust-store-file=not-there.ks", "--hostname-strict=false"); result.assertExitCode(-1); result.assertMessage("ERROR: Unable to determine 'https-trust-store-type' automatically. Adjust the file extension or specify the property"); - result = dist.run("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk"); + result = runner.run("start", "--https-key-store-file=not-there.ks", "--hostname-strict=false", "--https-key-store-type=jdk"); result.assertExitCode(-1); result.assertMessage("ERROR: Failed to load 'https-*' material: NoSuchFileException not-there.ks"); - dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.p12")); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + rawDist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.p12")); Path truststorePath = rawDist.getDistPath().resolve("conf").resolve("server.p12").toAbsolutePath(); - result = dist.run("start", "--https-trust-store-file=" + truststorePath, "--hostname-strict=false"); + result = runner.run("start", "--https-trust-store-file=" + truststorePath, "--hostname-strict=false"); result.assertExitCode(-1); result.assertMessage("ERROR: No trust store password provided"); } @@ -123,9 +124,9 @@ public class HttpDistTest { } @Test - public void testShutdownParametersNegativeValue(KeycloakDistribution dist) { + public void testShutdownParametersNegativeValue(KeycloakRunner runner) { // Test that negative values are rejected - CLIResult result = dist.run("start-dev", "--shutdown-delay=-1s"); + CLIResult result = runner.run("start-dev", "--shutdown-delay=-1s"); result.assertError("Invalid duration '-1s'. Duration must be zero or positive"); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java index 4b4ed1f4a4f..8fe53c1ba23 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java @@ -25,8 +25,8 @@ import java.util.function.Consumer; import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.deployment.util.FileUtil; @@ -50,15 +50,15 @@ public class ImportAtStartupDistTest { @Test @BeforeStartDistribution(CreateRealmConfigurationFile.class) - void testMultipleImport(KeycloakDistribution dist) throws IOException { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testMultipleImport(KeycloakRunner runner) throws IOException { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); Path dir = rawDist.getDistPath().resolve("data").resolve("import"); // add another realm Files.write(dir.resolve("realm2.json"), Files.readAllLines(dir.resolve("realm.json")).stream() .map(s -> s.replace("quickstart-realm", "other-realm")).toList()); - CLIResult cliResult = dist.run("start-dev", "--import-realm"); + CLIResult cliResult = runner.run("start-dev", "--import-realm"); cliResult.assertMessage("Realm 'quickstart-realm' imported"); cliResult.assertMessage("Realm 'other-realm' imported"); } @@ -80,13 +80,13 @@ public class ImportAtStartupDistTest { @Test @BeforeStartDistribution(CreateRealmConfigurationFile.class) - void testImportFromFileCreatedByExportAllRealms(KeycloakDistribution dist) throws IOException { - dist.run("start-dev", "--import-realm"); - dist.run("--profile=dev", "export", "--file=../data/import/realm.json", "--verbose"); + void testImportFromFileCreatedByExportAllRealms(KeycloakRunner runner) throws IOException { + runner.run("start-dev", "--import-realm"); + runner.run("--profile=dev", "export", "--file=../data/import/realm.json", "--verbose"); - dist.unwrap(RawKeycloakDistribution.class).resetH2Dir(); + runner.getDistribution(RawKeycloakDistribution.class).resetH2Dir(); - CLIResult result = dist.run("start-dev", "--import-realm"); + CLIResult result = runner.run("start-dev", "--import-realm"); result.assertMessage("Realm 'quickstart-realm' imported"); result.assertMessage("Realm 'master' imported"); result.assertNoMessage("Realm 'master' already exists. Import skipped"); @@ -94,56 +94,53 @@ public class ImportAtStartupDistTest { @Test @BeforeStartDistribution(CreateRealmConfigurationFile.class) - void testImportFromFileCreatedByExportSingleRealm(KeycloakDistribution dist) throws IOException { - dist.run("start-dev", "--import-realm"); - dist.run("--profile=dev", "export", "--realm=quickstart-realm", "--file=../data/import/realm.json"); + void testImportFromFileCreatedByExportSingleRealm(KeycloakRunner runner) throws IOException { + runner.run("start-dev", "--import-realm"); + runner.run("--profile=dev", "export", "--realm=quickstart-realm", "--file=../data/import/realm.json"); - dist.unwrap(RawKeycloakDistribution.class).resetH2Dir(); + runner.getDistribution(RawKeycloakDistribution.class).resetH2Dir(); - CLIResult result = dist.run("start-dev", "--import-realm"); + CLIResult result = runner.run("start-dev", "--import-realm"); result.assertMessage("Realm 'quickstart-realm' imported"); result.assertNoMessage("Not importing realm master from file"); } @Test @BeforeStartDistribution(CreateRealmConfigurationFile.class) - void testImportFromDirCreatedByExport(KeycloakDistribution dist) throws IOException { - dist.run("start-dev", "--import-realm"); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); + void testImportFromDirCreatedByExport(KeycloakRunner runner) throws IOException { + runner.run("start-dev", "--import-realm"); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); FileUtil.deleteDirectory(rawDist.getDistPath().resolve("data").resolve("import").toAbsolutePath()); - dist.run("--profile=dev", "export", "--dir=../data/import"); + runner.run("--profile=dev", "export", "--dir=../data/import"); - dist.unwrap(RawKeycloakDistribution.class).resetH2Dir(); + runner.getDistribution(RawKeycloakDistribution.class).resetH2Dir(); - CLIResult result = dist.run("start-dev", "--import-realm"); + CLIResult result = runner.run("start-dev", "--import-realm"); result.assertMessage("Realm 'quickstart-realm' imported"); result.assertNoMessage("Not importing realm master from file"); } - public static class CreateRealmConfigurationFile implements Consumer { + public static class CreateRealmConfigurationFile implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm.json")); } } - public static class CreateRealmConfigurationFileAndDir implements Consumer { - + public static class CreateRealmConfigurationFileAndDir implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm.json")); - RawKeycloakDistribution rawDist = distribution.unwrap(RawKeycloakDistribution.class); - - rawDist.getDistPath().resolve("data").resolve("import").resolve("sub-dir").toFile().mkdirs(); + distribution.getDistPath().resolve("data").resolve("import").resolve("sub-dir").toFile().mkdirs(); } } - public static class CreateRealmConfigurationFileWithUnsupportedExtension implements Consumer { + public static class CreateRealmConfigurationFileWithUnsupportedExtension implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm")); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java index 91e69a3a27e..8adb24bc638 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java @@ -23,8 +23,10 @@ import java.io.IOException; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.representations.idm.RealmRepresentation; @@ -46,12 +48,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ImportDistTest { @Test - void testImport(KeycloakDistribution dist) throws IOException { - CLIResult cliResult = dist.run("build"); + void testImport(KeycloakRunner runner) throws IOException { + CLIResult cliResult = runner.run("build"); File dir = new File("target"); - cliResult = dist.run("export", "--realm=master", "--dir=" + dir.getAbsolutePath()); + cliResult = runner.run("export", "--realm=master", "--dir=" + dir.getAbsolutePath()); cliResult.assertMessage("Export of realm 'master' requested."); cliResult.assertMessage("Export finished successfully"); cliResult.assertNoMessage("local_addr"); @@ -63,31 +65,31 @@ public class ImportDistTest { node.put("enabled", "${REALM_ENABLED}"); mapper.writer().writeValue(file, node); - dist.setEnvVar("REALM_ENABLED", "true"); - dist.setEnvVar("KC_HOSTNAME_STRICT", "false"); - dist.setEnvVar("KC_CACHE", "ispn"); - cliResult = dist.run("import", "--dir=" + dir.getAbsolutePath()); + runner.setEnvVar("REALM_ENABLED", "true"); + runner.setEnvVar("KC_HOSTNAME_STRICT", "false"); + runner.setEnvVar("KC_CACHE", "ispn"); + cliResult = runner.run("import", "--dir=" + dir.getAbsolutePath()); cliResult.assertMessage("Realm 'master' imported"); cliResult.assertMessage("Import finished successfully"); cliResult.assertNoMessage("Changes detected in configuration"); cliResult.assertNoMessage("Listening on: http"); cliResult.assertNoMessage("local_addr"); - cliResult = dist.run("import"); + cliResult = runner.run("import"); cliResult.assertError("Must specify either --dir or --file options."); } + @StopServer(Mode.MANUAL) @Test - void testImportNewRealm(KeycloakDistribution dist) throws IOException { - dist.setEnvVar("MY_SECRET", "admin123"); + void testImportNewRealm(KeycloakRunner runner) throws IOException { + runner.setEnvVar("MY_SECRET", "admin123"); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); - CLIResult result = rawDist.run("bootstrap-admin", "service", "--db=dev-file", "--client-id=admin", "--client-secret:env=MY_SECRET"); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + CLIResult result = rawDist.kc("bootstrap-admin", "service", "--db=dev-file", "--client-id=admin", "--client-secret:env=MY_SECRET"); assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput()); - rawDist.setManualStop(true); - result = rawDist.run("start-dev", "--db-url-properties=;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=TRUE"); + result = runner.run("start-dev", "--db-url-properties=;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=TRUE"); File file = new File("target/realm.json"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java index e03f7022b81..bf9b530dfa7 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java @@ -23,6 +23,7 @@ import java.net.ConnectException; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Test; @@ -32,7 +33,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -@DistributionTest(keepAlive = true, defaultOptions = { "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"}) +@DistributionTest(stopServer = Mode.MANUAL, defaultOptions = { "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"}) @RawDistOnly(reason = "Containers are immutable") public class IpStackDistTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java index cd036e4f9bc..353221ed877 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java @@ -18,8 +18,8 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithEnvVars; import io.quarkus.test.junit.main.Launch; @@ -34,8 +34,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.matchesPattern; -@DryRun -@DistributionTest +@DistributionTest(stopServer = Mode.BEFORE_QUARKUS) @RawDistOnly(reason = "No need to test script again on container") @WithEnvVars({"PRINT_ENV", "true"}) @Tag(DistributionTest.WIN) diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JaxRsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JaxRsDistTest.java index d3cd73dc82f..e44b75bc913 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JaxRsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JaxRsDistTest.java @@ -22,9 +22,10 @@ import java.util.concurrent.TimeUnit; import org.keycloak.it.jaxrs.filter.TestFilterTestProvider; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import org.awaitility.Awaitility; import org.junit.jupiter.api.Tag; @@ -33,15 +34,15 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; import static org.junit.jupiter.api.Assertions.assertEquals; -@DistributionTest(keepAlive = true) +@DistributionTest(stopServer = Mode.MANUAL) @RawDistOnly(reason = "Containers are immutable") @Tag(DistributionTest.SMOKE) public class JaxRsDistTest { @Test @TestProvider(TestFilterTestProvider.class) - public void requestFilterTest(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev"); + public void requestFilterTest(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev"); cliResult.assertStartedDevMode(); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LiquibaseDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LiquibaseDistTest.java index 799b6a5fd15..20797367636 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LiquibaseDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LiquibaseDistTest.java @@ -3,8 +3,8 @@ package org.keycloak.it.cli.dist; import java.io.IOException; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.deployment.util.FileUtil; @@ -17,16 +17,16 @@ import org.junit.jupiter.api.Test; public class LiquibaseDistTest { @Test - public void dbLockMultipleExecution(KeycloakDistribution distribution) throws IOException { + public void dbLockMultipleExecution(KeycloakRunner runner) throws IOException { // force a full db initialization - RawKeycloakDistribution rawDist = distribution.unwrap(RawKeycloakDistribution.class); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); FileUtil.deleteDirectory(rawDist.getDistPath().resolve("data").resolve("h2").toAbsolutePath()); - var result = distribution.run("start-dev", "--log-level=org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService:trace"); + var result = runner.run("start-dev", "--log-level=org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService:trace"); result.assertMessage("Initialize Database Lock Table, current locks []"); result.assertMessage("Initialized record in the database lock table"); // the code block in the CustomLockService should not be executed for the second time - result = distribution.run("start-dev", "--log-level=org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService:trace"); + result = runner.run("start-dev", "--log-level=org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService:trace"); result.assertNoMessage("Initialize Database Lock Table, current locks"); result.assertNoMessage("Initialized record in the database lock table"); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java index fff96c44440..61686f631d4 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/LoggingDistTest.java @@ -32,9 +32,10 @@ import org.keycloak.connections.httpclient.HttpClientBuilder; import org.keycloak.cookie.CookieType; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.utils.RawDistRootPath; import org.keycloak.it.utils.RawKeycloakDistribution; @@ -61,7 +62,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@DistributionTest(keepAlive = true) +@DistributionTest(stopServer = Mode.MANUAL) @RawDistOnly(reason = "Too verbose for docker and enough to check raw dist") @Tag(DistributionTest.SLOW) public class LoggingDistTest { @@ -107,18 +108,18 @@ public class LoggingDistTest { } @Test - void testJsonFormatApplied(KeycloakDistribution dist) throws IOException { - dist.unwrap(RawKeycloakDistribution.class).resetH2Dir(); - CLIResult cliResult = dist.run("start-dev", "--log-console-output=json"); + void testJsonFormatApplied(KeycloakRunner runner) throws IOException { + runner.getDistribution(RawKeycloakDistribution.class).resetH2Dir(); + CLIResult cliResult = runner.run("start-dev", "--log-console-output=json"); cliResult.assertJsonLogDefaultsApplied(); cliResult.assertStartedDevMode(); assertFalse(cliResult.getOutput().contains("\"loggerName\":\"liquibase.servicelocator\",\"level\":\"FINE\"")); } @Test - void testLogLevelSettingsAppliedWhenJsonEnabled(KeycloakDistribution dist) throws IOException { - dist.unwrap(RawKeycloakDistribution.class).resetH2Dir(); - CLIResult cliResult = dist.run("start-dev", "--log-level=off,org.keycloak:debug,liquibase:debug", "--log-console-output=json"); + void testLogLevelSettingsAppliedWhenJsonEnabled(KeycloakRunner runner) throws IOException { + runner.getDistribution(RawKeycloakDistribution.class).resetH2Dir(); + CLIResult cliResult = runner.run("start-dev", "--log-level=off,org.keycloak:debug,liquibase:debug", "--log-console-output=json"); assertFalse(cliResult.getOutput().contains("\"loggerName\":\"io.quarkus\",\"level\":\"INFO\")")); assertTrue(cliResult.getOutput().contains("\"loggerName\":\"org.keycloak.services.resources.KeycloakApplication\",\"level\":\"DEBUG\"")); assertTrue(cliResult.getOutput().contains("\"loggerName\":\"liquibase.servicelocator\",\"level\":\"FINE\"")); @@ -147,16 +148,16 @@ public class LoggingDistTest { } @Test - void failUnknownHandlersInConfFile(KeycloakDistribution dist) { - dist.copyOrReplaceFileFromClasspath("/logging/keycloak.conf", Paths.get("conf", "keycloak.conf")); - CLIResult cliResult = dist.run("start-dev"); + void failUnknownHandlersInConfFile(KeycloakRunner runner) { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/logging/keycloak.conf", Paths.get("conf", "keycloak.conf")); + CLIResult cliResult = runner.run("start-dev"); cliResult.assertError("Invalid value for option 'kc.log' in keycloak.conf: foo. Expected values are: console, file, syslog"); } @Test - void failEmptyLogErrorFromConfFileError(KeycloakDistribution dist) { - dist.copyOrReplaceFileFromClasspath("/logging/emptylog.conf", Paths.get("conf", "emptylog.conf")); - CLIResult cliResult = dist.run(CONFIG_FILE_LONG_NAME+"=../conf/emptylog.conf", "start-dev"); + void failEmptyLogErrorFromConfFileError(KeycloakRunner runner) { + runner.getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/logging/emptylog.conf", Paths.get("conf", "emptylog.conf")); + CLIResult cliResult = runner.run(CONFIG_FILE_LONG_NAME+"=../conf/emptylog.conf", "start-dev"); cliResult.assertError("Invalid value for option 'kc.log' in emptylog.conf: . Expected values are: console, file, syslog"); } @@ -182,14 +183,14 @@ public class LoggingDistTest { @Test @Launch({"start-dev", "--log-console-level=wrong"}) - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) void wrongLevelForHandlers(CLIResult cliResult) { cliResult.assertError("Invalid value for option '--log-console-level': wrong. Expected values are (case insensitive): off, fatal, error, warn, info, debug, trace, all"); } @Test @Launch({"start-dev", "--log-level-org.keycloak=wrong"}) - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) void wrongLevelForCategory(CLIResult cliResult) { cliResult.assertError("Invalid log level: wrong. Possible values are: warn, trace, debug, error, fatal, info."); } @@ -324,7 +325,7 @@ public class LoggingDistTest { // HTTP Access log @Test @Launch({"start-dev", "--http-access-log-enabled=true", "--http-access-log-pattern='%A %{METHOD} %{REQUEST_URL} %{i,User-Agent}'", "--http-access-log-exclude=/realms/master/clients/.*"}) - void httpAccessLogNotNamedPattern(CLIResult cliResult, KeycloakDistribution dist, RawDistRootPath path) { + void httpAccessLogNotNamedPattern(CLIResult cliResult, KeycloakRunner runner, RawDistRootPath path) { when().get("http://127.0.0.1:8080/realms/master/.well-known/openid-configuration").then() .statusCode(200); Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted( @@ -337,7 +338,7 @@ public class LoggingDistTest { cliResult.assertNoMessage("127.0.0.1 GET /realms/master/clients/account/redirect"); // file - CLIResult fileCliResult = dist.run("start-dev", "--http-access-log-enabled=true", "--http-access-log-file-enabled=true", "--http-access-log-pattern='%A %{METHOD} %{REQUEST_URL} %{i,User-Agent}'", "--http-access-log-exclude=/realms/master/clients/.*"); + CLIResult fileCliResult = runner.run("start-dev", "--http-access-log-enabled=true", "--http-access-log-file-enabled=true", "--http-access-log-pattern='%A %{METHOD} %{REQUEST_URL} %{i,User-Agent}'", "--http-access-log-exclude=/realms/master/clients/.*"); fileCliResult.assertStartedDevMode(); when().get("http://127.0.0.1:8080/realms/master/.well-known/openid-configuration").then() .statusCode(200); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementDistTest.java index ceb793ec6a8..98e5d556501 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementDistTest.java @@ -21,7 +21,8 @@ import java.io.IOException; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionType; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.KeycloakRunner; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -37,7 +38,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; -@DistributionTest(keepAlive = true, +@DistributionTest(stopServer = Mode.MANUAL, defaultOptions = {"--db=dev-file", "--health-enabled=true", "--metrics-enabled=true"}, requestPort = 9000, containerExposedPorts = {9000, 8080, 9005}) @@ -57,14 +58,14 @@ public class ManagementDistTest { @Test @Order(2) @Launch({"start-dev", "--legacy-observability-interface=true"}) - void testManagementDisabled(LaunchResult result, KeycloakDistribution distribution) { + void testManagementDisabled(LaunchResult result, KeycloakRunner runner) { CLIResult cliResult = (CLIResult) result; cliResult.assertNoMessage("Management interface listening on"); assertThrows(IOException.class, () -> when().get("/"), "Connection refused must be thrown"); assertThrows(IOException.class, () -> when().get("/health"), "Connection refused must be thrown"); - distribution.setRequestPort(8080); + runner.setRequestPort(8080); when().get("/health").then() .statusCode(200); @@ -124,11 +125,11 @@ public class ManagementDistTest { @Test @Launch({"start-dev", "--http-management-port=9005"}) - void testManagementDifferentPort(LaunchResult result, KeycloakDistribution distribution) { + void testManagementDifferentPort(LaunchResult result, KeycloakRunner runner) { CLIResult cliResult = (CLIResult) result; cliResult.assertMessage("Management interface listening on http://0.0.0.0:9005"); - distribution.setRequestPort(9005); + runner.setRequestPort(9005); when().get("/").then() .statusCode(200) @@ -158,10 +159,10 @@ public class ManagementDistTest { @Test @Launch({"start-dev", "--http-relative-path=/auth", "--http-management-relative-path=/management"}) - void testManagementRootRedirects(LaunchResult result, KeycloakDistribution distribution) { + void testManagementRootRedirects(LaunchResult result, KeycloakRunner runner) { assertRelativePath(result, "/management"); - distribution.setRequestPort(8080); + runner.setRequestPort(8080); given().redirects().follow(false).when().get("/").then().statusCode(302).header("Location", is("/auth")); when().get("/").then().statusCode(200).body(containsString("Welcome to Keycloak")); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementHttpsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementHttpsDistTest.java index 9985b2b6173..779673392b2 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementHttpsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementHttpsDistTest.java @@ -19,6 +19,7 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -30,7 +31,7 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; import static org.hamcrest.CoreMatchers.containsString; -@DistributionTest(keepAlive = true, +@DistributionTest(stopServer = Mode.MANUAL, enableTls = true, defaultOptions = {"--db=dev-file", "--health-enabled=true", "--metrics-enabled=true"}, requestPort = 9000) diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementOffDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementOffDistTest.java index 862bed94d77..38c9f009e4f 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementOffDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ManagementOffDistTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -28,7 +29,7 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; import static org.junit.jupiter.api.Assertions.assertThrows; -@DistributionTest(keepAlive = true, +@DistributionTest(stopServer = Mode.MANUAL, requestPort = 9000, containerExposedPorts = {9000, 8080}) public class ManagementOffDistTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java index 46343136111..5447fba5270 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/MetricsDistTest.java @@ -24,7 +24,8 @@ import java.util.concurrent.TimeUnit; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.KeycloakRunner; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Tag; @@ -37,7 +38,7 @@ import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertThrows; -@DistributionTest(keepAlive = true, +@DistributionTest(stopServer = Mode.MANUAL, requestPort = 9000, containerExposedPorts = {8080, 9000}) @Tag(DistributionTest.SLOW) @@ -45,11 +46,11 @@ public class MetricsDistTest { @Test @Launch({ "start-dev" }) - void testMetricsEndpointNotEnabled(KeycloakDistribution distribution) { + void testMetricsEndpointNotEnabled(KeycloakRunner runner) { assertThrows(IOException.class, () -> when().get("/metrics"), "Connection refused must be thrown"); assertThrows(IOException.class, () -> when().get("/q/metrics"), "Connection refused must be thrown"); - distribution.setRequestPort(8080); + runner.setRequestPort(8080); when().get("/metrics").then() .statusCode(404); @@ -138,10 +139,10 @@ public class MetricsDistTest { @Test @Launch({ "start-dev", "--metrics-enabled=true", "--tracing-enabled=true" }) - void testMetricsEndpointWithCacheMetricsHistogramsAndExemplars(KeycloakDistribution distribution) { - runClientCredentialGrantWithUnknownClientId(distribution); + void testMetricsEndpointWithCacheMetricsHistogramsAndExemplars(KeycloakRunner runner) { + runClientCredentialGrantWithUnknownClientId(runner); - distribution.setRequestPort(9000); + runner.setRequestPort(9000); // Exemplars are only present when metrics and traces are enabled given().accept("application/openmetrics-text; version=1.0.0; charset=utf-8"); when().get("/metrics").then() @@ -153,10 +154,10 @@ public class MetricsDistTest { @Test @Launch({ "start-dev", "--metrics-enabled=true", "--features=user-event-metrics", "--event-metrics-user-enabled=true" }) - void testMetricsEndpointWithUserEventMetrics(KeycloakDistribution distribution) { - runClientCredentialGrantWithUnknownClientId(distribution); + void testMetricsEndpointWithUserEventMetrics(KeycloakRunner runner) { + runClientCredentialGrantWithUnknownClientId(runner); - distribution.setRequestPort(9000); + runner.setRequestPort(9000); when().get("/metrics").then() .statusCode(200) .body(containsString("keycloak_user_events_total{error=\"client_not_found\",event=\"client_login\",realm=\"master\"}")); @@ -165,18 +166,18 @@ public class MetricsDistTest { @Test @Launch({ "start-dev", "--metrics-enabled=true", "--features=user-event-metrics", "--event-metrics-user-enabled=false" }) - void testMetricsEndpointWithoutUserEventMetrics(KeycloakDistribution distribution) { - runClientCredentialGrantWithUnknownClientId(distribution); + void testMetricsEndpointWithoutUserEventMetrics(KeycloakRunner runner) { + runClientCredentialGrantWithUnknownClientId(runner); - distribution.setRequestPort(9000); + runner.setRequestPort(9000); when().get("/metrics").then() .statusCode(200) .body(not(containsString("keycloak_user_events_total{error=\"client_not_found\",event=\"client_login\",realm=\"master\"}"))); } - private static void runClientCredentialGrantWithUnknownClientId(KeycloakDistribution distribution) { - distribution.setRequestPort(8080); + private static void runClientCredentialGrantWithUnknownClientId(KeycloakRunner runner) { + runner.setRequestPort(8080); given().formParam("grant_type", "client_credentials") .formParam("client_id", "unknown") .formParam("client_secret", "unknown"). @@ -186,21 +187,21 @@ public class MetricsDistTest { } @Test - void testUsingRelativePath(KeycloakDistribution distribution) { + void testUsingRelativePath(KeycloakRunner runner) { for (String relativePath : List.of("/auth", "/auth/", "auth")) { - distribution.run("start-dev", "--metrics-enabled=true", "--http-management-relative-path=" + relativePath); + runner.run("start-dev", "--metrics-enabled=true", "--http-management-relative-path=" + relativePath); if (!relativePath.endsWith("/")) { relativePath = relativePath + "/"; } when().get(relativePath + "metrics").then().statusCode(200); - distribution.stop(); + runner.stop(); } } @Test - void testMultipleRequests(KeycloakDistribution distribution) throws Exception { + void testMultipleRequests(KeycloakRunner runner) throws Exception { for (String relativePath : List.of("/", "/auth/", "auth")) { - distribution.run("start-dev", "--metrics-enabled=true", "--http-management-relative-path=" + relativePath); + runner.run("start-dev", "--metrics-enabled=true", "--http-management-relative-path=" + relativePath); CompletableFuture future = CompletableFuture.completedFuture(null); for (int i = 0; i < 3; i++) { @@ -222,7 +223,7 @@ public class MetricsDistTest { future.get(5, TimeUnit.MINUTES); - distribution.stop(); + runner.stop(); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OpenApiDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OpenApiDistTest.java index 59499f2f9aa..9f9fdbc046f 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OpenApiDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OpenApiDistTest.java @@ -21,7 +21,9 @@ import java.io.IOException; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.utils.KeycloakDistribution; import io.quarkus.test.junit.main.Launch; @@ -36,7 +38,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertThrows; -@DistributionTest(keepAlive = true, requestPort = 9000, containerExposedPorts = {8080, 9000}) +@DistributionTest(stopServer = Mode.MANUAL, requestPort = 9000, containerExposedPorts = {8080, 9000}) @Tag(DistributionTest.SLOW) public class OpenApiDistTest { @@ -48,11 +50,11 @@ public class OpenApiDistTest { @Test @Launch({"start-dev", FEATURES_OPTION}) - void testOpenApiEndpointNotEnabled(KeycloakDistribution distribution) { + void testOpenApiEndpointNotEnabled(KeycloakRunner runner) { assertThrows(IOException.class, () -> when().get(OPENAPI_ENDPOINT), "Connection refused must be thrown"); assertThrows(IOException.class, () -> when().get(OPENAPI_UI_ENDPOINT), "Connection refused must be thrown"); - distribution.setRequestPort(8080); + runner.setRequestPort(8080); when().get(OPENAPI_ENDPOINT).then() .statusCode(404); @@ -76,20 +78,20 @@ public class OpenApiDistTest { .statusCode(200); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev", "--openapi-ui-enabled=true", FEATURES_OPTION}) void testOpenApiUiFailsWhenOpenApiIsNotEnabled(CLIResult cliResult) { cliResult.assertError("Disabled option: '--openapi-ui-enabled'. Available only when OpenAPI Endpoint is enabled"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test - void testOpenApiRequiresFeatures(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev", "--openapi-enabled=true", "--features=openapi"); + void testOpenApiRequiresFeatures(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev", "--openapi-enabled=true", "--features=openapi"); cliResult.assertError("ERROR: Feature openapi depends on disabled feature client-admin-api-v2"); - cliResult = dist.run("start-dev", "--openapi-enabled=true", "--features=client-admin-api:v2"); + cliResult = runner.run("start-dev", "--openapi-enabled=true", "--features=client-admin-api:v2"); cliResult.assertError("Disabled option: '--openapi-enabled'. Available only when OpenAPI feature is enabled"); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OptionsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OptionsDistTest.java index ec1e2be9639..3e438de0032 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OptionsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/OptionsDistTest.java @@ -21,10 +21,11 @@ import java.nio.file.Paths; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.MethodOrderer; @@ -38,7 +39,7 @@ import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_LONG_NAM @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class OptionsDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(1) @Launch({"build", "--db=invalid"}) @@ -46,7 +47,7 @@ public class OptionsDistTest { result.assertError("Invalid value for option '--db': invalid. Expected values are: dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(2) @Launch({"start", "--db=dev-file", "--test=invalid"}) @@ -54,7 +55,7 @@ public class OptionsDistTest { result.assertError("Unknown option: '--test'"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(3) @Launch({"start", "--db=dev-file", "--log=console", "--log-file-output=json", "--http-enabled=true", "--hostname-strict=false"}) @@ -63,7 +64,7 @@ public class OptionsDistTest { result.assertError("Possible solutions: --log-console-output, --log-level, --log, --log-console-level, --log-console-format, --log-console-async, --log-async"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(4) @Launch({"start", "--db=dev-file", "--log=file", "--log-file-output=json", "--http-enabled=true", "--hostname-strict=false"}) @@ -82,13 +83,13 @@ public class OptionsDistTest { cliResult.assertStarted(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(6) @RawDistOnly(reason = "Raw is enough and we avoid issues with including custom conf file in the container") - public void testExpressionsInConfigFile(KeycloakDistribution distribution) { - distribution.setEnvVar("MY_LOG_LEVEL", "warn"); - CLIResult result = distribution.run(CONFIG_FILE_LONG_NAME + "=" + Paths.get("src/test/resources/OptionsDistTest/keycloak.conf").toAbsolutePath().normalize(), "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"); + public void testExpressionsInConfigFile(KeycloakRunner runner) { + runner.setEnvVar("MY_LOG_LEVEL", "warn"); + CLIResult result = runner.run(CONFIG_FILE_LONG_NAME + "=" + Paths.get("src/test/resources/OptionsDistTest/keycloak.conf").toAbsolutePath().normalize(), "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"); result.assertNoMessage("INFO [io.quarkus]"); result.assertNoMessage("Listening on:"); @@ -100,7 +101,7 @@ public class OptionsDistTest { // Start-dev should be executed as last tests - build is done for development mode - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(7) @Launch({"start-dev", "--test=invalid"}) @@ -108,7 +109,7 @@ public class OptionsDistTest { result.assertError("Unknown option: '--test'"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(8) @Launch({"start-dev", "--log=console", "--log-file-output=json"}) @@ -117,7 +118,7 @@ public class OptionsDistTest { result.assertError("Possible solutions: --log-console-output, --log-level, --log, --log-console-level, --log-console-format, --log-console-async, --log-async"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(9) @Launch({"start-dev", "--log=file", "--log-file-output=json", "--log-console-color=true"}) @@ -127,7 +128,7 @@ public class OptionsDistTest { result.assertError("Possible solutions: --log, --log-file, --log-level, --log-file-level, --log-file-json-format, --log-file-format, --log-file-async"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(10) @Launch({"start-dev", "--cache-remote-host=localhost"}) @@ -135,7 +136,7 @@ public class OptionsDistTest { result.assertError( "cache-remote-host available only when feature 'multi-site' or 'clusterless' is set"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(11) @Launch({"start-dev", "--cache-remote-port=11222"}) @@ -143,7 +144,7 @@ public class OptionsDistTest { assertDisabledDueToMissingRemoteHost(result, "--cache-remote-port"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(12) @Launch({"start-dev", "--cache-remote-username=user"}) @@ -151,7 +152,7 @@ public class OptionsDistTest { assertDisabledDueToMissingRemoteHost(result, "--cache-remote-username"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(13) @Launch({"start-dev", "--cache-remote-password=pass"}) @@ -159,7 +160,7 @@ public class OptionsDistTest { assertDisabledDueToMissingRemoteHost(result, "--cache-remote-password"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(14) @Launch({"start-dev", "--cache-remote-tls-enabled=false"}) @@ -167,7 +168,7 @@ public class OptionsDistTest { assertDisabledDueToMissingRemoteHost(result, "--cache-remote-tls-enabled"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(15) @Launch({"start-dev", "--features=multi-site"}) @@ -175,7 +176,7 @@ public class OptionsDistTest { result.assertError("- cache-remote-host: Required when feature 'multi-site' or 'clusterless' is set."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(16) @Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-username=user"}) @@ -183,7 +184,7 @@ public class OptionsDistTest { result.assertError("The option 'cache-remote-password' is required when 'cache-remote-username' is set."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Order(17) @Launch({"start-dev", "--features=multi-site", "--cache-remote-host=localhost", "--cache-remote-password=secret"}) diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java index 892d1c6df00..3d1211f5f31 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java @@ -19,11 +19,12 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.junit5.extension.WithEnvVars; import org.keycloak.it.resource.realm.TestRealmResourceTestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import io.quarkus.test.junit.main.Launch; @@ -40,7 +41,7 @@ import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.when; import static org.hamcrest.Matchers.containsString; -@DistributionTest(keepAlive = true, enableTls = true) +@DistributionTest(stopServer = Mode.MANUAL, enableTls = true) @WithEnvVars({"KC_BOOTSTRAP_ADMIN_USERNAME", "admin123", "KC_BOOTSTRAP_ADMIN_PASSWORD", "admin123"}) @RawDistOnly(reason = "Containers are immutable") public class ProxyHostnameV2DistTest { @@ -66,14 +67,14 @@ public class ProxyHostnameV2DistTest { } @Test - void testTrustedProxiesWithoutProxyHeaders(KeycloakDistribution distribution) { - CLIResult result = distribution.run("start-dev", "--proxy-trusted-addresses=1.0.0.0"); + void testTrustedProxiesWithoutProxyHeaders(KeycloakRunner runner) { + CLIResult result = runner.run("start-dev", "--proxy-trusted-addresses=1.0.0.0"); result.assertError("proxy-trusted-addresses available only when proxy-headers is set"); } @Test - void testTrustedProxiesWithInvalidAddress(KeycloakDistribution distribution) { - CLIResult result = distribution.run("start-dev", "--proxy-headers=xforwarded", "--proxy-trusted-addresses=1.0.0.0:8080"); + void testTrustedProxiesWithInvalidAddress(KeycloakRunner runner) { + CLIResult result = runner.run("start-dev", "--proxy-headers=xforwarded", "--proxy-trusted-addresses=1.0.0.0:8080"); result.assertError("1.0.0.0:8080 is not a valid IP address (IPv4 or IPv6) nor valid CIDR notation."); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesAutoBuildDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesAutoBuildDistTest.java index 06ab896e763..986aac7ee15 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesAutoBuildDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesAutoBuildDistTest.java @@ -24,7 +24,7 @@ import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Disabled; @@ -124,35 +124,35 @@ public class QuarkusPropertiesAutoBuildDistTest { cliResult.assertStarted(); } - public static class EnableAdditionalConsoleHandler implements Consumer { + public static class EnableAdditionalConsoleHandler implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setQuarkusProperty("quarkus.log.handler.console.\"console-2\".enable", "true"); distribution.setQuarkusProperty("quarkus.log.handler.console.\"console-2\".format", "Keycloak is the best"); distribution.setQuarkusProperty("quarkus.log.handlers", "console-2"); } } - public static class DisableAdditionalConsoleHandler implements Consumer { + public static class DisableAdditionalConsoleHandler implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setQuarkusProperty("quarkus.log.handler.console.\"console-2\".enable", "false"); } } - public static class AddAdditionalDatasource implements Consumer { + public static class AddAdditionalDatasource implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setProperty("db-kind-user-store", "dev-mem"); distribution.setProperty("db-username-user-store", "sa"); distribution.setProperty("db-url-full-user-store", "jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1"); } } - public static class AddAdditionalDatasource2 implements Consumer { + public static class AddAdditionalDatasource2 implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setProperty("db-kind-user-store2", "dev-mem"); distribution.setProperty("transaction-xa-enabled-user-store2", "true"); distribution.setProperty("db-username-user-store2", "sa"); @@ -160,9 +160,9 @@ public class QuarkusPropertiesAutoBuildDistTest { } } - public static class AddNonXADatasource implements Consumer { + public static class AddNonXADatasource implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setProperty("db-kind-user-store3", "dev-mem"); distribution.setProperty("transaction-xa-enabled-user-store3", "false"); distribution.setProperty("db-username-user-store3", "sa"); @@ -170,24 +170,23 @@ public class QuarkusPropertiesAutoBuildDistTest { } } - public static class ChangeAdditionalDatasourceUsername implements Consumer { + public static class ChangeAdditionalDatasourceUsername implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setProperty("db-username-user-store", "foo"); } } - public static class ChangeAdditionalDatasourceDbKind implements Consumer { + public static class ChangeAdditionalDatasourceDbKind implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setProperty("db-kind-user-store", "dev-mem"); } } - public static class SetDatabaseKind implements Consumer { + public static class SetDatabaseKind implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { - distribution.setManualStop(true); + public void accept(RawKeycloakDistribution distribution) { distribution.setQuarkusProperty("quarkus.datasource.db-kind", "postgres"); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesDistTest.java index fa605f632a5..10e8678ce2c 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/QuarkusPropertiesDistTest.java @@ -24,11 +24,10 @@ import java.util.function.Consumer; import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; -import org.keycloak.it.junit5.extension.KeepServerAlive; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; +import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import io.restassured.RestAssured; @@ -47,7 +46,6 @@ import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertTrue; @DistributionTest(defaultOptions = "--db=dev-file") -@WithEnvVars({"KC_CACHE", "local"}) // avoid flakey port conflicts @RawDistOnly(reason = "Containers are immutable") @Tag(DistributionTest.WIN) @TestMethodOrder(OrderAnnotation.class) @@ -64,7 +62,7 @@ public class QuarkusPropertiesDistTest { cliResult.assertMessage("Keycloak is the best"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @BeforeStartDistribution(UpdateConsoleHandlerFromKeycloakConf.class) @Launch({"build"}) @@ -74,7 +72,7 @@ public class QuarkusPropertiesDistTest { cliResult.assertBuild(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @BeforeStartDistribution(UpdateConsoleHandlerFromQuarkusProps.class) @Launch({"start", "--http-enabled=true", "--hostname-strict=false"}) @@ -95,7 +93,7 @@ public class QuarkusPropertiesDistTest { @Test @BeforeStartDistribution(UpdateHibernateMetricsFromQuarkusProps.class) - @KeepServerAlive + @StopServer(Mode.MANUAL) @Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--metrics-enabled=true"}) @Order(8) void testUnknownQuarkusBuildTimePropertyApplied(CLIResult cliResult) { @@ -170,45 +168,45 @@ public class QuarkusPropertiesDistTest { cliResult.assertMessage("ERROR: Failed to load 'https-*' material: NoSuchFileException C:"); } - public static class AddConsoleHandlerFromQuarkusProps implements Consumer { + public static class AddConsoleHandlerFromQuarkusProps implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.setQuarkusProperty(QUARKUS_RUNTIME_CONSOLE_HANDLER_ENABLED_KEY, "true"); distribution.setQuarkusProperty("quarkus.log.handler.console.\"console-2\".format", "Keycloak is the best"); distribution.setQuarkusProperty("quarkus.log.handlers", "console-2"); } } - public static class UpdateConsoleHandlerFromKeycloakConf implements Consumer { + public static class UpdateConsoleHandlerFromKeycloakConf implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.deleteQuarkusProperties(); distribution.setProperty(QUARKUS_RUNTIME_CONSOLE_HANDLER_ENABLED_KEY, "false"); } } - public static class UpdateConsoleHandlerFromQuarkusProps implements Consumer { + public static class UpdateConsoleHandlerFromQuarkusProps implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.deleteQuarkusProperties(); distribution.setQuarkusProperty(QUARKUS_RUNTIME_CONSOLE_HANDLER_ENABLED_KEY, "true"); } } - public static class UpdateHibernateMetricsFromQuarkusProps implements Consumer { + public static class UpdateHibernateMetricsFromQuarkusProps implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.deleteQuarkusProperties(); distribution.setQuarkusProperty(QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY, "true"); } } - public static class CopyKeystoreToConf implements Consumer { + public static class CopyKeystoreToConf implements Consumer { @Override - public void accept(KeycloakDistribution distribution) { + public void accept(RawKeycloakDistribution distribution) { distribution.copyOrReplaceFileFromClasspath("/keystore", Path.of("conf", "keystore")); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ShowConfigCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ShowConfigCommandDistTest.java index 14eb9cf72f4..050c56d22f3 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ShowConfigCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ShowConfigCommandDistTest.java @@ -1,14 +1,14 @@ package org.keycloak.it.cli.dist; -import java.nio.file.Path; import java.nio.file.Paths; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithEnvVars; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.quarkus.runtime.cli.command.ShowConfig; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; @@ -26,23 +26,23 @@ import static org.hamcrest.Matchers.not; @DistributionTest public class ShowConfigCommandDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - void testShowConfigPicksUpRightConfigDependingOnCurrentMode(KeycloakDistribution distribution) { - CLIResult initialResult = distribution.run("show-config"); + void testShowConfigPicksUpRightConfigDependingOnCurrentMode(KeycloakRunner runner) { + CLIResult initialResult = runner.run("show-config"); initialResult.assertMessage("Current Mode: production"); initialResult.assertNoMessage("kc.db = dev-file"); - distribution.run("start-dev"); + runner.run("start-dev"); - CLIResult devModeResult = distribution.run("show-config"); + CLIResult devModeResult = runner.run("show-config"); devModeResult.assertMessage("Current Mode: development"); devModeResult.assertMessage("kc.db = dev-file"); - distribution.run("build", "--db=dev-file"); + runner.run("build", "--db=dev-file"); - CLIResult resetResult = distribution.run("show-config"); + CLIResult resetResult = runner.run("show-config"); resetResult.assertMessage("Current Mode: production"); resetResult.assertMessage("kc.db = dev-file"); } @@ -65,8 +65,8 @@ public class ShowConfigCommandDistTest { @Test @RawDistOnly(reason = "Containers are immutable") - void testShowConfigCommandHidesCredentialsInProfiles(KeycloakDistribution distribution) { - CLIResult result = distribution.run(String.format("%s=%s", CONFIG_FILE_LONG_NAME, Paths.get("src/test/resources/ShowConfigCommandTest/keycloak.conf").toAbsolutePath().normalize()), ShowConfig.NAME, "all"); + void testShowConfigCommandHidesCredentialsInProfiles(KeycloakRunner runner) { + CLIResult result = runner.run(String.format("%s=%s", CONFIG_FILE_LONG_NAME, Paths.get("src/test/resources/ShowConfigCommandTest/keycloak.conf").toAbsolutePath().normalize()), ShowConfig.NAME, "all"); String output = result.getOutput(); Assertions.assertFalse(output.contains("testpw1")); Assertions.assertFalse(output.contains("testpw2")); @@ -76,9 +76,9 @@ public class ShowConfigCommandDistTest { @Test @RawDistOnly(reason = "Containers are immutable") - void testSmallRyeKeyStoreConfigSource(KeycloakDistribution distribution) { + void testSmallRyeKeyStoreConfigSource(KeycloakRunner runner) { // keystore is shared with QuarkusPropertiesDistTest#testSmallRyeKeyStoreConfigSource - CLIResult result = distribution.run( + CLIResult result = runner.run( String.format("%s=%s", CONFIG_FILE_LONG_NAME, Paths.get("src/test/resources/ShowConfigCommandTest/keycloak-keystore.conf").toAbsolutePath().normalize()), "--config-keystore=" + Paths.get("src/test/resources/keystore").toAbsolutePath().normalize(), ShowConfig.NAME, "all"); @@ -104,14 +104,14 @@ public class ShowConfigCommandDistTest { @Test @RawDistOnly(reason = "Containers are immutable") - void testConfigSourceNames(KeycloakDistribution distribution) { - CLIResult result = distribution.run("build"); + void testConfigSourceNames(KeycloakRunner runner) { + CLIResult result = runner.run("build"); result.assertBuild(); - distribution.setEnvVar("KC_LOG", "file"); - distribution.copyOrReplaceFile(Paths.get("src/test/resources/ShowConfigCommandTest/quarkus.properties"), Path.of("conf", "quarkus.properties")); + runner.setEnvVar("KC_LOG", "file"); + runner.getDistribution().copyConfigFile(Paths.get("src/test/resources/ShowConfigCommandTest/quarkus.properties")); - result = distribution.run( + result = runner.run( String.format("%s=%s", CONFIG_FILE_LONG_NAME, Paths.get("src/test/resources/ShowConfigCommandTest/keycloak-keystore.conf").toAbsolutePath().normalize()), "--config-keystore=" + Paths.get("src/test/resources/keystore").toAbsolutePath().normalize(), ShowConfig.NAME, "all", "--db=dev-file"); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartAutoBuildDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartAutoBuildDistTest.java index ed0ab57b397..b84ad31c1a4 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartAutoBuildDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartAutoBuildDistTest.java @@ -19,8 +19,10 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.utils.KeycloakDistribution; @@ -40,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class StartAutoBuildDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "--verbose", "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false" }) @Order(1) @@ -54,7 +56,7 @@ public class StartAutoBuildDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false" }) @Order(2) @@ -63,7 +65,7 @@ public class StartAutoBuildDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false" }) @Order(3) @@ -72,7 +74,7 @@ public class StartAutoBuildDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-mem", "--http-enabled=true", "--hostname-strict=false" }) @Order(4) @@ -81,7 +83,7 @@ public class StartAutoBuildDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "build", "--db=postgres" }) @Order(5) @@ -89,7 +91,7 @@ public class StartAutoBuildDistTest { cliResult.assertBuild(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false" }) @Order(6) @@ -98,7 +100,7 @@ public class StartAutoBuildDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=postgres", "--http-enabled=true", "--hostname-strict=false" }) @Order(7) @@ -106,7 +108,7 @@ public class StartAutoBuildDistTest { cliResult.assertBuild(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG}) @Order(8) @@ -114,7 +116,7 @@ public class StartAutoBuildDistTest { cliResult.assertNoBuild(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev" }) @Order(8) @@ -123,7 +125,7 @@ public class StartAutoBuildDistTest { cliResult.assertStartedDevMode(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev" }) @Order(9) @@ -133,46 +135,46 @@ public class StartAutoBuildDistTest { cliResult.assertStartedDevMode(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @TestProvider(CustomUserProvider.class) @Order(10) - void testSpiAutoBuild(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev", "--spi-user-provider=custom_jpa", "--spi-user-jpa-enabled=false"); + void testSpiAutoBuild(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev", "--spi-user-provider=custom_jpa", "--spi-user-jpa-enabled=false"); cliResult.assertMessage("Updating the configuration"); cliResult.assertStartedDevMode(); - dist.stop(); + runner.stop(); // we should persist the spi provider and know not to rebuild - cliResult = dist.run("start-dev", "--spi-user-provider=custom_jpa", "--spi-user-jpa-enabled=false"); + cliResult = runner.run("start-dev", "--spi-user-provider=custom_jpa", "--spi-user-jpa-enabled=false"); cliResult.assertNoMessage("Updating the configuration"); cliResult.assertStartedDevMode(); } @Test @Order(11) - void testLogLevelNotPeristed(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start", "--db=dev-file", "--log-level=org.hibernate.SQL:debug", "--http-enabled=true", "--hostname-strict=false"); + void testLogLevelNotPeristed(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start", "--db=dev-file", "--log-level=org.hibernate.SQL:debug", "--http-enabled=true", "--hostname-strict=false"); cliResult.assertMessage("DEBUG [org.hibernate.SQL]"); cliResult.assertStarted(); - dist.stop(); + runner.stop(); // logging runtime defaults should not be used - cliResult = dist.run("start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"); + cliResult = runner.run("start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false"); cliResult.assertNoMessage("DEBUG [org.hibernate.SQL]"); cliResult.assertStarted(); } @Test @Order(12) - void testLogLevelWildcardNotPeristed(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev", "--log-level-org.hibernate.SQL=debug"); + void testLogLevelWildcardNotPeristed(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev", "--log-level-org.hibernate.SQL=debug"); cliResult.assertMessage("DEBUG [org.hibernate.SQL]"); cliResult.assertStartedDevMode(); - dist.stop(); + runner.stop(); // logging runtime defaults should not be used - cliResult = dist.run("start-dev"); + cliResult = runner.run("start-dev"); cliResult.assertNoMessage("DEBUG [org.hibernate.SQL]"); cliResult.assertStartedDevMode(); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java index 4a579ea7333..99e265c7b01 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartCommandDistTest.java @@ -21,8 +21,10 @@ import java.util.concurrent.TimeUnit; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.junit5.extension.WithEnvVars; import org.keycloak.it.resource.realm.TestRealmResourceTestProvider; @@ -32,7 +34,6 @@ import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.main.Launch; -import org.awaitility.Awaitility; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; @@ -47,14 +48,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -@WithEnvVars({"KC_CACHE", "local"}) // avoid flakey port conflicts @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @DistributionTest @Tag(DistributionTest.WIN) @QuarkusTestResource(RawDistributionLifecycleManager.class) public class StartCommandDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--hostname-strict=false" }) void failNoTls(CLIResult cliResult) { @@ -62,7 +62,7 @@ public class StartCommandDistTest { () -> "The Output:\n" + cliResult.getErrorOutput() + "doesn't contains the expected string."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level" }) void failSpiArgMissingValue(CLIResult cliResult) { @@ -70,7 +70,7 @@ public class StartCommandDistTest { () -> "The Output:\n" + cliResult.getErrorOutput() + "doesn't contains the expected string."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "build", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level=debug" }) void warnSpiRuntimeAtBuildtime(CLIResult cliResult) { @@ -78,47 +78,47 @@ public class StartCommandDistTest { () -> "The Output:\n" + cliResult.getOutput() + "doesn't contains the expected string."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - void errorSpiBuildtimeAtRuntime(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build", "--db=dev-file"); + void errorSpiBuildtimeAtRuntime(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build", "--db=dev-file"); cliResult.assertBuild(); - cliResult = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false", "--spi-events-listener--jboss-logging--enabled=false"); + cliResult = runner.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false", "--spi-events-listener--jboss-logging--enabled=false"); cliResult.assertError("The following build time options have values that differ from what is persisted - the new values will NOT be used until another build is run: kc.spi-events-listener--jboss-logging--enabled"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @WithEnvVars({"KC_SPI_EVENTS_LISTENER__JBOSS_LOGGING__ENABLED", "false"}) @Test @RawDistOnly(reason = "Containers are immutable") - void noErrorSpiBuildtimeNotChanged(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build", "--db=dev-file"); + void noErrorSpiBuildtimeNotChanged(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build", "--db=dev-file"); cliResult.assertBuild(); - cliResult = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); + cliResult = runner.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); cliResult.assertNoError("The following build time options"); } @Test @RawDistOnly(reason = "Containers are immutable") - void terminateStartOptimized(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build", "--db=dev-file"); + void terminateStartOptimized(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build", "--db=dev-file"); cliResult.assertBuild(); - dist.setManualStop(true); - cliResult = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); + runner.setStopServer(Mode.MANUAL); + cliResult = runner.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); cliResult.assertStarted(); // if the child java process does not clean up, then subsequent start will fail - dist.stop(); + runner.stop(); - cliResult = dist.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); + cliResult = runner.run("start", "--optimized", "--http-enabled=true", "--hostname-strict=false"); cliResult.assertStarted(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "--profile=dev", "start", "--db=dev-file" }) void failUsingDevProfile(CLIResult cliResult) { @@ -132,7 +132,7 @@ public class StartCommandDistTest { cliResult.assertStarted(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "--profile=dev", "start", "--http-enabled=true", "--hostname-strict=false" }) void failIfAutoBuildUsingDevProfile(CLIResult cliResult) { @@ -140,7 +140,7 @@ public class StartCommandDistTest { assertEquals(4, cliResult.getErrorStream().size()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @WithEnvVars({"KC_HTTP_ENABLED", "true", "KC_HOSTNAME_STRICT", "false"}) @Test @Launch({ "start", "--optimized" }) @@ -149,7 +149,7 @@ public class StartCommandDistTest { cliResult.assertError("The '--optimized' flag was used for first ever server start."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--optimized", "--http-enabled=true", "--hostname-strict=false" }) @Order(2) @@ -164,7 +164,7 @@ public class StartCommandDistTest { () -> "The Output:\n" + cliResult.getOutput() + "doesn't contains the expected string."); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--http-enabled=true", "--hostname-strict=false", "--metrics-enabled=true" }) void testStartUsingAutoBuild(CLIResult cliResult) { @@ -178,14 +178,14 @@ public class StartCommandDistTest { assertTrue(cliResult.getErrorOutput().isBlank(), cliResult.getErrorOutput()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start", "--db=dev-file", "--http-enabled=true", "--cache-remote-host=localhost", "--hostname-strict=false", "--cache-remote-tls-enabled=false", "--transaction-xa-enabled=true" }) void testStartNoWarningOnDisabledRuntimeOption(CLIResult cliResult) { cliResult.assertNoMessage("cache-remote-tls-enabled: Available only when remote host is set"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @WithEnvVars({"KC_LOG", "invalid"}) @Launch({ "start", "--db=dev-file", "--http-enabled=false", "--hostname-strict=false" }) @@ -193,13 +193,13 @@ public class StartCommandDistTest { cliResult.assertError("Invalid value for option 'KC_LOG': invalid. Expected values are: console, file, syslog"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - void testWarningWhenOverridingBuildOptionsDuringStart(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build", "--db=postgres", "--features=preview"); + void testWarningWhenOverridingBuildOptionsDuringStart(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build", "--db=postgres", "--features=preview"); cliResult.assertBuild(); - cliResult = dist.run("start", "--db=dev-file", "--hostname=localhost", "--http-enabled=true"); + cliResult = runner.run("start", "--db=dev-file", "--hostname=localhost", "--http-enabled=true"); cliResult.assertMessage("The previous optimized build will be overridden with the following build options:"); cliResult.assertMessage("- db=postgres > db=dev-file"); // back to the default value cliResult.assertMessage("- features=preview > features="); // no default value, the is shown @@ -207,51 +207,51 @@ public class StartCommandDistTest { assertTrue(cliResult.getErrorOutput().isBlank()); // should not show warning if the re-augmentation did not happen through the build command // an optimized server image should ideally be created by running a build - cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); + cliResult = runner.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); cliResult.assertNoMessage("The previous optimized build will be overridden with the following build options:"); assertTrue(cliResult.getErrorOutput().isBlank()); - dist.run("build", "--db=postgres"); - cliResult = dist.run("start", "--db=dev-file", "--hostname=localhost", "--http-enabled=true"); + runner.run("build", "--db=postgres"); + cliResult = runner.run("start", "--db=dev-file", "--hostname=localhost", "--http-enabled=true"); cliResult.assertMessage("- db=postgres > db=dev-file"); cliResult.assertNoMessage("- features=preview > features="); assertTrue(cliResult.getErrorOutput().isBlank()); - dist.run("build", "--db=postgres"); - cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); + runner.run("build", "--db=postgres"); + cliResult = runner.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); cliResult.assertMessage("- db=postgres > db=dev-mem"); // option overridden during the start assertTrue(cliResult.getErrorOutput().isBlank()); - dist.run("build", "--db=dev-mem"); - cliResult = dist.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); + runner.run("build", "--db=dev-mem"); + cliResult = runner.run("start", "--db=dev-mem", "--hostname=localhost", "--http-enabled=true"); cliResult.assertNoMessage("- db=postgres > db=postgres"); // option did not change not need to show assertTrue(cliResult.getErrorOutput().isBlank()); - dist.run("build", "--db=dev-mem"); - cliResult = dist.run("start", "--db=dev-mem", "--cache=local", "--hostname=localhost", "--http-enabled=true"); + runner.run("build", "--db=dev-mem"); + cliResult = runner.run("start", "--db=dev-mem", "--cache=local", "--hostname=localhost", "--http-enabled=true"); cliResult.assertNoMessage("The previous optimized build will be overridden with the following build options:"); // no message, same values provided during auto-build } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - void testStartAfterStartDev(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev"); + void testStartAfterStartDev(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev"); cliResult.assertStartedDevMode(); - cliResult = dist.run("start", "--db=dev-file", "--http-enabled", "true", "--hostname-strict", "false"); + cliResult = runner.run("start", "--db=dev-file", "--http-enabled", "true", "--hostname-strict", "false"); cliResult.assertNotDevMode(); assertTrue(cliResult.getErrorOutput().isBlank()); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @RawDistOnly(reason = "Containers are immutable") - void testErrorWhenOverridingNonCliBuildOptionsDuringStart(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("build", "--db=dev-file", "--features=preview"); + void testErrorWhenOverridingNonCliBuildOptionsDuringStart(KeycloakRunner runner) { + CLIResult cliResult = runner.run("build", "--db=dev-file", "--features=preview"); cliResult.assertBuild(); - dist.setEnvVar("KC_DB", "postgres"); - cliResult = dist.run("start", "--optimized", "--hostname=localhost", "--http-enabled=true"); + runner.setEnvVar("KC_DB", "postgres"); + cliResult = runner.run("start", "--optimized", "--hostname=localhost", "--http-enabled=true"); cliResult.assertError("The following build time options have values that differ from what is persisted - the new values will NOT be used until another build is run: kc.db"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({CONFIG_FILE_LONG_NAME + "=src/test/resources/non-existing.conf", "start", "--db=dev-file"}) void testInvalidConfigFileOption(CLIResult cliResult) { @@ -259,7 +259,7 @@ public class StartCommandDistTest { cliResult.assertError(String.format("Try '%s --help' for more information on the available options.", KeycloakDistribution.SCRIPT_CMD)); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({CONFIG_FILE_LONG_NAME + "=src/test/resources/keycloak.properties", "start", "--db=dev-file"}) void testConfigFileWithWrongExtension(CLIResult cliResult) { @@ -268,20 +268,20 @@ public class StartCommandDistTest { @RawDistOnly(reason = "Containers are immutable") @Test - void testRuntimeValuesAreNotCaptured(KeycloakDistribution dist) { + void testRuntimeValuesAreNotCaptured(KeycloakRunner runner) { // confirm that the invalid value prevents startup - if this passes, then we need to use a different // spi provider - CLIResult cliResult = dist.run("start", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level=invalid", "--http-enabled", "true", "--hostname-strict", "false"); + CLIResult cliResult = runner.run("start", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level=invalid", "--http-enabled", "true", "--hostname-strict", "false"); cliResult.assertError("Failed to start quarkus"); // if there was no auto-build use an explicit build to potentially capture the runtime default if (!cliResult.getOutput().contains("Server configuration updated and persisted")) { - cliResult = dist.run("build", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level=invalid"); + cliResult = runner.run("build", "--db=dev-file", "--spi-events-listener-jboss-logging-success-level=invalid"); cliResult.assertBuild(); } // the invalid value should not be the default - cliResult = dist.run("start", "--db=dev-file", "--http-enabled", "true", "--hostname-strict", "false"); + cliResult = runner.run("start", "--db=dev-file", "--http-enabled", "true", "--hostname-strict", "false"); cliResult.assertNoBuild(); cliResult.assertStarted(); } @@ -289,15 +289,15 @@ public class StartCommandDistTest { @RawDistOnly(reason = "Containers are immutable") @Test @TestProvider(TestRealmResourceTestProvider.class) - void testAsyncBootstrapFails(KeycloakDistribution dist) { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); - dist.setManualStop(true); - CLIResult result = dist.run("start", "--server-async-bootstrap=true", "--hostname-strict=false", "--db=dev-file", "--http-enabled=true", "--spi-realm-restapi-extension--test-resources--fail=true"); - Awaitility.await().atMost(2, TimeUnit.MINUTES).until(() -> !rawDist.isRunning()); - dist.stop(); + void testAsyncBootstrapFails(KeycloakRunner runner) { + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); + runner.setStopServer(Mode.MANUAL); + CLIResult result = runner.run("start", "--server-async-bootstrap=true", "--hostname-strict=false", "--db=dev-file", "--http-enabled=true", "--spi-realm-restapi-extension--test-resources--fail=true"); + rawDist.waitFor(false, TimeUnit.MINUTES.toMillis(2)); + runner.stop(); result.assertMessage("Failed to start server"); result.assertMessage("I've failed"); - assertEquals(1, dist.getExitCode()); + assertEquals(1, rawDist.getExitCode()); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java index 8a925edcc6d..3cfab7c00d6 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/StartDevCommandDistTest.java @@ -22,10 +22,10 @@ import java.nio.file.Paths; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; -import org.keycloak.it.junit5.extension.DryRun; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; -import org.keycloak.it.utils.RawKeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.MethodOrderer; @@ -42,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @Tag(DistributionTest.WIN) public class StartDevCommandDistTest { - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev" }) void testDevModeWarning(CLIResult cliResult) { @@ -53,7 +53,7 @@ public class StartDevCommandDistTest { assertFalse(out.contains("0.0.0.0") || out.contains("all addresses")); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev", "--db=dev-mem" }) void testBuildPropertyAvailable(CLIResult cliResult) { @@ -70,7 +70,7 @@ public class StartDevCommandDistTest { cliResult.assertNoMessage("Build time property cannot"); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "build", "--debug", "--db=dev-file" }) void testBuildMustNotRunTwoJVMs(CLIResult cliResult) { @@ -78,7 +78,7 @@ public class StartDevCommandDistTest { cliResult.assertBuild(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test @Launch({ "start-dev", "--verbose" }) void testVerboseAfterCommand(CLIResult cliResult) { @@ -86,8 +86,8 @@ public class StartDevCommandDistTest { } @Test - void testConfigKeystoreAbsolutePath(KeycloakDistribution dist) { - CLIResult cliResult = dist.run("start-dev", "--config-keystore=" + Paths.get("src/test/resources/keystore").toAbsolutePath().normalize(), + void testConfigKeystoreAbsolutePath(KeycloakRunner runner) { + CLIResult cliResult = runner.run("start-dev", "--config-keystore=" + Paths.get("src/test/resources/keystore").toAbsolutePath().normalize(), "--config-keystore-password=secret"); // keytool -importpass -alias kc.log-level -keystore keystore -storepass secret -storetype PKCS12 -v (with "org.keycloak.timer:debug" as the stored password) @@ -99,17 +99,16 @@ public class StartDevCommandDistTest { cliResult.assertStartedDevMode(); } - @DryRun + @StopServer(Mode.BEFORE_QUARKUS) @Test - void testStartDevThenImportRebuild(KeycloakDistribution dist) throws Exception { - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); - CLIResult result = rawDist.run("start-dev"); + void testStartDevThenImportRebuild(KeycloakRunner runner) throws Exception { + CLIResult result = runner.run("start-dev"); assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput()); File target = new File("./target"); // feature change should trigger a build - result = rawDist.run("--profile=dev", "export", "--features=docker", "--dir=" + target.getAbsolutePath()); + result = runner.run("--profile=dev", "export", "--features=docker", "--dir=" + target.getAbsolutePath()); result.assertMessage("Updating the configuration and installing your custom providers, if any. Please wait."); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TracingDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TracingDistTest.java index d5f7ce74d05..49d68489885 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TracingDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TracingDistTest.java @@ -20,7 +20,7 @@ package org.keycloak.it.cli.dist; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.junit5.extension.SkipRealmBootstrap; +import org.keycloak.it.junit5.extension.StopServer.Mode; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; @@ -30,9 +30,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@DistributionTest +@DistributionTest(stopServer = Mode.BEFORE_BOOTSTRAP) @RawDistOnly(reason = "Containers are immutable") -@SkipRealmBootstrap public class TracingDistTest { static void assertTracingEnabled(CLIResult result) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TransactionDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TransactionDistTest.java index 6842da59d2d..3fa1798d595 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TransactionDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TransactionDistTest.java @@ -21,10 +21,11 @@ import java.io.IOException; import java.util.Map; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.resource.realm.TestRealmResourceTestProvider; -import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.util.JsonSerialization; import com.fasterxml.jackson.core.type.TypeReference; @@ -34,38 +35,38 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; import static org.hamcrest.MatcherAssert.assertThat; -@DistributionTest(keepAlive = true) +@DistributionTest(stopServer = Mode.MANUAL) @RawDistOnly(reason = "Containers are immutable") public class TransactionDistTest { @Test - void testZeroTransactionTimeout(KeycloakDistribution dist) { - var result = dist.run("start-dev", "--transaction-default-timeout=0s"); + void testZeroTransactionTimeout(KeycloakRunner runner) { + var result = runner.run("start-dev", "--transaction-default-timeout=0s"); result.assertError("Invalid duration '0s' for option 'transaction-default-timeout. Duration must be positive."); - result = dist.run("start-dev", "--transaction-setup-timeout=0s"); + result = runner.run("start-dev", "--transaction-setup-timeout=0s"); result.assertError("Invalid duration '0s' for option 'transaction-setup-timeout. Duration must be positive."); } @Test - void testNegativeTransactionTimeout(KeycloakDistribution dist) { - var result = dist.run("start-dev", "--transaction-default-timeout=-1s"); + void testNegativeTransactionTimeout(KeycloakRunner runner) { + var result = runner.run("start-dev", "--transaction-default-timeout=-1s"); result.assertError("Invalid duration '-1s' for option 'transaction-default-timeout. Duration must be positive."); - result = dist.run("start-dev", "--transaction-setup-timeout=-2s"); + result = runner.run("start-dev", "--transaction-setup-timeout=-2s"); result.assertError("Invalid duration '-2s' for option 'transaction-setup-timeout. Duration must be positive."); } @Test - void testNonNumberTransactionTimeout(KeycloakDistribution dist) { - var result = dist.run("start-dev", "--transaction-default-timeout=abc"); + void testNonNumberTransactionTimeout(KeycloakRunner runner) { + var result = runner.run("start-dev", "--transaction-default-timeout=abc"); result.assertError("Invalid duration format 'abc' for option 'transaction-default-timeout'. May be an ISO 8601 duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]."); - result = dist.run("start-dev", "--transaction-setup-timeout=def"); + result = runner.run("start-dev", "--transaction-setup-timeout=def"); result.assertError("Invalid duration format 'def' for option 'transaction-setup-timeout'. May be an ISO 8601 duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]."); } @Test @TestProvider(TestRealmResourceTestProvider.class) - void testValidTransactionTimeout(KeycloakDistribution dist) throws IOException { - var result = dist.run("start-dev", "--transaction-default-timeout=123s", "--transaction-setup-timeout=456s"); + void testValidTransactionTimeout(KeycloakRunner runner) throws IOException { + var result = runner.run("start-dev", "--transaction-default-timeout=123s", "--transaction-setup-timeout=456s"); result.assertStartedDevMode(); assertTimeouts(); } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java index d4377a5f21f..76e12e3ff28 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/TruststoreDistTest.java @@ -22,8 +22,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.truststore.TruststoreBuilder; @@ -35,7 +36,7 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; -@DistributionTest(keepAlive = true) +@DistributionTest(stopServer = Mode.MANUAL) @RawDistOnly(reason = "Containers are immutable") @Tag(DistributionTest.SMOKE) public class TruststoreDistTest { @@ -46,19 +47,19 @@ public class TruststoreDistTest { } @Test - void testMutualAuthWithTruststorePaths(KeycloakDistribution dist) { + void testMutualAuthWithTruststorePaths(KeycloakRunner runner) { String[] truststoreNames = new String[] { "keycloak-truststore.p12", "self-signed.pem" }; + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); Stream.of(truststoreNames).forEach(truststoreName -> { - dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName)); + rawDist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", truststoreName)); }); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); String paths = Stream.of(truststoreNames).map(truststoreName -> rawDist.getDistPath().resolve("conf") .resolve(truststoreName).toAbsolutePath().toString()).collect(Collectors.joining(",")); - dist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12")); + rawDist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12")); Path keyStore = rawDist.getDistPath().resolve("conf").resolve("self-signed.p12").toAbsolutePath(); - rawDist.run("--verbose", "start", "--db=dev-file", "--http-enabled=true", "--hostname=mykeycloak.org", + runner.run("--verbose", "start", "--db=dev-file", "--http-enabled=true", "--hostname=mykeycloak.org", "--truststore-paths=" + paths, "--https-client-auth=required", "--https-key-store-file=" + keyStore); given().trustStore(TruststoreDistTest.class.getResource("/self-signed-truststore.p12").getPath(), TruststoreBuilder.DUMMY_PASSWORD) @@ -67,17 +68,17 @@ public class TruststoreDistTest { } @Test - void testMutualAuthWithDefaultTruststoresDir(KeycloakDistribution dist) { + void testMutualAuthWithDefaultTruststoresDir(KeycloakRunner runner) { String[] truststoreNames = new String[] { "keycloak-truststore.p12", "self-signed.pem" }; + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); Stream.of(truststoreNames).forEach(truststoreName -> { - dist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", "truststores", truststoreName)); + rawDist.copyOrReplaceFileFromClasspath("/" + truststoreName, Path.of("conf", "truststores", truststoreName)); }); - RawKeycloakDistribution rawDist = dist.unwrap(RawKeycloakDistribution.class); - dist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12")); + rawDist.copyOrReplaceFileFromClasspath("/self-signed.p12", Path.of("conf", "self-signed.p12")); Path keyStore = rawDist.getDistPath().resolve("conf").resolve("self-signed.p12").toAbsolutePath(); - rawDist.run("--verbose", "start", "--db=dev-file", "--http-enabled=true", "--hostname=mykeycloak.org", + runner.run("--verbose", "start", "--db=dev-file", "--http-enabled=true", "--hostname=mykeycloak.org", "--https-client-auth=required", "--https-key-store-file=" + keyStore); given().trustStore(TruststoreDistTest.class.getResource("/self-signed-truststore.p12").getPath(), TruststoreBuilder.DUMMY_PASSWORD) diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/WindowsServiceDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/WindowsServiceDistTest.java index 09bcc4311f6..4f36d883cb6 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/WindowsServiceDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/WindowsServiceDistTest.java @@ -29,8 +29,9 @@ import java.util.concurrent.TimeUnit; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.KeycloakRunner; import org.keycloak.it.junit5.extension.RawDistOnly; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawDistRootPath; import org.keycloak.it.utils.RawKeycloakDistribution; import org.junit.jupiter.api.AfterEach; @@ -57,20 +58,16 @@ public class WindowsServiceDistTest { private static final int SERVICE_START_TIMEOUT_SECONDS = 120; private static final int SERVICE_STOP_TIMEOUT_SECONDS = 60; - private RawKeycloakDistribution rawDist; - private Path distPath; private String testServiceName; private boolean serviceCreated = false; private boolean prunsrvAvailable = false; @BeforeEach - void setUp(KeycloakDistribution dist) { - this.rawDist = dist.unwrap(RawKeycloakDistribution.class); - this.distPath = rawDist.getDistPath(); + void setUp(KeycloakRunner runner, RawDistRootPath path) { this.testServiceName = TEST_SERVICE_NAME_PREFIX + System.currentTimeMillis(); // Check if prunsrv.exe is available in the distribution - Path prunsrvPath = distPath.resolve("bin").resolve("prunsrv.exe"); + Path prunsrvPath = path.getDistRootPath().resolve("bin").resolve("prunsrv.exe"); if (!Files.exists(prunsrvPath)) { String prunsrvSystemPath = findPrunsrvInSystem(); if (prunsrvSystemPath != null) { @@ -87,7 +84,7 @@ public class WindowsServiceDistTest { } @AfterEach - void tearDown() { + void tearDown(KeycloakRunner runner) { if (serviceCreated) { try { stopService(); @@ -95,7 +92,7 @@ public class WindowsServiceDistTest { System.err.println("Failed to stop service during cleanup: " + e.getMessage()); } try { - deleteService(); + deleteService(runner); } catch (Exception e) { System.err.println("Failed to delete service during cleanup: " + e.getMessage()); } @@ -103,20 +100,21 @@ public class WindowsServiceDistTest { } @Test - void testServiceLifecycle() throws Exception { + void testServiceLifecycle(KeycloakRunner runner) throws Exception { assertPrunsrvAvailable(); assertAdminPrivileges(); String customDisplayName = "Keycloak Test Service " + testServiceName; String customDescription = "Keycloak integration test service"; + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); rawDist.setProperty("http-enabled", "true"); rawDist.setProperty("hostname-strict", "false"); rawDist.setProperty("log", "console,file"); - rawDist.setProperty("log-file", distPath.resolve("log").resolve("keycloak.log").toString()); + rawDist.setProperty("log-file", rawDist.getDistPath().resolve("log").resolve("keycloak.log").toString()); // Install the service with custom name and display name - CLIResult installResult = rawDist.run("tools", "windows-service", "install", + CLIResult installResult = runner.run("tools", "windows-service", "install", "--name=" + testServiceName, "--display-name=" + customDisplayName, "--description=" + customDescription, @@ -137,7 +135,7 @@ public class WindowsServiceDistTest { assertEquals("RUNNING", getServiceState(testServiceName), "Service should be in RUNNING state"); // Verify log file was created and contains startup message - Path logFile = distPath.resolve("log").resolve("keycloak.log"); + Path logFile = rawDist.getDistPath().resolve("log").resolve("keycloak.log"); assertTrue(waitForLogFile(logFile), "Log file should be created"); String logContent = Files.readString(logFile); assertThat("Log should contain Keycloak startup message", logContent, containsString("Listening on:")); @@ -148,7 +146,7 @@ public class WindowsServiceDistTest { assertFalse(isKeycloakAccessible(), "Keycloak should not be accessible after service stop"); // Test service uninstall - CLIResult uninstallResult = rawDist.run("tools", "windows-service", "uninstall", "--name=" + testServiceName); + CLIResult uninstallResult = runner.run("tools", "windows-service", "uninstall", "--name=" + testServiceName); assertEquals(0, uninstallResult.exitCode(), "Service uninstallation failed: " + uninstallResult.getOutput()); assertThat(uninstallResult.getOutput(), containsString("uninstalled successfully")); serviceCreated = false; @@ -237,8 +235,8 @@ public class WindowsServiceDistTest { return true; } - private void deleteService() { - rawDist.run("tools", "windows-service", "uninstall", "--name=" + testServiceName); + private void deleteService(KeycloakRunner runner) { + runner.run("tools", "windows-service", "uninstall", "--name=" + testServiceName); } private boolean isServiceCreated(String serviceName) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java index 113f8f7c315..669c90164ff 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/ExternalInfinispanTest.java @@ -22,6 +22,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.InfinispanContainer; +import org.keycloak.it.junit5.extension.StopServer.Mode; import org.keycloak.it.junit5.extension.WithExternalInfinispan; import io.quarkus.test.junit.main.Launch; @@ -30,7 +31,7 @@ import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.when; -@DistributionTest(keepAlive = true) +@DistributionTest(stopServer = Mode.MANUAL) @WithExternalInfinispan @Tag(DistributionTest.STORAGE) public class ExternalInfinispanTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MariaDBDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MariaDBDistTest.java index 680609b1063..0c5c48d538c 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MariaDBDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MariaDBDistTest.java @@ -28,7 +28,7 @@ import io.quarkus.test.junit.main.Launch; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -@DistributionTest(removeBuildOptionsAfterBuild = true) +@DistributionTest @WithDatabase(alias = "mariadb") public class MariaDBDistTest extends MariaDBTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MssqlDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MssqlDistTest.java index 603fffcfc00..67dc5cc4560 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MssqlDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MssqlDistTest.java @@ -23,7 +23,7 @@ import org.keycloak.it.storage.database.MssqlSQLTest; import org.junit.jupiter.api.Tag; -@DistributionTest(removeBuildOptionsAfterBuild = true) +@DistributionTest @WithDatabase(alias = "mssql") @Tag(DistributionTest.STORAGE) public class MssqlDistTest extends MssqlSQLTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MySQLDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MySQLDistTest.java index d9dc59bb242..81be8ef5916 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MySQLDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/MySQLDistTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -@DistributionTest(removeBuildOptionsAfterBuild = true) +@DistributionTest @WithDatabase(alias = "mysql") public class MySQLDistTest extends MySQLTest { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/PostgreSQLDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/PostgreSQLDistTest.java index dd6036c71c6..077c496d933 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/PostgreSQLDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/storage/database/dist/PostgreSQLDistTest.java @@ -17,11 +17,15 @@ package org.keycloak.it.storage.database.dist; +import java.util.function.Consumer; + +import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.WithDatabase; import org.keycloak.it.storage.database.PostgreSQLTest; import org.keycloak.it.utils.RawDistRootPath; +import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand; import io.quarkus.test.junit.main.Launch; @@ -31,16 +35,24 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -@DistributionTest(removeBuildOptionsAfterBuild = true) +@DistributionTest @WithDatabase(alias = "postgres") @Tag(DistributionTest.STORAGE) public class PostgreSQLDistTest extends PostgreSQLTest { + @BeforeStartDistribution(RemoveDB.class) @Test @Launch("show-config") public void testDbOptionFromPersistedConfigSource(CLIResult cliResult) { assertThat(cliResult.getOutput(),containsString("postgres (Persisted)")); } + + public static final class RemoveDB implements Consumer { + @Override + public void accept(RawKeycloakDistribution distribution) { + distribution.removeProperty("db"); + } + } @Tag(DistributionTest.STORAGE) @Test diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/BeforeStartDistribution.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/BeforeStartDistribution.java index 25681b59c6a..9f55b36edf7 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/BeforeStartDistribution.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/BeforeStartDistribution.java @@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.function.Consumer; -import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawKeycloakDistribution; /** * {@link BeforeStartDistribution} is used to perform additional steps prior to starting the distribution. @@ -32,6 +32,6 @@ import org.keycloak.it.utils.KeycloakDistribution; @Retention(RetentionPolicy.RUNTIME) public @interface BeforeStartDistribution { - Class> value(); + Class> value(); } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java index 2a4037e8a7f..431faea5319 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/CLITestExtension.java @@ -22,6 +22,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import org.keycloak.Keycloak; @@ -29,7 +30,6 @@ import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.RawDistRootPath; import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.quarkus.runtime.Environment; -import org.keycloak.quarkus.runtime.cli.command.DryRunMixin; import org.keycloak.quarkus.runtime.cli.command.Start; import org.keycloak.quarkus.runtime.cli.command.StartDev; @@ -50,7 +50,7 @@ import static org.keycloak.quarkus.runtime.Environment.forceExitAfterStartLaunch public class CLITestExtension extends QuarkusMainTestExtension { - private KeycloakDistribution dist; + private KeycloakRunner runner; private DatabaseContainer databaseContainer; private InfinispanContainer infinispanContainer; private CLIResult result; @@ -74,9 +74,9 @@ public class CLITestExtension extends QuarkusMainTestExtension { }); } - if (isRaw() && distConfig != null && dist != null) { + if (isRaw() && distConfig != null && runner != null) { try { - dist.unwrap(RawKeycloakDistribution.class).reset(beforeAll); + runner.getDistribution(RawKeycloakDistribution.class).reset(beforeAll); beforeAll = false; } catch (Exception cause) { throw new RuntimeException("Failed to partially reset", cause); @@ -87,12 +87,10 @@ public class CLITestExtension extends QuarkusMainTestExtension { infinispanContainer = configureExternalInfinispan(context); if (distConfig != null) { - if (dist == null) { - dist = createDistribution(distConfig, getStoreConfig(context), getDatabaseConfig(context)); + if (runner == null) { + runner = createDistribution(distConfig); } - onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class), true); - copyTestProvider(context.getRequiredTestClass().getAnnotation(TestProvider.class)); copyTestProvider(context.getRequiredTestMethod().getAnnotation(TestProvider.class)); onBeforeStartDistribution(context.getRequiredTestClass().getAnnotation(BeforeStartDistribution.class)); @@ -100,19 +98,13 @@ public class CLITestExtension extends QuarkusMainTestExtension { configureEnvVars(context.getRequiredTestClass().getAnnotation(WithEnvVars.class)); configureEnvVars(context.getRequiredTestMethod().getAnnotation(WithEnvVars.class)); - boolean dryRun = context.getRequiredTestClass().getAnnotation(DryRun.class) != null - || context.getRequiredTestMethod().getAnnotation(DryRun.class) != null; - if (dryRun && isRaw()) { - dist.setEnvVar(DryRunMixin.KC_DRY_RUN_ENV, "true"); - dist.setEnvVar(DryRunMixin.KC_DRY_RUN_BUILD_ENV, "true"); - } - if (isRaw() && (context.getRequiredTestClass().getAnnotation(SkipRealmBootstrap.class) != null - || context.getRequiredTestMethod().getAnnotation(SkipRealmBootstrap.class) != null)) { - dist.unwrap(RawKeycloakDistribution.class).setLaunchMode(Environment.LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP); - } + + var stopServer = Optional.ofNullable(context.getRequiredTestMethod().getAnnotation(StopServer.class)).map(StopServer::value).orElse(distConfig.stopServer()); + + runner.setStopServer(stopServer); if (launch != null) { - result = dist.run(List.of(launch.value())); + result = runner.run(List.of(launch.value())); } } else { if (!Keycloak.initSys(launch == null ? new String[] {} : launch.value())) { @@ -123,10 +115,6 @@ public class CLITestExtension extends QuarkusMainTestExtension { } } - private static Storage getStoreConfig(ExtensionContext context) { - return context.getTestClass().get().getDeclaredAnnotation(Storage.class); - } - private void copyTestProvider(TestProvider provider) { if (provider == null) { return; @@ -134,7 +122,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { if (isRaw()) { try { - dist.unwrap(RawKeycloakDistribution.class).copyProvider(provider.value().getDeclaredConstructor().newInstance()); + runner.getDistribution(RawKeycloakDistribution.class).copyProvider(provider.value().getDeclaredConstructor().newInstance()); } catch (Exception cause) { throw new RuntimeException("Failed to instantiate test provider: " + provider.getClass(), cause); } @@ -148,7 +136,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { @Override public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { - if (dist == null) { + if (runner == null) { super.interceptTestMethod(invocation, invocationContext, extensionContext); } else { invocation.proceed(); @@ -158,34 +146,20 @@ public class CLITestExtension extends QuarkusMainTestExtension { private void onBeforeStartDistribution(BeforeStartDistribution annotation) { if (annotation != null) { try { - annotation.value().getDeclaredConstructor().newInstance().accept(dist); + annotation.value().getDeclaredConstructor().newInstance().accept(runner.getDistribution(RawKeycloakDistribution.class)); } catch (Exception cause) { throw new RuntimeException("Error when invoking " + annotation.value() + " instance before starting distribution", cause); } } } - private void onKeepServerAlive(KeepServerAlive annotation, boolean setting) { - if(annotation != null && dist != null) { - try { - dist.setManualStop(setting); - } catch (Exception cause) { - throw new RuntimeException("Error when invoking " + annotation, cause); - } - } - } - @Override public void afterEach(ExtensionContext context) throws Exception { DistributionTest distConfig = getDistributionConfig(context); - if (dist != null) { - onKeepServerAlive(context.getRequiredTestMethod().getAnnotation(KeepServerAlive.class), false); - dist.stop(); - dist.clearEnv(); - if (isRaw()) { - dist.unwrap(RawKeycloakDistribution.class).setLaunchMode(Environment.LAUNCH_MODE_EXIT_AFTER_START); - } + if (runner != null) { + runner.stop(); + runner.getDistribution().clearEnv(); } super.afterEach(context); @@ -229,7 +203,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { DistributionTest distConfig = getDistributionConfig(context); if (distConfig != null) { - dist = createDistribution(distConfig, getStoreConfig(context), getDatabaseConfig(context)); + runner = createDistribution(distConfig); } else { forceExitAfterStartLaunchMode(); } @@ -239,15 +213,15 @@ public class CLITestExtension extends QuarkusMainTestExtension { @Override public void afterAll(ExtensionContext context) throws Exception { - if (dist != null) { + if (runner != null) { // just to make sure the server is stopped after all tests - dist.stop(); + runner.stop(); } super.afterAll(context); } - private KeycloakDistribution createDistribution(DistributionTest config, Storage storeConfig, WithDatabase databaseConfig) { - return new KeycloakDistributionDecorator(storeConfig, databaseConfig, config, DistributionType.getCurrent().orElse(RAW).newInstance(config)); + private KeycloakRunner createDistribution(DistributionTest config) { + return new KeycloakRunner(config, DistributionType.getCurrent().orElse(RAW).newInstance(config)); } @Override @@ -274,12 +248,16 @@ public class CLITestExtension extends QuarkusMainTestExtension { //assuming the path to the distribution directory return getDistPath(); } + + if (type.equals(KeycloakRunner.class)) { + return this.runner; + } - if (type.equals(KeycloakDistribution.class)) { + if (KeycloakDistribution.class.isAssignableFrom(type)) { if (context.getTestClass().orElse(Object.class).getDeclaredAnnotation(DistributionTest.class) == null) { throw new RuntimeException("Only tests annotated with " + DistributionTest.class + " can inject a distribution instance"); } - return dist; + return runner.getDistribution((Class) type); } // for now, no support for manual launching using QuarkusMainLauncher @@ -290,7 +268,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { Class type = parameterContext.getParameter().getType(); - return type == LaunchResult.class || type == CLIResult.class || type == RawDistRootPath.class || type == KeycloakDistribution.class; + return type == LaunchResult.class || type == CLIResult.class || type == RawDistRootPath.class || type == KeycloakDistribution.class || type == KeycloakRunner.class; } private void configureProfile(ExtensionContext context) { @@ -310,7 +288,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { WithDatabase database = getDatabaseConfig(context); if (database != null) { - if (dist == null) { + if (runner == null) { configureDevServices(); setProperty("kc.db", database.alias()); setProperty("kc.db-password", DatabaseContainer.DEFAULT_PASSWORD); @@ -319,19 +297,20 @@ public class CLITestExtension extends QuarkusMainTestExtension { databaseContainer.start(); + RawKeycloakDistribution rawDist = runner.getDistribution(RawKeycloakDistribution.class); if (database.buildOptions().length == 0) { - dist.setProperty("db", database.alias()); + rawDist.setProperty("db", database.alias()); } else { for (String option : database.buildOptions()) { - dist.setProperty(option.substring(0, option.indexOf('=')), option.substring(option.indexOf('=') + 1)); + rawDist.setProperty(option.substring(0, option.indexOf('=')), option.substring(option.indexOf('=') + 1)); } } - databaseContainer.configureDistribution(dist); + databaseContainer.configureDistribution(rawDist); - dist.run("build"); + runner.run("build"); } - } else if (dist == null) { + } else if (runner == null) { // This is for re-creating the H2 database instead of using the default in home setProperty("kc.db-url-path", Keycloak.initTempDirectory("h2-home").toFile().getAbsolutePath()); } @@ -374,7 +353,7 @@ public class CLITestExtension extends QuarkusMainTestExtension { } for (int i=0; i + *

* Note with the raw distribution test methods are not completely isolated for performance reasons. * Only a single distribution will be installed and it will only have its augmentation state reset before each test class. + *

+ * Also the test logic assumes defaults, such as local cache mode (see {@link DistributionTest#localCache()} and + * a 0 second shutdown delay. */ @Target(ElementType.TYPE) @ExtendWith({ CLITestExtension.class }) @@ -42,16 +45,11 @@ public @interface DistributionTest { boolean debug() default false; /** - * If the distribution should be left running after the launch. + * Controls how the server stops. */ - boolean keepAlive() default false; + StopServer.Mode stopServer() default StopServer.Mode.AFTER_START; boolean enableTls() default false; - /** - * If any build option must be unset after the running the build command. - */ - boolean removeBuildOptionsAfterBuild() default false; - /** * If any option must be set when starting the server. */ @@ -66,4 +64,9 @@ public @interface DistributionTest { * Default port for making HTTP requests with RestAssured */ int requestPort() default 8080; + + /** + * If the cache should default to local even when the start command is used. + */ + boolean localCache() default true; } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DistributionType.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DistributionType.java index 9e7b932e2eb..0e2db6f77f8 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DistributionType.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DistributionType.java @@ -32,20 +32,11 @@ public enum DistributionType { private static KeycloakDistribution createDockerDistribution(DistributionTest config) { return new DockerKeycloakDistribution( - config.debug(), - config.keepAlive(), - config.requestPort(), config.containerExposedPorts()); } private static KeycloakDistribution createRawDistribution(DistributionTest config) { - return new RawKeycloakDistribution( - config.debug(), - config.keepAlive(), - config.enableTls(), - false, - config.removeBuildOptionsAfterBuild(), - config.requestPort()); + return new RawKeycloakDistribution(false); } private final Function factory; diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DryRun.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DryRun.java deleted file mode 100644 index 2983f754d12..00000000000 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DryRun.java +++ /dev/null @@ -1,32 +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.junit5.extension; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * {@link DryRun} is used to configure a non-running, non-augmenting distribution - */ -@Target({ ElementType.METHOD, ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface DryRun { - -} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakDistributionDecorator.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakDistributionDecorator.java deleted file mode 100644 index 6ea4b0c39db..00000000000 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakDistributionDecorator.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2022 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.junit5.extension; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.keycloak.it.utils.KeycloakDistribution; - -public class KeycloakDistributionDecorator implements KeycloakDistribution { - - private Storage storageConfig; - private WithDatabase databaseConfig; - private DistributionTest config; - private KeycloakDistribution delegate; - - public KeycloakDistributionDecorator(Storage storageConfig, WithDatabase databaseConfig, DistributionTest config, - KeycloakDistribution delegate) { - this.storageConfig = storageConfig; - this.databaseConfig = databaseConfig; - this.config = config; - this.delegate = delegate; - } - - @Override - public CLIResult run(List rawArgs) { - List args = new ArrayList<>(rawArgs); - args.addAll(List.of(config.defaultOptions())); - setEnvVar("KC_SHUTDOWN_DELAY", "0s"); - return delegate.run(new ServerOptions(storageConfig, databaseConfig, args)); - } - - @Override - public void stop() { - delegate.stop(); - } - - @Override - public List getOutputStream() { - return delegate.getOutputStream(); - } - - @Override - public List getErrorStream() { - return delegate.getErrorStream(); - } - - @Override - public int getExitCode() { - return delegate.getExitCode(); - } - - @Override - public boolean isDebug() { - return delegate.isDebug(); - } - - @Override - public boolean isManualStop() { - return delegate.isManualStop(); - } - - @Override - public String[] getCliArgs(List arguments) { - return delegate.getCliArgs(arguments); - } - - @Override - public void setManualStop(boolean manualStop) { - delegate.setManualStop(manualStop); - } - - @Override - public void setQuarkusProperty(String key, String value) { - delegate.setQuarkusProperty(key, value); - } - - @Override - public void setProperty(String key, String value) { - delegate.setProperty(key, value); - } - - @Override - public void deleteQuarkusProperties() { - delegate.deleteQuarkusProperties(); - } - - @Override - public void copyOrReplaceFileFromClasspath(String file, Path distDir) { - delegate.copyOrReplaceFileFromClasspath(file, distDir); - } - - @Override - public void removeProperty(String name) { - delegate.removeProperty(name); - } - - @Override - public void setEnvVar(String name, String value) { - delegate.setEnvVar(name, value); - } - - @Override - public void copyOrReplaceFile(Path file, Path targetFile) { - delegate.copyOrReplaceFile(file, targetFile); - } - - @Override - public void setRequestPort() { - delegate.setRequestPort(); - } - - @Override - public void setRequestPort(int port) { - delegate.setRequestPort(port); - } - - @Override - public D unwrap(Class type) { - if (!KeycloakDistribution.class.isAssignableFrom(type)) { - throw new IllegalArgumentException("Not a " + KeycloakDistribution.class + " type"); - } - - if (type.isInstance(delegate)) { - //noinspection unchecked - return (D) delegate; - } - - throw new IllegalArgumentException("Not a " + type + " type"); - } - - @Override - public void clearEnv() { - delegate.clearEnv(); - } -} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakRunner.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakRunner.java new file mode 100644 index 00000000000..d400e79d0a4 --- /dev/null +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeycloakRunner.java @@ -0,0 +1,134 @@ +/* + * Copyright 2022 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.junit5.extension; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.keycloak.it.junit5.extension.StopServer.Mode; +import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawKeycloakDistribution; +import org.keycloak.quarkus.runtime.cli.command.DryRunMixin; + +import io.restassured.RestAssured; + +import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE; +import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE_EXIT_AFTER_START; +import static org.keycloak.quarkus.runtime.Environment.LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP; + +/** + * Wraps the distribution to provide a run methods that configure the distribution + * to match the expectations of the test related annotations. + */ +public class KeycloakRunner { + + private DistributionTest config; + private KeycloakDistribution delegate; + private StopServer.Mode stopServer; + private long startTimeout = TimeUnit.SECONDS.toMillis(Long.getLong("keycloak.distribution.start.timeout", 120L)); + + public KeycloakRunner(DistributionTest config, + KeycloakDistribution delegate) { + this.config = config; + this.delegate = delegate; + } + + public CLIResult run(String ... rawArgs) { + return run(List.of(rawArgs)); + } + + public CLIResult run(List rawArgs) { + delegate.stop(); + + List args = new ArrayList<>(rawArgs); + args.addAll(List.of(config.defaultOptions())); + if (config.debug() && delegate.supportsDebug()) { + delegate.setEnvVar("KC_DEBUG", "true"); + delegate.setEnvVar("KC_DEBUG_SUSPEND", "y"); + } + delegate.setEnvVar("KC_SHUTDOWN_DELAY", "0s"); + if (config.localCache()) { + delegate.setEnvVar("KC_CACHE", "local"); + } else { + args.add("-Djgroups.join_timeout=50"); + } + if (stopServer == Mode.BEFORE_QUARKUS) { + delegate.setEnvVar(DryRunMixin.KC_DRY_RUN_ENV, "true"); + delegate.setEnvVar(DryRunMixin.KC_DRY_RUN_BUILD_ENV, "true"); + } else if (stopServer != Mode.MANUAL) { + args.add("-D" + LAUNCH_MODE + "=" + (stopServer == Mode.BEFORE_BOOTSTRAP ? LAUNCH_MODE_EXIT_BEFORE_BOOTSTRAP : LAUNCH_MODE_EXIT_AFTER_START)); + } + + if (config.enableTls()) { + getDistribution(RawKeycloakDistribution.class).copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore")); + } + try { + delegate.runKc(args); + delegate.waitFor(stopServer == Mode.MANUAL, startTimeout); + } finally { + if (stopServer != Mode.MANUAL) { + delegate.stop(); + } + } + + setRequestPort(config.requestPort()); + + return CLIResult.create(delegate.getOutputStream(), delegate.getErrorStream(), delegate.getExitCode()); + } + + public void stop() { + delegate.stop(); + } + + public void setStopServer(Mode mode) { + this.stopServer = mode; + } + + public void setRequestPort(int port) { + RestAssured.port = delegate.getMappedPort(port); + } + + /** + * Get the underlying distribution - NOTE directly calling {@link KeycloakDistribution#runKc(List)} + * on the unwrapped distribution is not recommended. The {@link #run(List)} methods on class should be used + * instead as they ensure the arguments and env match the expectations of the test and method annotations + */ + public D getDistribution(Class type) { + if (!KeycloakDistribution.class.isAssignableFrom(type)) { + throw new IllegalArgumentException("Not a " + KeycloakDistribution.class + " type"); + } + + if (type.isInstance(delegate)) { + //noinspection unchecked + return (D) delegate; + } + + throw new IllegalArgumentException("Not a " + type + " type"); + } + + public KeycloakDistribution getDistribution() { + return this.delegate; + } + + public void setEnvVar(String key, String value) { + this.delegate.setEnvVar(key, value); + } + +} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/ServerOptions.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/ServerOptions.java deleted file mode 100644 index b9f17ffa797..00000000000 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/ServerOptions.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2022 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.junit5.extension; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; - -import org.keycloak.quarkus.runtime.cli.command.Export; -import org.keycloak.quarkus.runtime.cli.command.Import; -import org.keycloak.quarkus.runtime.cli.command.ShowConfig; - -import static org.keycloak.quarkus.runtime.cli.command.AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG; - -final class ServerOptions extends ArrayList { - - private static final Predicate IGNORED_ARGUMENTS = ((Predicate) s -> false) - .or(OPTIMIZED_BUILD_OPTION_LONG::equals) - .or(Export.NAME::equals) - .or(Import.NAME::equals) - .or("--help"::equals) - .or("--help-all"::equals) - .or("-h"::equals) - .or(ShowConfig.NAME::equals); - - private boolean isBuildPhase = false; - - ServerOptions(Storage storageConfig, WithDatabase withDatabase, List rawOptions) { - if (rawOptions.isEmpty()) { - return; - } - - this.isBuildPhase = rawOptions.contains("build"); - - for (Map.Entry> entry : getDefaultOptions(storageConfig, withDatabase).entrySet()) { - if (contains(entry.getKey())) { - continue; - } - - if (!rawOptions.stream().anyMatch(entry.getValue())) { - add(entry.getKey()); - } - } - - addAll(0, rawOptions); - } - - private Map> getDefaultOptions(Storage storageConfig, WithDatabase withDatabase) { - Map> defaultOptions = new HashMap<>(); - - if (!isBuildPhase) { - defaultOptions.put("--cache=local", ignoreCacheLocal(storageConfig)); - } - - return defaultOptions; - } - - private Predicate ignoreCacheLocal(Storage storageConfig) { - return new Predicate() { - @Override - public boolean test(String arg) { - return arg.contains("--cache") || storageConfig == null || !storageConfig.defaultLocalCache(); - } - }.or(IGNORED_ARGUMENTS); - } - -} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/SkipRealmBootstrap.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/SkipRealmBootstrap.java deleted file mode 100644 index 5a00592f49f..00000000000 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/SkipRealmBootstrap.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.keycloak.it.junit5.extension; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Skips realm bootstrap (DB lock, realm creation, admin user setup) after provider initialization. - * Liquibase schema migration and provider {@code postInit()} still run. - * Useful for tests that only assert on log messages from server startup and don't need realm data. - */ -@Target({ ElementType.METHOD, ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface SkipRealmBootstrap { - -} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeepServerAlive.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/StopServer.java similarity index 50% rename from quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeepServerAlive.java rename to quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/StopServer.java index 364f0e7e754..3bad6f29d0e 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/KeepServerAlive.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/StopServer.java @@ -22,13 +22,37 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.keycloak.it.utils.KeycloakDistribution; + /** - * {@link KeepServerAlive} is used in a distributiontest to keep the server alive on test / method level. - * Used when when {@link DistributionTest} is invoked on class level not using keepAlive=true param, but - * for a specific test we need to run e.g. RestAssured verifications. + * {@link StopServer} is used to control when a distribution server stops at a + * method level. */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) -public @interface KeepServerAlive { +public @interface StopServer { + + public enum Mode { + /** + * Stops the server process before quarkus augmentation or startup. + */ + BEFORE_QUARKUS, + /** + * Stops the server process after database initialization, but before + * bootstrapping (creating the master realm, boostrap admin, etc.). + */ + BEFORE_BOOTSTRAP, + /** + * Stops the server immediately after it successfully starts. + */ + AFTER_START, + /** + * Server will stop if {@link KeycloakDistribution#stop()} is called, another run is called. + * If the server is still running at the end of the method, it will be stopped automatically. + */ + MANUAL + } + + Mode value() default Mode.AFTER_START; } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/Storage.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/Storage.java deleted file mode 100644 index cf7818eb051..00000000000 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/Storage.java +++ /dev/null @@ -1,39 +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.junit5.extension; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.extension.ExtendWith; - -/** - * Use this annotation to change the default storage configuration when running a test. - */ -@Target(ElementType.TYPE) -@ExtendWith({ CLITestExtension.class }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Storage { - - /** - * If {@code true}, the cache is set to local by default. - */ - boolean defaultLocalCache() default true; -} diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/DockerKeycloakDistribution.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/DockerKeycloakDistribution.java index a6185515e6b..9d9a523f821 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/DockerKeycloakDistribution.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/DockerKeycloakDistribution.java @@ -12,11 +12,9 @@ import java.util.function.Consumer; import java.util.stream.IntStream; import org.keycloak.common.Version; -import org.keycloak.it.junit5.extension.CLIResult; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.exception.NotFoundException; -import io.restassured.RestAssured; import org.jboss.logging.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; @@ -59,9 +57,6 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { public static final int STARTUP_TIMEOUT_SECONDS = 120; - private final boolean debug; - private final boolean manualStop; - private final int requestPort; private final Integer[] exposedPorts; private int exitCode = -1; @@ -79,17 +74,19 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { private final Map copyToContainer = new HashMap<>(); - public DockerKeycloakDistribution(boolean debug, boolean manualStop, int requestPort, int[] exposedPorts) { - this(debug, manualStop, requestPort, exposedPorts, null); + public DockerKeycloakDistribution(int[] exposedPorts) { + this(exposedPorts, null); } - public DockerKeycloakDistribution(boolean debug, boolean manualStop, int requestPort, int[] exposedPorts, LazyFuture image) { - this.debug = debug; - this.manualStop = manualStop; - this.requestPort = requestPort; + public DockerKeycloakDistribution(int[] exposedPorts, LazyFuture image) { this.exposedPorts = IntStream.of(exposedPorts).boxed().toArray(Integer[]::new); this.image = image == null ? createImage(false) : image; } + + @Override + public boolean supportsDebug() { + return false; + } @Override public void setEnvVar(String name, String value) { @@ -150,8 +147,10 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { } @Override - public CLIResult run(List arguments) { - stop(); + public void runKc(List arguments) { + if (keycloakContainer != null) { + throw new IllegalStateException("Stop has not been called"); + } try { this.exitCode = -1; this.stdout = ""; @@ -168,35 +167,17 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { .withCommand(arguments.toArray(new String[0])) .start(); containerId = keycloakContainer.getContainerId(); - - waitForStableOutput(); } catch (Exception cause) { this.exitCode = -1; this.stdout = backupConsumer.stdOut.toUtf8String(); this.stderr = backupConsumer.stdErr.toUtf8String(); - cleanupContainer(); - keycloakContainer = null; - LOGGER.warn("Failed to start Keycloak container", cause); - } finally { - if (!manualStop) { - stop(); + try { + cleanupContainer(); + } catch (Exception stopException) { + cause.addSuppressed(stopException); } - } - - setRequestPort(); - - return CLIResult.create(getOutputStream(), getErrorStream(), getExitCode()); - } - - @Override - public void setRequestPort() { - setRequestPort(requestPort); - } - - @Override - public void setRequestPort(int port) { - if (keycloakContainer != null) { - RestAssured.port = keycloakContainer.getMappedPort(port); + keycloakContainer = null; + throw new RuntimeException("Failed to start the server", cause); } } @@ -212,9 +193,11 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { public void copyConfigFile(Path configFilePath) { copyToContainer.put(MountableFile.forHostPath(configFilePath), "/opt/keycloak/conf/" + configFilePath.getFileName()); } - + // After the web server is responding we are still producing some logs that got checked in the tests - private void waitForStableOutput() { + @Override + public void waitFor(boolean ready, long timeoutMillis) { + // TODO: doesn't differentiate ready, nor implements the timeout int retry = 10; String lastLine = ""; boolean stableOutput = false; @@ -319,34 +302,12 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution { return this.exitCode; } - @Override - public boolean isDebug() { - return this.debug; - } - - @Override - public boolean isManualStop() { - return this.manualStop; - } - - @Override - public D unwrap(Class type) { - if (!KeycloakDistribution.class.isAssignableFrom(type)) { - throw new IllegalArgumentException("Not a " + KeycloakDistribution.class + " type"); - } - - if (type.isInstance(this)) { - return type.cast(this); - } - - throw new IllegalArgumentException("Not a " + type + " type"); - } - @Override public void clearEnv() { this.envVars.clear(); } + @Override public int getMappedPort(int port) { if (keycloakContainer == null || !keycloakContainer.isRunning()) { throw new IllegalStateException("KeycloakContainer is not running."); diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/KeycloakDistribution.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/KeycloakDistribution.java index 5ca854ab707..4ac93681890 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/KeycloakDistribution.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/KeycloakDistribution.java @@ -3,7 +3,6 @@ package org.keycloak.it.utils; import java.nio.file.Path; import java.util.List; -import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.quarkus.runtime.Environment; public interface KeycloakDistribution { @@ -12,64 +11,40 @@ public interface KeycloakDistribution { String SCRIPT_KCADM_CMD = Environment.isWindows() ? "kcadm.bat" : "kcadm.sh"; - CLIResult run(List arguments); - default CLIResult run(String... arguments) { - return run(List.of(arguments)); + /** + * Run the kc command and immediately return without waiting for the process + */ + void runKc(List arguments); + /** + * Run the kc command and immediately return without waiting for the process + */ + default void runKc(String... arguments) { + runKc(List.of(arguments)); } - + + void waitFor(boolean ready, long timeoutMillis); + void stop(); + + int getMappedPort(int port); List getOutputStream(); List getErrorStream(); + /** + * Available after the main process exits, which may require {@link #stop()} to be called + */ int getExitCode(); + + boolean supportsDebug(); - boolean isDebug(); - - boolean isManualStop(); - - void setRequestPort(); - - void setRequestPort(int port); - - default String[] getCliArgs(List arguments) { - throw new RuntimeException("Not implemented"); - } - - default void setManualStop(boolean manualStop) { - throw new RuntimeException("Not implemented"); - } - - default void setQuarkusProperty(String key, String value) { - throw new RuntimeException("Not implemented"); - } - - default void setProperty(String key, String value) { - throw new RuntimeException("Not implemented"); - } - - default void deleteQuarkusProperties() { - throw new RuntimeException("Not implemented"); - } - - default void copyOrReplaceFileFromClasspath(String file, Path distDir) { - throw new RuntimeException("Not implemented"); - } - - default void removeProperty(String name) { - throw new RuntimeException("Not implemented"); - } - - default void setEnvVar(String name, String value) { - throw new RuntimeException("Not implemented"); - } - - default void copyOrReplaceFile(Path file, Path targetFile) { - throw new RuntimeException("Not implemented"); - } - - D unwrap(Class type); + void setEnvVar(String name, String value); void clearEnv(); + + void copyProvider(String groupId, String artifactId); + + void copyConfigFile(Path configFilePath); + } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawDistributionLifecycleManager.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawDistributionLifecycleManager.java index 9351eb8b61a..608a79560c4 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawDistributionLifecycleManager.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawDistributionLifecycleManager.java @@ -13,13 +13,7 @@ public class RawDistributionLifecycleManager implements QuarkusTestResourceLifec @Override public Map start() { - dist = new RawKeycloakDistribution( - false, - false, - false, - true, - false, - 8080); + dist = new RawKeycloakDistribution(true); return null; } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java index 3c3119409a2..4b9ce2e42bb 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java @@ -24,18 +24,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.UncheckedIOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -45,31 +39,24 @@ import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.LockSupport; import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import org.keycloak.common.Version; import org.keycloak.it.TestProvider; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.quarkus.runtime.Environment; -import org.keycloak.quarkus.runtime.cli.command.Build; -import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; -import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; +import org.keycloak.quarkus.runtime.KeycloakMain; import io.quarkus.deployment.util.FileUtil; import io.quarkus.fs.util.ZipUtils; -import io.restassured.RestAssured; import org.apache.commons.io.FileUtils; import org.awaitility.Awaitility; import org.jboss.logging.Logger; @@ -91,31 +78,12 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { private Process keycloak; private int exitCode = -1; private Path distPath; - private boolean manualStop; - private String relativePath; - private int httpPort; - private int httpsPort; - private final boolean debug; - private final boolean enableTls; - private final boolean reCreate; - private final boolean removeBuildOptionsAfterBuild; - private final int requestPort; private ExecutorService outputExecutor; private final Map envVars = new HashMap<>(); - private final OutputConsumer outputConsumer; - private long startTimeout = TimeUnit.SECONDS.toMillis(Long.getLong("keycloak.distribution.start.timeout", 120L)); - private boolean throwErrorIfFailedToStart = false; - private boolean threadDump = true; - private String launchMode = Environment.LAUNCH_MODE_EXIT_AFTER_START; + private final DefaultOutputConsumer outputConsumer; - public RawKeycloakDistribution(boolean debug, boolean manualStop, boolean enableTls, boolean reCreate, boolean removeBuildOptionsAfterBuild, int requestPort) { - this.debug = debug; - this.manualStop = manualStop; - this.enableTls = enableTls; - this.reCreate = reCreate; - this.removeBuildOptionsAfterBuild = removeBuildOptionsAfterBuild; - this.requestPort = requestPort; - this.distPath = prepareDistribution(); + public RawKeycloakDistribution(boolean reCreate) { + this.distPath = prepareDistribution(reCreate); if (reCreate) { try { preInitH2(distPath); @@ -125,20 +93,34 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } this.outputConsumer = new DefaultOutputConsumer(); } - - public RawKeycloakDistribution withThrowErrorIfFailedToStart(boolean throwErrorIfFailedToStart) { - this.throwErrorIfFailedToStart = throwErrorIfFailedToStart; - return this; + + @Override + public int getMappedPort(int port) { + return port; + } + + @Override + public synchronized void waitFor(boolean ready, long timeoutMillis) { + if (!isRunning()) { + return; + } + try { + if (ready) { + this.outputConsumer.running.get(timeoutMillis, TimeUnit.MILLISECONDS); + } else { + this.keycloak.onExit().get(timeoutMillis, TimeUnit.MILLISECONDS); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException|TimeoutException e) { + throw new RuntimeException(e); + } } - public RawKeycloakDistribution withThreadDump(boolean threadDump) { - this.threadDump = threadDump; - return this; - } - - public RawKeycloakDistribution withStartTimeout(long startTimeout) { - this.startTimeout = startTimeout; - return this; + @Override + public boolean supportsDebug() { + return true; } public CLIResult kc(String... arguments) throws IOException { @@ -160,22 +142,13 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { private CLIResult invoke(String script, List arguments) throws IOException { List allArgs = new ArrayList<>(); - invoke(allArgs, script); - - if (this.isDebug()) { - allArgs.add("-x"); - } + addPlatformSpecificCommand(allArgs, script); allArgs.addAll(arguments); ProcessBuilder pb = new ProcessBuilder(allArgs); ProcessBuilder builder = pb.directory(distPath.resolve("bin").toFile()); - // TODO: it is possible to debug kcadm, but it's more involved - /*if (debug) { - builder.environment().put("DEBUG_SUSPEND", "y"); - }*/ - addAOTEnvVars(); builder.environment().putAll(envVars); @@ -190,30 +163,23 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { return CLIResult.create(outputConsumer.getStdOut(), outputConsumer.getErrOut(), exitValue); } - private void invoke(List allArgs, String cmd) { + private void addPlatformSpecificCommand(List allArgs, String cmd) { if (isWindows()) { allArgs.add(distPath.resolve("bin") + File.separator + cmd); } else { allArgs.add("./" + cmd); } } - - @Override - public CLIResult run(List arguments) { - stop(); - if (manualStop && isRunning()) { - throw new IllegalStateException("Server already running. You should manually stop the server before starting it again."); + + @Override + public void runKc(List arguments) { + if (isRunning()) { + throw new IllegalStateException("Stop has not been called"); } - reset(); + resetForNextRun(); try { - configureServer(); startServer(arguments); - if (manualStop) { - asyncReadOutput(); - waitForReadiness(); - } else { - readOutput(); - } + asyncReadOutput(); } catch (Exception cause) { try { stop(); @@ -221,27 +187,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { cause.addSuppressed(stopException); } throw new RuntimeException("Failed to start the server", cause); - } finally { - if (arguments.contains(Build.NAME) && removeBuildOptionsAfterBuild) { - for (List> mappers : PropertyMappers.getBuildTimeMappers().values()) { - for (PropertyMapper mapper : mappers) { - removeProperty(mapper.getFrom().substring(3)); - } - } - } - if (!manualStop) { - stop(); - } - } - - setRequestPort(); - - return CLIResult.create(getOutputStream(), getErrorStream(), getExitCode()); - } - - private void configureServer() { - if (enableTls) { - copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore")); } } @@ -325,124 +270,14 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { return exitCode; } - @Override - public boolean isDebug() { return this.debug; } - - @Override - public boolean isManualStop() { return this.manualStop; } - - @Override - public String[] getCliArgs(List arguments) { - List allArgs = new ArrayList<>(); - - invoke(allArgs, SCRIPT_CMD); - - if (this.isDebug()) { - allArgs.add("--debug"); - } - - if (!this.isManualStop()) { - allArgs.add("-D" + LAUNCH_MODE + "=" + launchMode); - } - - allArgs.add("-Djgroups.join_timeout=50"); - - 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.httpsPort = Integer.parseInt(arguments.stream().filter(arg -> arg.startsWith("--https-port")).map(arg -> arg.substring(arg.indexOf('=') + 1)).findAny().orElse("8443")); - - allArgs.add("-Dkc.home.dir=" + distPath + File.separator); - allArgs.addAll(arguments); - - return allArgs.toArray(String[]::new); - } - - @Override - public void setRequestPort() { - setRequestPort(requestPort); - } - - @Override - public void setRequestPort(int port) { - RestAssured.port = port; - } - - private void waitForReadiness() throws MalformedURLException { - waitForReadiness("http", httpPort); - - if (enableTls) { - waitForReadiness("https", httpsPort); - } - } - - private void waitForReadiness(String scheme, int port) throws MalformedURLException { - var myRelativePath = relativePath; - if (!myRelativePath.endsWith("/")) { - myRelativePath += "/"; - } - URL contextRoot = new URL(scheme + "://localhost:" + port + myRelativePath + "realms/master/"); - HttpURLConnection connection = null; - long startTime = System.currentTimeMillis(); - Exception ex = null; - - while (true) { - if (System.currentTimeMillis() - startTime > getStartTimeout()) { - threadDump(); - throw new IllegalStateException( - "Timeout [" + getStartTimeout() + "] while waiting for Quarkus server", ex); - } - - if (!keycloak.isAlive()) { - if (throwErrorIfFailedToStart) { - throw new RuntimeException("Keycloak failed to start: process terminated"); - } else { - return; - } - } - - try { - // wait before checking for opening a new connection - 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) { - break; - } - } catch (Exception ignore) { - ex = ignore; - } finally { - if (connection != null) { - connection.disconnect(); - } - try { - Thread.sleep(1000); - } catch (Exception ignore) { - } - } - } - } - private void threadDump() { - if (!threadDump) { - return; - } - if (Environment.isWindows()) { return; } try { ProcessBuilder builder = new ProcessBuilder("kill", "-3", String.valueOf(keycloak.pid())); Process p = builder.start(); - p.onExit().get(getStartTimeout(), TimeUnit.MILLISECONDS); + p.onExit().get(DEFAULT_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (Exception e) { LOG.warn("A thread dump may not have been successfully triggered", e); return; @@ -451,43 +286,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { .until(() -> getOutputStream().stream().anyMatch(s -> s.contains("JNI global refs"))); } - private long getStartTimeout() { - return startTimeout; - } - - private HostnameVerifier createInsecureHostnameVerifier() { - return (s, sslSession) -> true; - } - - private SSLSocketFactory createInsecureSslSocketFactory() throws IOException { - TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { - @Override - public void checkClientTrusted(final X509Certificate[] chain, final String authType) { - } - - @Override - public void checkServerTrusted(final X509Certificate[] chain, final String authType) { - } - - @Override - 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; - } - public boolean isRunning() { return keycloak != null && keycloak.isAlive(); } @@ -511,7 +309,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } - private void reset() { + private void resetForNextRun() { outputConsumer.reset(); exitCode = -1; shutdownOutputExecutor(); @@ -533,7 +331,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { throw new RuntimeException(String.format("ZIP file '%s' doesn't contain any directories", distPath)); } - private Path prepareDistribution() { + public Path prepareDistribution(boolean reCreate) { try { Path distRootPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve("kc-tests"); distRootPath.toFile().mkdirs(); @@ -575,7 +373,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { private void preInitH2(Path dPath) throws IOException { ProcessHandle.current().info().command().ifPresent(command -> { - Runtime.Version runtimeVersion = Runtime.version(); boolean useAot = false; if (Boolean.getBoolean("kc.quarkus.tests.aot")) { if (Runtime.version().feature() < 25) { @@ -658,18 +455,25 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { * @throws Exception if something bad happens */ private void startServer(List arguments) throws Exception { - ProcessBuilder pb = new ProcessBuilder(getCliArgs(arguments)); + List allArgs = new ArrayList<>(); + + addPlatformSpecificCommand(allArgs, SCRIPT_CMD); + + // used to detect readiness rather than http(s) probing + allArgs.add("-D%s=true".formatted(KeycloakMain.KC_SERVER_PRINT_RUNNING)); + + allArgs.addAll(arguments); + + ProcessBuilder pb = new ProcessBuilder(allArgs); ProcessBuilder builder = pb.directory(distPath.resolve("bin").toFile()); - if (debug) { - builder.environment().put("DEBUG_SUSPEND", "y"); - } - addAOTEnvVars(); builder.environment().putAll(envVars); keycloak = builder.start(); + var future = outputConsumer.running; + keycloak.onExit().whenComplete((p, t) -> future.complete(null)); } private void addAOTEnvVars() { @@ -681,12 +485,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } - @Override - public void setManualStop(boolean manualStop) { - this.manualStop = manualStop; - } - - @Override public void setProperty(String key, String value) { updateProperties(properties -> properties.put(key, value), distPath.resolve("conf").resolve("keycloak.conf").toFile()); } @@ -696,21 +494,14 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { this.envVars.put(name, value); } - public void setLaunchMode(String launchMode) { - this.launchMode = launchMode; - } - - @Override public void removeProperty(String name) { updateProperties(properties -> properties.remove(name), distPath.resolve("conf").resolve("keycloak.conf").toFile()); } - @Override public void setQuarkusProperty(String key, String value) { updateProperties(properties -> properties.put(key, value), getQuarkusPropertiesFile()); } - @Override public void deleteQuarkusProperties() { File file = getQuarkusPropertiesFile(); @@ -719,7 +510,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } - @Override public void copyOrReplaceFileFromClasspath(String file, Path targetFile) { Path path = distPath.resolve(targetFile); @@ -732,7 +522,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } - @Override public void copyOrReplaceFile(Path file, Path targetFile) { if (!file.toFile().exists()) { return; @@ -749,6 +538,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } + @Override public void copyProvider(String groupId, String artifactId) { copyProvider(getDistPath(), groupId, artifactId); } @@ -766,6 +556,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { } } + @Override public void copyConfigFile(Path configFilePath) { try { Files.copy(configFilePath, distPath.resolve("conf").resolve(configFilePath.getFileName())); @@ -833,19 +624,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { providerJar.as(ZipExporter.class).exportTo(getDistPath().resolve("providers").resolve(providerJar.getName()).toFile()); } - @Override - public D unwrap(Class type) { - if (!KeycloakDistribution.class.isAssignableFrom(type)) { - throw new IllegalArgumentException("Not a " + KeycloakDistribution.class + " type"); - } - - if (type.isInstance(this)) { - return type.cast(type); - } - - throw new IllegalArgumentException("Not a " + type + " type"); - } - @Override public void clearEnv() { this.envVars.clear(); @@ -855,9 +633,13 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { private final List stdOut = Collections.synchronizedList(new ArrayList<>()); private final List errOut = Collections.synchronizedList(new ArrayList<>()); - + private CompletableFuture running = new CompletableFuture(); + @Override public void onStdOut(String line) { + if (line.equals(KeycloakMain.RUNNING_MESSAGE)) { + running.complete(null); + } System.out.println(line); stdOut.add(line); } @@ -872,6 +654,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { public void reset() { stdOut.clear(); errOut.clear(); + this.running = new CompletableFuture<>(); } @Override @@ -883,6 +666,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { public List getStdOut() { return stdOut; } + } public void resetH2Dir() throws IOException { diff --git a/test-framework/clustering/src/main/java/org/keycloak/testframework/server/ClusteredKeycloakServer.java b/test-framework/clustering/src/main/java/org/keycloak/testframework/server/ClusteredKeycloakServer.java index f8215a2980b..4e47f72dd29 100644 --- a/test-framework/clustering/src/main/java/org/keycloak/testframework/server/ClusteredKeycloakServer.java +++ b/test-framework/clustering/src/main/java/org/keycloak/testframework/server/ClusteredKeycloakServer.java @@ -35,7 +35,6 @@ import org.testcontainers.utility.LazyFuture; public class ClusteredKeycloakServer implements KeycloakServer { private static final String CLUSTER_VIEW_REGEX = ".*ISPN000093.*(?<=\\()(%1$d)(?=\\)).*|.*ISPN000094.*(?<=\\()(%1$d)(?=\\)).*"; - private static final boolean MANUAL_STOP = true; private static final int REQUEST_PORT = 8080; private static final int MANAGEMENT_PORT = 9000; public static final String SNAPSHOT_IMAGE = "-"; @@ -100,13 +99,13 @@ public class ClusteredKeycloakServer implements KeycloakServer { } else { resolvedImage = new RemoteDockerImage(DockerImageName.parse(imagePeServer[i])); } - var container = new DockerKeycloakDistribution(false, MANUAL_STOP, REQUEST_PORT, exposedPorts, resolvedImage); + var container = new DockerKeycloakDistribution(exposedPorts, resolvedImage); containers[i] = container; copyProvidersAndConfigs(container, configBuilder); configureLogConsumers(container, i, clusterLatch); - container.run(configBuilder.toArgs()); + container.runKc(configBuilder.toArgs()); } } @@ -116,12 +115,12 @@ public class ClusteredKeycloakServer implements KeycloakServer { defaultImage() : new RemoteDockerImage(DockerImageName.parse(image)); for (int i = 0; i < containers.length; ++i) { - var container = new DockerKeycloakDistribution(false, MANUAL_STOP, REQUEST_PORT, exposedPorts, imageFuture); + var container = new DockerKeycloakDistribution(exposedPorts, imageFuture); containers[i] = container; copyProvidersAndConfigs(container, configBuilder); configureLogConsumers(container, i, clusterLatch); - container.run(configBuilder.toArgs()); + container.runKc(configBuilder.toArgs()); } }