Skip to content

Commit

Permalink
Working version of refactored tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pvannierop committed Jan 9, 2024
1 parent 275eb25 commit 78c62ac
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 257 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
package org.cbioportal.test.integration.security;

import dasniko.testcontainers.keycloak.KeycloakContainer;
import org.cbioportal.test.integration.*;
import org.cbioportal.test.integration.MysqlInitializer;
import org.cbioportal.test.integration.OAuth2KeycloakInitializer;
import org.cbioportal.test.integration.SamlKeycloakInitializer;
import org.openqa.selenium.chrome.ChromeOptions;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.BrowserWebDriverContainer;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
import org.testcontainers.utility.DockerImageName;

import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

import static org.cbioportal.test.integration.security.util.Util.isHostMappingPresent;
import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL;

public class AbstractContainerTest {


public final static int CBIO_PORT = 8080;
public final static int SESSION_SERVICE_PORT = 5000;
public final static int MONGO_PORT = 27017;
public final static int KEYCLOAK_PORT = 8084;
public final static int MYSQL_PORT = 3306;
public final static int MOCKSERVER_PORT = 8085;
public final static String DOWNLOAD_FOLDER = "/tmp/browser_downloads";

private static final String SESSION_IMAGE_VERSION = "docker.io/cbioportal/session-service:0.6.1";
private static final String MONGO_IMAGE_VERSION = "docker.io/mongo:3.7.9";
private static final String KEYCLOAK_IMAGE_VERSION = "quay.io/keycloak/keycloak:23.0";
private static final String MYSQL_IMAGE_VERSION = "mysql:5.7";
private static final String MOCKSERVER_IMAGE_VERSION = "docker.io/mockserver/mockserver:5.15.0";

static final GenericContainer sessionServiceContainer;
static final GenericContainer mongoContainer;
static final MySQLContainer mysqlContainer;
Expand All @@ -16,18 +50,110 @@ public class AbstractContainerTest {
static final BrowserWebDriverContainer chromedriverContainer;

static {

String hostToCheck = "host.testcontainers.internal";
String ipAddressToCheck = "localhost";
try {
if (!isHostMappingPresent(hostToCheck, ipAddressToCheck)) {
throw new IllegalStateException(hostToCheck + " is not mapped to " + ipAddressToCheck + " in /etc/hosts. Please add this mapping.");
}
} catch (IOException e) {
throw new RuntimeException("Unable to read /etc/hosts file.", e);
}

sessionServiceContainer = SharedSessionServiceContainer.getInstance();
mongoContainer = SharedMongoContainer.getInstance();
mysqlContainer = SharedMysqlContainer.getInstance();
mockServerContainer = MockServerContainer.getInstance();
keycloakContainer = SharedKeycloakContainer.getInstance();
chromedriverContainer = SharedChromeContainer.getInstance();

sessionServiceContainer = new GenericContainer(DockerImageName.parse(SESSION_IMAGE_VERSION))
.withAccessToHost(true)
.withEnv("SERVER_PORT", "5000")
.withEnv("JAVA_OPTS", "-Dspring.data.mongodb.uri=mongodb://host.testcontainers.internal:27017/session-service");
sessionServiceContainer.setPortBindings(ImmutableList.of(String.format("%s:5000", SESSION_SERVICE_PORT)));

mongoContainer = new GenericContainer(DockerImageName.parse(MONGO_IMAGE_VERSION))
.withEnv("MONGO_INITDB_DATABASE", "session_service")
.withReuse(true);
mongoContainer.setPortBindings(ImmutableList.of(String.format("%s:27017", MONGO_PORT, MONGO_PORT)));

keycloakContainer = new KeycloakContainer(KEYCLOAK_IMAGE_VERSION)
.withRealmImportFile("security/keycloak-configuration-generated.json")
.withAdminUsername("admin")
.withAdminPassword("admin")
.withEnv("KC_HOSTNAME", "host.testcontainers.internal")
.withEnv("KC_HOSTNAME_ADMIN", "localhost")
.withReuse(true);
keycloakContainer.setPortBindings(ImmutableList.of(String.format("%s:8080", KEYCLOAK_PORT)));

mockServerContainer = new GenericContainer(MOCKSERVER_IMAGE_VERSION)
.withExposedPorts(1080);
mockServerContainer.setPortBindings(ImmutableList.of(String.format("%s:1080", MOCKSERVER_PORT)));

mysqlContainer = (MySQLContainer) new MySQLContainer(MYSQL_IMAGE_VERSION)
.withClasspathResourceMapping("cgds.sql", "/docker-entrypoint-initdb.d/a_schema.sql", BindMode.READ_ONLY)
.withClasspathResourceMapping("seed_mini.sql", "/docker-entrypoint-initdb.d/b_seed.sql", BindMode.READ_ONLY)
.withStartupTimeout(Duration.ofMinutes(10))
.withReuse(true);
mysqlContainer.setPortBindings(ImmutableList.of(String.format("%s:3306", MYSQL_PORT)));

ChromeOptions options = new ChromeOptions();
Map<String, Object> prefs = new HashMap<>();
prefs.put("download.default_directory", DOWNLOAD_FOLDER);
prefs.put("profile.default_content_settings.popups", 0);
prefs.put("download.prompt_for_download", "false");
prefs.put("download.directory_upgrade", "true");
options.setExperimentalOption("prefs", prefs);

chromedriverContainer = new BrowserWebDriverContainer<>()
// activate this to record movies of the tests (greate for debugging)
.withRecordingMode(RECORD_ALL, new File("/home/pnp300"))
.withAccessToHost(true)
.withCapabilities(options)
.withReuse(true);

sessionServiceContainer.start();
mongoContainer.start();
mysqlContainer.start();
mockServerContainer.start();
keycloakContainer.start();
chromedriverContainer.start();
}

// Update application properties with connection info on Keycloak container
public static class MySamlKeycloakInitializer extends SamlKeycloakInitializer {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
super.initializeImpl(configurableApplicationContext, keycloakContainer);
}
}

// Update application properties with connection info on Keycloak container
public static class MyOAuth2KeycloakInitializer extends OAuth2KeycloakInitializer {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
super.initializeImpl(configurableApplicationContext, keycloakContainer);
}
}

// Update application properties with connection info on Mysql container
public static class MyMysqlInitializer extends MysqlInitializer {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
super.initializeImpl(configurableApplicationContext, mysqlContainer);
}
}

// Expose the ports for the cBioPortal Spring application and keycloak inside
// the Chrome container. Each address is available on http://host.testcontainers.internal:<port>
// in the browser container.
public static class PortInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(
(ApplicationListener<WebServerInitializedEvent>) event -> {
Testcontainers.exposeHostPorts(CBIO_PORT, KEYCLOAK_PORT, SESSION_SERVICE_PORT);
});
}
}

}
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package org.cbioportal.test.integration.security;

import org.cbioportal.PortalApplication;
import org.cbioportal.test.integration.*;
import org.cbioportal.test.integration.MysqlInitializer;
import org.cbioportal.test.integration.OAuth2KeycloakInitializer;
import org.cbioportal.test.integration.security.util.Util;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationContextInitializer;
Expand All @@ -22,20 +17,9 @@
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import static org.cbioportal.test.integration.security.OAuth2AuthIntegrationTest.*;
import static org.cbioportal.test.integration.security.util.Util.isHostMappingPresent;
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
import static org.cbioportal.test.integration.security.OAuth2AuthIntegrationTest.MyKeycloakInitializer;

@RunWith(SpringRunner.class)
@SpringBootTest(
Expand Down Expand Up @@ -72,9 +56,9 @@
}
)
@ContextConfiguration(initializers = {
MyMysqlInitializer.class,
OAuth2AuthIntegrationTest.MyMysqlInitializer.class,
MyKeycloakInitializer.class,
PortInitializer.class
OAuth2AuthIntegrationTest.PortInitializer.class
})
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@DirtiesContext // needed to reuse port 8080 for multiple tests
Expand All @@ -86,21 +70,6 @@ public class OAuth2AuthIntegrationTest extends AbstractContainerTest {
public final static String CBIO_URL_FROM_BROWSER =
String.format("http://host.testcontainers.internal:%d", CBIO_PORT);

// @ClassRule
// public static SharedMysqlContainer mysqlContainer = SharedMysqlContainer.getInstance();
//
// @ClassRule
// public static KeycloakContainer keycloakContainer = SharedKeycloakContainer.getInstance();
//
// @ClassRule
// public static BrowserWebDriverContainer chrome = SharedChromeContainer.getInstance();
//
// @ClassRule
// public static GenericContainer mongoContainer = SharedMongoContainer.getInstance();
//
// @ClassRule
// public static GenericContainer sessionService = SharedSessionServiceContainer.getInstance();

// Update application properties with connection info on Keycloak container
public static class MyKeycloakInitializer extends OAuth2KeycloakInitializer {
@Override
Expand Down Expand Up @@ -133,111 +102,24 @@ public void initialize(ConfigurableApplicationContext applicationContext) {
}
}

// For these tests to work, the host name 'host.testcontainers.internal'
// must be mapped to 'localhost' in /etc/hosts/ file.
static {
String hostToCheck = "host.testcontainers.internal";
String ipAddressToCheck = "localhost";
try {
if (!isHostMappingPresent(hostToCheck, ipAddressToCheck)) {
throw new IllegalStateException(hostToCheck + " is not mapped to " + ipAddressToCheck + " in /etc/hosts. Please add this mapping.");
}
} catch (IOException e) {
throw new RuntimeException("Unable to read /etc/hosts file.", e);
}
}

@Test
public void a_loginSuccess() {
// String vncAddress = chrome.getVncAddress();
RemoteWebDriver driver = performLogin();
WebElement loggedInButton = driver.findElement(By.id("dat-dropdown"));
Assertions.assertEquals("Logged in as [email protected]", loggedInButton.getText());
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.xpath("//span[.='Breast Invasive Carcinoma (TCGA,Nature 2012)']")),
"Study could not be found on the landing page. Permissions are not correctly passed from IDP to client.");
Util.testLogin(CBIO_URL_FROM_BROWSER, chromedriverContainer);
}

