From 8f5b2cf1a2790c38ab0a3aa0799e56732581e14d Mon Sep 17 00:00:00 2001 From: Balaji Jayabal Date: Mon, 29 Nov 2021 18:45:17 +0100 Subject: [PATCH] Initial commit --- .gitignore | 5 + README.md | 4 +- blog-web/.gitignore | 1 + blog-web/pom.xml | 63 ++++- .../blog/AccessControlResponseFilter.java | 31 +++ .../pierceecom/blog/JAXRSConfiguration.java | 5 + .../com/pierceecom/blog/api/PostsApiImpl.java | 91 +++++++ .../pierceecom/blog/service/PostService.java | 20 ++ .../blog/service/PostServiceImpl.java | 45 ++++ .../com/pierceecom/resources/PostsApi.yaml | 129 ++++++++++ .../com/pierceecom/blog/api/PostsApiTest.java | 140 +++++++++++ integration-test/.gitignore | 1 + integration-test/pom.xml | 23 +- .../com/pierceecom/blog/BlogTestIntegr.java | 227 +++++++++--------- 14 files changed, 656 insertions(+), 129 deletions(-) create mode 100644 blog-web/.gitignore create mode 100644 blog-web/src/main/java/com/pierceecom/blog/AccessControlResponseFilter.java create mode 100644 blog-web/src/main/java/com/pierceecom/blog/api/PostsApiImpl.java create mode 100644 blog-web/src/main/java/com/pierceecom/blog/service/PostService.java create mode 100644 blog-web/src/main/java/com/pierceecom/blog/service/PostServiceImpl.java create mode 100644 blog-web/src/main/java/com/pierceecom/resources/PostsApi.yaml create mode 100644 blog-web/src/test/java/com/pierceecom/blog/api/PostsApiTest.java create mode 100644 integration-test/.gitignore diff --git a/.gitignore b/.gitignore index 32858aa..6f8df1c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# Eclipse +.classpath +.project +.settings diff --git a/README.md b/README.md index 2fa877e..0d11695 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ Please follow the steps below: Payara Micro is enough to run 3. Run the command: "mvn install" in the root folder of the distributionfolder. 4. Run the blog-web - > java -jar payara-micro-4.1.152.1.jar --deploy blog-web\target\blog-web.war + > java -jar payara-micro-5.2021.8.jar --deploy blog-web\target\blog-web.war 5. Go to address: http://localhost:8080/blog-web/hello-pierce. The server should respond with a text that reads: {"message":"Hello Pierce"}. 6. Now basic environment and Blog Ping project is installed correctly. @@ -214,7 +214,7 @@ Please follow the steps below: The integration tests is (and should be) located under the maven artifact "integration test". These tests should test the expected behavior of a correct implementation of Blog posts. -There is a simple test to start with. You run the test with "mvn test-Dtest =*TestIntegr" . +There is a simple test to start with. You run the test with "mvn test -Dtest=*TestIntegr" . The tests and flow is a suggestion and may not use the most convinient APIs to do it, you may change. You should fill in with extra tests (and expect that we do to ;) ) diff --git a/blog-web/.gitignore b/blog-web/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/blog-web/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/blog-web/pom.xml b/blog-web/pom.xml index 8ec9481..38514ce 100644 --- a/blog-web/pom.xml +++ b/blog-web/pom.xml @@ -7,7 +7,7 @@ 1.0-SNAPSHOT - com.pierceecom.sample + com.pierceecom.sample.web blog-web 1.0-SNAPSHOT war @@ -19,6 +19,21 @@ + + io.swagger + swagger-annotations + 1.6.2 + + + io.swagger + swagger-jaxrs + 1.6.3 + + + com.fasterxml.jackson.core + jackson-annotations + 2.13.0 + javax javaee-api @@ -30,11 +45,22 @@ test - org.glassfish.jersey.core - jersey-client - 2.17 - test - + com.sun.jersey + jersey-client + 1.12 + + + org.mockito + mockito-core + 4.0.0 + test + + + org.hamcrest + hamcrest-core + 2.2 + test + @@ -58,6 +84,31 @@ + + org.openapitools + openapi-generator-maven-plugin + 5.2.0 + + + + generate + + + ${project.basedir}/src/main/java/com/pierceecom/resources/PostsApi.yaml + jaxrs-resteasy-eap + ${project.build.directory}/generated-sources + com.pierceecom.blog.swagger + com.pierceecom.blog.model + true + false + + src/gen/java/main + true + + + + + diff --git a/blog-web/src/main/java/com/pierceecom/blog/AccessControlResponseFilter.java b/blog-web/src/main/java/com/pierceecom/blog/AccessControlResponseFilter.java new file mode 100644 index 0000000..d390bc9 --- /dev/null +++ b/blog-web/src/main/java/com/pierceecom/blog/AccessControlResponseFilter.java @@ -0,0 +1,31 @@ +package com.pierceecom.blog; + +import java.io.IOException; + +import javax.annotation.Priority; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.Provider; +import javax.ws.rs.Priorities; + +/** + * This is a response filter class to add CORS headers in the response. + * So that other applications (e.g. Angular application) can access resources from this web service resources. + */ + +@Provider +@Priority(Priorities.HEADER_DECORATOR) +public class AccessControlResponseFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { + final MultivaluedMap headers = responseContext.getHeaders(); + headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); + headers.add("Access-Control-Allow-Origin", "*"); + if (requestContext.getMethod().equalsIgnoreCase("OPTIONS")) { + headers.add("Access-Control-Allow-Headers", requestContext.getHeaderString("Access-Control-Request-Headers")); + } + } +} \ No newline at end of file diff --git a/blog-web/src/main/java/com/pierceecom/blog/JAXRSConfiguration.java b/blog-web/src/main/java/com/pierceecom/blog/JAXRSConfiguration.java index 96a0744..f04a44f 100644 --- a/blog-web/src/main/java/com/pierceecom/blog/JAXRSConfiguration.java +++ b/blog-web/src/main/java/com/pierceecom/blog/JAXRSConfiguration.java @@ -5,11 +5,16 @@ import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; +import com.pierceecom.blog.api.PostsApiImpl; + + @ApplicationPath("/") public class JAXRSConfiguration extends Application { @Override public Set> getClasses() { HashSet> classes = new HashSet<>(); + classes.add(AccessControlResponseFilter.class); + classes.add(PostsApiImpl.class); classes.add(HelloPierceResource.class); return classes; } diff --git a/blog-web/src/main/java/com/pierceecom/blog/api/PostsApiImpl.java b/blog-web/src/main/java/com/pierceecom/blog/api/PostsApiImpl.java new file mode 100644 index 0000000..1051f4e --- /dev/null +++ b/blog-web/src/main/java/com/pierceecom/blog/api/PostsApiImpl.java @@ -0,0 +1,91 @@ +package com.pierceecom.blog.api; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; + +import com.pierceecom.blog.model.Post; +import com.pierceecom.blog.service.PostService; +import com.pierceecom.blog.swagger.PostsApi; + +public class PostsApiImpl implements PostsApi{ + + private static final AtomicLong postSequenceId = new AtomicLong(); + private static final String VALID_XML_CONTENT_REGEX = "[\\u0009\\u000a\\u000d\\u0020-\\uD7FF\\uE000-\\uFFFD]+$"; // regex to match valid XML Characters. + private static final int TITLE_MAX_LENGTH = 40; + private static final int CONTENT_MAX_LENGTH = 100; + + private PostService postService; + + @Inject + public PostsApiImpl(PostService postService) { + this.postService = postService; + } + + @Override + public Response addPost(Post post, SecurityContext securityContext) { + if (!hasValidPostContent(post) || !isNullOrEmpty(post.getId())) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + post.setId(Long.toString(postSequenceId.incrementAndGet())); + postService.addPost(post); + return Response.status(Response.Status.CREATED).entity(post).build(); + } + + @Override + public Response deletePost(String postId, SecurityContext securityContext) { + Optional optPost = postService.getPostById(postId); + if (optPost.isEmpty()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + postService.deletePost(postId); + return Response.ok().build(); + } + + @Override + public Response getAllPosts(SecurityContext securityContext) { + return Response.ok(postService.getAllPosts()).build(); + } + + @Override + public Response getPostById(String postId, SecurityContext securityContext) { + Optional optPost = postService.getPostById(postId); + if (optPost.isEmpty()) { + return Response.status(Response.Status.NO_CONTENT).build(); + } + return Response.ok(optPost.get()).build(); + } + + @Override + public Response updatePost(Post post, SecurityContext securityContext) { + if (isNullOrEmpty(post.getId()) || !hasValidPostContent(post)) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + Optional optPost = postService.getPostById(post.getId()); + if (optPost.isEmpty()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + postService.updatePost(post); + return Response.status(Response.Status.CREATED).entity(post).build(); + } + + private boolean isNullOrEmpty(String s) { + if (s == null) { + return true; + } + return s.isBlank(); + } + + private boolean hasValidPostContent(Post post) { + if (!post.getTitle().matches(VALID_XML_CONTENT_REGEX) || !post.getContent().matches(VALID_XML_CONTENT_REGEX)) { + return false; + } + if (post.getTitle().length() > TITLE_MAX_LENGTH || post.getContent().length() > CONTENT_MAX_LENGTH) { + return false; + } + return true; + } +} diff --git a/blog-web/src/main/java/com/pierceecom/blog/service/PostService.java b/blog-web/src/main/java/com/pierceecom/blog/service/PostService.java new file mode 100644 index 0000000..74d7816 --- /dev/null +++ b/blog-web/src/main/java/com/pierceecom/blog/service/PostService.java @@ -0,0 +1,20 @@ +package com.pierceecom.blog.service; + +import java.util.Collection; +import java.util.Optional; + +import com.pierceecom.blog.model.Post; + +public interface PostService { + + void addPost(Post post); + + void deletePost(String postId); + + Collection getAllPosts(); + + Optional getPostById(String postId); + + void updatePost(Post post); + +} diff --git a/blog-web/src/main/java/com/pierceecom/blog/service/PostServiceImpl.java b/blog-web/src/main/java/com/pierceecom/blog/service/PostServiceImpl.java new file mode 100644 index 0000000..1d514c4 --- /dev/null +++ b/blog-web/src/main/java/com/pierceecom/blog/service/PostServiceImpl.java @@ -0,0 +1,45 @@ +package com.pierceecom.blog.service; + +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.enterprise.context.ApplicationScoped; + +import com.pierceecom.blog.model.Post; + +@ApplicationScoped +public class PostServiceImpl implements PostService { + + private ConcurrentMap postMap = new ConcurrentHashMap<>(); + + protected PostServiceImpl() { + } + + @Override + public void addPost(Post post) { + postMap.put(post.getId(), post); + } + + @Override + public void deletePost(String postId) { + postMap.remove(postId); + } + + @Override + public Collection getAllPosts() { + return postMap.values(); + } + + @Override + public Optional getPostById(String postId) { + return Optional.ofNullable(postMap.get(postId)); + } + + @Override + public void updatePost(Post post) { + postMap.put(post.getId(), post); + } + +} diff --git a/blog-web/src/main/java/com/pierceecom/resources/PostsApi.yaml b/blog-web/src/main/java/com/pierceecom/resources/PostsApi.yaml new file mode 100644 index 0000000..064417a --- /dev/null +++ b/blog-web/src/main/java/com/pierceecom/resources/PostsApi.yaml @@ -0,0 +1,129 @@ +swagger: "2.0" +info: + description: "This is the definition of the API for code test as Pierce AB" + version: "1.0.0" + title: "Simple blog post API" +host: "localhost:8080" +basePath: "/blog-web" +schemes: +- "http" +paths: + /posts: + get: + tags: + - "post" + summary: "Get all posts" + description: "Returns all posts" + operationId: "getAllPosts" + produces: + - "application/json" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Post" + post: + tags: + - "post" + summary: "Add a new post" + description: "" + operationId: "addPost" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Post object that needs to be added" + required: true + schema: + $ref: "#/definitions/Post" + responses: + 201: + description: "OK of post" + schema: + $ref: "#/definitions/Post" + 405: + description: "Invalid input" + put: + tags: + - "post" + summary: "Updates a post" + description: "" + operationId: "updatePost" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Post object that needs to be updated" + required: true + schema: + $ref: "#/definitions/Post" + responses: + 201: + description: "OK of post" + schema: + $ref: "#/definitions/Post" + 404: + description: "Post not found" + 405: + description: "Invalid input" + /posts/{postId}: + get: + tags: + - "post" + summary: "Find post by ID" + description: "Returns a single post" + operationId: "getPostById" + produces: + - "application/json" + parameters: + - name: "postId" + in: "path" + description: "ID of post to return" + required: true + type: "string" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/Post" + 204: + description: "No content" + delete: + tags: + - "post" + summary: "Deletes a post" + description: "" + operationId: "deletePost" + parameters: + - name: "postId" + in: "path" + description: "Post id to delete" + required: true + type: "string" + responses: + 200: + description: "successful operation" + 404: + description: "Post not found" +definitions: + Post: + type: "object" + required: + - "title" + - "content" + properties: + id: + type: "string" + example: "1" + title: + type: "string" + example: "what I did today" + content: + type: "string" + example: "wrote a boring post" \ No newline at end of file diff --git a/blog-web/src/test/java/com/pierceecom/blog/api/PostsApiTest.java b/blog-web/src/test/java/com/pierceecom/blog/api/PostsApiTest.java new file mode 100644 index 0000000..54ceca8 --- /dev/null +++ b/blog-web/src/test/java/com/pierceecom/blog/api/PostsApiTest.java @@ -0,0 +1,140 @@ +package com.pierceecom.blog.api; + +import javax.ws.rs.core.Response; + +import org.junit.Before; +import org.junit.Test; + +import com.pierceecom.blog.model.Post; +import com.pierceecom.blog.service.PostService; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public class PostsApiTest { + + private PostService postService; + private PostsApiImpl postsApi; + + @Before + public void setup() { + postService = mock(PostService.class); + postsApi = new PostsApiImpl(postService); + } + + @Test + public void test_1_0_AddPost_Ok() { + Post post = samplePost("","Valid post","It's valid post!"); + Response response = postsApi.addPost(post, null); + assertEquals(response.getStatus(),Response.Status.CREATED.getStatusCode()); + assertEquals((Post)response.getEntity(), post); + } + + @Test + public void test_1_1_AddPost_TitleLengthMoreThanTwenty() { + Post post = samplePost("","Trying to add a post's title more than 20 characters","It's invalid post!"); + Response response = postsApi.addPost(post, null); + assertEquals(response.getStatus(),Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + public void test_1_2_AddPost_BadRequest() { + Post post = samplePost("1","Trying to create a post with Id!","Id is auto generated field. So it's invalid post!"); + Response response = postsApi.addPost(post, null); + assertEquals(response.getStatus(),Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + public void test_1_3_AddPost_WithInvalidChar() { + Post post = samplePost("","Content with invalid emoji character","It contains invalid char 😦"); + Response response = postsApi.addPost(post, null); + assertEquals(response.getStatus(),Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + public void test_2_0_DeletePost_Ok() { + Post post = samplePost("3","Delete Post","Post to be deleted!"); + when(postService.getPostById(post.getId())).thenReturn(Optional.of(post)); + Response response = postsApi.deletePost(post.getId(), null); + assertEquals(response.getStatus(),Response.Status.OK.getStatusCode()); + } + + @Test + public void test_2_1_DeletePost_NotFound() { + Post post = samplePost("4","Delete Post","Post to be deleted!"); + when(postService.getPostById(post.getId())).thenReturn(Optional.of(post)); + Response response = postsApi.deletePost("100", null); + assertEquals(response.getStatus(),Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + public void test_3_0_GetAllPosts_Ok() { + Post post1 = samplePost("5","Post 1","Getting all post1!"); + Post post2 = samplePost("6","Post 2","Getting all post2!"); + Collection postList = Arrays.asList(post1, post2); + when(postService.getAllPosts()).thenReturn(postList); + Response response = postsApi.getAllPosts(null); + assertEquals(response.getStatus(),Response.Status.OK.getStatusCode()); + Collection returnedList = (Collection) response.getEntity(); + assertEquals(returnedList.size(),2); + } + + @Test + public void test_3_1_GetAllPosts_WithOutData() { + when(postService.getAllPosts()).thenReturn(List.of()); + Response response = postsApi.getAllPosts(null); + assertEquals(response.getStatus(),Response.Status.OK.getStatusCode()); + Collection returnedList =(Collection) response.getEntity(); + assertEquals(returnedList.size(),0); + } + + @Test + public void test_4_0_GetPostById_Ok() { + Post post = samplePost("7","Get Post","Getting post by ID!"); + when(postService.getPostById(post.getId())).thenReturn(Optional.of(post)); + Response response = postsApi.getPostById(post.getId(), null); + assertEquals(response.getStatus(),Response.Status.OK.getStatusCode()); + assertEquals((Post)response.getEntity(), post); + } + + @Test + public void test_4_1_GetPostById_NoContent() { + when(postService.getPostById(anyString())).thenReturn(Optional.empty()); + Response response = postsApi.getPostById(anyString(), null); + assertEquals(response.getStatus(),Response.Status.NO_CONTENT.getStatusCode()); + } + + @Test + public void test_5_0_UpdatePost_Ok() { + Post updatedPost = samplePost("8","Update Post","Updating a post!"); + when(postService.getPostById(updatedPost.getId())).thenReturn(Optional.of(updatedPost)); + Response updateResponse = postsApi.updatePost(updatedPost, null); + assertEquals(updateResponse.getStatus(),Response.Status.CREATED.getStatusCode()); + assertEquals((Post)updateResponse.getEntity(), updatedPost); + } + + @Test + public void test_5_1_UpdatePost_NotFound() { + Post updatedPost = samplePost("9","Update Post","Updating a post!"); + when(postService.getPostById(updatedPost.getId())).thenReturn(Optional.empty()); + Response updateResponse = postsApi.updatePost(updatedPost, null); + assertEquals(updateResponse.getStatus(),Response.Status.NOT_FOUND.getStatusCode()); + } + + // helper method. + private Post samplePost(String id, String title, String content) { + Post post = new Post(); + post.setId(id); + post.setTitle(title); + post.setContent(content); + return post; + } + +} diff --git a/integration-test/.gitignore b/integration-test/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/integration-test/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/integration-test/pom.xml b/integration-test/pom.xml index e5d22fa..3308247 100644 --- a/integration-test/pom.xml +++ b/integration-test/pom.xml @@ -14,17 +14,22 @@ junit 4.12 - - - diff --git a/integration-test/src/test/java/com/pierceecom/blog/BlogTestIntegr.java b/integration-test/src/test/java/com/pierceecom/blog/BlogTestIntegr.java index 85e0a71..e353161 100644 --- a/integration-test/src/test/java/com/pierceecom/blog/BlogTestIntegr.java +++ b/integration-test/src/test/java/com/pierceecom/blog/BlogTestIntegr.java @@ -1,19 +1,20 @@ package com.pierceecom.blog; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; -import static org.junit.Assert.*; +import static io.restassured.RestAssured.with; +import static io.restassured.RestAssured.get; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; +import io.restassured.RestAssured; +import jakarta.ws.rs.core.MediaType; + + /** * TODO, Consider it part of the test to replace HttpURLConnection with better * APIs (for example Jersey-client, JSON-P etc-) to call REST-service @@ -21,124 +22,126 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class BlogTestIntegr { - private static final String POST_1 = "{\"id\":\"1\",\"title\":\"First title\",\"content\":\"First content\"}"; - private static final String POST_2 = "{\"id\":\"2\",\"title\":\"Second title\",\"content\":\"Second content\"}"; - private static final String POSTS_URI = "http://localhost:8080/blog-web/posts/"; - + private static final String VALID_POST = "{\"id\":\"\",\"title\":\"First title\",\"content\":\"First content\"}"; + private static final String INVALID_POST = "{\"id\":\"\",\"title\":\"Second title\",\"content\":\"Invalid content🙂\"}"; + private static final String UPDATE_POST = "{\"id\":\"ID\",\"title\":\"First title\",\"content\":\"Content Updated\"}"; public BlogTestIntegr() { } - - @Test - public void test_1_BlogWithoutPosts() { - String output = GET(POSTS_URI, 200); - assertEquals("[]", output); + + @Before + public void setup(){ + RestAssured.baseURI = "http://localhost:8080/blog-web"; } - + @Test - public void test_2_AddPosts() { - String location = POST(POSTS_URI, POST_1); - assertEquals(POSTS_URI + "1", location); - - location = POST(POSTS_URI, POST_2); - assertEquals(POSTS_URI + "2", location); - } - + public void test_1_0_GetAllPosts_NoPost() { + get("/posts") + .then() + .assertThat() + .body(is("[]")); + } + @Test - public void test_3_GetPost() { - String postJson = GET(POSTS_URI + "1", 200); - assertEquals(POST_1, postJson); - - postJson = GET(POSTS_URI + "2", 200); - assertEquals(POST_2, postJson); + public void test_2_0_AddPost_Ok() { + with() + .body(VALID_POST) + .contentType(MediaType.APPLICATION_JSON) + .when() + .request("POST", "/posts") + .then() + .statusCode(201); + } + + @Test + public void test_2_1_AddPost_InvalidContent() { + with() + .body(INVALID_POST) + .contentType(MediaType.APPLICATION_JSON) + .when() + .request("POST", "/posts") + .then() + .statusCode(400); + } + + @Test + public void test_3_0_GetPost_Ok() { + get("/posts/"+getPostId()) + .then() + .statusCode(200); } - + + @Test + public void test_3_1_GetPost_NoContent() { + get("/posts/WrongId") + .then() + .statusCode(204); + } + @Test - public void test_4_GetAllPosts() { - String output = GET(POSTS_URI, 200); - assertEquals("[" + POST_1 + "," + POST_2 + "]", output); + public void test_4_0_GetAllPosts() { + get("/posts") + .then() + .assertThat() + .statusCode(200); } @Test - public void test_5_DeletePosts() { - DELETE(POSTS_URI + "1", 200); - // Should now be gone - GET(POSTS_URI + "1", 204); - - DELETE(POSTS_URI + "2", 200); - // Should now be gone - GET(POSTS_URI + "2", 204); - + public void test_5_0_UpdatePost_Ok() { + String post = UPDATE_POST.replace("ID", getPostId()); + with() + .body(post) + .contentType(MediaType.APPLICATION_JSON) + .when() + .request("PUT", "/posts") + .then() + .statusCode(201) + .assertThat() + .body("content", is("Content Updated")); } - + @Test - public void test_6_GetAllPostsShouldNowBeEmpty() { - String output = GET(POSTS_URI, 200); - assertEquals("[]", output); + public void test_5_1_UpdatePost_NotFound() { + with() + .body(UPDATE_POST) + .contentType(MediaType.APPLICATION_JSON) + .when() + .request("PUT", "/posts") + .then() + .statusCode(404); } - - /* Helper methods */ - private String GET(String uri, int expectedResponseCode) { - StringBuilder sb = new StringBuilder(); - try { - URL url = new URL(uri); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("GET"); - conn.setRequestProperty("Accept", "application/json"); - assertEquals(expectedResponseCode, conn.getResponseCode()); - BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); - - String output; - while ((output = br.readLine()) != null) { - sb.append(output); - } - - conn.disconnect(); - } catch (MalformedURLException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } - return sb.toString(); + + @Test + public void test_6_0_DeletePost_NotFound() { + with() + .request("DELETE", "/posts/WrongId") + .then() + .statusCode(404); } - - private String POST(String uri, String json) { - String location = ""; - try { - URL url = new URL(uri); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Content-Type", "application/json"); - - OutputStream os = conn.getOutputStream(); - os.write(json.getBytes()); - os.flush(); - assertEquals(201, conn.getResponseCode()); - - location = conn.getHeaderField("Location"); - conn.disconnect(); - - } catch (MalformedURLException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } - return location; + + @Test + public void test_6_1_DeletePost_Ok() { + with() + .request("DELETE", "/posts/"+getPostId()) + .then() + .statusCode(200); } - - private void DELETE(String uri, int expectedResponseCode) { - try { - URL url = new URL(uri); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("DELETE"); - conn.setRequestProperty("Accept", "application/json"); - assertEquals(expectedResponseCode, conn.getResponseCode()); - } catch (MalformedURLException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(BlogTestIntegr.class.getName()).log(Level.SEVERE, null, ex); - } + + @Test + public void test_7_0_GetAllPostsShouldNowBeEmpty() { + get("/posts") + .then() + .assertThat() + .body(is("[]")); + } + + // Helper method. + private String getPostId() { + List postIds = get("/posts") + .getBody() + .jsonPath() + .getList("id"); + return postIds.get(0); } }