Created
October 28, 2021 08:55
-
-
Save xgp/a23f3357ef977c70f6dc6e74a3b3f2e0 to your computer and use it in GitHub Desktop.
JUnit4 Keycloak Rule
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.hamcrest.CoreMatchers.*; | |
import static org.junit.Assert.*; | |
import org.junit.BeforeClass; | |
import org.junit.ClassRule; | |
import org.junit.Test; | |
import org.keycloak.admin.client.Keycloak; | |
import org.keycloak.representations.idm.RealmRepresentation; | |
public class ExampleTest { | |
@ClassRule public static KeycloakSuite server = KeycloakSuite.SERVER; | |
@BeforeClass | |
public static void commonSetup() { | |
Keycloak keycloak = server.client(); | |
//do setup here that is common to all tests in this class | |
} | |
@Test | |
public void exampleTest() { | |
Keycloak keycloak = server.client(); | |
RealmRepresentation realm = keycloak.realm("master").toRepresentation(); | |
assertThat(realm.getId(), is("master")); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.collect.ObjectArrays; | |
import com.google.common.io.MoreFiles; | |
import com.google.common.io.RecursiveDeleteOption; | |
import java.io.IOException; | |
import java.net.ServerSocket; | |
import java.nio.file.Files; | |
import java.nio.file.Path; | |
import java.nio.file.Paths; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
import org.junit.rules.TestRule; | |
import org.junit.runner.Description; | |
import org.junit.runners.model.Statement; | |
import org.keycloak.OAuth2Constants; | |
import org.keycloak.admin.client.Keycloak; | |
import org.keycloak.admin.client.KeycloakBuilder; | |
import org.keycloak.testsuite.KeycloakServer; | |
public class KeycloakSuite implements TestRule { | |
public static final KeycloakSuite SERVER = new KeycloakSuite(); | |
private final AtomicBoolean initialized = new AtomicBoolean(); | |
private KeycloakServer keycloak; | |
private int port; | |
@Override | |
public synchronized Statement apply(Statement base, Description description) { | |
if (!initialized.get()) { | |
init(); | |
initialized.set(true); | |
} | |
return base; | |
} | |
public KeycloakServer getKeycloak() { | |
return this.keycloak; | |
} | |
public int getPort() { | |
return this.port; | |
} | |
public String getAuthUrl() { | |
return String.format("%sauth", getRootUrl()); | |
} | |
public String getRootUrl() { | |
return String.format("http://127.0.0.1:%d/", getPort()); | |
} | |
private static final String[] ARGS = {}; | |
private static final String[] PROPS = { | |
"keycloak.bind.address=127.0.0.1", | |
"java.net.preferIPv4Stack=true", | |
"keycloak.connectionsJpa.url=jdbc:h2:file:./target/data/keycloak_4_x_master", | |
"keycloak.connectionsJpa.driver=org.h2.Driver", | |
"keycloak.connectionsJpa.driverDialect=org.hibernate.dialect.H2Dialect", | |
"keycloak.connectionsJpa.user=sa", | |
"keycloak.connectionsJpa.password=", | |
"profile=COMMUNITY", | |
"product.default-profile=COMMUNITY", | |
"keycloak.password.blacklists.path=./target/data/blacklists/", | |
"com.sun.net.ssl.checkRevocation=false", | |
"keycloak.product.name=keycloak", | |
"product.name=keycloak", | |
"keycloak.profile=preview", | |
"keycloak.profile.feature.account_api=enabled", | |
"keycloak.profile.feature.account2=enabled", | |
"keycloak.profile.feature.scripts=enabled" | |
}; | |
// "product.version=${keycloak.version}", | |
private void setSystemProps(String[] props) { | |
for (String prop : props) { | |
String[] t = prop.split("="); | |
String val = t.length < 2 ? "" : t[1]; | |
System.setProperty(t[0], val); | |
} | |
} | |
private void deleteDataDirs() throws IOException { | |
Path data = Paths.get("./target/data"); | |
if (Files.exists(data)) { | |
MoreFiles.deleteRecursively(data, RecursiveDeleteOption.ALLOW_INSECURE); | |
} | |
} | |
private void init() { | |
port = nextFreePort(8082, 10000); | |
String portProp = String.format("keycloak.port=%d", port); | |
String[] props = ObjectArrays.concat(PROPS, portProp); | |
setSystemProps(props); | |
try { | |
deleteDataDirs(); | |
keycloak = KeycloakServer.bootstrapKeycloakServer(ARGS); | |
} catch (Throwable e) { | |
throw new IllegalStateException("Unable to start KeycloakServer", e); | |
} | |
} | |
private static int nextFreePort(int from, int to) { | |
for (int port = from; port <= to; port++) { | |
if (isLocalPortFree(port)) { | |
return port; | |
} | |
} | |
throw new IllegalStateException("No free port found"); | |
} | |
private static boolean isLocalPortFree(int port) { | |
try { | |
new ServerSocket(port).close(); | |
return true; | |
} catch (IOException e) { | |
return false; | |
} | |
} | |
public Keycloak client() { | |
return getAdminClient(getAuthUrl(), "master", "admin-cli", "admin", "admin"); | |
} | |
private static Keycloak getAdminClient( | |
String url, String realm, String clientId, String username, String password) { | |
Keycloak keycloak = | |
KeycloakBuilder.builder() | |
.serverUrl(url) | |
.realm(realm) | |
.grantType(OAuth2Constants.PASSWORD) | |
.clientId(clientId) | |
.username(username) | |
.password(password) | |
.build(); | |
return keycloak; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment