From 638f4f63aa42d99803988cb2e5ec9dfdc8104b9d Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 9 Jul 2021 10:21:33 +0200 Subject: [PATCH 1/7] add container image generation --- .../pr-build-container-images-comment.yml | 20 ++++++++++++ .../workflows/pr-build-container-images.yml | 31 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .github/workflows/pr-build-container-images-comment.yml create mode 100644 .github/workflows/pr-build-container-images.yml diff --git a/.github/workflows/pr-build-container-images-comment.yml b/.github/workflows/pr-build-container-images-comment.yml new file mode 100644 index 0000000..11075e8 --- /dev/null +++ b/.github/workflows/pr-build-container-images-comment.yml @@ -0,0 +1,20 @@ +name: PR build container images comment + +on: + pull_request_target: + types: [opened] + +jobs: + comment: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Thanks for your contribution! \n Every commit will generate a new build at https://github.com/${{github.event.pull_request.head.repo.full_name}}/actions/workflows/pr-build-container-images.yml?query=branch%3A${{github.event.pull_request.head.ref}} and once the build finishes you should be able to use the container image `docker.pkg.github.com/${{github.event.pull_request.head.repo.full_name}}/${{github.event.repository.name}}:${{github.event.pull_request.head.ref}}` \n To see the full set of container images generated visit https://github.com/${{github.event.pull_request.head.repo.full_name}}/packages' + }) diff --git a/.github/workflows/pr-build-container-images.yml b/.github/workflows/pr-build-container-images.yml new file mode 100644 index 0000000..9310e05 --- /dev/null +++ b/.github/workflows/pr-build-container-images.yml @@ -0,0 +1,31 @@ +name: PR build container images + +on: [push] + +jobs: + container-images: + if: ${{ github.event_name != 'pull_request' && github.repository_owner != 'konveyor' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: "11" + distribution: "adopt" + java-package: jdk + - name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + - name: Build and Push to GitHub Packages + run: | + mvn -U -B package --file pom.xml \ + -Pcontainer-image -Pnative -DskipTests \ + -Dquarkus.container-image.push=true \ + -Dquarkus.container-image.group=${{ github.repository_owner }} \ + -Dquarkus.container-image.name=${{ github.event.repository.name }}/tackle-application-inventory \ + -Dquarkus.container-image.tag=${{ steps.extract_branch.outputs.branch }} \ + -Dquarkus.container-image.registry=docker.pkg.github.com \ + -Dquarkus.container-image.username=${{ github.actor }} \ + -Dquarkus.container-image.password=${{ secrets.GITHUB_TOKEN }} From 257f978b53ad89835c1d28c4dd21587166ab5adc Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 9 Aug 2021 17:44:41 +0200 Subject: [PATCH 2/7] Add hibernate-validation and tests --- .../entities/ApplicationImport.java | 95 ++++++++++++++ .../services/ImportService.java | 29 ++++- .../issues/Issue268Test.java | 118 ++++++++++++++++++ .../resources/long_characters_columns.csv | 4 + 4 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java create mode 100644 src/test/resources/long_characters_columns.csv diff --git a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java index 96c7b48..9d7808e 100644 --- a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java +++ b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java @@ -7,62 +7,157 @@ import io.tackle.commons.entities.AbstractEntity; import javax.persistence.*; +import javax.validation.constraints.Size; @Entity @Table(name = "application_import") public class ApplicationImport extends AbstractEntity { + public static final int APP_NAME_MAX_LENGTH = 120; + private String recordType1; + + @Size(max = APP_NAME_MAX_LENGTH) private String applicationName; + + @Size(max = 250) private String description; + + @Size(max = 250) private String comments; + + @Size(max = 120) private String businessService; + + @Size(max = 40) private String tagType1; + + @Size(max = 40) private String tag1; + + @Size(max = 40) private String tagType2; + + @Size(max = 40) private String tag2; + + @Size(max = 40) private String tagType3; + + @Size(max = 40) private String tag3; + + @Size(max = 40) private String tagType4; + + @Size(max = 40) private String tag4; + + @Size(max = 40) private String tagType5; + + @Size(max = 40) private String tag5; + + @Size(max = 40) private String tagType6; + + @Size(max = 40) private String tag6; + + @Size(max = 40) private String tagType7; + + @Size(max = 40) private String tag7; + + @Size(max = 40) private String tagType8; + + @Size(max = 40) private String tag8; + + @Size(max = 40) private String tagType9; + + @Size(max = 40) private String tag9; + + @Size(max = 40) private String tagType10; + + @Size(max = 40) private String tag10; + + @Size(max = 40) private String tagType11; + + @Size(max = 40) private String tag11; + + @Size(max = 40) private String tagType12; + + @Size(max = 40) private String tag12; + + @Size(max = 40) private String tagType13; + + @Size(max = 40) private String tag13; + + @Size(max = 40) private String tagType14; + + @Size(max = 40) private String tag14; + + @Size(max = 40) private String tagType15; + + @Size(max = 40) private String tag15; + + @Size(max = 40) private String tagType16; + + @Size(max = 40) private String tag16; + + @Size(max = 40) private String tagType17; + + @Size(max = 40) private String tag17; + + @Size(max = 40) private String tagType18; + + @Size(max = 40) private String tag18; + + @Size(max = 40) private String tagType19; + + @Size(max = 40) private String tag19; + + @Size(max = 40) private String tagType20; + + @Size(max = 40) private String tag20; + private String errorMessage; + @Filterable(check = CheckType.EQUAL) public Boolean isValid = true; + @Filterable public String filename; private String status; + @ManyToOne(optional = false) @JsonIgnore @Filterable(filterName = "importSummary.id") diff --git a/src/main/java/io/tackle/applicationinventory/services/ImportService.java b/src/main/java/io/tackle/applicationinventory/services/ImportService.java index b1815e4..2b34d01 100644 --- a/src/main/java/io/tackle/applicationinventory/services/ImportService.java +++ b/src/main/java/io/tackle/applicationinventory/services/ImportService.java @@ -11,6 +11,7 @@ import io.tackle.applicationinventory.entities.ImportSummary; import io.tackle.applicationinventory.mapper.ApplicationInventoryAPIMapper; import io.tackle.applicationinventory.mapper.ApplicationMapper; +import org.apache.commons.lang3.StringUtils; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; @@ -19,6 +20,7 @@ import javax.persistence.EntityManager; import javax.transaction.Transactional; import javax.transaction.UserTransaction; +import javax.validation.Validator; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -52,6 +54,9 @@ public class ImportService { @RestClient BusinessServiceService businessServiceService; + @Inject + Validator validator; + @POST @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) @@ -120,14 +125,28 @@ public Response importFile(@MultipartForm MultipartImportBody data) { private List writeFile(String content, String filename, ImportSummary parentObject) throws IOException { MappingIterator iter = decode(content); - List importList = new ArrayList(); + List importList = new ArrayList<>(); while (iter.hasNext()) { ApplicationImport importedApplication = iter.next(); - importedApplication.setFilename(filename); - importedApplication.importSummary = parentObject; - importList.add(importedApplication); - importedApplication.persistAndFlush(); + + ApplicationImport appToPersist; + if (validator.validate(importedApplication).isEmpty()) { + appToPersist = importedApplication; + + importList.add(appToPersist); + } else { + String truncatedAppName = StringUtils.truncate(importedApplication.getApplicationName().trim(), ApplicationImport.APP_NAME_MAX_LENGTH);; + + appToPersist = new ApplicationImport(); + appToPersist.setApplicationName(truncatedAppName); + appToPersist.setValid(false); + appToPersist.setErrorMessage("Max length error: one or more column's max length were exceeded"); + } + + appToPersist.setFilename(filename); + appToPersist.importSummary = parentObject; + appToPersist.persistAndFlush(); } return importList; } diff --git a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java new file mode 100644 index 0000000..8830d1c --- /dev/null +++ b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java @@ -0,0 +1,118 @@ +package io.tackle.applicationimporter.issues; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.ResourceArg; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.config.EncoderConfig; +import io.restassured.http.ContentType; +import io.restassured.response.Response; +import io.tackle.applicationinventory.entities.ApplicationImport; +import io.tackle.applicationinventory.entities.ImportSummary; +import io.tackle.commons.testcontainers.KeycloakTestResource; +import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; +import io.tackle.commons.tests.SecuredResourceTest; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import javax.transaction.*; +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.util.Arrays; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@QuarkusTest +@QuarkusTestResource(value = PostgreSQLDatabaseTestResource.class, + initArgs = { + @ResourceArg(name = PostgreSQLDatabaseTestResource.DB_NAME, value = "application_inventory_db"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.USER, value = "application_inventory"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.PASSWORD, value = "application_inventory") + } +) +@QuarkusTestResource(value = KeycloakTestResource.class, + initArgs = { + @ResourceArg(name = KeycloakTestResource.IMPORT_REALM_JSON_PATH, value = "keycloak/quarkus-realm.json"), + @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") + } +) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class Issue268Test extends SecuredResourceTest { + + @Inject + UserTransaction userTransaction; + + @BeforeAll + public static void init() { + PATH = "/file/upload"; + } + + @Test + @Order(4) + protected void testImportServiceDuplicatesInFile() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("long_characters_columns.csv").getFile()); + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", importFile) + .multiPart("fileName", "desc_comments_char_limit_rows.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + given() + .accept("application/hal+json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'import-summary'[0].'importStatus'", is("Completed")); + + given() + .accept("application/json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("size()", is(1), + "[0].'importStatus'", is("Completed"), + "[0].'validCount'", is(1), + "[0].'invalidCount'", is(2) + ); + + ImportSummary summary = ImportSummary.findAll().firstResult(); + + Response r = + given() + .accept("text/csv") + .when() + .get("/csv-export?importSummaryId=" + summary.id); + + String csv = r.body().print(); + String[] csvFields = csv.split(","); + + int numberOfRows = (int) Arrays.stream(csvFields).filter("\n"::equals).count(); + assertEquals(2, numberOfRows); + + assertEquals(1, (int) Arrays.stream(csvFields).filter("Import-app-9"::equals).count()); + assertEquals(1, Arrays.stream(csvFields).filter(f -> f.startsWith("\"Very-long-app-name-name-")).count()); + + + userTransaction.begin(); + ApplicationImport.deleteAll(); + ImportSummary.deleteAll(); + userTransaction.commit(); + } + +} + diff --git a/src/test/resources/long_characters_columns.csv b/src/test/resources/long_characters_columns.csv new file mode 100644 index 0000000..06d288a --- /dev/null +++ b/src/test/resources/long_characters_columns.csv @@ -0,0 +1,4 @@ +Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1,Tag Type 2,Tag 2,Tag Type 3,Tag 3,Tag Type 4,Tag 4,Tag Type 5,Tag 5,Tag Type 6,Tag 6,Tag Type 7,Tag 7,Tag Type 8,Tag 8,Tag Type 9,Tag 9,Tag Type 10,Tag 10,Tag Type 11,Tag 11,Tag Type 12,Tag 12,Tag Type 13,Tag 13,Tag Type 14,Tag 14,Tag Type 15,Tag 15,Tag Type 16,Tag 16,Tag Type 17,Tag 17,Tag Type 18,Tag 18,Tag Type 19,Tag 19,Tag Type 20,Tag 20 +,Import-app-8,Food vendors maintain,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Import-app-9,Description for imported app,Food vendors maintain their meFood vendors maintain their menu and price data which is curated and presented to customers via OrderhubFood vendors maintain their menu and price data which is curated and presented to customers via OrderhubFood vendors maintain their menu and price data which is curated and presented to customers via OrderhubFood vendors maintain their menu and price data which is curated and presented to customers via OrderhubFood vendors maintain their menu and price data which is curated and presented to customers via Orderhubnu and price data which is curated and presented to customers via Orderhub,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,Very-long-app-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name-name,Description,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, From b7baac88f493e4b470942cee48cd8f390de25caa Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:02:46 +0200 Subject: [PATCH 3/7] Fix test --- .../pr-build-container-images-comment.yml | 20 ------------ .../workflows/pr-build-container-images.yml | 31 ------------------- .../issues/Issue268Test.java | 4 +-- 3 files changed, 2 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/pr-build-container-images-comment.yml delete mode 100644 .github/workflows/pr-build-container-images.yml diff --git a/.github/workflows/pr-build-container-images-comment.yml b/.github/workflows/pr-build-container-images-comment.yml deleted file mode 100644 index 11075e8..0000000 --- a/.github/workflows/pr-build-container-images-comment.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: PR build container images comment - -on: - pull_request_target: - types: [opened] - -jobs: - comment: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v4 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: 'Thanks for your contribution! \n Every commit will generate a new build at https://github.com/${{github.event.pull_request.head.repo.full_name}}/actions/workflows/pr-build-container-images.yml?query=branch%3A${{github.event.pull_request.head.ref}} and once the build finishes you should be able to use the container image `docker.pkg.github.com/${{github.event.pull_request.head.repo.full_name}}/${{github.event.repository.name}}:${{github.event.pull_request.head.ref}}` \n To see the full set of container images generated visit https://github.com/${{github.event.pull_request.head.repo.full_name}}/packages' - }) diff --git a/.github/workflows/pr-build-container-images.yml b/.github/workflows/pr-build-container-images.yml deleted file mode 100644 index 9310e05..0000000 --- a/.github/workflows/pr-build-container-images.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: PR build container images - -on: [push] - -jobs: - container-images: - if: ${{ github.event_name != 'pull_request' && github.repository_owner != 'konveyor' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: "11" - distribution: "adopt" - java-package: jdk - - name: Extract branch name - shell: bash - run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - id: extract_branch - - name: Build and Push to GitHub Packages - run: | - mvn -U -B package --file pom.xml \ - -Pcontainer-image -Pnative -DskipTests \ - -Dquarkus.container-image.push=true \ - -Dquarkus.container-image.group=${{ github.repository_owner }} \ - -Dquarkus.container-image.name=${{ github.event.repository.name }}/tackle-application-inventory \ - -Dquarkus.container-image.tag=${{ steps.extract_branch.outputs.branch }} \ - -Dquarkus.container-image.registry=docker.pkg.github.com \ - -Dquarkus.container-image.username=${{ github.actor }} \ - -Dquarkus.container-image.password=${{ secrets.GITHUB_TOKEN }} diff --git a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java index 8830d1c..1ac6b9b 100644 --- a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java +++ b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java @@ -51,7 +51,7 @@ public static void init() { @Test @Order(4) - protected void testImportServiceDuplicatesInFile() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { + protected void testImportServiceLongCSVColumnValues() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { ClassLoader classLoader = getClass().getClassLoader(); File importFile = new File(classLoader.getResource("long_characters_columns.csv").getFile()); @@ -60,7 +60,7 @@ protected void testImportServiceDuplicatesInFile() throws SystemException, NotSu .contentType(MediaType.MULTIPART_FORM_DATA) .accept(MediaType.MULTIPART_FORM_DATA) .multiPart("file", importFile) - .multiPart("fileName", "desc_comments_char_limit_rows.csv") + .multiPart("fileName", "long_characters_columns.csv") .when().post(PATH) .then() .log().all() From b9fbb24c13bc0e6e35f916ff81f704a70e93dcff Mon Sep 17 00:00:00 2001 From: Carlos Esteban Feria Vila <2582866+carlosthe19916@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:32:05 +0200 Subject: [PATCH 4/7] Mock controls endpoints --- .../issues/Issue268Test.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java index 1ac6b9b..56b6a4a 100644 --- a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java +++ b/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java @@ -3,22 +3,28 @@ import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.ResourceArg; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.mockito.InjectMock; import io.restassured.RestAssured; import io.restassured.config.EncoderConfig; import io.restassured.http.ContentType; import io.restassured.response.Response; import io.tackle.applicationinventory.entities.ApplicationImport; import io.tackle.applicationinventory.entities.ImportSummary; +import io.tackle.applicationinventory.services.BusinessServiceService; +import io.tackle.applicationinventory.services.TagService; import io.tackle.commons.testcontainers.KeycloakTestResource; import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; import io.tackle.commons.tests.SecuredResourceTest; +import org.eclipse.microprofile.rest.client.inject.RestClient; import org.junit.jupiter.api.*; +import org.mockito.Mockito; import javax.inject.Inject; import javax.transaction.*; import javax.ws.rs.core.MediaType; import java.io.File; import java.util.Arrays; +import java.util.Collections; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.is; @@ -44,14 +50,24 @@ public class Issue268Test extends SecuredResourceTest { @Inject UserTransaction userTransaction; + @InjectMock + @RestClient + TagService mockTagService; + + @InjectMock + @RestClient + BusinessServiceService mockBusinessServiceService; + @BeforeAll public static void init() { PATH = "/file/upload"; } @Test - @Order(4) protected void testImportServiceLongCSVColumnValues() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { + Mockito.when(mockTagService.getListOfTags()).thenReturn(Collections.emptySet()); + Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(Collections.emptySet()); + ClassLoader classLoader = getClass().getClassLoader(); File importFile = new File(classLoader.getResource("long_characters_columns.csv").getFile()); From d8ece91739aaa6c14c38b0fdc06ecc525d23df4b Mon Sep 17 00:00:00 2001 From: Marco Rizzi Date: Mon, 23 Aug 2021 12:58:02 +0200 Subject: [PATCH 5/7] Properly clean data inserted during test execution (#2) * TACKLE-271: case insensitive headers now allowed in import csv file (#82) * TACKLE-290 importer native image tests (#88) * TACKLE-290 importer native image tests * TACKLE-290: removed userTransactions in tests * TACKLE-290: convert to use endpoints * TACKLE-290: convert to use endpoints so NativeImageTests don't fail * TACKLE-290:inititialise mocks * TACKLE-290: initialise mocks * TACKLE-290: add wiremock http server to enable native image tests to call out to remote rest api * TACKLE-290: tidy up importer test code * TACKLE-290: increase test coverage for importer * TACKLE-290: added extra test * TACKLE-290: test coverage * TACKLE-290: fix problem with controls services query parameters not being picked up * TACKLE-290: remove status field from ApplicationImport * Tackle 290 - Fix native tests (#6) * Comment tests * Restart changes * comment test * comment test * comment test * Working tests * build tests inside own gh account * fix ImportServiceTest * Remove comments * restore gh workflow * restore gh workflow * TACKLE-290: fix merged test to be native-compliant * TACKLE-290 Removed 'baseUri' and changed 'mp-rest/uri' props (#7) * TACKLE-290: alter order of ImportServiceTest methods * TACKLE-290: alter test method ordering * TACKLE-290 Refactored some tests and WireMock (#8) Co-authored-by: Carlos E. Feria Vila Co-authored-by: Marco Rizzi * Maven pom.xml(deps-dev): Bump mockito-core from 3.8.0 to 3.12.0 (#117) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.8.0 to 3.12.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.8.0...v3.12.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Maven pom.xml(deps-dev): Bump wiremock-jre8 from 2.29.0 to 2.30.1 (#120) Bumps [wiremock-jre8](https://github.com/tomakehurst/wiremock) from 2.29.0 to 2.30.1. - [Release notes](https://github.com/tomakehurst/wiremock/releases) - [Commits](https://github.com/tomakehurst/wiremock/compare/2.29.0...2.30.1) --- updated-dependencies: - dependency-name: com.github.tomakehurst:wiremock-jre8 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Maven pom.xml(deps-dev): Bump assertj-core from 2.6.0 to 3.20.2 (#76) Bumps [assertj-core](https://github.com/assertj/assertj-core) from 2.6.0 to 3.20.2. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-2.6.0...assertj-core-3.20.2) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Maven pom.xml(deps-dev): Bump postgresql from 1.15.2 to 1.16.0 (#96) Bumps [postgresql](https://github.com/testcontainers/testcontainers-java) from 1.15.2 to 1.16.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.15.2...1.16.0) --- updated-dependencies: - dependency-name: org.testcontainers:postgresql dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Maven pom.xml(deps-dev): Bump mockito-core from 3.12.0 to 3.12.1 (#121) Bumps [mockito-core](https://github.com/mockito/mockito) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.12.0...v3.12.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * TACKLE-268 Properly clean data inserted during test execution ('Import-app-8') Co-authored-by: Mark Brophy <36955467+m-brophy@users.noreply.github.com> Co-authored-by: Carlos E. Feria Vila Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 12 +- .../entities/ApplicationImport.java | 13 +- .../entities/ApplicationImportForCsv.java | 2 +- .../mapper/ApplicationInventoryAPIMapper.java | 1 - .../services/BusinessServiceService.java | 9 +- .../services/ImportService.java | 7 +- .../services/TagService.java | 9 +- src/main/resources/application.properties | 4 +- .../V20210809.1__alter_application_import.sql | 2 + .../ImportServiceTest.java | 687 ------------------ .../flyway/FlywayMigrationTest.java | 4 +- .../resources/ApplicationImportTest.java | 166 +++-- .../resources/ApplicationNameNullTest.java | 106 +++ .../resources/NativeApplicationInportIT.java | 7 + .../resources/issues/IssueTACKLE282Test.java | 4 +- .../CsvExportServiceExceptionTest.java | 137 ++++ .../services/ImportServiceTest.java | 596 +++++++++++++++ .../services/NativeImportServiceIT.java | 7 + .../services/WireMockControlsServices.java | 93 +++ .../services}/issues/Issue268Test.java | 40 +- .../resources/mixed_case_column_headers.csv | 8 + 21 files changed, 1146 insertions(+), 768 deletions(-) create mode 100644 src/main/resources/db/migration/V20210809.1__alter_application_import.sql delete mode 100644 src/test/java/io/tackle/applicationimporter/ImportServiceTest.java create mode 100644 src/test/java/io/tackle/applicationinventory/resources/ApplicationNameNullTest.java create mode 100644 src/test/java/io/tackle/applicationinventory/resources/NativeApplicationInportIT.java create mode 100644 src/test/java/io/tackle/applicationinventory/services/CsvExportServiceExceptionTest.java create mode 100644 src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java create mode 100644 src/test/java/io/tackle/applicationinventory/services/NativeImportServiceIT.java create mode 100644 src/test/java/io/tackle/applicationinventory/services/WireMockControlsServices.java rename src/test/java/io/tackle/{applicationimporter => applicationinventory/services}/issues/Issue268Test.java (77%) create mode 100644 src/test/resources/mixed_case_column_headers.csv diff --git a/pom.xml b/pom.xml index 8a62571..77b1c58 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ io.quarkus 1.13.1.Final 2.22.2 - 1.15.2 + 1.16.0 3.0.0 0.8.7 jar @@ -183,13 +183,13 @@ org.assertj assertj-core - 2.6.0 + 3.20.2 test org.mockito mockito-core - 3.8.0 + 3.12.1 test @@ -197,6 +197,12 @@ quarkus-junit5-mockito test + + com.github.tomakehurst + wiremock-jre8 + 2.30.1 + test + diff --git a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java index 9d7808e..e003ee9 100644 --- a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java +++ b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImport.java @@ -6,7 +6,9 @@ import io.tackle.commons.annotations.Filterable; import io.tackle.commons.entities.AbstractEntity; -import javax.persistence.*; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; import javax.validation.constraints.Size; @Entity @@ -156,8 +158,6 @@ public class ApplicationImport extends AbstractEntity { @Filterable public String filename; - private String status; - @ManyToOne(optional = false) @JsonIgnore @Filterable(filterName = "importSummary.id") @@ -596,13 +596,6 @@ public void setFilename(String filename) { this.filename = filename; } - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } } diff --git a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImportForCsv.java b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImportForCsv.java index 66db63a..c8523db 100644 --- a/src/main/java/io/tackle/applicationinventory/entities/ApplicationImportForCsv.java +++ b/src/main/java/io/tackle/applicationinventory/entities/ApplicationImportForCsv.java @@ -5,7 +5,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection; @RegisterForReflection -@JsonIgnoreProperties({ "createUser", "updateUser", "errorMessage", "valid", "isValid", "filename", "status", "id"}) +@JsonIgnoreProperties({ "createUser", "updateUser", "errorMessage", "valid", "isValid", "filename", "id"}) public abstract class ApplicationImportForCsv { @JsonProperty("Record Type 1") private String recordType1; diff --git a/src/main/java/io/tackle/applicationinventory/mapper/ApplicationInventoryAPIMapper.java b/src/main/java/io/tackle/applicationinventory/mapper/ApplicationInventoryAPIMapper.java index 9dbf7c8..482e146 100644 --- a/src/main/java/io/tackle/applicationinventory/mapper/ApplicationInventoryAPIMapper.java +++ b/src/main/java/io/tackle/applicationinventory/mapper/ApplicationInventoryAPIMapper.java @@ -24,7 +24,6 @@ public ApplicationInventoryAPIMapper( Set tags, Set busine @Override public Response map(ApplicationImport importApp, Long parentId) { - //importApp.setParentId(parentId); Application newApp = new Application(); Set tags = new HashSet<>(); diff --git a/src/main/java/io/tackle/applicationinventory/services/BusinessServiceService.java b/src/main/java/io/tackle/applicationinventory/services/BusinessServiceService.java index 6d18553..ec70c7d 100644 --- a/src/main/java/io/tackle/applicationinventory/services/BusinessServiceService.java +++ b/src/main/java/io/tackle/applicationinventory/services/BusinessServiceService.java @@ -2,6 +2,8 @@ import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import io.quarkus.oidc.token.propagation.AccessToken; import io.tackle.applicationinventory.BusinessService; @@ -9,13 +11,12 @@ import java.util.Set; - - -@RegisterRestClient() +@RegisterRestClient @AccessToken @ApplicationScoped public interface BusinessServiceService { @GET - Set getListOfBusinessServices(); + @Path("/controls/business-service") + Set getListOfBusinessServices(@QueryParam("page") int page, @QueryParam("size") int size); } diff --git a/src/main/java/io/tackle/applicationinventory/services/ImportService.java b/src/main/java/io/tackle/applicationinventory/services/ImportService.java index 2b34d01..0f9fd34 100644 --- a/src/main/java/io/tackle/applicationinventory/services/ImportService.java +++ b/src/main/java/io/tackle/applicationinventory/services/ImportService.java @@ -1,5 +1,6 @@ package io.tackle.applicationinventory.services; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.dataformat.csv.CsvMapper; @@ -68,14 +69,14 @@ public Response importFile(@MultipartForm MultipartImportBody data) { parentRecord.filename = data.getFileName(); parentRecord.importStatus = IN_PROGRESS_STATUS; parentRecord.persistAndFlush(); - Set tags = tagService.getListOfTags(); + Set tags = tagService.getListOfTags(0, 1000); if (tags == null) { String msg = "Unable to retrieve TagTypes from remote resource"; parentRecord.errorMessage = msg; throw new Exception(msg); } - Set businessServices =businessServiceService.getListOfBusinessServices(); + Set businessServices =businessServiceService.getListOfBusinessServices(0, 1000); if (businessServices == null) { String msg = "Unable to retrieve BusinessServices from remote resource"; @@ -155,6 +156,8 @@ private List writeFile(String content, String filename, Impor private MappingIterator decode(String inputFileContent) throws IOException{ CsvMapper mapper = new CsvMapper(); + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES); + mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); CsvSchema csvSchema = CsvSchema.emptySchema().withHeader(); String columnSeparator = ","; diff --git a/src/main/java/io/tackle/applicationinventory/services/TagService.java b/src/main/java/io/tackle/applicationinventory/services/TagService.java index c91f3fd..e3f4e3b 100644 --- a/src/main/java/io/tackle/applicationinventory/services/TagService.java +++ b/src/main/java/io/tackle/applicationinventory/services/TagService.java @@ -2,6 +2,8 @@ import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import io.tackle.applicationinventory.Tag; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @@ -9,15 +11,14 @@ import java.util.Set; - - -@RegisterRestClient() +@RegisterRestClient @AccessToken @ApplicationScoped public interface TagService { @GET - Set getListOfTags(); + @Path("/controls/tag") + Set getListOfTags(@QueryParam("page") int page, @QueryParam("size") int size); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5cd46db..0e5ae92 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -83,5 +83,5 @@ quarkus.kubernetes.labels."app.kubernetes.io/component"=rest quarkus.openshift.labels."app.kubernetes.io/component"=rest io.tackle.applicationinventory.services.Controls.service=tackle-controls:8080 -io.tackle.applicationinventory.services.TagService/mp-rest/uri=http://${io.tackle.applicationinventory.services.Controls.service}/controls/tag?page=0&size=1000 -io.tackle.applicationinventory.services.BusinessServiceService/mp-rest/uri=http://${io.tackle.applicationinventory.services.Controls.service}/controls/business-service?page=0&size=1000 +io.tackle.applicationinventory.services.TagService/mp-rest/uri=http://${io.tackle.applicationinventory.services.Controls.service} +io.tackle.applicationinventory.services.BusinessServiceService/mp-rest/uri=http://${io.tackle.applicationinventory.services.Controls.service} diff --git a/src/main/resources/db/migration/V20210809.1__alter_application_import.sql b/src/main/resources/db/migration/V20210809.1__alter_application_import.sql new file mode 100644 index 0000000..cc13133 --- /dev/null +++ b/src/main/resources/db/migration/V20210809.1__alter_application_import.sql @@ -0,0 +1,2 @@ +alter table if exists application_import + drop column status; diff --git a/src/test/java/io/tackle/applicationimporter/ImportServiceTest.java b/src/test/java/io/tackle/applicationimporter/ImportServiceTest.java deleted file mode 100644 index 31905d7..0000000 --- a/src/test/java/io/tackle/applicationimporter/ImportServiceTest.java +++ /dev/null @@ -1,687 +0,0 @@ -package io.tackle.applicationimporter; - -import io.quarkus.test.common.QuarkusTestResource; -import io.quarkus.test.common.ResourceArg; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; -import io.restassured.RestAssured; -import io.restassured.config.EncoderConfig; -import io.restassured.http.ContentType; -import io.restassured.response.Response; -import io.tackle.applicationinventory.BusinessService; -import io.tackle.applicationinventory.MultipartImportBody; -import io.tackle.applicationinventory.Tag; -import io.tackle.applicationinventory.entities.Application; -import io.tackle.applicationinventory.entities.ApplicationImport; -import io.tackle.applicationinventory.entities.ImportSummary; -import io.tackle.applicationinventory.services.BusinessServiceService; -import io.tackle.applicationinventory.services.ImportService; -import io.tackle.applicationinventory.services.TagService; -import io.tackle.commons.testcontainers.KeycloakTestResource; -import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; -import io.tackle.commons.tests.SecuredResourceTest; -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.junit.jupiter.api.*; -import org.mockito.Mockito; - -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.transaction.*; -import javax.ws.rs.core.MediaType; -import java.io.File; -import java.util.*; -import java.util.stream.Collectors; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.containsInRelativeOrder; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@QuarkusTest -@QuarkusTestResource(value = PostgreSQLDatabaseTestResource.class, - initArgs = { - @ResourceArg(name = PostgreSQLDatabaseTestResource.DB_NAME, value = "application_inventory_db"), - @ResourceArg(name = PostgreSQLDatabaseTestResource.USER, value = "application_inventory"), - @ResourceArg(name = PostgreSQLDatabaseTestResource.PASSWORD, value = "application_inventory") - } -) -@QuarkusTestResource(value = KeycloakTestResource.class, - initArgs = { - @ResourceArg(name = KeycloakTestResource.IMPORT_REALM_JSON_PATH, value = "keycloak/quarkus-realm.json"), - @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") - } -) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class ImportServiceTest extends SecuredResourceTest { - @Inject - EntityManager entityManager; - - @Inject - UserTransaction userTransaction; - - @InjectMock - @RestClient - TagService mockTagService; - - @InjectMock - @RestClient - BusinessServiceService mockBusinessServiceService; - - @BeforeAll - public static void init() { - PATH = "/file/upload"; - } - - - @Test - @Order(1) - protected void testImportServicePost() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - - userTransaction.begin(); - Set tags = new HashSet<>() ; - Tag.TagType tagType1 = new Tag.TagType(); - tagType1.id = "1"; - tagType1.name = "Operating System"; - Tag tag = new Tag(); - tag.id = "1"; - tag.name = "RHEL 8"; - tag.tagType = tagType1; - tags.add(tag); - Tag.TagType tagType2 = new Tag.TagType(); - tagType2.id = "2"; - tagType2.name = "Database"; - Tag tag1 = new Tag(); - tag1.id = "2"; - tag1.name = "Oracle"; - tag1.tagType = tagType2; - tags.add(tag1); - Tag.TagType tagType3 = new Tag.TagType(); - tagType3.id = "3"; - tagType3.name = "Language"; - Tag tag2 = new Tag(); - tag2.id = "3"; - tag2.name = "Java EE"; - tag2.tagType = tagType3; - tags.add(tag2); - Tag.TagType tagType4 = new Tag.TagType(); - tagType4.id = "4"; - tagType4.name = "Runtime"; - Tag tag3 = new Tag(); - tag3.id = "3"; - tag3.name = "Tomcat"; - tag3.tagType = tagType4; - tags.add(tag3); - Mockito.when(mockTagService.getListOfTags()).thenReturn(tags); - - - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "Food2Go"; - businessServices.add(businessService); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(businessServices); - - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); - - - Response response = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file",importFile) - .multiPart("fileName","sample_application_import.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); - //check the correct number of application imports have been persisted - assertEquals(8, ApplicationImport.listAll().size()); - userTransaction.commit(); - - given() - .accept("application/hal+json") - .queryParam("isValid", Boolean.TRUE) - .when() - .get("/application-import") - .then() - .statusCode(200) - .log().body() - .body("_embedded.'application-import'.size()", is(1)); - - userTransaction.begin(); - - Response response2 = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file",importFile) - .multiPart("fileName","sample_application_import.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response2.getStatusCode()); - //check the correct number of application imports have been persisted - assertEquals(16, ApplicationImport.listAll().size()); - userTransaction.commit(); - - given() - .accept("application/hal+json") - .queryParam("isValid", Boolean.TRUE) - .when() - .get("/application-import") - .then() - .statusCode(200) - .log().body() - .body("_embedded.'application-import'.size()", is(1)); - - ApplicationImport successful = ApplicationImport.find("isValid",true).firstResult(); - Application newOne = Application.find("name",successful.getApplicationName()).firstResult(); - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - Application.deleteById(newOne.id); - userTransaction.commit(); - - } - - @Test - @Order(2) - protected void testMapToApplicationRejected() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - userTransaction.begin(); - ImportService svc = new ImportService(); - ImportSummary appImportParent = new ImportSummary(); - appImportParent.persistAndFlush(); - - ApplicationImport appImport1 = new ApplicationImport(); - appImport1.setBusinessService("BS 1"); - appImport1.importSummary = appImportParent; - appImport1.setDescription("hello"); - appImport1.persistAndFlush(); - ApplicationImport appImport2 = new ApplicationImport(); - appImport2.setBusinessService("BS 2"); - appImport2.setApplicationName(""); - appImport2.importSummary = appImportParent; - appImport2.setDescription("this"); - appImport2.setTag5("tag 1"); - appImport2.setTagType5("tag type 1"); - appImport2.setTag6("tag 1"); - appImport2.setTagType6("tag type 1"); - appImport2.setTag7("tag 1"); - appImport2.setTagType7("tag type 1"); - appImport2.setTag8("tag 1"); - appImport2.setTagType8("tag type 1"); - appImport2.setTag9("tag 1"); - appImport2.setTagType9("tag type 1"); - appImport2.setTag10("tag 1"); - appImport2.setTagType10("tag type 1"); - appImport2.setTag11("tag 1"); - appImport2.setTagType11("tag type 1"); - appImport2.setTag12("tag 1"); - appImport2.setTagType12("tag type 1"); - appImport2.setTag13("tag 1"); - appImport2.setTagType13("tag type 1"); - appImport2.setTag14("tag 1"); - appImport2.setTagType14("tag type 1"); - appImport2.setTag15("tag 1"); - appImport2.setTagType15("tag type 1"); - appImport2.setTag16("tag 1"); - appImport2.setTagType16("tag type 1"); - appImport2.setTag17("tag 1"); - appImport2.setTagType17("tag type 1"); - appImport2.setTag18("tag 1"); - appImport2.setTagType18("tag type 1"); - appImport2.setTag19("tag 1"); - appImport2.setTagType19("tag type 1"); - appImport2.setTag20("tag 1"); - appImport2.setTagType20("tag type 1"); - appImport2.persistAndFlush(); - ApplicationImport appImport3 = new ApplicationImport(); - appImport3.setBusinessService("BS 2"); - appImport3.setApplicationName("name 1"); - appImport3.importSummary = appImportParent; - appImport3.setDescription("and this"); - appImport3.setTag1(""); - appImport3.setTag2(""); - appImport3.setTagType2(""); - appImport3.setTag3("mystery tag"); - appImport3.setTagType3(""); - appImport3.setTag4(""); - appImport3.setTagType4(""); - appImport3.persistAndFlush(); - ApplicationImport appImport4 = new ApplicationImport(); - appImport4.setBusinessService("BS 2"); - appImport4.setApplicationName("name 4"); - appImport4.importSummary = appImportParent; - appImport4.setDescription("and this"); - appImport4.setTagType1(""); - appImport4.setTagType2("mystery tag type"); - appImport4.persistAndFlush(); - ApplicationImport appImport5 = new ApplicationImport(); - appImport5.setBusinessService("BS 2"); - appImport5.setApplicationName("name 5"); - appImport5.importSummary = appImportParent; - appImport5.setDescription("and this"); - appImport5.setTag1("yes"); - appImport5.persistAndFlush(); - - List appList = new ArrayList(); - - - appList.add(appImport1); - appList.add(appImport2); - appList.add(appImport3); - appList.add(appImport4); - appList.add(appImport5); - - - Long id1 = appImport1.id; - Long id2 = appImport2.id; - Long id3 = appImport3.id; - Long id4 = appImport4.id; - Long id5 = appImport5.id; - - Set tags = new HashSet<>() ; - Tag.TagType tagType1 = new Tag.TagType(); - tagType1.id = "1"; - tagType1.name = "Unknown tag type"; - Tag tag = new Tag(); - tag.id = "1"; - tag.name = "Unknown OS"; - tag.tagType = tagType1; - tags.add(tag); - - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "BS 2"; - businessServices.add(businessService); - svc.mapImportsToApplication(appList, tags, businessServices, appImportParent); - - - userTransaction.commit(); - - Long summaryId = appImportParent.id; - - - - ApplicationImport refusedImport = ApplicationImport.findById(id1); - assertEquals(Boolean.FALSE, refusedImport.getValid()); - assertEquals(summaryId, refusedImport.importSummary.id); - ApplicationImport refusedImport2 = ApplicationImport.findById(id2); - assertEquals(Boolean.FALSE, refusedImport2.getValid()); - assertEquals(summaryId, refusedImport2.importSummary.id); - ApplicationImport refusedImport3 = ApplicationImport.findById(id3); - assertEquals(Boolean.FALSE, refusedImport3.getValid()); - assertEquals(summaryId, refusedImport3.importSummary.id); - ApplicationImport refusedImport4 = ApplicationImport.findById(id4); - assertEquals(Boolean.FALSE, refusedImport4.getValid()); - assertEquals(summaryId, refusedImport4.importSummary.id); - ApplicationImport refusedImport5 = ApplicationImport.findById(id5); - assertEquals(Boolean.FALSE, refusedImport5.getValid()); - assertEquals(summaryId, refusedImport5.importSummary.id); - - given() - .accept("application/hal+json") - .when() - .get("/import-summary") - .then() - .statusCode(200) - .body("_embedded.import-summary.size()", is(1), - "_embedded.import-summary.invalidCount", containsInRelativeOrder(5), - "total_count", is(1)); - - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - @Test - @Order(2) - protected void testMultipartImport() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - userTransaction.begin(); - ImportService svc = new ImportService(); - MultipartImportBody multipartImport = new MultipartImportBody(); - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); - multipartImport.setFilename("testImport"); - multipartImport.setFile(importFile.toString()); - - javax.ws.rs.core.Response response = svc.importFile(multipartImport); - assertEquals(javax.ws.rs.core.Response.Status.OK.getStatusCode(),response.getStatus()); - - - - - - userTransaction.commit(); - - - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - @Test - @Order(2) - protected void testMapToApplicationMissingFields() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - userTransaction.begin(); - ImportService svc = new ImportService(); - - ImportSummary appImportParent = new ImportSummary(); - appImportParent.persistAndFlush(); - Long parentId = appImportParent.id; - - ApplicationImport appImport1 = new ApplicationImport(); - appImport1.setApplicationName("Test App 1"); - appImport1.importSummary = appImportParent; - appImport1.persistAndFlush(); - ApplicationImport appImport2 = new ApplicationImport(); - appImport2.setApplicationName("Test App 2"); - appImport2.importSummary = appImportParent; - appImport2.setBusinessService(("")); - appImport2.persistAndFlush(); - ApplicationImport appImport3= new ApplicationImport(); - appImport3.setApplicationName("Test App 3"); - appImport3.importSummary = appImportParent; - appImport3.setBusinessService(("BS 2")); - appImport3.persistAndFlush(); - ApplicationImport appImport4= new ApplicationImport(); - appImport4.setApplicationName("Test App 4"); - appImport4.importSummary = appImportParent; - appImport4.setBusinessService(("BS 2")); - appImport4.setDescription(""); - appImport4.persistAndFlush(); - - - List appList = new ArrayList(); - - - appList.add(appImport1); - appList.add(appImport2); - appList.add(appImport3); - appList.add(appImport4); - - - Long id = appImport1.id; - Long id2 = appImport2.id; - Long id3 = appImport3.id; - Long id4 = appImport4.id; - - Set tags = new HashSet<>(); - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "BS 2"; - businessServices.add(businessService); - svc.mapImportsToApplication(appList, tags, businessServices, appImportParent); - - - userTransaction.commit(); - - ApplicationImport refusedImport1 = ApplicationImport.findById(id); - assertEquals(Boolean.TRUE, refusedImport1.getValid());; - - ApplicationImport refusedImport2 = ApplicationImport.findById(id2); - assertEquals(Boolean.TRUE, refusedImport2.getValid()); - - ApplicationImport refusedImport3 = ApplicationImport.findById(id3); - assertEquals(Boolean.TRUE, refusedImport3.getValid()); - - ApplicationImport refusedImport4 = ApplicationImport.findById(id4); - assertEquals(Boolean.TRUE, refusedImport4.getValid()); - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - @Test - @Order(3) - protected void testImportServiceNoMatchingTag() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - - - Set tags = new HashSet<>() ; - Tag.TagType tagType1 = new Tag.TagType(); - tagType1.id = "1"; - tagType1.name = "Unknown tag type"; - Tag tag = new Tag(); - tag.id = "1"; - tag.name = "Unknown OS"; - tag.tagType = tagType1; - tags.add(tag); - Mockito.when(mockTagService.getListOfTags()).thenReturn(tags); - - - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "Foot2Go"; - businessServices.add(businessService); - - BusinessService businessService2 = new BusinessService(); - businessService2.id = "2"; - businessService2.name = "Food2Go"; - businessServices.add(businessService2); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(businessServices); - - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); - - - Response response = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file", importFile) - .multiPart("fileName","sample_application_import.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - } - - @Test - @Order(4) - protected void testImportServiceDuplicatesInFile() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - - Set tags = new HashSet<>() ; - Tag.TagType tagType1 = new Tag.TagType(); - tagType1.id = "1"; - tagType1.name = "Operating System"; - Tag tag = new Tag(); - tag.id = "1"; - tag.name = "RHEL"; - tag.tagType = tagType1; - tags.add(tag); - Mockito.when(mockTagService.getListOfTags()).thenReturn(tags); - - - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "Food2Go"; - businessServices.add(businessService); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(businessServices); - - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); - - - Response response = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file",importFile) - .multiPart("fileName","duplicate_application_names.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); - - given() - .accept("application/hal+json") - .queryParam("isValid", Boolean.FALSE) - .when() - .get("/application-import") - .then() - .statusCode(200) - .log().body() - .body("_embedded.'application-import'[0].'errorMessage'", is("Duplicate Application Name within file: OrderHub")); - - given() - .accept("application/hal+json") - .when() - .get("/import-summary") - .then() - .statusCode(200) - .log().body() - .body("_embedded.'import-summary'[0].'importStatus'", is("Completed")); - - given() - .accept("application/json") - .when() - .get("/import-summary") - .then() - .statusCode(200) - .log().body() - .body("size()", is(1)); - - ImportSummary summary = ImportSummary.findAll().firstResult(); - - Response r = - given() - .accept("text/csv") - .when() - .get("/csv-export?importSummaryId=" + summary.id); - /** .then() - .statusCode(200) - .log().body(); - .body("", is("Completed"));*/ - - - String csv = r.body().print(); - String[] csvFields = csv.split(","); - List found = Arrays.stream(csvFields).filter("Comments"::equals).collect(Collectors.toList()); - assertEquals(1,found.size()); - - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - @Test - @Order(5) - protected void testImportServiceNoTagsRetrieved() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - - - Mockito.when(mockTagService.getListOfTags()).thenReturn(null); - - - Set businessServices = new HashSet<>() ; - BusinessService businessService = new BusinessService(); - businessService.id = "1"; - businessService.name = "Food2Go"; - businessServices.add(businessService); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(businessServices); - - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); - - - Response response = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file",importFile) - .multiPart("fileName","sample_application_import.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); - - - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - @Test - @Order(5) - protected void testImportServiceNoBSRetrieved() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - - - Mockito.when(mockTagService.getListOfTags()).thenReturn(null); - - - Set tags = new HashSet<>() ; - Tag.TagType tagType1 = new Tag.TagType(); - tagType1.id = "1"; - tagType1.name = "Operating System"; - Tag tag = new Tag(); - tag.id = "1"; - tag.name = "RHEL"; - tag.tagType = tagType1; - tags.add(tag); - Mockito.when(mockTagService.getListOfTags()).thenReturn(tags); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(null); - - ClassLoader classLoader = getClass().getClassLoader(); - File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); - - - Response response = given() - .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) - .contentType(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.MULTIPART_FORM_DATA) - .multiPart("file",importFile) - .multiPart("fileName","sample_application_import.csv") - .when().post(PATH) - .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); - - - - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); - - } - - -} - diff --git a/src/test/java/io/tackle/applicationinventory/flyway/FlywayMigrationTest.java b/src/test/java/io/tackle/applicationinventory/flyway/FlywayMigrationTest.java index 0aa31a0..55ac7c4 100644 --- a/src/test/java/io/tackle/applicationinventory/flyway/FlywayMigrationTest.java +++ b/src/test/java/io/tackle/applicationinventory/flyway/FlywayMigrationTest.java @@ -20,9 +20,9 @@ public class FlywayMigrationTest { @Test public void testMigration() { // check the number of migrations applied equals the number of files in resources/db/migration folder - assertEquals(16, flyway.info().applied().length); + assertEquals(17, flyway.info().applied().length); // check the current migration version is the one from the last file in resources/db/migration folder - assertEquals("20210622.2", flyway.info().current().getVersion().toString()); + assertEquals("20210809.1", flyway.info().current().getVersion().toString()); // just a basic test to double check the application started // to prove the flyway scripts ran successfully during startup given() diff --git a/src/test/java/io/tackle/applicationinventory/resources/ApplicationImportTest.java b/src/test/java/io/tackle/applicationinventory/resources/ApplicationImportTest.java index cfcb25d..6260db9 100644 --- a/src/test/java/io/tackle/applicationinventory/resources/ApplicationImportTest.java +++ b/src/test/java/io/tackle/applicationinventory/resources/ApplicationImportTest.java @@ -1,22 +1,28 @@ package io.tackle.applicationinventory.resources; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.ResourceArg; import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.config.EncoderConfig; +import io.restassured.http.ContentType; import io.tackle.applicationinventory.entities.ApplicationImport; import io.tackle.applicationinventory.entities.ImportSummary; import io.tackle.commons.testcontainers.KeycloakTestResource; import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; import io.tackle.commons.tests.SecuredResourceTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import javax.ws.rs.core.MediaType; +import java.util.Arrays; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.transaction.*; - +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static io.restassured.RestAssured.given; -import static javax.transaction.Transactional.TxType.REQUIRED; import static org.hamcrest.Matchers.is; @QuarkusTest @@ -34,60 +40,67 @@ } ) public class ApplicationImportTest extends SecuredResourceTest { - @Inject - EntityManager entityManager; - - @Inject - UserTransaction userTransaction; + private static StubMapping tagStubMapping; + private static StubMapping businessServiceStubMapping; @BeforeAll public static void init() { - PATH = "/application-import"; + tagStubMapping = WireMock.stubFor(get(urlPathEqualTo("/controls/tag")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "[\n" + + " {\n" + + " \"id\": 1,\n" + + " \"name\": \"tag1\",\n" + + " \"tagType\": {\n" + + " \"id\": 1,\n" + + " \"name\": \"tag type 1\"\n" + + " }\n" + + " }]"))); + + + businessServiceStubMapping = WireMock.stubFor(get(urlPathEqualTo("/controls/business-service")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody( "[\n" + + " {\n" + + " \"id\": 1,\n" + + " \"name\": \"BS 2\"\n" + + " }," + + " {\n" + + " \"id\": 2,\n" + + " \"name\": \"BS 1\"\n" + + " }," + + " {\n" + + " \"id\": 3,\n" + + " \"name\": \"BS 3\"\n" + + " }]"))); + } + @AfterAll + public static void tearDown() { + WireMock.removeStub(tagStubMapping); + WireMock.removeStub(businessServiceStubMapping); } @Test - public void testFilterByIsValid() throws HeuristicRollbackException, SystemException, HeuristicMixedException, RollbackException, NotSupportedException { - - userTransaction.begin(); - - ImportSummary appImportParent = new ImportSummary(); - appImportParent.persistAndFlush(); - - ApplicationImport appImport1 = new ApplicationImport(); - appImport1.setBusinessService("BS 1"); - appImport1.importSummary = appImportParent; - appImport1.setFilename("File1"); - appImport1.persistAndFlush(); - ApplicationImport appImport2 = new ApplicationImport(); - appImport2.setBusinessService("BS 2"); - appImport2.importSummary = appImportParent; - appImport2.setFilename("File1"); - appImport2.setTag1("tag 1"); - appImport2.setTagType1("tag type 1"); - appImport2.setValid(Boolean.FALSE); - appImport2.persistAndFlush(); - ApplicationImport appImport3 = new ApplicationImport(); - appImport3.setBusinessService("BS 3"); - appImport3.importSummary = appImportParent; - appImport3.setFilename("File2"); - appImport3.setValid(Boolean.FALSE); - appImport3.persistAndFlush(); - - userTransaction.commit(); + public void testFilterByIsValid() { + createTestData(); given() .accept("application/hal+json") .queryParam("isValid", Boolean.FALSE) .queryParam("filename","File1") + .queryParam("sort","-id") .when() .get(PATH) .then() .statusCode(200) .log().body() - .body("_embedded.'application-import'.size()", is(1)) + .body("_embedded.'application-import'.size()", is(2)) .body("_embedded.'application-import'[0].'Business Service'", is("BS 2")) .body("_embedded.'application-import'[0].'Tag Type 1'", is("tag type 1")); @@ -99,11 +112,74 @@ public void testFilterByIsValid() throws HeuristicRollbackException, SystemExcep .then() .statusCode(200) .log().body() - .body("size()", is(2)); + .body("size()", is(3)); + + //Remove test data before finishing + ImportSummary[] summaryList = + given() + .accept("application/json") + .when() + .get("/import-summary") + .as(ImportSummary[].class); + + Arrays.asList(summaryList).forEach(summary -> + given() + .accept(ContentType.JSON) + .pathParam("id", summary.id) + .when() + .delete("/import-summary/{id}") + .then() + .statusCode(204)); + + + ApplicationImport[] importList = + given() + .accept("application/json") + .when() + .get("/application-import") + .as(ApplicationImport[].class); + + + Arrays.asList(importList).forEach(thisImport -> + given() + .accept(ContentType.JSON) + .pathParam("id", thisImport.id) + .when() + .delete("/application-import/{id}") + .then() + .statusCode(204)); + } - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); + protected void createTestData() + { + // import 2 applications + final String multipartPayload = "Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1\n" + + "1,,,,BS 1,,\n" + + "1,,,,BS 2,tag type 1,tag1"; + given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", multipartPayload) + .multiPart("fileName", "File1") + .when() + .post("/file/upload") + .then() + .statusCode(200); + + + // import 1 application + final String multipartPayload2 = "Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1\n" + + "1,,,,BS 3,,"; + given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", multipartPayload2) + .multiPart("fileName", "File2") + .when() + .post("/file/upload") + .then() + .statusCode(200); } } diff --git a/src/test/java/io/tackle/applicationinventory/resources/ApplicationNameNullTest.java b/src/test/java/io/tackle/applicationinventory/resources/ApplicationNameNullTest.java new file mode 100644 index 0000000..e2e8351 --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/resources/ApplicationNameNullTest.java @@ -0,0 +1,106 @@ +package io.tackle.applicationinventory.resources; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.ResourceArg; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import io.tackle.applicationinventory.BusinessService; +import io.tackle.applicationinventory.Tag; +import io.tackle.applicationinventory.entities.ApplicationImport; +import io.tackle.applicationinventory.entities.ImportSummary; +import io.tackle.applicationinventory.mapper.ApplicationInventoryAPIMapper; +import io.tackle.commons.testcontainers.KeycloakTestResource; +import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; +import io.tackle.commons.tests.SecuredResourceTest; +import org.junit.jupiter.api.Test; + +import javax.transaction.Transactional; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +@QuarkusTest +@QuarkusTestResource(value = PostgreSQLDatabaseTestResource.class, + initArgs = { + @ResourceArg(name = PostgreSQLDatabaseTestResource.DB_NAME, value = "application_inventory_db"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.USER, value = "application_inventory"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.PASSWORD, value = "application_inventory") + } +) +@QuarkusTestResource(value = KeycloakTestResource.class, + initArgs = { + @ResourceArg(name = KeycloakTestResource.IMPORT_REALM_JSON_PATH, value = "keycloak/quarkus-realm.json"), + @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") + } +) +public class ApplicationNameNullTest extends SecuredResourceTest { + + @Test + @Transactional + public void testNullApplicationName() { + + ImportSummary importSummary = new ImportSummary(); + importSummary.persistAndFlush(); + + ApplicationImport importItem = new ApplicationImport(); + importItem.setApplicationName(null); + + Set tags = new HashSet<>(); + Tag tag1 = new Tag(); + tag1.id = "1"; + tag1.name = "tag1"; + tags.add(tag1); + + Set businessServices = new HashSet<>(); + BusinessService bs = new BusinessService(); + bs.id = "1"; + bs.name = "bs"; + businessServices.add(bs); + + ApplicationInventoryAPIMapper apiMapper = new ApplicationInventoryAPIMapper(tags, businessServices); + apiMapper.map(importItem, importSummary.id); + + assertEquals("Application Name is mandatory", importItem.getErrorMessage()); + + //Remove test data before finishing + ImportSummary[] summaryList = + given() + .accept("application/json") + .when() + .get("/import-summary") + .as(ImportSummary[].class); + + Arrays.asList(summaryList).forEach(summary -> + given() + .accept(ContentType.JSON) + .pathParam("id", summary.id) + .when() + .delete("/import-summary/{id}") + .then() + .statusCode(204)); + + + ApplicationImport[] importList = + given() + .accept("application/json") + .when() + .get("/application-import") + .as(ApplicationImport[].class); + + + Arrays.asList(importList).forEach(thisImport -> + given() + .accept(ContentType.JSON) + .pathParam("id", thisImport.id) + .when() + .delete("/application-import/{id}") + .then() + .statusCode(204)); + + + } +} diff --git a/src/test/java/io/tackle/applicationinventory/resources/NativeApplicationInportIT.java b/src/test/java/io/tackle/applicationinventory/resources/NativeApplicationInportIT.java new file mode 100644 index 0000000..25a3e8f --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/resources/NativeApplicationInportIT.java @@ -0,0 +1,7 @@ +package io.tackle.applicationinventory.resources; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeApplicationInportIT extends ApplicationImportTest{ +} diff --git a/src/test/java/io/tackle/applicationinventory/resources/issues/IssueTACKLE282Test.java b/src/test/java/io/tackle/applicationinventory/resources/issues/IssueTACKLE282Test.java index 39eaa96..fcd194d 100644 --- a/src/test/java/io/tackle/applicationinventory/resources/issues/IssueTACKLE282Test.java +++ b/src/test/java/io/tackle/applicationinventory/resources/issues/IssueTACKLE282Test.java @@ -75,10 +75,10 @@ public void test() { final Set tags = new HashSet<>() ; tags.add(tagOracle); tags.add(tagDB2); - Mockito.when(mockTagService.getListOfTags()).thenReturn(tags); + Mockito.when(mockTagService.getListOfTags(0, 1000)).thenReturn(tags); // setup mockito for BusinessService - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(Collections.emptySet()); + Mockito.when(mockBusinessServiceService.getListOfBusinessServices(0, 1000)).thenReturn(Collections.emptySet()); // import 2 applications final String multipartPayload = "Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1\n" + diff --git a/src/test/java/io/tackle/applicationinventory/services/CsvExportServiceExceptionTest.java b/src/test/java/io/tackle/applicationinventory/services/CsvExportServiceExceptionTest.java new file mode 100644 index 0000000..ddc8639 --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/services/CsvExportServiceExceptionTest.java @@ -0,0 +1,137 @@ +package io.tackle.applicationinventory.services; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.ResourceArg; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.mockito.InjectMock; +import io.restassured.RestAssured; +import io.restassured.config.EncoderConfig; +import io.restassured.http.ContentType; +import io.restassured.response.Response; +import io.tackle.applicationinventory.entities.ApplicationImport; +import io.tackle.applicationinventory.entities.ImportSummary; +import io.tackle.commons.testcontainers.KeycloakTestResource; +import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; +import io.tackle.commons.tests.SecuredResourceTest; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; +import javax.inject.Inject; +import javax.ws.rs.core.MediaType; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doThrow; + + +@QuarkusTest +@QuarkusTestResource(value = PostgreSQLDatabaseTestResource.class, + initArgs = { + @ResourceArg(name = PostgreSQLDatabaseTestResource.DB_NAME, value = "application_inventory_db"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.USER, value = "application_inventory"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.PASSWORD, value = "application_inventory") + } +) +@QuarkusTestResource(value = KeycloakTestResource.class, + initArgs = { + @ResourceArg(name = KeycloakTestResource.IMPORT_REALM_JSON_PATH, value = "keycloak/quarkus-realm.json"), + @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") + } +) +public class CsvExportServiceExceptionTest extends SecuredResourceTest { + + + @Test + public void testCsvExportExceptionTest() { + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","duplicate_application_names.csv") + .when().post("/file/upload") + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + Long summaryId = Long.valueOf(given() + .accept("application/hal+json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .extract().path("_embedded.'import-summary'[0].id").toString()); + + + RuntimeException exception = null; + try{ + ObjectWriter objectWriter = Mockito.mock(ObjectWriter.class); + doThrow(JsonProcessingException.class).when(objectWriter).writeValueAsString(List.class); + CsvExportService csvExportService = new CsvExportService(); + csvExportService.getCsvExportForImportSummaryId(summaryId); + } + catch(RuntimeException jpe) + { + exception = jpe; + } + catch(JsonProcessingException thrown) + { + + } + + assert(exception.getCause().toString().contains("JsonProcessingException")); + + //Remove test data before finishing + ImportSummary[] summaryList = + given() + .accept("application/json") + .when() + .get("/import-summary") + .as(ImportSummary[].class); + + Arrays.asList(summaryList).forEach(summary -> + given() + .accept(ContentType.JSON) + .pathParam("id", summary.id) + .when() + .delete("/import-summary/{id}") + .then() + .statusCode(204)); + + + ApplicationImport[] importList = + given() + .accept("application/json") + .when() + .get("/application-import") + .as(ApplicationImport[].class); + + + Arrays.asList(importList).forEach(thisImport -> + given() + .accept(ContentType.JSON) + .pathParam("id", thisImport.id) + .when() + .delete("/application-import/{id}") + .then() + .statusCode(204)); + + } +} diff --git a/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java b/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java new file mode 100644 index 0000000..9c30be0 --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java @@ -0,0 +1,596 @@ +package io.tackle.applicationinventory.services; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.ResourceArg; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.config.EncoderConfig; +import io.restassured.http.ContentType; +import io.restassured.response.Response; +import io.tackle.applicationinventory.MultipartImportBody; +import io.tackle.applicationinventory.entities.ApplicationImport; +import io.tackle.applicationinventory.entities.ImportSummary; +import io.tackle.commons.testcontainers.KeycloakTestResource; +import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; +import io.tackle.commons.tests.SecuredResourceTest; +import org.hamcrest.core.Is; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.containsInRelativeOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@QuarkusTest +@QuarkusTestResource(value = PostgreSQLDatabaseTestResource.class, + initArgs = { + @ResourceArg(name = PostgreSQLDatabaseTestResource.DB_NAME, value = "application_inventory_db"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.USER, value = "application_inventory"), + @ResourceArg(name = PostgreSQLDatabaseTestResource.PASSWORD, value = "application_inventory") + } +) +@QuarkusTestResource(value = KeycloakTestResource.class, + initArgs = { + @ResourceArg(name = KeycloakTestResource.IMPORT_REALM_JSON_PATH, value = "keycloak/quarkus-realm.json"), + @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") + } +) +@QuarkusTestResource(WireMockControlsServices.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ImportServiceTest extends SecuredResourceTest { + + + @BeforeAll + public static void init() { + + PATH = "/file/upload"; + } + + + @Test + @Order(1) + protected void testImportServicePost() { + + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","sample_application_import.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + + + given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.TRUE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(1)); + + + + Response response2 = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","sample_application_import.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response2.getStatusCode()); + + + final String successfulimportSummaryApplicationName = String.valueOf(given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.TRUE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(1)) + .extract().path("_embedded.'application-import'[0].'Application Name'").toString()); + + final Long successfulimportApplicationId = Long.valueOf(given() + .accept("application/hal+json") + .queryParam("name", successfulimportSummaryApplicationName) + .when() + .get("/application") + .then() + .statusCode(200) + .log().body() + .body("_embedded.application.size()", is(1)) + .extract().path("_embedded.application[0].id").toString()); + + + + given() + .accept(ContentType.JSON) + .pathParam("id", successfulimportApplicationId) + .when() + .delete("/application/{id}") + .then() + .statusCode(204); + + removeTestObjects(Collections.emptyList()); + + + } + + @Test + @Order(2) + protected void testMapToApplicationRejected() { + + + createDummyRejectedImports(); + + + + + given() + .accept("application/hal+json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .body("_embedded.import-summary.size()", is(1), + "_embedded.import-summary.invalidCount", containsInRelativeOrder(5), + "total_count", is(1)); + + + + removeTestObjects(Collections.emptyList()); + + } + + protected void createDummyRejectedImports() + { + + // import 2 applications + final String multipartPayload = "Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1,Tag Type 2,Tag 2,Tag Type 3,Tag 3" + + ",Tag Type 4,Tag 4,Tag Type 5,Tag 5,Tag Type 6,Tag 6,Tag Type 7,Tag 7,Tag Type 8,Tag 8,Tag Type 9,Tag 9" + + ",Tag Type 10,Tag 10,Tag Type 11,Tag 11,Tag Type 12,Tag 12,Tag Type 13,Tag 13,Tag Type 14,Tag 14,Tag Type 15,Tag 15,Tag Type 16,Tag 16" + + ",Tag Type 17,Tag 17,Tag Type 18,Tag 18,Tag Type 19,Tag 19,Tag Type 20,Tag 20\n" + + "1,,hello,,BS 1,,\n" + + "1, ,,,BS 2,,,,,,,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1,tag type 1,tag 1\n" + + "1,name 1,and this,,BS 2,,,,,,mystery tag,,\n" + + "1,name 4,and this,,BS 1,,,mystery tag type,\n" + + "1,name 5,and this,,BS 2,,tag1"; + given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", multipartPayload) + .multiPart("fileName", "File1") + .when() + .post("/file/upload") + .then() + .statusCode(200); + + + } + + @Test + @Order(3) + protected void testMultipartImport() { + + MultipartImportBody multipartImport = new MultipartImportBody(); + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); + multipartImport.setFilename("testImport"); + multipartImport.setFile(importFile.toString()); + + assertEquals(multipartImport.getFileName(),"testImport"); + + removeTestObjects(Collections.emptyList()); + + } + + + @Test + @Order(4) + protected void testMapToApplicationMissingFields() { + + createMissingFieldsObjects(); + + + + given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.TRUE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(4)); + + + removeTestObjects(Arrays.asList("Test App 1", "Test App 2", "Test App 3", "Test App 4")); + + } + + protected void createMissingFieldsObjects() + { + + // import 2 applications + final String multipartPayload = "Record Type 1,Application Name,Description,Comments,Business Service,Tag Type 1,Tag 1,Tag Type 2,Tag 2,Tag Type 3,Tag 3" + + ",Tag Type 4,Tag 4,Tag Type 5,Tag 5,Tag Type 6,Tag 6,Tag Type 7,Tag 7,Tag Type 8,Tag 8,Tag Type 9,Tag 9" + + ",Tag Type 10,Tag 10,Tag Type 11,Tag 11,Tag Type 12,Tag 12,Tag Type 13,Tag 13,Tag Type 14,Tag 14,Tag Type 15,Tag 15,Tag Type 16,Tag 16" + + ",Tag Type 17,Tag 17,Tag Type 18,Tag 18,Tag Type 19,Tag 19,Tag Type 20,Tag 20\n" + + "1,Test App 1\n" + + "1,Test App 2,,,,,,,,,,,\n" + + "1,Test App 3,,,BS 1,,,,\n" + + "1,Test App 4,,,BS 1,,"; + given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", multipartPayload) + .multiPart("fileName", "File1") + .when() + .post("/file/upload") + .then() + .statusCode(200); + + } + + @Test + @Order(5) + protected void testImportServiceNoMatchingTag() { + + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("sample_application_import.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file", importFile) + .multiPart("fileName","sample_application_import.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + + removeTestObjects(Collections.singletonList("OrderHub")); + } + + @Test + @Order(6) + protected void testImportServiceDuplicatesInFile() { + + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","duplicate_application_names.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.FALSE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'[0].'errorMessage'", is("Duplicate Application Name within file: OrderHub")); + + given() + .accept("application/hal+json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'import-summary'[0].'importStatus'", is("Completed")); + + Long summaryId = Long.valueOf(given() + .accept("application/json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("size()", is(1)) + .extract().path("[0].id").toString()); + + Response r = + given() + .accept("text/csv") + .when() + .get("/csv-export?importSummaryId=" + summaryId); + + + + String csv = r.body().print(); + String[] csvFields = csv.split(","); + List found = Arrays.stream(csvFields).filter("Comments"::equals).collect(Collectors.toList()); + assertEquals(1,found.size()); + + + + removeTestObjects(Collections.emptyList()); + + } + + @Test + @Order(7) + protected void testImportServiceCaseInsensitiveColumnHeaders() { + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("mixed_case_column_headers.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","mixed_case_column_headers.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + + given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.TRUE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(1)); + + + + given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.FALSE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(6)); + + final String successfulimportSummaryApplicationName = String.valueOf(given() + .accept("application/hal+json") + .queryParam("isValid", Boolean.TRUE) + .when() + .get("/application-import") + .then() + .statusCode(200) + .log().body() + .body("_embedded.'application-import'.size()", is(1)) + .extract().path("_embedded.'application-import'[0].'Application Name'").toString()); + + final String successfulimportApplicationName = String.valueOf(given() + .accept("application/hal+json") + .queryParam("name", successfulimportSummaryApplicationName) + .when() + .get("/application") + .then() + .statusCode(200) + .log().body() + .body("_embedded.application.size()", is(1)) + .extract().path("_embedded.application[0].name").toString()); + + removeTestObjects(Arrays.asList(successfulimportApplicationName)); + + } + + @Test + @Order(8) + protected void testImportServiceNoTagsRetrieved() { + + WireMock.stubFor(get(urlPathEqualTo("/controls/tag")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(""))); + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); + + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","duplicate_application_names.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + given() + .accept("application/json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("[0].'errorMessage'", is("Unable to retrieve TagTypes from remote resource")); + + + + + removeTestObjects(Collections.emptyList()); + + } + + @Test + @Order(9) + protected void testImportServiceNoBSRetrieved() { + + final StubMapping tagStubMapping = WireMock.stubFor(get(urlPathEqualTo("/controls/tag")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "[\n" + + " {\n" + + " \"id\": 1,\n" + + " \"name\": \"RHEL 8\",\n" + + " \"tagType\": {\n" + + " \"id\": 1,\n" + + " \"name\": \"Operating System\"\n" + + " }\n" + + " }]"))); + + final StubMapping businessServiceStubMapping = WireMock.stubFor(get(urlPathEqualTo("/controls/business-service")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(""))); + + ClassLoader classLoader = getClass().getClassLoader(); + File importFile = new File(classLoader.getResource("duplicate_application_names.csv").getFile()); + + Response response = given() + .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) + .contentType(MediaType.MULTIPART_FORM_DATA) + .accept(MediaType.MULTIPART_FORM_DATA) + .multiPart("file",importFile) + .multiPart("fileName","duplicate_application_names.csv") + .when().post(PATH) + .then() + .log().all() + .statusCode(200).extract().response(); + + assertEquals(200, response.getStatusCode()); + + given() + .accept("application/json") + .when() + .get("/import-summary") + .then() + .statusCode(200) + .log().body() + .body("[0].'errorMessage'", is("Unable to retrieve BusinessServices from remote resource")); + + removeTestObjects(Collections.emptyList()); + WireMock.removeStub(tagStubMapping); + WireMock.removeStub(businessServiceStubMapping); + } + + + + private void removeTestObjects(List appNamesToDelete) + { + ImportSummary[] summaryList = + given() + .accept("application/json") + .when() + .get("/import-summary") + .as(ImportSummary[].class); + + Arrays.asList(summaryList).forEach(summary -> + given() + .accept(ContentType.JSON) + .pathParam("id", summary.id) + .when() + .delete("/import-summary/{id}") + .then() + .statusCode(204)); + + + ApplicationImport[] importList = + given() + .accept("application/json") + .when() + .get("/application-import") + .as(ApplicationImport[].class); + + + Arrays.asList(importList).forEach(thisImport -> + given() + .accept(ContentType.JSON) + .pathParam("id", thisImport.id) + .when() + .delete("/application-import/{id}") + .then() + .statusCode(204)); + + for (String appName : appNamesToDelete) { + long firstApplicationId = Long.parseLong(given() + .queryParam("name", appName) + .accept(ContentType.JSON) + .when() + .get("/application") + .then() + .statusCode(200) + .body("size()", Is.is(1)) + .extract() + .path("[0].id") + .toString()); + + given() + .accept(ContentType.JSON) + .pathParam("id", firstApplicationId) + .when() + .delete("/application/{id}") + .then() + .statusCode(204); + } + + } + + +} + diff --git a/src/test/java/io/tackle/applicationinventory/services/NativeImportServiceIT.java b/src/test/java/io/tackle/applicationinventory/services/NativeImportServiceIT.java new file mode 100644 index 0000000..7999b6c --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/services/NativeImportServiceIT.java @@ -0,0 +1,7 @@ +package io.tackle.applicationinventory.services; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeImportServiceIT extends ImportServiceTest { +} diff --git a/src/test/java/io/tackle/applicationinventory/services/WireMockControlsServices.java b/src/test/java/io/tackle/applicationinventory/services/WireMockControlsServices.java new file mode 100644 index 0000000..eaef881 --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/services/WireMockControlsServices.java @@ -0,0 +1,93 @@ +package io.tackle.applicationinventory.services; + +import com.github.tomakehurst.wiremock.WireMockServer; +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import java.util.HashMap; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class WireMockControlsServices implements QuarkusTestResourceLifecycleManager { + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(options().proxyVia("tackle-controls",8080)); + wireMockServer.start(); + + wireMockServer.stubFor(get(urlPathEqualTo("/controls/tag")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "[\n" + + " {\n" + + " \"id\": 1,\n" + + " \"name\": \"RHEL 8\",\n" + + " \"tagType\": {\n" + + " \"id\": 1,\n" + + " \"name\": \"Operating System\"\n" + + " }\n" + + " }," + + " {\n" + + " \"id\": 2,\n" + + " \"name\": \"Oracle\",\n" + + " \"tagType\": {\n" + + " \"id\": 2,\n" + + " \"name\": \"Database\"\n" + + " }\n" + + " }," + + " {\n" + + " \"id\": 3,\n" + + " \"name\": \"Java EE\",\n" + + " \"tagType\": {\n" + + " \"id\": 3,\n" + + " \"name\": \"Language\"\n" + + " }\n" + + " }," + + " {\n" + + " \"id\": 4,\n" + + " \"name\": \"Tomcat\",\n" + + " \"tagType\": {\n" + + " \"id\": 4,\n" + + " \"name\": \"Runtime\"\n" + + " }\n" + + " }]" + ))); + + wireMockServer.stubFor(get(urlPathEqualTo("/controls/business-service")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody( + "[\n" + + " {\n" + + " \"id\": 1,\n" + + " \"name\": \"Food2Go\"\n" + + " }," + + " {\n" + + " \"id\": 2,\n" + + " \"name\": \"BS 1\"\n" + + " }]" + ))); + + //stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://restcountries.eu/rest"))); + + HashMap returnedMap =new HashMap<>(); + returnedMap.put("io.tackle.applicationinventory.services.TagService/mp-rest/uri", wireMockServer.baseUrl()); + returnedMap.put("io.tackle.applicationinventory.services.BusinessServiceService/mp-rest/uri", wireMockServer.baseUrl()); + return returnedMap; + } + + @Override + public void stop() { + if (null != wireMockServer) { + wireMockServer.stop(); + } + } +} + + diff --git a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java b/src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java similarity index 77% rename from src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java rename to src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java index 56b6a4a..07fa67e 100644 --- a/src/test/java/io/tackle/applicationimporter/issues/Issue268Test.java +++ b/src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java @@ -1,4 +1,4 @@ -package io.tackle.applicationimporter.issues; +package io.tackle.applicationinventory.services.issues; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.ResourceArg; @@ -16,11 +16,20 @@ import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; import io.tackle.commons.tests.SecuredResourceTest; import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.junit.jupiter.api.*; +import org.hamcrest.core.Is; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.mockito.Mockito; import javax.inject.Inject; -import javax.transaction.*; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; import javax.ws.rs.core.MediaType; import java.io.File; import java.util.Arrays; @@ -65,8 +74,8 @@ public static void init() { @Test protected void testImportServiceLongCSVColumnValues() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - Mockito.when(mockTagService.getListOfTags()).thenReturn(Collections.emptySet()); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices()).thenReturn(Collections.emptySet()); + Mockito.when(mockTagService.getListOfTags(0, 1000)).thenReturn(Collections.emptySet()); + Mockito.when(mockBusinessServiceService.getListOfBusinessServices(0, 1000)).thenReturn(Collections.emptySet()); ClassLoader classLoader = getClass().getClassLoader(); File importFile = new File(classLoader.getResource("long_characters_columns.csv").getFile()); @@ -123,6 +132,27 @@ protected void testImportServiceLongCSVColumnValues() throws SystemException, No assertEquals(1, (int) Arrays.stream(csvFields).filter("Import-app-9"::equals).count()); assertEquals(1, Arrays.stream(csvFields).filter(f -> f.startsWith("\"Very-long-app-name-name-")).count()); + // Clean test data to not alter other tests execution + // Remove the successfully imported 'Import-app-8' application + final long importApp8Id = Long.parseLong(given() + .queryParam("name", "Import-app-8") + .accept(ContentType.JSON) + .when() + .get("/application") + .then() + .statusCode(200) + .body("size()", Is.is(1)) + .extract() + .path("[0].id") + .toString()); + + given() + .accept(ContentType.JSON) + .pathParam("id", importApp8Id) + .when() + .delete("/application/{id}") + .then() + .statusCode(204); userTransaction.begin(); ApplicationImport.deleteAll(); diff --git a/src/test/resources/mixed_case_column_headers.csv b/src/test/resources/mixed_case_column_headers.csv new file mode 100644 index 0000000..f038146 --- /dev/null +++ b/src/test/resources/mixed_case_column_headers.csv @@ -0,0 +1,8 @@ +Record Type 1,application Name,Description,comments,business service,Tag Type 1,Tag 1,tag Type 2,tag 2,Tag Type 3,Tag 3,Tag Type 4,Tag 4 +1,OrderHub,"Create, amend and cancel food orders",,Food2Go,Operating System,RHEL 8,Database,Oracle,Language,Java EE,Runtime,Tomcat +1,MenuManager,Food vendors maintain their menu and price data which is curated and presented to customers via Orderhub,,Food2Go,Operating System,Windows Server 2016,Database,SQL Server,Language,C# ASP .Net,, +1,HeadChef,Allocation of orders to restaurants for fulfillment then despatch,,Foot2Go,Operating System,RHEL 8,Database,Postgresql,Language,Java EE,Runtime,Spring Boot +1,Despatcher,Delivery service,,Foot2Go,Operating System,RHEL 8,Database,Postgresql,Language,Python,, +1,PocketMoney,Payment service,Incoming from orders and outgoing to delivery drivers,Payment Management,Operating System,Z/OS,Database,DB2,Language,COBOL,, +1,BeanCounter,Accounting Service,COTS ERP system,Accounting,Operating System,RHEL 8,Database,Oracle,Language,Java EE,Application Type,COTS +1,SmokeSignal,Notification Service,Updates customers regarding the progress of their orders. Allocates deliveries to despatch drivers,Foot2Go,Operating System,RHEL 8,Language,Python,DataCentre,London (UK),, \ No newline at end of file From 4fdfa84cd093fd2a6b904439dc6fc25b5f895cd9 Mon Sep 17 00:00:00 2001 From: mrizzi Date: Mon, 23 Aug 2021 16:20:23 +0200 Subject: [PATCH 6/7] TACKLE-268 Enhancements after the review --- ...ue268Test.java => IssueTACKLE268Test.java} | 102 +++++------------- .../issues/NativeIssueTACKLE268IT.java | 6 ++ 2 files changed, 35 insertions(+), 73 deletions(-) rename src/test/java/io/tackle/applicationinventory/services/issues/{Issue268Test.java => IssueTACKLE268Test.java} (56%) create mode 100644 src/test/java/io/tackle/applicationinventory/services/issues/NativeIssueTACKLE268IT.java diff --git a/src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java b/src/test/java/io/tackle/applicationinventory/services/issues/IssueTACKLE268Test.java similarity index 56% rename from src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java rename to src/test/java/io/tackle/applicationinventory/services/issues/IssueTACKLE268Test.java index 07fa67e..714ecfd 100644 --- a/src/test/java/io/tackle/applicationinventory/services/issues/Issue268Test.java +++ b/src/test/java/io/tackle/applicationinventory/services/issues/IssueTACKLE268Test.java @@ -3,37 +3,19 @@ import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.ResourceArg; import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; import io.restassured.RestAssured; import io.restassured.config.EncoderConfig; import io.restassured.http.ContentType; -import io.restassured.response.Response; -import io.tackle.applicationinventory.entities.ApplicationImport; -import io.tackle.applicationinventory.entities.ImportSummary; -import io.tackle.applicationinventory.services.BusinessServiceService; -import io.tackle.applicationinventory.services.TagService; +import io.tackle.applicationinventory.services.WireMockControlsServices; import io.tackle.commons.testcontainers.KeycloakTestResource; import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource; import io.tackle.commons.tests.SecuredResourceTest; -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.hamcrest.core.Is; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.mockito.Mockito; -import javax.inject.Inject; -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.NotSupportedException; -import javax.transaction.RollbackException; -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; import javax.ws.rs.core.MediaType; import java.io.File; import java.util.Arrays; -import java.util.Collections; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.is; @@ -53,19 +35,9 @@ @ResourceArg(name = KeycloakTestResource.REALM_NAME, value = "quarkus") } ) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class Issue268Test extends SecuredResourceTest { - - @Inject - UserTransaction userTransaction; - - @InjectMock - @RestClient - TagService mockTagService; - - @InjectMock - @RestClient - BusinessServiceService mockBusinessServiceService; +@QuarkusTestResource(WireMockControlsServices.class) +// https://issues.redhat.com/browse/TACKLE-268 +public class IssueTACKLE268Test extends SecuredResourceTest { @BeforeAll public static void init() { @@ -73,14 +45,11 @@ public static void init() { } @Test - protected void testImportServiceLongCSVColumnValues() throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException { - Mockito.when(mockTagService.getListOfTags(0, 1000)).thenReturn(Collections.emptySet()); - Mockito.when(mockBusinessServiceService.getListOfBusinessServices(0, 1000)).thenReturn(Collections.emptySet()); - + protected void testImportServiceLongCSVColumnValues() { ClassLoader classLoader = getClass().getClassLoader(); File importFile = new File(classLoader.getResource("long_characters_columns.csv").getFile()); - Response response = given() + given() .config(RestAssured.config().encoderConfig(EncoderConfig.encoderConfig().encodeContentTypeAs("multipart/form-data", ContentType.JSON))) .contentType(MediaType.MULTIPART_FORM_DATA) .accept(MediaType.MULTIPART_FORM_DATA) @@ -88,42 +57,27 @@ protected void testImportServiceLongCSVColumnValues() throws SystemException, No .multiPart("fileName", "long_characters_columns.csv") .when().post(PATH) .then() - .log().all() - .statusCode(200).extract().response(); - - assertEquals(200, response.getStatusCode()); + .statusCode(200); - given() + final long importSummaryId = Long.parseLong(given() .accept("application/hal+json") .when() .get("/import-summary") .then() .statusCode(200) - .log().body() - .body("_embedded.'import-summary'[0].'importStatus'", is("Completed")); - - given() - .accept("application/json") + .body("_embedded.import-summary.size()", is(1), + "_embedded.import-summary[0].importStatus", is("Completed"), + "_embedded.import-summary[0].validCount", is(1), + "_embedded.import-summary[0].invalidCount", is(2)) + .extract().path("_embedded.import-summary[0].id").toString()); + + final String csv = given() + .accept("text/csv") + .queryParam("importSummaryId", importSummaryId) .when() - .get("/import-summary") - .then() - .statusCode(200) - .log().body() - .body("size()", is(1), - "[0].'importStatus'", is("Completed"), - "[0].'validCount'", is(1), - "[0].'invalidCount'", is(2) - ); - - ImportSummary summary = ImportSummary.findAll().firstResult(); - - Response r = - given() - .accept("text/csv") - .when() - .get("/csv-export?importSummaryId=" + summary.id); - - String csv = r.body().print(); + .get("/csv-export") + .body() + .print(); String[] csvFields = csv.split(","); int numberOfRows = (int) Arrays.stream(csvFields).filter("\n"::equals).count(); @@ -141,7 +95,7 @@ protected void testImportServiceLongCSVColumnValues() throws SystemException, No .get("/application") .then() .statusCode(200) - .body("size()", Is.is(1)) + .body("size()", is(1)) .extract() .path("[0].id") .toString()); @@ -154,11 +108,13 @@ protected void testImportServiceLongCSVColumnValues() throws SystemException, No .then() .statusCode(204); - userTransaction.begin(); - ApplicationImport.deleteAll(); - ImportSummary.deleteAll(); - userTransaction.commit(); + // Remove the import summary record (on cascade also the ApplicationImport entities will be deleted) + given() + .accept(ContentType.JSON) + .pathParam("id", importSummaryId) + .when() + .delete("/import-summary/{id}") + .then() + .statusCode(204); } - } - diff --git a/src/test/java/io/tackle/applicationinventory/services/issues/NativeIssueTACKLE268IT.java b/src/test/java/io/tackle/applicationinventory/services/issues/NativeIssueTACKLE268IT.java new file mode 100644 index 0000000..46ec81e --- /dev/null +++ b/src/test/java/io/tackle/applicationinventory/services/issues/NativeIssueTACKLE268IT.java @@ -0,0 +1,6 @@ +package io.tackle.applicationinventory.services.issues; + +import io.quarkus.test.junit.NativeImageTest; + +@NativeImageTest +public class NativeIssueTACKLE268IT extends IssueTACKLE268Test {} From 696ae606f5a5ce398bbb1df355f814cc9ebca97a Mon Sep 17 00:00:00 2001 From: mrizzi Date: Mon, 23 Aug 2021 17:13:12 +0200 Subject: [PATCH 7/7] TACKLE-268 Removed temporary stub in ImportServiceTest --- .../applicationinventory/services/ImportServiceTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java b/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java index 9c30be0..1261c09 100644 --- a/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java +++ b/src/test/java/io/tackle/applicationinventory/services/ImportServiceTest.java @@ -438,7 +438,7 @@ protected void testImportServiceCaseInsensitiveColumnHeaders() { @Order(8) protected void testImportServiceNoTagsRetrieved() { - WireMock.stubFor(get(urlPathEqualTo("/controls/tag")) + final StubMapping tagStubMapping = WireMock.stubFor(get(urlPathEqualTo("/controls/tag")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody(""))); @@ -469,11 +469,8 @@ protected void testImportServiceNoTagsRetrieved() { .log().body() .body("[0].'errorMessage'", is("Unable to retrieve TagTypes from remote resource")); - - - removeTestObjects(Collections.emptyList()); - + WireMock.removeStub(tagStubMapping); } @Test