diff --git a/.gitignore b/.gitignore
index a451817..dd66320 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,10 @@ hs_err_pid*
target
.gradle
build
+
+# integration test parameters
+src/it/resources/integration-test.properties
+
+# Local artifactory parameters
+settings.xml
+gradle.properties
diff --git a/build.gradle b/build.gradle
index 255b993..2b78fa3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,6 @@
apply plugin: 'idea'
apply plugin: 'eclipse'
+apply plugin: 'jacoco'
//apply from: file('fortify-custom.gradle')
group = 'com.fortify'
@@ -17,7 +18,9 @@ buildscript {
}
repositories {
- jcenter()
+ maven {
+ url artifactory_dependencies_url
+ }
}
@@ -95,12 +98,88 @@ if(hasProperty('target') && target == 'android') {
}
}
+sourceSets {
+ integrationTest {
+ java {
+ compileClasspath += main.output + test.output
+ runtimeClasspath += main.output + test.output
+ srcDir file('src/it/java')
+ }
+ resources.srcDir file('src/it/resources')
+ }
+}
+
+configurations {
+ integrationTestCompile.extendsFrom testCompile
+ integrationTestRuntime.extendsFrom testRuntime
+}
+
+task integrationTest(type: Test) {
+ testClassesDirs = sourceSets.integrationTest.output.classesDirs
+ classpath = sourceSets.integrationTest.runtimeClasspath
+ outputs.upToDateWhen { false }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// https://stackoverflow.com/questions/19025138/gradle-how-to-generate-coverage-report-for-integration-test-using-jacoco
+// answered 2019-08-02 by Brice
+
+// Without it, the only data is the binary data,
+// but I need the XML and HTML report after any test task
+tasks.withType(Test) {
+ finalizedBy jacocoTestReport
+}
+
+// Configure the report to look for executionData generated during the test and integrationTest task
+jacocoTestReport {
+ executionData(file("${project.buildDir}/jacoco/test.exec"),
+ file("${project.buildDir}/jacoco/integrationTest.exec"))
+ reports {
+ // for sonarqube
+ xml.enabled true
+ xml.destination(file("${project.buildDir}/reports/jacoco/all-tests/jacocoAllTestReport.xml"))
+ // for devs
+ html.enabled true
+ html.destination file("${project.buildDir}/reports/jacoco/all-tests/html")
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+integrationTest.mustRunAfter test
+jacocoTestReport.dependsOn integrationTest
+
+jacocoTestCoverageVerification {
+ violationRules {
+ rule {
+ // enabled = false
+ element = 'CLASS'
+ excludes = ['*Test', '*IT']
+
+ limit {
+ counter = 'LINE'
+ value = 'COVEREDRATIO'
+ minimum = 0.0
+ }
+ }
+ }
+}
+
+jacocoTestCoverageVerification.dependsOn jacocoTestReport
+check.dependsOn jacocoTestCoverageVerification
+
dependencies {
compile 'io.swagger:swagger-annotations:1.5.15'
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
+ compile 'com.squareup.okhttp:mockwebserver:2.7.5'
compile 'com.google.code.gson:gson:2.8.1'
compile 'io.gsonfire:gson-fire:1.8.0'
compile 'org.threeten:threetenbp:1.3.5'
+ compile 'javax.annotation:javax.annotation-api:1.3.2'
testCompile 'junit:junit:4.12'
+ integrationTestCompile 'commons-codec:commons-codec:1.14'
+ integrationTestCompile 'org.apache.logging.log4j:log4j-api:2.12.1'
+ integrationTestRuntime 'org.apache.logging.log4j:log4j-core:2.12.1'
}
+
diff --git a/gradle.properties.example b/gradle.properties.example
new file mode 100644
index 0000000..8dbae4a
--- /dev/null
+++ b/gradle.properties.example
@@ -0,0 +1,2 @@
+artifactory_dependencies_url=https://SERVER/artifactory/REPO
+org.gradle.daemon=false
diff --git a/pom.xml b/pom.xml
index 671900c..bd2062b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,22 +53,38 @@
+
org.apache.maven.plugins
maven-surefire-plugin
- 2.12
+ 2.22.2
-
-
- loggerPath
- conf/log4j.properties
-
-
- -Xms512m -Xmx1500m
- methods
- pertest
+ ${surefire.jacoco.args} -Xms512m -Xmx1500m
+ 1
+ false
+
+ com.fortify.ssc.restclient.api.ProjectControllerApiTest
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 2.22.2
+
+
+
+ integration-test
+ verify
+
+
+
+
+ ${failsafe.jacoco.args} -Xms512m -Xmx1500m
+
+
+
maven-dependency-plugin
@@ -84,6 +100,12 @@
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.1.0
+
+
org.apache.maven.plugins
@@ -114,8 +136,7 @@
-
- src/main/java
+ src/main/java
@@ -127,8 +148,8 @@
-
- src/test/java
+ src/test/java
+ src/it/java
@@ -138,6 +159,9 @@
org.apache.maven.plugins
maven-javadoc-plugin
2.10.4
+
+ 8
+
attach-javadocs
@@ -160,7 +184,123 @@
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-version}
+
+
+
+ before-unit-test-execution
+
+ prepare-agent
+
+
+ ${project.build.directory}/jacoco-output/jacoco-unit-tests.exec
+ surefire.jacoco.args
+
+
+
+ after-unit-test-execution
+ test
+
+ report
+
+
+ ${project.build.directory}/jacoco-output/jacoco-unit-tests.exec
+ ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report
+
+
+
+ before-integration-test-execution
+ pre-integration-test
+
+ prepare-agent
+
+
+ ${project.build.directory}/jacoco-output/jacoco-integration-tests.exec
+ failsafe.jacoco.args
+
+
+
+ after-integration-test-execution
+ post-integration-test
+
+ report
+
+
+ ${project.build.directory}/jacoco-output/jacoco-integration-tests.exec
+ ${project.reporting.outputDirectory}/jacoco-integration-test-coverage-report
+
+
+
+ merge-unit-and-integration
+ post-integration-test
+
+ merge
+
+
+
+
+ ${project.build.directory}/jacoco-output/
+
+ *.exec
+
+
+
+ ${project.build.directory}/jacoco-output/merged.exec
+
+
+
+ create-merged-report
+ post-integration-test
+
+ report
+
+
+ ${project.build.directory}/jacoco-output/merged.exec
+ ${project.reporting.outputDirectory}/jacoco-merged-test-coverage-report
+
+
+
+ jacoco-check
+ verify
+
+ check
+
+
+
+
+ CLASS
+
+ *Test
+ *IT
+
+
+
+ LINE
+ COVEREDRATIO
+ 0%
+
+
+
+
+ ${project.build.directory}/jacoco-output/merged.exec
+
+
+
+
+
+
+
+ src/it/resources
+
+ **/*.properties
+ **/*.xml
+
+
+
@@ -198,6 +338,11 @@
okhttp
${okhttp-version}
+
+ com.squareup.okhttp
+ mockwebserver
+ ${okhttp-version}
+
com.squareup.okhttp
logging-interceptor
@@ -225,6 +370,26 @@
${junit-version}
test
+
+ commons-codec
+ commons-codec
+ 1.14
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j-version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j-version}
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
1.7
@@ -234,9 +399,11 @@
1.5.15
2.7.5
2.8.1
- 1.3.5
+ 1.3.5
1.0.0
4.12
+ 2.12.1
+ 0.8.5
UTF-8
diff --git a/settings.xml.example b/settings.xml.example
new file mode 100644
index 0000000..39ebf5a
--- /dev/null
+++ b/settings.xml.example
@@ -0,0 +1,65 @@
+
+
+
+
+ anyother
+ central
+ https://SERVER.test/artifactory/jcenter
+
+
+
+
+
+
+
+ false
+
+ libs-release
+ https://SERVER.test/artifactory/libs-release
+
+
+
+ libs-snapshot
+ https://SERVER.test/artifactory/libs-snapshot
+
+
+
+
+
+ false
+
+ plugins-release-local
+ https://SERVER.test/artifactory/plugins-release-local
+
+
+ artifactory
+
+
+
+ artifactory
+
+
+
+ anyother
+ USER
+ AKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8w
+
+
+ libs-release
+ USER
+ AKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8w
+
+
+ libs-snapshot
+ USER
+ AKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8w
+
+
+ plugins-release-local
+ USER
+ AKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8w
+
+
+
+
diff --git a/src/it/java/ListProjectsIT.java b/src/it/java/ListProjectsIT.java
new file mode 100644
index 0000000..a67ef21
--- /dev/null
+++ b/src/it/java/ListProjectsIT.java
@@ -0,0 +1,123 @@
+import com.fortify.ssc.restclient.*;
+import com.fortify.ssc.restclient.auth.*;
+import com.fortify.ssc.restclient.model.*;
+import com.fortify.ssc.restclient.api.ProjectControllerApi;
+import com.fortify.ssc.restclient.api.ProjectVersionOfProjectControllerApi;
+import org.apache.commons.codec.binary.Base64;
+
+import org.junit.Test;
+import org.junit.Before;
+import static org.junit.Assert.*;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+public class ListProjectsIT {
+ private static Logger LOGGER = LogManager.getLogger(ListProjectsIT.class.getName());
+
+ private Properties props;
+
+ private ApiClient defaultClient;
+ private ProjectControllerApi apiInstance;
+
+ public void initFromProperties(Properties props) throws java.io.UnsupportedEncodingException {
+ this.props = props;
+ defaultClient = Configuration.getDefaultApiClient();
+ String apiToken = props.getProperty("it.fortifyApiToken");
+ if (apiToken == null || apiToken.trim().length() == 0) {
+ throw new RuntimeException("Missing it.fortifyApiToken");
+ }
+ String apiBase = props.getProperty("it.fortifyApiBase");
+ if (apiBase == null || apiBase.trim().length() == 0) {
+ throw new RuntimeException("Missing it.fortifyApiBase");
+ }
+ defaultClient.setBasePath(apiBase);
+ // Configure API key authorization: FortifyToken
+ ApiKeyAuth fortifyToken = (ApiKeyAuth) defaultClient.getAuthentication("FortifyToken");
+ fortifyToken.setApiKey(Base64.encodeBase64String(apiToken.getBytes("UTF-8")));
+ fortifyToken.setApiKeyPrefix("FortifyToken");
+ }
+
+ @Before
+ public void init() throws java.io.IOException, java.io.UnsupportedEncodingException {
+ Properties props = new Properties();
+ try (java.io.InputStream stream = this.getClass().getResourceAsStream("/integration-test.properties")) {
+ if (stream == null) {
+ props = System.getProperties();
+ } else {
+ props.load(stream);
+ }
+ }
+ initFromProperties(props);
+ }
+
+ @Test
+ public void testList() throws ApiException {
+ String projectName = props.getProperty("it.fortifyProjectName");
+ if (projectName == null || projectName.trim().length() == 0) {
+ throw new RuntimeException("Missing it.fortifyProjectName");
+ }
+ String fields = null; // "id,name,description,createdBy,creationDate,issueTemplateId,id";
+
+ Integer start = null;
+ Integer limit = null;
+ String q = "name:" + projectName;
+ Boolean fulltextsearch = null;
+ String orderby = null;
+
+ LOGGER.info("Connecting to " + defaultClient.getBasePath() + " and reading a project " + projectName + "...");
+ ProjectControllerApi projectController = new ProjectControllerApi();
+ ApiResultListProject projectsResult = null;
+ try {
+ projectsResult = projectController.listProject(fields, start, limit, q, fulltextsearch, orderby);
+ LOGGER.info(projectsResult);
+
+ assertEquals(1L, projectsResult.getCount().longValue());
+ assertEquals(projectName, ((Project)projectsResult.getData().get(0)).getName());
+ } catch (ApiException e) {
+ LOGGER.error("HTTP response code " + e.getCode());
+ if (e.getCode() == 401) {
+ LOGGER.error("Expected a fresh CIToken in it.fortifyApiToken");
+ } else {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ LOGGER.warn(sw.toString());
+ }
+ throw e;
+ }
+
+ Long projectId = ((Project)projectsResult.getData().get(0)).getId();
+ q = null;
+ Boolean includeInactive = null;
+ Boolean myAssignedIssues = null;
+ LOGGER.info("Reading project " + projectId.longValue());
+ ProjectVersionOfProjectControllerApi versionsOfProjectController = new ProjectVersionOfProjectControllerApi();
+ try {
+ ApiResultListProjectVersion versionsResult = versionsOfProjectController.listProjectVersionOfProject(projectId,
+ fields, start, limit, q, fulltextsearch, orderby, includeInactive, myAssignedIssues);
+ LOGGER.info(versionsResult);
+ } catch (ApiException e) {
+ String codeMessage = "HTTP response code " + e.getCode();
+ if (e.getCode() == 401) {
+ LOGGER.error(codeMessage + ": Expected a fresh CIToken in it.fortifyApiToken");
+ } else {
+ LOGGER.error(codeMessage);
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ LOGGER.warn(sw.toString());
+ }
+ throw e;
+ }
+ }
+
+ public static void main(String[] args) throws java.io.IOException, java.io.UnsupportedEncodingException, ApiException {
+ ListProjectsIT ts = new ListProjectsIT();
+ ts.init();
+ ts.testList();
+ }
+}
+
diff --git a/src/it/resources/integration-test-sample.properties b/src/it/resources/integration-test-sample.properties
new file mode 100644
index 0000000..6a7a915
--- /dev/null
+++ b/src/it/resources/integration-test-sample.properties
@@ -0,0 +1,4 @@
+it.fortifyApiBase=https://fortify.example.com/ssc/api/v1
+it.fortifyApiToken=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+# it.fortifyProjectVersionId=8004
+it.fortifyProjectName=WebGoat
diff --git a/src/it/resources/log4j2-test.xml b/src/it/resources/log4j2-test.xml
new file mode 100644
index 0000000..f6765f3
--- /dev/null
+++ b/src/it/resources/log4j2-test.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/com/fortify/ssc/restclient/api/ProjectControllerApiTest.java b/src/test/java/com/fortify/ssc/restclient/api/ProjectControllerApiTest.java
index 680797f..2a8ee3c 100644
--- a/src/test/java/com/fortify/ssc/restclient/api/ProjectControllerApiTest.java
+++ b/src/test/java/com/fortify/ssc/restclient/api/ProjectControllerApiTest.java
@@ -20,10 +20,19 @@
import com.fortify.ssc.restclient.model.ApiResultApplicationNameTestResponse;
import com.fortify.ssc.restclient.model.ApiResultListProject;
import com.fortify.ssc.restclient.model.ApiResultProject;
+import com.fortify.ssc.restclient.model.ApiActionResponse;
import com.fortify.ssc.restclient.model.ApplicationNameTestRequest;
+import com.fortify.ssc.restclient.model.ApplicationNameTestResponse;
import com.fortify.ssc.restclient.model.Project;
+
import org.junit.Test;
import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Before;
+import static org.junit.Assert.*;
+
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.MockResponse;
import java.util.ArrayList;
import java.util.HashMap;
@@ -33,12 +42,14 @@
/**
* API tests for ProjectControllerApi
*/
-@Ignore
public class ProjectControllerApiTest {
private final ProjectControllerApi api = new ProjectControllerApi();
-
+ @Rule
+ public MockWebServer mockBackend = new MockWebServer();
+
+
/**
* doCollectionAction
*
@@ -49,10 +60,24 @@ public class ProjectControllerApiTest {
*/
@Test
public void doCollectionActionProjectTest() throws ApiException {
- ApiCollectionActionlong apiResourceAction = null;
- ApiResultApiActionResponse response = api.doCollectionActionProject(apiResourceAction);
+ api.getApiClient().setBasePath(mockBackend.url("/ssc/api/v1").toString());
+ mockBackend.enqueue(
+ new MockResponse().setBody("{" +
+ "\"data\":{" +
+ "\"status\":\"success\"," +
+ "\"message\":\"Application search completed\"," +
+ "\"values\":{\"found\":false}" +
+ "}," +
+ "\"responseCode\":200" +
+ "}")
+ );
+ ApiCollectionActionlong apiCollAction = new ApiCollectionActionlong()
+ .addIdsItem(1441L)
+ .type(ApiCollectionActionlong.TypeEnum.REFRESH);
+ ApiResultApiActionResponse response = api.doCollectionActionProject(apiCollAction);
- // TODO: test validations
+ assertEquals("Application search completed", response.getData().getMessage());
+ assertEquals(ApiActionResponse.StatusEnum.SUCCESS, response.getData().getStatus());
}
/**
@@ -71,9 +96,36 @@ public void listProjectTest() throws ApiException {
String q = null;
Boolean fulltextsearch = null;
String orderby = null;
+
+ api.getApiClient().setBasePath(mockBackend.url("/ssc/api/v1").toString());
+ mockBackend.enqueue(
+ new MockResponse().setBody("{\"data\": [" +
+ "{" +
+ "\"id\": 1441," +
+ "\"name\": \"APP_ONE\"," +
+ "\"description\": \"Description for APP_ONE\"," +
+ "\"creationDate\": \"2019-06-14T15:39:27.000+0000\"," +
+ "\"createdBy\": \"fortify_ci\"," +
+ "\"issueTemplateId\": null," +
+ "\"_href\": \"https://SERVER/ssc/api/v1/projects/1441\"" +
+ "}," +
+ "{" +
+ "\"id\": 1442," +
+ "\"name\": \"APP_TWO\"," +
+ "\"description\": \"Description for APP_TWO\"," +
+ "\"creationDate\": \"2019-06-14T15:46:50.000+0000\"," +
+ "\"createdBy\": \"fortify_ci\"," +
+ "\"issueTemplateId\": null," +
+ "\"_href\": \"https://SERVER/ssc/api/v1/projects/1442\"" +
+ "}" +
+ "]," +
+ "\"count\": 2," +
+ "\"responseCode\": 200" +
+ "}"));
+
ApiResultListProject response = api.listProject(fields, start, limit, q, fulltextsearch, orderby);
- // TODO: test validations
+ assertEquals(2L, response.getCount().longValue());
}
/**
@@ -86,11 +138,27 @@ public void listProjectTest() throws ApiException {
*/
@Test
public void readProjectTest() throws ApiException {
- Long id = null;
- String fields = null;
+ api.getApiClient().setBasePath(mockBackend.url("/ssc/api/v1").toString());
+ mockBackend.enqueue(
+ new MockResponse().setBody("{" +
+ "\"data\":{" +
+ "\"id\": 1441," +
+ "\"name\": \"APP_ONE\"," +
+ "\"description\": \"Description for APP_ONE\"," +
+ "\"creationDate\": \"2019-06-14T15:39:27.000+0000\"," +
+ "\"createdBy\": \"fortify_ci\"," +
+ "\"issueTemplateId\": null," +
+ "\"_href\": \"https://SERVER/ssc/api/v1/projects/1441\"" +
+ "}," +
+ "\"responseCode\": 200" +
+ "}")
+ );
+ Long id = 1441L;
+ String fields = "name,description";
ApiResultProject response = api.readProject(id, fields);
- // TODO: test validations
+ assertEquals("APP_ONE", response.getData().getName());
+ assertEquals("Description for APP_ONE", response.getData().getDescription());
}
/**
@@ -103,10 +171,14 @@ public void readProjectTest() throws ApiException {
*/
@Test
public void testProjectTest() throws ApiException {
- ApplicationNameTestRequest resource = null;
- ApiResultApplicationNameTestResponse response = api.testProject(resource);
+ api.getApiClient().setBasePath(mockBackend.url("/ssc/api/v1").toString());
+ mockBackend.enqueue(
+ new MockResponse().setBody("{\"data\": {\"found\": true}, \"responseCode\": 200}")
+ );
+ ApplicationNameTestRequest testReq = new ApplicationNameTestRequest().applicationName("APP_ONE");
+ ApiResultApplicationNameTestResponse response = api.testProject(testReq);
- // TODO: test validations
+ assertTrue(response.getData().isFound());
}
/**
@@ -119,11 +191,32 @@ public void testProjectTest() throws ApiException {
*/
@Test
public void updateProjectTest() throws ApiException {
- Long id = null;
- Project data = null;
+ api.getApiClient().setBasePath(mockBackend.url("/ssc/api/v1").toString());
+ mockBackend.enqueue(
+ new MockResponse().setBody("{" +
+ "\"data\":{" +
+ "\"id\":1441," +
+ "\"name\":\"APP_ONE_ORIG\"," +
+ "\"description\":\"Stashed description for the former APP_ONE\"," +
+ "\"creationDate\":null," +
+ "\"createdBy\":null," +
+ "\"issueTemplateId\":null," +
+ "\"_href\":\"https://SERVER/ssc/api/v1/projects/1441\"" +
+ "}," +
+ "\"responseCode\":200," +
+ "\"links\":{" +
+ "\"versions\":{" +
+ "\"href\":\"https://SERVER/ssc/api/v1/projects/1441/versions\"" +
+ "}" +
+ "}" +
+ "}")
+ );
+ Long id = 1441L;
+ Project data = new Project().name("APP_ONE_ORIG").description("Stashed description for the former APP_ONE");
ApiResultProject response = api.updateProject(id, data);
- // TODO: test validations
+ assertEquals("APP_ONE_ORIG", response.getData().getName());
+ assertEquals("Stashed description for the former APP_ONE", response.getData().getDescription());
}
}