@Test
public void b_downloadOfflineToken() throws Exception {
RemoteWebDriver driver = performLogin();
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.id("dat-dropdown")).click(),
"Logged-in menu could not be found on the page.");
driver.findElement(By.linkText("Data Access Token")).click();
driver.findElement(By.xpath("//button[text()='Download Token']")).click();

await().atMost(Duration.ofSeconds(5)).until(downloadedFile());

Assertions.assertTrue(downloadedFile().call());
Util.testDownloadOfflineToken(CBIO_URL_FROM_BROWSER, chromedriverContainer);
}

@Test
public void c_logoutSuccess() {
RemoteWebDriver driver = performLogin();
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.id("dat-dropdown")).click(),
"Logout menu could not be found on the page.");
driver.findElement(By.linkText("Sign out")).click();
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.id("username")),
"IDP login screen not visible on the page. Logout did not work correctly."
);
Util.testLogout(CBIO_URL_FROM_BROWSER, chromedriverContainer);
}

@Test
public void d_loginAgainSuccess() {
final RemoteWebDriver driver = performLogin();
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.id("dat-dropdown")).click(),
"Logout menu could not be found on the page.");
driver.findElement(By.linkText("Sign out")).click();
driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
Assertions.assertDoesNotThrow(
() -> driver.findElement(By.id("username")),
"IDP login screen not visible on the page. Logout did not work correctly."
);
final RemoteWebDriver newDriver = performLogin();
Assertions.assertDoesNotThrow(
() -> newDriver.findElement(By.id("dat-dropdown")),
"Logged-in menu could not be found on the page. Login did not work correctly.");
}

private RemoteWebDriver performLogin() {
RemoteWebDriver driver = chromedriverContainer.getWebDriver();
driver.get(CBIO_URL_FROM_BROWSER);
try {
// when the cbioportal logo is visible, skip login
driver.findElement(By.id("cbioportal-logo"));
} catch (NoSuchElementException e) {
WebElement userNameInput = driver.findElement(By.id("username"));
WebElement passwordInput = driver.findElement(By.id("password"));
WebElement loginButton = driver.findElement(By.id("kc-login"));
userNameInput.sendKeys("testuser");
passwordInput.sendKeys("P@ssword1");
loginButton.click();
}
// wait for the page to load
new WebDriverWait(driver, Duration.ofSeconds(20)).until(
ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@data-test='cancerTypeListContainer']")));
return driver;
}

private boolean containerFileExists(
@Nonnull final GenericContainer container, @Nonnull String path)
throws IOException, InterruptedException {
Assert.notNull(container, "Containers is null");
Assert.isTrue(!path.isEmpty(), "Path string is empty");
Container.ExecResult r = container.execInContainer("/bin/sh", "-c",
"if [ -f " + path
+ " ] ; then echo '0' ; else (>&2 echo '1') ; fi");
return !r.getStderr().contains("1");
}

private Callable<Boolean> downloadedFile() {
return () -> containerFileExists(chromedriverContainer,
String.format("%s/cbioportal_data_access_token.txt",
SharedChromeContainer.DOWNLOAD_FOLDER));
Util.testLoginAgain(CBIO_URL_FROM_BROWSER, chromedriverContainer);
}

}
Loading

0 comments on commit 78c62ac

Please sign in to comment.