keycloak-memoizeit

Added some Arquillian Drone tests

8/22/2013 6:14:44 AM

Details

pom.xml 15(+15 -0)

diff --git a/pom.xml b/pom.xml
index f937a7d..39e3315 100755
--- a/pom.xml
+++ b/pom.xml
@@ -210,6 +210,21 @@
       			<artifactId>javase</artifactId>
       			<version>2.2</version>
 			</dependency>
+			
+			<dependency>
+  				<groupId>org.jboss.arquillian</groupId>
+  				<artifactId>arquillian-bom</artifactId>
+  				<version>1.1.1.Final</version>
+  				<type>pom</type>
+  				<scope>import</scope>
+			</dependency>			
+			<dependency>
+				<groupId>org.jboss.arquillian.extension</groupId>
+				<artifactId>arquillian-drone-bom</artifactId>
+				<version>1.2.0.Alpha3</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
         </dependencies>
     </dependencyManagement>
 

testsuite/pom.xml 190(+190 -0)

diff --git a/testsuite/pom.xml b/testsuite/pom.xml
new file mode 100755
index 0000000..523bbf1
--- /dev/null
+++ b/testsuite/pom.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0"?>
+<project>
+	<parent>
+		<artifactId>keycloak-parent</artifactId>
+		<groupId>org.keycloak</groupId>
+		<version>1.0-alpha-1</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+
+	<artifactId>keycloak-testsuite</artifactId>
+	<name>Keycloak TestSuite</name>
+	<description />
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.keycloak</groupId>
+				<artifactId>keycloak-as7-adapter</artifactId>
+				<version>${project.version}</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.jboss.resteasy</groupId>
+			<artifactId>jose-jwt</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-core</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-services</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-social-core</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-social-google</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-social-twitter</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-social-facebook</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.keycloak</groupId>
+			<artifactId>keycloak-forms</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.picketlink</groupId>
+			<artifactId>picketlink-idm-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.picketlink</groupId>
+			<artifactId>picketlink-idm-impl</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.picketlink</groupId>
+			<artifactId>picketlink-idm-simple-schema</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.picketlink</groupId>
+			<artifactId>picketlink-config</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.resteasy</groupId>
+			<artifactId>resteasy-jaxrs</artifactId>
+			<scope>provided</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>log4j</groupId>
+					<artifactId>log4j</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.slf4j</groupId>
+					<artifactId>slf4j-api</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.slf4j</groupId>
+					<artifactId>slf4j-simple</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.resteasy</groupId>
+			<artifactId>jaxrs-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+			<version>1.3.161</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.arquillian.junit</groupId>
+			<artifactId>arquillian-junit-container</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.arquillian.extension</groupId>
+			<artifactId>arquillian-drone-impl</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.arquillian.extension</groupId>
+			<artifactId>arquillian-drone-selenium</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.jboss.arquillian.extension</groupId>
+			<artifactId>arquillian-drone-selenium-server</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.seleniumhq.selenium</groupId>
+			<artifactId>selenium-java</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.seleniumhq.selenium</groupId>
+			<artifactId>selenium-server</artifactId>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.mortbay.jetty</groupId>
+					<artifactId>servlet-api-2.5</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.6</source>
+					<target>1.6</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>jboss-managed</id>
+			<dependencies>
+				<dependency>
+					<groupId>org.jboss.as</groupId>
+					<artifactId>jboss-as-arquillian-container-managed</artifactId>
+					<scope>test</scope>
+					<version>7.1.1.Final</version>
+				</dependency>
+			</dependencies>
+		</profile>
+		<profile>
+			<id>jboss-remote</id>
+			<dependencies>
+				<dependency>
+					<groupId>org.jboss.as</groupId>
+					<artifactId>jboss-as-arquillian-container-remote</artifactId>
+					<scope>test</scope>
+					<version>7.1.1.Final</version>
+				</dependency>
+			</dependencies>
+		</profile>
+	</profiles>
+</project>
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/AbstractDroneTest.java b/testsuite/src/test/java/org/keycloak/testsuite/AbstractDroneTest.java
new file mode 100644
index 0000000..eecc84a
--- /dev/null
+++ b/testsuite/src/test/java/org/keycloak/testsuite/AbstractDroneTest.java
@@ -0,0 +1,154 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+
+import com.thoughtworks.selenium.DefaultSelenium;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractDroneTest {
+
+    @Deployment(name = "app", testable = false, order = 2)
+    public static WebArchive appDeployment() {
+        File[] libs = Maven.resolver().loadPomFromFile("pom.xml")
+                .resolve("org.keycloak:keycloak-core", "org.keycloak:keycloak-as7-adapter").withoutTransitivity().asFile();
+
+        WebArchive archive = ShrinkWrap.create(WebArchive.class, "app.war").addClasses(TestApplication.class)
+                .addAsLibraries(libs).addAsWebInfResource("jboss-deployment-structure.xml")
+                .addAsWebInfResource("app-web.xml", "web.xml").addAsWebInfResource("app-jboss-web.xml", "jboss-web.xml")
+                .addAsWebInfResource("app-resteasy-oauth.json", "resteasy-oauth.json").addAsWebResource("user.jsp");
+        return archive;
+    }
+
+    @Deployment(name = "auth-server", testable = false, order = 1)
+    public static WebArchive deployment() {
+        File[] libs = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity()
+                .asFile();
+
+        WebArchive archive = ShrinkWrap.create(WebArchive.class, "auth-server.war").addClasses(TestApplication.class)
+                .addAsLibraries(libs).addAsWebInfResource("jboss-deployment-structure.xml").addAsWebInfResource("web.xml")
+                .addAsResource("persistence.xml", "META-INF/persistence.xml")
+                .addAsResource("testrealm.json", "META-INF/testrealm.json");
+
+        return archive;
+    }
+
+    URL appUrl;
+
+    URL authServerUrl;
+
+    String DEFAULT_WAIT = "10000";
+
+    @Drone
+    DefaultSelenium selenium;
+
+    @After
+    public void after() {
+        logout();
+    }
+
+    @Before
+    public void before() throws MalformedURLException {
+        authServerUrl = new URL("http://localhost:8080/auth-server");
+        appUrl = new URL("http://localhost:8080/app/user.jsp");
+    }
+
+    public void login(String username, String password) {
+        login(username, password, null);
+    }
+
+    public void login(String username, String password, String expectErrorMessage) {
+        selenium.open(appUrl.toString());
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertEquals("Log in to demo", selenium.getTitle());
+
+        if (username != null) {
+            selenium.type("id=username", username);
+        }
+
+        if (password != null) {
+            selenium.type("id=password", password);
+        }
+
+        selenium.click("css=input[type=\"submit\"]");
+
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        if (expectErrorMessage == null) {
+            Assert.assertEquals(username, selenium.getText("id=user"));
+        } else {
+            Assert.assertTrue(selenium.isTextPresent(expectErrorMessage));
+        }
+    }
+
+    public void logout() {
+        selenium.open(authServerUrl + "/rest/realms/demo/tokens/logout?redirect_uri=" + appUrl);
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertEquals("Log in to demo", selenium.getTitle());
+    }
+
+    public void registerUser(String username, String password) {
+        registerUser(username, password, null);
+    }
+
+    public void registerUser(String username, String password, String expectErrorMessage) {
+        selenium.open(appUrl.toString());
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        selenium.click("link=Register");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+        selenium.type("id=name", "Test User");
+        selenium.type("id=email", "test@user.com");
+        if (username != null) {
+            selenium.type("id=username", username);
+        }
+        if (password != null) {
+            selenium.type("id=password", password);
+            selenium.type("id=password-confirm", password);
+        }
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        if (expectErrorMessage == null) {
+            Assert.assertEquals(username, selenium.getText("id=user"));
+        } else {
+            Assert.assertTrue(selenium.isTextPresent(expectErrorMessage));
+        }
+    }
+
+}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/LoginTest.java b/testsuite/src/test/java/org/keycloak/testsuite/LoginTest.java
new file mode 100644
index 0000000..fab2d16
--- /dev/null
+++ b/testsuite/src/test/java/org/keycloak/testsuite/LoginTest.java
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import org.jboss.arquillian.junit.Arquillian;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@RunWith(Arquillian.class)
+public class LoginTest extends AbstractDroneTest {
+
+    @Test
+    public void loginInvalidPassword() {
+        login("invalid", "password", "Invalid username or password");
+    }
+
+    @Test
+    public void loginInvalidUsername() {
+        login("invalid", "password", "Invalid username or password");
+    }
+
+    @Test
+    public void loginSuccess() {
+        login("bburke@redhat.com", "password");
+    }
+
+}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java b/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java
new file mode 100644
index 0000000..96cf70d
--- /dev/null
+++ b/testsuite/src/test/java/org/keycloak/testsuite/RegisterTest.java
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import org.jboss.arquillian.junit.Arquillian;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@RunWith(Arquillian.class)
+public class RegisterTest extends AbstractDroneTest {
+
+    @Test
+    public void registerUserMissingPassword() {
+        registerUser("registerUserMissingPassword", null, "Please specify password");
+    }
+
+    @Test
+    public void registerUserMissingUsername() {
+        registerUser(null, "password", "Please specify username");
+    }
+
+    @Test
+    public void registerUserSuccess() {
+        registerUser("registerUserSuccess", "password");
+    }
+
+}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/TestApplication.java b/testsuite/src/test/java/org/keycloak/testsuite/TestApplication.java
new file mode 100755
index 0000000..0b1dcf5
--- /dev/null
+++ b/testsuite/src/test/java/org/keycloak/testsuite/TestApplication.java
@@ -0,0 +1,94 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.models.KeycloakSession;
+import org.keycloak.services.models.RealmModel;
+import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.services.resources.SaasService;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class TestApplication extends KeycloakApplication {
+
+    public static RealmRepresentation loadJson(String path)
+    {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        int c;
+        try {
+            while ( (c = is.read()) != -1)
+            {
+                os.write(c);
+            }
+            byte[] bytes = os.toByteArray();
+            //System.out.println(new String(bytes));
+
+            return JsonSerialization.fromBytes(RealmRepresentation.class, bytes);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public TestApplication() {
+        super();
+        KeycloakSession session = factory.createSession();
+        session.getTransaction().begin();
+        RealmManager realmManager = new RealmManager(session);
+        if (realmManager.defaultRealm() == null) {
+            install(realmManager);
+        }
+        session.getTransaction().commit();
+    }
+
+    public void install(RealmManager manager) {
+        RealmModel defaultRealm = manager.createRealm(RealmModel.DEFAULT_REALM, RealmModel.DEFAULT_REALM);
+        defaultRealm.setName(RealmModel.DEFAULT_REALM);
+        defaultRealm.setEnabled(true);
+        defaultRealm.setTokenLifespan(300);
+        defaultRealm.setAccessCodeLifespan(60);
+        defaultRealm.setSslNotRequired(true);
+        defaultRealm.setCookieLoginAllowed(true);
+        defaultRealm.setRegistrationAllowed(true);
+        manager.generateRealmKeys(defaultRealm);
+        defaultRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
+        defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
+        defaultRealm.addDefaultRole(SaasService.REALM_CREATOR_ROLE);
+
+        RealmRepresentation rep = loadJson("META-INF/testrealm.json");
+        RealmModel realm = manager.createRealm("demo", rep.getRealm());
+        manager.importRealm(rep, realm);
+
+    }
+
+
+}
diff --git a/testsuite/src/test/java/org/keycloak/testsuite/TotpTest.java b/testsuite/src/test/java/org/keycloak/testsuite/TotpTest.java
new file mode 100644
index 0000000..ca557f1
--- /dev/null
+++ b/testsuite/src/test/java/org/keycloak/testsuite/TotpTest.java
@@ -0,0 +1,115 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import java.net.MalformedURLException;
+
+import org.jboss.arquillian.junit.Arquillian;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.picketlink.idm.credential.util.TimeBasedOTP;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@RunWith(Arquillian.class)
+public class TotpTest extends AbstractDroneTest {
+
+    private TimeBasedOTP totp;
+    private String totpSecret;
+
+    @Before
+    public void before() throws MalformedURLException {
+        super.before();
+
+        totp = new TimeBasedOTP();
+    }
+
+    public void configureTotp() {
+        selenium.open(authServerUrl + "/rest/realms/demo/account/totp");
+        selenium.waitForPageToLoad("10000");
+
+        Assert.assertTrue(selenium.isTextPresent("To setup Google Authenticator"));
+
+        totpSecret = selenium.getValue("totpSecret");
+        String code = totp.generate(totpSecret);
+
+        selenium.type("id=totp", code);
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad("30000");
+
+        Assert.assertTrue(selenium.isTextPresent("Google Authenticator enabled"));
+    }
+
+    @Test
+    public void loginWithTotpFailure() {
+        registerUser("loginWithTotpFailure", "password");
+        configureTotp();
+        logout();
+
+        selenium.type("id=username", "loginWithTotpFailure");
+        selenium.type("id=password", "password");
+
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertEquals("Log in to demo", selenium.getTitle());
+
+        selenium.type("id=totp", "123456");
+
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertTrue(selenium.isTextPresent("Invalid username or password"));
+    }
+
+    @Test
+    public void loginWithTotpSuccess() {
+        registerUser("loginWithTotpSuccess", "password");
+        configureTotp();
+        logout();
+
+        selenium.type("id=username", "loginWithTotpSuccess");
+        selenium.type("id=password", "password");
+
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertEquals("Log in to demo", selenium.getTitle());
+
+        selenium.type("id=totp", totp.generate(totpSecret));
+
+        selenium.click("css=input[type=\"submit\"]");
+        selenium.waitForPageToLoad(DEFAULT_WAIT);
+
+        Assert.assertEquals("loginWithTotpSuccess", selenium.getText("id=user"));
+    }
+
+    @Test
+    public void setupTotp() {
+        registerUser("setupTotp", "password");
+        configureTotp();
+    }
+
+}
diff --git a/testsuite/src/test/resources/app-jboss-web.xml b/testsuite/src/test/resources/app-jboss-web.xml
new file mode 100755
index 0000000..3cec19c
--- /dev/null
+++ b/testsuite/src/test/resources/app-jboss-web.xml
@@ -0,0 +1,5 @@
+<jboss-web>
+    <valve>
+        <class-name>org.keycloak.adapters.as7.OAuthManagedResourceValve</class-name>
+    </valve>
+</jboss-web>
\ No newline at end of file
diff --git a/testsuite/src/test/resources/app-resteasy-oauth.json b/testsuite/src/test/resources/app-resteasy-oauth.json
new file mode 100755
index 0000000..a36b5cf
--- /dev/null
+++ b/testsuite/src/test/resources/app-resteasy-oauth.json
@@ -0,0 +1,11 @@
+{
+  "realm" : "demo",
+  "resource" : "customer-portal",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/login",
+  "code-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes",
+   "ssl-not-required" : true,
+   "credentials" : {
+      "password" : "password"
+   }
+}
diff --git a/testsuite/src/test/resources/app-web.xml b/testsuite/src/test/resources/app-web.xml
new file mode 100755
index 0000000..4ab9fac
--- /dev/null
+++ b/testsuite/src/test/resources/app-web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+	<module-name>app</module-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Customers</web-resource-name>
+            <url-pattern>/user.jsp</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>user</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>commerce</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>user</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/src/test/resources/arquillian.xml b/testsuite/src/test/resources/arquillian.xml
new file mode 100644
index 0000000..378dfed
--- /dev/null
+++ b/testsuite/src/test/resources/arquillian.xml
@@ -0,0 +1,9 @@
+<arquillian xmlns="http://jboss.org/schema/arquillian"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="
+        http://jboss.org/schema/arquillian
+        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
+    <extension qualifier="selenium">
+        <property name="browser">*googlechrome</property>
+    </extension>
+</arquillian> 
\ No newline at end of file
diff --git a/testsuite/src/test/resources/jboss-deployment-structure.xml b/testsuite/src/test/resources/jboss-deployment-structure.xml
new file mode 100755
index 0000000..b4aedd4
--- /dev/null
+++ b/testsuite/src/test/resources/jboss-deployment-structure.xml
@@ -0,0 +1,12 @@
+<jboss-deployment-structure>
+    <deployment>
+        <!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
+        <dependencies>
+            <module name="org.bouncycastle"/>
+            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
+            <module name="org.jboss.resteasy.resteasy-crypto"/>
+            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
+            <module name="org.jboss.resteasy.jose-jwt" />
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/testsuite/src/test/resources/persistence.xml b/testsuite/src/test/resources/persistence.xml
new file mode 100755
index 0000000..ad40046
--- /dev/null
+++ b/testsuite/src/test/resources/persistence.xml
@@ -0,0 +1,33 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+    version="1.0">
+    <persistence-unit name="keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
+        
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
+        <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
+        <class>org.keycloak.services.models.picketlink.mappings.RealmEntity</class>
+        <class>org.keycloak.services.models.picketlink.mappings.ApplicationEntity</class>
+
+        <exclude-unlisted-classes>true</exclude-unlisted-classes>
+        
+        <properties>
+            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+            <property name="hibernate.show_sql" value="false" />
+            <property name="hibernate.format_sql" value="false" />
+        </properties>
+    </persistence-unit>
+
+</persistence>
diff --git a/testsuite/src/test/resources/testrealm.json b/testsuite/src/test/resources/testrealm.json
new file mode 100755
index 0000000..cf42937
--- /dev/null
+++ b/testsuite/src/test/resources/testrealm.json
@@ -0,0 +1,76 @@
+{
+    "realm": "demo",
+    "enabled": true,
+    "tokenLifespan": 300,
+    "accessCodeLifespan": 10,
+    "sslNotRequired": true,
+    "cookieLoginAllowed": true,
+    "registrationAllowed": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "requiredApplicationCredentials": [ "password" ],
+    "requiredOAuthClientCredentials": [ "password" ],
+    "defaultRoles": [ "user" ],
+    "users" : [
+        {
+            "username" : "bburke@redhat.com",
+            "enabled" : true,
+            "attributes" : {
+                "email" : "bburke@redhat.com"
+            },
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "third-party",
+            "enabled" : true,
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "roles": [
+        {
+            "name": "user",
+            "description": "Have User privileges"
+        },
+        {
+            "name": "admin",
+            "description": "Have Administrator privileges"
+        }
+    ],
+    "roleMappings": [
+        {
+            "username": "bburke@redhat.com",
+            "roles": ["user"]
+        },
+        {
+            "username": "third-party",
+            "roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
+        }
+    ],
+    "scopeMappings": [
+        {
+            "username": "third-party",
+            "roles": ["user"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "customer-portal",
+            "enabled": true,
+            "adminUrl": "http://localhost:8080/app/j_admin_request",
+            "useRealmMappings": true,
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/testsuite/src/test/resources/user.jsp b/testsuite/src/test/resources/user.jsp
new file mode 100755
index 0000000..75afd76
--- /dev/null
+++ b/testsuite/src/test/resources/user.jsp
@@ -0,0 +1,14 @@
+<%@ page import="javax.ws.rs.core.*" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
+<html>
+<head>
+<title>User</title>
+</head>
+<body>
+	<%
+	    String user = request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : "not logged in";
+	    String redirectUri = request.getRequestURL().toString();
+	%>
+	<a href="http://localhost:8080/auth-server/rest/realms/demo/tokens/logout?redirect_uri=<%=redirectUri%>" id="logout">logout</a>
+	<span id="user"><%=user%></span>
+</body>
+</html>
\ No newline at end of file
diff --git a/testsuite/src/test/resources/web.xml b/testsuite/src/test/resources/web.xml
new file mode 100755
index 0000000..76426e6
--- /dev/null
+++ b/testsuite/src/test/resources/web.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+	<module-name>auth-server</module-name>
+
+    <servlet>
+        <servlet-name>Resteasy</servlet-name>
+        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.keycloak.testsuite.TestApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>resteasy.servlet.mapping.prefix</param-name>
+            <param-value>/rest</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+        <async-supported>true</async-supported>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Resteasy</servlet-name>
+        <url-pattern>/rest/*</url-pattern>
+    </servlet-mapping>
+
+    <!--
+    <security-constraint>
+        <web-resource-collection>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint> -->
+
+</web-app>