Skip to content

Commit

Permalink
chore(agama): add test cases for agama engine (#2673)
Browse files Browse the repository at this point in the history
* chore: add Java test cases #2672

* docs: minor update #2672

* chore: add testNG configurations #2672

* chore: add agama resources for reference #2672

* chore: log statements optimization #2672
  • Loading branch information
jgomer2001 authored Oct 19, 2022
1 parent f97bfce commit c57c0dd
Show file tree
Hide file tree
Showing 26 changed files with 897 additions and 4 deletions.
73 changes: 73 additions & 0 deletions agama/engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,46 @@
</repository>
</repositories>

<build>
<filters>
<filter>profiles/${cfg}/config-agama-test.properties</filter>
</filters>

<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>target/test-classes/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>set-configuration-name</id>
<activation>
<property>
<name>!cfg</name>
</property>
</activation>
<properties>
<cfg>default</cfg>
</properties>
</profile>
</profiles>

<dependencies>

<dependency>
Expand Down Expand Up @@ -146,12 +186,45 @@
<artifactId>commons-codec</artifactId>
</dependency>

<!-- SERIALIZATION -->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.3.0</version>
</dependency>

<!-- TESTS -->
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.65.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jans</groupId>
<artifactId>agama-inbound</artifactId>
<version>${jans.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220924</version>
<scope>test</scope>
</dependency>
<!-- Needed for htmlunit <-> log4j2 intergration -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.19.0</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
6 changes: 6 additions & 0 deletions agama/engine/profiles/default/config-agama-test.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#The URL of your Jans installation
server=https://jgomer2001-guiding-herring.gluu.info

clientId=1800.5e01b3bb-1f68-4847-b9ba-d5c999a05e33

custParamName=customParam1
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ public Object callAction(Object instance, String className, String methodName, O
throw new InstantiationException(msg);
}

logger.debug("Constructor found: {}", constr.toGenericString());
if (logger.isDebugEnabled()) {
logger.debug("Constructor found: {}", constr.toGenericString());
}
Object[] args = getArgsForCall(constr, arity, rhinoArgs);

logger.debug("Creating an instance");
Expand All @@ -128,7 +130,9 @@ public Object callAction(Object instance, String className, String methodName, O
throw new NoSuchMethodException(msg);
}

logger.debug("Method found: {}", javaMethod.toGenericString());
if (logger.isDebugEnabled()) {
logger.debug("Method found: {}", javaMethod.toGenericString());
}
Object[] args = getArgsForCall(javaMethod, arity, rhinoArgs);

logger.debug("Performing method call");
Expand Down Expand Up @@ -174,7 +178,7 @@ private Object[] getArgsForCall(Executable javaExec, int arity, Object[] argumen
logger.trace("Parameter is a primitive (or wrapped) {}", typeName);
javaArgs[i] = arg;

} else if (argClass.isAssignableFrom(Number.class)) {
} else if (Number.class.isAssignableFrom(argClass)) {
Object number = PrimitiveUtils.primitiveNumberFrom((Number) arg, paramType);

if (number != null) {
Expand Down
183 changes: 183 additions & 0 deletions agama/engine/src/test/java/io/jans/agama/test/BaseTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package io.jans.agama.test;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebClientOptions;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

import io.jans.inbound.oauth2.CodeGrantUtil;
import io.jans.inbound.oauth2.OAuthParams;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.net.URISyntaxException;
import java.net.URLEncoder;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.testng.ITestContext;
import org.testng.annotations.BeforeSuite;

import static java.nio.charset.StandardCharsets.UTF_8;

//import org.testng.annotations.Test;
import static org.testng.Assert.*;

public class BaseTest {

private static String AGAMA_ACR = "agama";
private Map<String, String> map = null;

Logger logger = LogManager.getLogger(getClass());
WebClient client = null;

BaseTest() {

client = new WebClient(BrowserVersion.CHROME);
WebClientOptions options = client.getOptions();

options.setThrowExceptionOnFailingStatusCode(false);
//prevents the finish page to autosubmit the POST to AS's postlogin endpoint
options.setJavaScriptEnabled(false);

}

@BeforeSuite
public void init(ITestContext context) throws IOException {

String propertiesFile = context.getCurrentXmlTest().getParameter("propertiesFile");
Properties prop = new Properties();
prop.load(Files.newBufferedReader(Paths.get(propertiesFile), UTF_8));

map = new Hashtable<>();
//do not bother about empty keys... but
//If a value is found null, this will throw a NPE since we are using a Hashtable
prop.forEach((key, value) -> map.put(key.toString(), value.toString()));
context.getSuite().getXmlSuite().setParameters(map);

}

String authzRequestUrl(String flowQName, Map<String, Object> inputs) {

OAuthParams p = new OAuthParams();
p.setAuthzEndpoint(map.get("authzEndpoint"));
p.setClientId(map.get("clientId"));
p.setRedirectUri(map.get("redirectUri"));
p.setScopes(Collections.singletonList("openid"));

String queryParam = URLEncoder.encode(map.get("custParamName"), UTF_8);

StringBuilder builder = new StringBuilder(flowQName);
if (inputs != null) {
JSONObject jo = new JSONObject(inputs);
builder.append("-").append(jo.toString());
}

Map<String, String> custParams = new HashMap<>();
custParams.put("acr_values", AGAMA_ACR);
custParams.put(queryParam, builder.toString());
p.setCustParamsAuthReq(custParams);

String url = null;
CodeGrantUtil grant = new CodeGrantUtil(p);

try {
url = grant.makeAuthzRequest().getFirst();
logger.debug("Authentication request built is: {}", url);
} catch (URISyntaxException e) {
fail(e.getMessage(), e);
}
return url;

}

HtmlPage launch(String flowQName, Map<String, Object> parameters) {

//Generate an authn request and launch it in the htmlUnit browser
String url = authzRequestUrl(flowQName, parameters);
logger.info("Starting flow {}", flowQName);
try {
Page p = client.getPage(url);

//Check it is an ok web page
assertTrue(p.isHtmlPage(), "Not an html page");
assertOK(p);
return (HtmlPage) p;

} catch (IOException e) {
fail(e.getMessage(), e);
return null;
}

}

void validateFinishPage(HtmlPage page, boolean success) {

assertOK(page);
//check we are effectively at the finish page
String title = page.getTitleText().toLowerCase();

if (success) {
assertTrue(title.contains("redirect"), "'redirect' word not found in page title");

List<HtmlForm> forms = page.getForms();
assertEquals(forms.size(), 1, "Page should have one and only one form");

HtmlForm form = forms.get(0);
assertTrue(form.getActionAttribute().contains("postlogin"), "Form does not have the expected action attribute");
assertEquals(form.getMethodAttribute().toLowerCase(), "post", "Form does not use POST");

} else {

assertTrue(title.contains("error"), "'error' word not found in page title");

String text = page.getVisibleText().toLowerCase();
assertTrue(text.contains("authentication"), "'authentication' word not found in page text");
assertTrue(text.contains("failed"), "'failed' word not found in page text");
}

}

void assertOK(Page page) {
assertEquals(page.getWebResponse().getStatusCode(), WebResponse.OK);
}

void assertServerError(Page page) {
assertEquals(page.getWebResponse().getStatusCode(), WebResponse.INTERNAL_SERVER_ERROR);
}

<P extends Page> P doClick(DomElement el) {

try {
return el.click();
} catch (IOException e) {
fail(e.getMessage(), e);
return null;
}

}

void typeInInputWithName(HtmlForm form, String name, String text) {

try {
//See f1/index.ftl
form.getInputByName("something").type(text);
} catch (IOException e) {
fail(e.getMessage(), e);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.jans.agama.test;

import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.WebResponse;

import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

import static org.testng.Assert.*;

@org.testng.annotations.Ignore
public class CustomConfigsFlowTest extends BaseTest {

private static final String QNAME = "io.jans.agama.test.showConfig";

@Test
public void withTimeout() {

HtmlPage page = launchAndWait(10);
//Flow should have timed out now - see flow impl
//The page currently shown may correspond to the Agama timeout template or to
//the mismatch (not found) page in case the cleaner job already wiped the flow execution

int status = page.getWebResponse().getStatusCode();
String text = page.getVisibleText().toLowerCase();

if (status == WebResponse.OK) {
//See timeout.ftlh
assertTrue(text.contains("took"));
assertTrue(text.contains("more"));
assertTrue(text.contains("expected"));
} else if (status == WebResponse.NOT_FOUND) {
//See mismatch.ftlh
assertTrue(text.contains("not"));
assertTrue(text.contains("found"));
} else {
fail("Unexpected status code " + status);
}

}

@Test
public void noTimeout() {
HtmlPage page = launchAndWait(2);
validateFinishPage(page, false);
}

private HtmlPage launchAndWait(int wait) {

HtmlPage page = launch(QNAME, null);
try {
Thread.sleep(1000L * wait);
} catch (InterruptedException e) {
fail(e.getMessage(), e);
}

//click on the "Continue" button
HtmlSubmitInput button = page.getForms().get(0).getInputByValue("Continue");
return doClick(button);

}

}
Loading

0 comments on commit c57c0dd

Please sign in to comment.