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()); } }