diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/CHALLENGE.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/CHALLENGE.java index 15f0eb80..fc2bd9ef 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/CHALLENGE.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/CHALLENGE.java @@ -49,5 +49,7 @@ public enum CHALLENGE { POST_TODOS_INVALID_EXTRA_FIELD, POST_MAX_OUT_TITILE_DESCRIPTION_LENGTH, PUT_TODOS_400, - POST_TODOS_404; + POST_TODOS_404, + PUT_TODOS_FULL_200, + PUT_TODOS_PARTIAL_200, PUT_TODOS_MISSING_TITLE_400, PUT_TODOS_400_NO_AMEND_ID; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java index fd9289f4..acef010d 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java @@ -117,6 +117,30 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } + if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 200) { + if (request.getBody().toLowerCase().contains("donestatus") && request.getBody().toLowerCase().contains("description")){ + challengers.pass(challenger, CHALLENGE.PUT_TODOS_FULL_200); + } + } + + if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 400) { + if (response.getBody().contains("title : field is mandatory")){ + challengers.pass(challenger, CHALLENGE.PUT_TODOS_MISSING_TITLE_400); + } + } + + if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 200){ + if(!request.getBody().toLowerCase().contains("donestatus") && !request.getBody().toLowerCase().contains("description")) { + challengers.pass(challenger, CHALLENGE.PUT_TODOS_PARTIAL_200); + } + } + + if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 400) { + if (response.getBody().contains("Can not amend id from")){ + challengers.pass(challenger, CHALLENGE.PUT_TODOS_400_NO_AMEND_ID); + } + } + if (request.getVerb() == POST && request.getPath().contentEquals("secret/token") && request.getHeaders().headerExists("Authorization") && diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java index 60429734..d25c4967 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java @@ -28,6 +28,8 @@ private String renderChallengeNumber(int challengeOrder){ return String.format("%02d", challengeOrder); } + // TODO: refactor this into private methods to make it easier to re-order and manage + public ChallengeDefinitions(){ challengeData = new HashMap<>(); @@ -50,6 +52,7 @@ public ChallengeDefinitions(){ "Issue a POST request on the `/challenger` end point, with no body, to create a new challenger session. Use the generated X-CHALLENGER header in future requests to track challenge completion." ); aChallenge.addHint("In multi-user mode, you need to create an X-CHALLENGER Session first", "/gui/multiuser.html"); + aChallenge.addSolutionLink("Send request using POST to /challenger endpoint. The response has an X-CHALLENGER header, add this header X-CHALLENGER and the GUID value to all future requests.","",""); aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/post-challenger-201"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); getStarted.addChallenge(aChallenge); @@ -105,6 +108,9 @@ public ChallengeDefinitions(){ aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "1S5kpd8-xfM"); challengeOrder++; + getChallenges.addChallenge(getTodosFiltered200(challengeOrder)); + challengeOrder++; + // HEAD ChallengeSection headChallenges = new ChallengeSection("HEAD Challenges", "A HEAD request, is like a GET request, but only returns the headers and status code."); @@ -130,16 +136,7 @@ public ChallengeDefinitions(){ challengeOrder++; - aChallenge = createChallenge(CHALLENGE.GET_TODOS_FILTERED, renderChallengeNumber(challengeOrder), "GET /todos (200) ?filter", - "Issue a GET request on the `/todos` end point with a query filter to get only todos which are 'done'. There must exist both 'done' and 'not done' todos, to pass this challenge."); - postCreateChallenges.addChallenge(aChallenge); - aChallenge.addHint("A query filter is a URL parameter using the field name and a value"); - aChallenge.addHint("A URL parameter is added to the end of a url with a ? e.g. /todos?id=1"); - aChallenge.addHint("To filter on 'done' we use the 'doneStatus' field ? e.g. ?doneStatus=true"); - aChallenge.addHint("Make sure there are todos which are done, and not yet done"); - aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/get-todos-200-filter"); - aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "G-sLuhyPMuw"); - challengeOrder++; + aChallenge = createChallenge(CHALLENGE.POST_TODOS_BAD_DONE_STATUS, renderChallengeNumber(challengeOrder), "POST /todos (400) doneStatus", "Issue a POST request to create a todo but fail validation on the `doneStatus` field"); @@ -230,6 +227,52 @@ public ChallengeDefinitions(){ //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "feXdRpZ_tgs"); challengeOrder++; + + + // UPDATE wtih PUT + ChallengeSection putUpdateChallenges = new ChallengeSection("Update Challenges with PUT", + "A PUT request can be used to amend data. REST Put requests are idempotent, they provide the same result each time."); + sections.add(putUpdateChallenges); + + aChallenge = createChallenge(CHALLENGE.PUT_TODOS_FULL_200, renderChallengeNumber(challengeOrder), "PUT /todos/{id} full (200)", + "Issue a PUT request to update an existing todo with a complete payload i.e. title, description and donestatus."); + putUpdateChallenges.addChallenge(aChallenge); + // todo: create solution for PUT todos full 200 challenge + //aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/post-todos-201"); + //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + challengeOrder++; + + aChallenge = createChallenge(CHALLENGE.PUT_TODOS_PARTIAL_200, renderChallengeNumber(challengeOrder), "PUT /todos/{id} partial (200)", + "Issue a PUT request to update an existing todo with just mandatory items in payload i.e. title."); + putUpdateChallenges.addChallenge(aChallenge); + // todo: create solution for PUT todos partial 200 challenge + //aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/post-todos-201"); + //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + challengeOrder++; + + aChallenge = createChallenge(CHALLENGE.PUT_TODOS_MISSING_TITLE_400, renderChallengeNumber(challengeOrder), "PUT /todos/{id} no title (400)", + "Issue a PUT request to fail to update an existing todo because title is missing in payload."); + putUpdateChallenges.addChallenge(aChallenge); + // todo: create solution for PUT todos partial 200 challenge + aChallenge.addHint("Title is required for Put requests because they are idempotent. You can amend using POST without a title, but not using a PUT."); + //aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/post-todos-201"); + //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + // TODO: add solution text to summarise solution + challengeOrder++; + + aChallenge = createChallenge(CHALLENGE.PUT_TODOS_400_NO_AMEND_ID, renderChallengeNumber(challengeOrder), "PUT /todos/{id} no amend id (400)", + "Issue a PUT request to fail to update an existing todo because id different in payload."); + putUpdateChallenges.addChallenge(aChallenge); + // todo: create solution for PUT todos partial 200 challenge + aChallenge.addHint("ID is auto generated you can not amend it in the payload."); + aChallenge.addHint("If you have a different id in the payload from the url then this is viewed as an amendment and you can not amend an auto generated field."); + //aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/post-todos-201"); + //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + // TODO: add solution text to summarise solution + challengeOrder++; + + + // DELETE ChallengeSection deleteChallenges = new ChallengeSection("DELETE Challenges", "Use a DELETE request to delete an entity. Since this is an extreme request, normally you have to be logged in or authenticated, but we wanted to make life easier for you so we cover authentication later. Anyone can delete To Do items without authentication in this system."); @@ -612,6 +655,18 @@ public ChallengeDefinitions(){ } } + private ChallengeDefinitionData getTodosFiltered200(int challengeOrder) { + ChallengeDefinitionData aChallenge = createChallenge(CHALLENGE.GET_TODOS_FILTERED, renderChallengeNumber(challengeOrder), "GET /todos (200) ?filter", + "Issue a GET request on the `/todos` end point with a query filter to get only todos which are 'done'. There must exist both 'done' and 'not done' todos, to pass this challenge."); + aChallenge.addHint("A query filter is a URL parameter using the field name and a value"); + aChallenge.addHint("A URL parameter is added to the end of a url with a ? e.g. /todos?id=1"); + aChallenge.addHint("To filter on 'done' we use the 'doneStatus' field ? e.g. ?doneStatus=true"); + aChallenge.addHint("Make sure there are todos which are done, and not yet done"); + aChallenge.addSolutionLink("Read Solution", "HREF", "https://www.eviltester.com/apichallenges/howto/get-todos-200-filter"); + aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "G-sLuhyPMuw"); + return aChallenge; + } + private ChallengeDefinitionData createChallenge(final CHALLENGE id, final String orderId, final String name, diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java index bc389f4f..ddabc4c0 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java @@ -6,16 +6,29 @@ public class ChallengeSolutionLink { public final String linkType; public final String linkData; - public ChallengeSolutionLink(final String linkText, final String linkType, final String linkData) { - this.linkText = linkText; - this.linkType = linkType.toUpperCase(); - this.linkData = linkData; + public ChallengeSolutionLink(final String linkText, final String linkType, final String linkUrl) { + this.linkText = linkText.trim(); + + if(linkType==null){ + this.linkType = ""; + }else{ + this.linkType = linkType.trim().toUpperCase(); + } + + if(linkUrl==null) { + this.linkData = ""; + }else{ + this.linkData = linkUrl.trim(); + } } public String asHtmlAHref() { if(linkType.equals("YOUTUBE")){ return String.format("%s",linkData, linkText); } + if(linkData.isEmpty()){ + return linkText; + } return String.format("%s",linkData, linkText); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java index a95a1a65..466ad6fa 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java @@ -411,9 +411,17 @@ private String renderChallengeData(final ChallengeDefinitions challengeDefinitio final Collection sections = challengeDefinitions.getChallengeSections(); + // add a toc + html.append("

Sections

"); + html.append(""); + for(ChallengeSection section : sections){ - html.append("

" + section.getTitle() + "

"); + html.append(String.format("

", section.getTitle().replaceAll(" ", "").toLowerCase()) + section.getTitle() + "

"); html.append("

" + section.getDescription() + "

"); List sectionData = new ArrayList<>(); @@ -429,6 +437,7 @@ private String renderChallengeData(final ChallengeDefinitions challengeDefinitio } html.append(renderChallengeData(sectionData)); + html.append("

Back to Section List

"); } return html.toString(); diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java index db2612c4..2ed4575b 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java @@ -14,6 +14,7 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -232,6 +233,105 @@ public void canPutTodos400FailCreatePass() { Assertions.assertTrue(challenger.statusOfChallenge(CHALLENGE.PUT_TODOS_400)); } + @Test + public void canPutTodosFull200AmendPass() { + + final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + + Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); + + Map headers = new HashMap<>(); + headers.putAll(x_challenger_header); + headers.put("Content-Type", "application/json"); + + EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + + // amend a todo successfully + final HttpResponseDetails response = + http.send("/todos/" + aTodo.getPrimaryKeyValue(), + "PUT", headers, + "{\"title\":\"my put todo\",\"description\":\"a put description\",\"doneStatus\":true}"); + // complete payload to avoid defaults + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertTrue(challenger.statusOfChallenge(CHALLENGE.PUT_TODOS_FULL_200)); + + + } + + @Test + public void canPutTodosPartial200AmendPass() { + + final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + + Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); + + Map headers = new HashMap<>(); + headers.putAll(x_challenger_header); + headers.put("Content-Type", "application/json"); + + EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + + // amend a todo successfully + final HttpResponseDetails response = + http.send("/todos/" + aTodo.getPrimaryKeyValue(), + "PUT", headers, + "{\"title\":\"my put todo\"}"); + // only title is mandatory the rest would be set to defaults + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertTrue(challenger.statusOfChallenge(CHALLENGE.PUT_TODOS_PARTIAL_200)); + } + + @Test + public void canPutTodos200MissingTitleAmendPass() { + + final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + + Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); + + Map headers = new HashMap<>(); + headers.putAll(x_challenger_header); + headers.put("Content-Type", "application/json"); + + EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + + // amend a todo unsuccessfully + final HttpResponseDetails response = + http.send("/todos/" + aTodo.getPrimaryKeyValue(), + "PUT", headers, + "{\"description\":\"my description\"}"); + // title is mandatory so this will fail + + Assertions.assertEquals(400, response.statusCode); + Assertions.assertTrue(challenger.statusOfChallenge(CHALLENGE.PUT_TODOS_MISSING_TITLE_400)); + } + + @Test + public void canNotPutTodos400ChangeIdAmendPass() { + + final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + + Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); + + Map headers = new HashMap<>(); + headers.putAll(x_challenger_header); + headers.put("Content-Type", "application/json"); + + EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + + // amend a todo unsuccessfully + final HttpResponseDetails response = + http.send("/todos/" + aTodo.getPrimaryKeyValue(), + "PUT", headers, + String.format("{\"id\":%d, \"description\":\"my description\"}", Integer.parseInt(aTodo.getPrimaryKeyValue()+1)) + ); + // title is mandatory so this will fail + + Assertions.assertEquals(400, response.statusCode); + Assertions.assertTrue(challenger.statusOfChallenge(CHALLENGE.PUT_TODOS_400_NO_AMEND_ID)); + } + @Test public void canPostTodos404Pass() { diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanCreateTodosWithPOSTTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostCreateTodosTest.java similarity index 98% rename from challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanCreateTodosWithPOSTTest.java rename to challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostCreateTodosTest.java index 51e4babf..e15165cf 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanCreateTodosWithPOSTTest.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostCreateTodosTest.java @@ -7,18 +7,15 @@ import io.restassured.response.Response; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import uk.co.compendiumdev.challenger.payloads.ErrorMessages; import uk.co.compendiumdev.challenger.payloads.Todo; import uk.co.compendiumdev.challenger.restassured.api.ChallengesStatus; import uk.co.compendiumdev.challenger.restassured.api.RestAssuredBaseTest; import uk.co.compendiumdev.challenger.restassured.api.TodosApi; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class CanCreateTodosWithPOSTTest extends RestAssuredBaseTest { +public class CanPostCreateTodosTest extends RestAssuredBaseTest { @Test void canCreateATodoWithPost(){ diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanUpdateTodosWithPOSTTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostUpdateTodosTest.java similarity index 96% rename from challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanUpdateTodosWithPOSTTest.java rename to challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostUpdateTodosTest.java index cf89062c..3670f98d 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanUpdateTodosWithPOSTTest.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPostUpdateTodosTest.java @@ -1,7 +1,5 @@ package uk.co.compendiumdev.challenger.restassured; -import com.google.gson.Gson; -import com.google.gson.JsonElement; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; @@ -15,7 +13,7 @@ import java.util.List; -public class CanUpdateTodosWithPOSTTest extends RestAssuredBaseTest { +public class CanPostUpdateTodosTest extends RestAssuredBaseTest { @Test void canUpdateATodoWithPost(){ diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPutTodosTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPutTodosTest.java index 08878e86..ec157d31 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPutTodosTest.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/CanPutTodosTest.java @@ -1,5 +1,7 @@ package uk.co.compendiumdev.challenger.restassured; +import com.google.gson.Gson; +import com.google.gson.JsonElement; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; @@ -11,37 +13,191 @@ import uk.co.compendiumdev.challenger.restassured.api.RestAssuredBaseTest; import uk.co.compendiumdev.challenger.restassured.api.TodosApi; +import java.util.List; + public class CanPutTodosTest extends RestAssuredBaseTest { @Test void canFailToCreateTodoWithPut(){ - Todo createMe = new Todo(); - createMe.id = 200; - createMe.title = "my name " + System.currentTimeMillis(); - createMe.description = "my description " + System.currentTimeMillis(); - createMe.doneStatus = true; - - final Response response = RestAssured. - given(). - header("X-CHALLENGER", xChallenger). - accept("application/json"). - contentType("application/json"). - body(createMe). - put(apiPath("/todos/" + createMe.id)). - then(). - statusCode(400). - contentType(ContentType.JSON). - extract().response(); - - ErrorMessages errors = response.body().as(ErrorMessages.class); - - Assertions.assertEquals(1, errors.errorMessages.size()); - Assertions.assertEquals("Cannot create todo with PUT due to Auto fields id", errors.errorMessages.get(0)); - - ChallengesStatus statuses = new ChallengesStatus(); - statuses.get(); - Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} (400)").status); - } + Todo createMe = new Todo(); + createMe.id = 200; + createMe.title = "my name " + System.currentTimeMillis(); + createMe.description = "my description " + System.currentTimeMillis(); + createMe.doneStatus = true; + + final Response response = RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + accept("application/json"). + contentType("application/json"). + body(createMe). + put(apiPath("/todos/" + createMe.id)). + then(). + statusCode(400). + contentType(ContentType.JSON). + extract().response(); + + ErrorMessages errors = response.body().as(ErrorMessages.class); + + Assertions.assertEquals(1, errors.errorMessages.size()); + Assertions.assertEquals("Cannot create todo with PUT due to Auto fields id", errors.errorMessages.get(0)); + + ChallengesStatus statuses = new ChallengesStatus(); + statuses.get(); + Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} (400)").status); + } + + + @Test + void canAmendATodoWithPut(){ + + TodosApi api = new TodosApi(); + List todos = api.getTodos(); + + Todo amendMe = todos.get(0); + // amendMe.id = - cannot amend the id as it is auto assigned + amendMe.title = "my name " + System.currentTimeMillis(); // title is mandatory and must be in the message + amendMe.description = "my description " + System.currentTimeMillis(); // if not present default "" will be set + amendMe.doneStatus = true; // if not present then default false will be set + + final Response response = RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + accept("application/json"). + contentType("application/json"). + body(amendMe). + put(apiPath("/todos/" + amendMe.id)). + then(). + statusCode(200). + contentType(ContentType.JSON). + extract().response(); + + Todo amendedTodo = response.body().as(Todo.class); + + Assertions.assertEquals(amendMe.id, amendedTodo.id); + Assertions.assertEquals(amendMe.title, amendedTodo.title); + Assertions.assertEquals(amendMe.description, amendedTodo.description); + Assertions.assertEquals(amendMe.doneStatus, amendedTodo.doneStatus); + + ChallengesStatus statuses = new ChallengesStatus(); + statuses.get(); + Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} full (200)").status); + } + + @Test + void canAmendATodoWithPutUsingDefaults(){ + + TodosApi api = new TodosApi(); + List todos = api.getTodos(); + + Todo amendMe = todos.get(0); + + // amendMe.id = - cannot amend the id as it is auto assigned + amendMe.title = "my name " + System.currentTimeMillis(); // title is mandatory and must be in the message + + // if description not present default "" will be set + // if doneStatus not present then default false will be set + + final JsonElement amendTodoJson = new Gson().toJsonTree(amendMe); + amendTodoJson.getAsJsonObject(). + remove("doneStatus"); + amendTodoJson.getAsJsonObject(). + remove("description"); + + + + final Response response = RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + accept("application/json"). + contentType("application/json"). + body(amendTodoJson.toString()). + put(apiPath("/todos/" + amendMe.id)). + then(). + statusCode(200). + contentType(ContentType.JSON). + extract().response(); + + Todo amendedTodo = response.body().as(Todo.class); + + Assertions.assertEquals(amendMe.id, amendedTodo.id); + Assertions.assertEquals(amendMe.title, amendedTodo.title); + Assertions.assertEquals("", amendedTodo.description); + Assertions.assertEquals(false, amendedTodo.doneStatus); + + ChallengesStatus statuses = new ChallengesStatus(); + statuses.get(); + Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} partial (200)").status); + } + + @Test + void canFailToAmendATodoDueToMissingTitle(){ + + TodosApi api = new TodosApi(); + List todos = api.getTodos(); + + Todo amendMe = todos.get(0); + + // amendMe.id = - cannot amend the id as it is auto assigned + // title is mandatory and must be in the message + + final JsonElement amendTodoJson = new Gson().toJsonTree(amendMe); + amendTodoJson.getAsJsonObject(). + remove("title"); + + final Response response = RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + accept("application/json"). + contentType("application/json"). + body(amendTodoJson.toString()). + put(apiPath("/todos/" + amendMe.id)). + then(). + statusCode(400). + contentType(ContentType.JSON). + extract().response(); + + ErrorMessages error = response.body().as(ErrorMessages.class); + Assertions.assertTrue(error.errorMessages.get(0).contains("title : field is mandatory")); + + ChallengesStatus statuses = new ChallengesStatus(); + statuses.get(); + Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} no title (400)").status); + } + + @Test + void canFailToAmendATodoDueToAttemptToChangeId(){ + + TodosApi api = new TodosApi(); + List todos = api.getTodos(); + + Todo amendMe = todos.get(0); + + int oldId = amendMe.id; + int newId = oldId +1; + amendMe.id= newId; + // amendMe.id = - cannot amend the id as it is auto assigned + // title is mandatory and must be in the message + + final Response response = RestAssured. + given(). + header("X-CHALLENGER", xChallenger). + accept("application/json"). + contentType("application/json"). + body(amendMe). + put(apiPath("/todos/" + oldId)). + then(). + statusCode(400). + contentType(ContentType.JSON). + extract().response(); + + ErrorMessages error = response.body().as(ErrorMessages.class); + Assertions.assertTrue(error.errorMessages.get(0).contains(String.format("Can not amend id from %d to %d", oldId, newId))); + + ChallengesStatus statuses = new ChallengesStatus(); + statuses.get(); + Assertions.assertTrue(statuses.getChallengeNamed("PUT /todos/{id} no amend id (400)").status); + } }