diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85ed9b2..057611f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,11 @@ jobs: java-version: "17" cache: "maven" - name: Compile - run: mvn compile - - name: Test - run: mvn verify + run: mvn clean compile + - name: Unit Test + run: mvn test + - name: Integration API Test + run: mvn verify -Pintegration-test - uses: dorny/test-reporter@v1 with: name: Test Results diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml index c1d5a19..1edc962 100644 --- a/.github/workflows/test-report.yml +++ b/.github/workflows/test-report.yml @@ -3,9 +3,8 @@ on: workflow_call: workflow_dispatch: workflow_run: - workflows: [ 'CI' ] # runs after CI workflow - types: - - completed + workflows: 'CI' # runs after CI workflow + jobs: report: runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fc66aa7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM maven:3.8.4-jdk-11 +MAINTAINER ricardogarfe + +RUN adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --disabled-password jetty +USER jetty +WORKDIR /home/jetty + +EXPOSE 8080 +# Run subsequent commands as jetty user +USER jetty + +ADD pom.xml /home/jetty +ADD src /home/jetty/src + +RUN mvn package +# Run script +CMD mvn jetty:run diff --git a/README.md b/README.md index 1814330..7548055 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,14 @@ Create a service to wrap those methods * Divide: divide two numbers and return result * divide by zero should return error message +Create a REST service to expose those methods with URLs in browser: + +- /api/calculator/ping +- /api/calculator/add?x=8&y=26 +- /api/calculator/sub?x=12&y=8 +- /api/calculator/mul?x=11&y=8 +- /api/calculator/div?x=12&y=12 + ## System requirements * JDK-11 @@ -35,7 +43,7 @@ Create a service to wrap those methods ## 1. Manually Build, Test, and Deploy By Maven -### 1.2 Run JUnit Test +### 1.1 Run JUnit Test Maven execution: @@ -55,6 +63,29 @@ Execute (linux/macos mvnw or windows mvnw.cmd): $ ./mvnw clean test ``` +### 1.2 Run Integration Test + +```console +$ mvn clean integration-test +``` + +### 1.3 Run Locally + +```console +$ mvn jetty:run +[INFO] Started Jetty Server +``` + +By default, the jetty port is 8080, so you should visit following urls in browser: + +- http://localhost:8080/calculator/api/calculator/ping +- http://localhost:8080/calculator/api/calculator/add?x=8&y=26 +- http://localhost:8080/calculator/api/calculator/sub?x=12&y=8 +- http://localhost:8080/calculator/api/calculator/mul?x=11&y=8 +- http://localhost:8080/calculator/api/calculator/div?x=12&y=12 + +To run in a different port, `mvn jetty:run -Djetty.port=`. + ## 2. Automatically Build and Test [Github action ci](.github/workflows/ci.yml) step definition: @@ -78,4 +109,42 @@ steps: ## 3. Containerize Your Web App -**TODO** +### 3.1. Build a docker image using Dockerfile: + +```console +$ docker build -t myjetty --no-cache -f Dockerfile . +[+] Building 18.9s (11/11) FINISHED +``` + +### 3.2. Run docker image locally + +```console +$ docker run --rm -p 8080:8080 myjetty +``` + +> Explain: --rm means delete the container after stopping it. + +Access the web app at http://localhost:8080/api/calculator/ping in browser. + +Press Control-C to stop and remove the container. + +### 3.3. Run docker-compose environment + +Define a service to use repository Docker image: + +```yaml +version: '3.8' +services: + calculator: + build: . + ports: + - "8080:8080" +``` + +Run docker-compose environment: + +```console +$ docker-compse up +``` + +Access the web app at http://localhost:8080/api/calculator/ping in browser. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..08a02cd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3.8' +services: + calculator: + build: . + ports: + - "8080:8080" diff --git a/pom.xml b/pom.xml index 99c0aae..392ff37 100644 --- a/pom.xml +++ b/pom.xml @@ -4,25 +4,76 @@ 4.0.0 com.geekshubs.javawebapp java-maven-calculator-web-app - jar + war 1.1-SNAPSHOT Calculator Web A Java Maven Calculator Web Application + + dev + UTF-8 17 17 17 2.2 5.8.2 + 4.5.13 + 11.0.7 + 3.0.3 3.10.1 + 3.3.2 3.2.0 2.22.2 0.8.7 + + + all-tests + + all-tests + + false + false + + + + dev + + + integration-test + + + integration-test + + false + true + + + + + org.glassfish.jersey.containers + jersey-container-servlet-core + ${jersey.version} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + + + org.glassfish.jersey.core + jersey-server + ${jersey.version} + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + org.hamcrest hamcrest-library @@ -35,6 +86,12 @@ ${junit.version} test + + org.apache.httpcomponents + httpclient + ${apache.httpcomponents.version} + test + org.apache.maven.plugins maven-resources-plugin @@ -54,21 +111,78 @@ ${maven.compiler.target} + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty.maven.plugin.version} + + + /calculator + + 8079 + stop-jetty-for-it + 10 + + + + start-jetty + pre-integration-test + + start + + + + stop-jetty + post-integration-test + + stop + + + + org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.plugin.version} + + + ${surefireArgLine} + + ${skip.unit.tests} + + + **/IT*.java + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + - run-integration-test - integration-test + integration-tests - test + integration-test + verify - - **/*IT.java - + + ${failsafeArgLine} + + ${skip.integration.tests} @@ -78,17 +192,82 @@ jacoco-maven-plugin ${jacoco.maven.plugin.version} + + pre-unit-test prepare-agent + + + ${project.build.directory}/coverage-reports/jacoco.exec + + surefireArgLine + + + - report + post-unit-test test report + + + ${project.build.directory}/coverage-reports/jacoco.exec + + ${project.reporting.outputDirectory}/jacoco + + + + + + + + pre-integration-test + pre-integration-test + + prepare-agent + + + + ${project.build.directory}/coverage-reports/jacoco-it.exec + + failsafeArgLine + + + + + post-integration-test + post-integration-test + + report + + + + ${project.build.directory}/coverage-reports/jacoco-it.exec + + ${project.reporting.outputDirectory}/jacoco-it + diff --git a/src/main/java/com/geekshubs/calculator/rest/CalculatorAPI.java b/src/main/java/com/geekshubs/calculator/rest/CalculatorAPI.java new file mode 100644 index 0000000..8d60dd0 --- /dev/null +++ b/src/main/java/com/geekshubs/calculator/rest/CalculatorAPI.java @@ -0,0 +1,53 @@ +package com.geekshubs.calculator.rest; + +import com.geekshubs.calculator.Calculator; +import com.geekshubs.calculator.service.CalculatorService; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +import java.util.Date; + +@Path("/calculator") +public class CalculatorAPI { + + CalculatorService calculatorService = new CalculatorService(); + + @GET + @Path("ping") + @Produces(MediaType.TEXT_PLAIN) + public String ping() { + return "Welcome to Java Maven Calculator Web App!!!\n" + new Date(); + } + + @GET + @Path("add") + @Produces(MediaType.APPLICATION_JSON) + public Calculator Add(@QueryParam("x") int x, @QueryParam("y") int y) { + return calculatorService.Add(x, y); + } + + @GET + @Path("sub") + @Produces(MediaType.APPLICATION_JSON) + public Calculator Sub(@QueryParam("x") int x, @QueryParam("y") int y) { + return calculatorService.Sub(x, y); + } + + @GET + @Path("mul") + @Produces(MediaType.APPLICATION_JSON) + public Calculator Mul(@QueryParam("x") int x, @QueryParam("y") int y) { + return calculatorService.Mul(x, y); + } + + @GET + @Path("div") + @Produces(MediaType.APPLICATION_JSON) + public Calculator Div(@QueryParam("x") int x, @QueryParam("y") int y) { + return calculatorService.Div(x, y); + } +} diff --git a/src/main/webapp/META-INF/context.xml b/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..6c7356e --- /dev/null +++ b/src/main/webapp/META-INF/context.xml @@ -0,0 +1,2 @@ + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..06ccf0b --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,27 @@ + + Calculator Web + + + index.html + index.jsp + + + + Calculator Service + org.glassfish.jersey.servlet.ServletContainer + + + jersey.config.server.provider.packages + com.geekshubs.calculator.rest + + + jersey.config.server.provider.scanning.recursive + false + + 1 + + + Calculator Service + /api/* + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 0000000..45ea54c --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,10 @@ + + + + HTML - Calculator API + + + +
Hello, Calculator!
+ + diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..f01dce8 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,12 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + + JSP - Calculator API + + + <%="Hello,JSP!" %> + + diff --git a/src/test/java/com/geekshubs/calculator/integration/ITCalculatorAPITest.java b/src/test/java/com/geekshubs/calculator/integration/ITCalculatorAPITest.java new file mode 100644 index 0000000..a8e32fc --- /dev/null +++ b/src/test/java/com/geekshubs/calculator/integration/ITCalculatorAPITest.java @@ -0,0 +1,65 @@ +package com.geekshubs.calculator.integration; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ITCalculatorAPITest { + + CloseableHttpClient httpclient = HttpClients.createDefault(); + + @Test + public void testPing() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/ping"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(EntityUtils.toString(response.getEntity()), containsString("Welcome to Java Maven Calculator Web App!!!")); + } + + @Test + public void testAdd() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/add?x=2&y=3"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(EntityUtils.toString(response.getEntity()), containsString("5")); + } + + @Test + public void testSub() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/sub?x=2&y=3"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(EntityUtils.toString(response.getEntity()), containsString("-1")); + } + + @Test + public void testMul() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/mul?x=2&y=3"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(EntityUtils.toString(response.getEntity()), containsString("6")); + } + + @Test + public void testDiv() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/div?x=6&y=3"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(EntityUtils.toString(response.getEntity()), containsString("2")); + } + + @Test + public void testDivByZero() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:8080/calculator/api/calculator/div?x=6&y=0"); + HttpResponse response = httpclient.execute(httpGet); + assertEquals(500, response.getStatusLine().getStatusCode()); + } +} diff --git a/src/test/java/com/geekshubs/calculator/rest/CalculatorAPITest.java b/src/test/java/com/geekshubs/calculator/rest/CalculatorAPITest.java new file mode 100644 index 0000000..a7d2e34 --- /dev/null +++ b/src/test/java/com/geekshubs/calculator/rest/CalculatorAPITest.java @@ -0,0 +1,37 @@ +package com.geekshubs.calculator.rest; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CalculatorAPITest { + + CalculatorAPI calculatorAPI = new CalculatorAPI(); + + @Test + public void testPing() { + assertThat(new CalculatorAPI().ping(), containsString("Welcome to Java Maven Calculator Web App!!!")); + } + + @Test + public void testAdd() { + assertEquals(34, calculatorAPI.Add(8, 26).getResult()); + } + + @Test + public void testSub() { + assertEquals(4, calculatorAPI.Sub(12, 8).getResult()); + } + + @Test + public void testMul() { + assertEquals(88, calculatorAPI.Mul(11, 8).getResult()); + } + + @Test + public void testDiv() { + assertEquals(1, calculatorAPI.Div(12, 12).getResult()); + } +}