summaryrefslogtreecommitdiffstats
path: root/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java
diff options
context:
space:
mode:
Diffstat (limited to 'clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java')
-rw-r--r--clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java442
1 files changed, 270 insertions, 172 deletions
diff --git a/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java b/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java
index d0378dd9065..47b12f883ff 100644
--- a/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java
+++ b/clustercontroller-utils/src/test/java/com/yahoo/vespa/clustercontroller/utils/staterestapi/StateRestAPITest.java
@@ -1,22 +1,35 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.utils.staterestapi;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yahoo.vespa.clustercontroller.utils.communication.async.AsyncOperation;
import com.yahoo.vespa.clustercontroller.utils.communication.async.AsyncUtils;
import com.yahoo.vespa.clustercontroller.utils.communication.http.HttpRequest;
import com.yahoo.vespa.clustercontroller.utils.communication.http.HttpResult;
-import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.*;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.DeadlineExceededException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InternalFailure;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidContentException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.InvalidOptionValueException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.OperationNotSupportedForUnitException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.OtherMasterException;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.errors.UnknownMasterException;
import com.yahoo.vespa.clustercontroller.utils.staterestapi.server.RestApiHandler;
import com.yahoo.vespa.clustercontroller.utils.test.TestTransport;
-import org.codehaus.jettison.json.JSONObject;
import org.junit.jupiter.api.Test;
import java.util.Optional;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class StateRestAPITest {
+ private static final ObjectMapper mapper = new ObjectMapper();
+
private static void populateDummyBackend(DummyBackend backend) {
backend.addCluster(new DummyBackend.Cluster("foo")
.addNode(new DummyBackend.Node("1")
@@ -61,14 +74,15 @@ public class StateRestAPITest {
if (!op.isSuccess()) { // Don't call getCause() unless it fails
assertTrue(op.isSuccess(), op.getCause().toString());
}
- assertTrue(op.getResult() != null);
+ assertNotNull(op.getResult());
return op.getResult();
}
- private JSONObject executeOkJsonRequest(HttpRequest request) {
+
+ private JsonNode executeOkJsonRequest(HttpRequest request) {
HttpResult result = execute(request);
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
- return (JSONObject) result.getContent();
+ return (JsonNode) result.getContent();
}
@Test
@@ -77,11 +91,18 @@ public class StateRestAPITest {
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2"));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
- String expected = "{\"cluster\": {\n"
- + " \"foo\": {\"link\": \"\\/cluster\\/v2\\/foo\"},\n"
- + " \"bar\": {\"link\": \"\\/cluster\\/v2\\/bar\"}\n"
- + "}}";
- assertEquals(expected, ((JSONObject) result.getContent()).toString(2));
+ assertEquals("""
+ {
+ "cluster" : {
+ "foo" : {
+ "link" : "/cluster/v2/foo"
+ },
+ "bar" : {
+ "link" : "/cluster/v2/bar"
+ }
+ }
+ }""",
+ ((JsonNode) result.getContent()).toPrettyString());
}
@Test
@@ -90,11 +111,18 @@ public class StateRestAPITest {
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo"));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
- String expected = "{\"node\": {\n"
- + " \"1\": {\"link\": \"\\/cluster\\/v2\\/foo\\/1\"},\n"
- + " \"3\": {\"link\": \"\\/cluster\\/v2\\/foo\\/3\"}\n"
- + "}}";
- assertEquals(expected, ((JSONObject) result.getContent()).toString(2));
+ assertEquals("""
+ {
+ "node" : {
+ "1" : {
+ "link" : "/cluster/v2/foo/1"
+ },
+ "3" : {
+ "link" : "/cluster/v2/foo/3"
+ }
+ }
+ }""",
+ ((JsonNode) result.getContent()).toPrettyString());
}
@Test
@@ -103,118 +131,170 @@ public class StateRestAPITest {
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3"));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
- String expected = "{\n"
- + " \"attributes\": {\"group\": \"mygroup\"},\n"
- + " \"state\": {\"current\": {\n"
- + " \"state\": \"up\",\n"
- + " \"reason\": \"\"\n"
- + " }},\n"
- + " \"metrics\": {\"doc-count\": 8}\n"
- + "}";
- assertEquals(expected, ((JSONObject) result.getContent()).toString(2));
+ String expected = """
+ {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "up",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 8
+ }
+ }""";
+ assertEquals(expected, ((JsonNode) result.getContent()).toPrettyString());
}
@Test
void testRecursiveMode() throws Exception {
setupDummyStateApi();
{
- JSONObject json = executeOkJsonRequest(
+ JsonNode json = executeOkJsonRequest(
new HttpRequest().setPath("/cluster/v2").addUrlOption("recursive", "true"));
- String expected =
- "{\"cluster\": {\n" +
- " \"foo\": {\"node\": {\n" +
- " \"1\": {\n" +
- " \"attributes\": {\"group\": \"mygroup\"},\n" +
- " \"state\": {\"current\": {\n" +
- " \"state\": \"initializing\",\n" +
- " \"reason\": \"\"\n" +
- " }},\n" +
- " \"metrics\": {\"doc-count\": 5}\n" +
- " },\n" +
- " \"3\": {\n" +
- " \"attributes\": {\"group\": \"mygroup\"},\n" +
- " \"state\": {\"current\": {\n" +
- " \"state\": \"up\",\n" +
- " \"reason\": \"\"\n" +
- " }},\n" +
- " \"metrics\": {\"doc-count\": 8}\n" +
- " }\n" +
- " }},\n" +
- " \"bar\": {\"node\": {\"2\": {\n" +
- " \"attributes\": {\"group\": \"mygroup\"},\n" +
- " \"state\": {\"current\": {\n" +
- " \"state\": \"down\",\n" +
- " \"reason\": \"\"\n" +
- " }},\n" +
- " \"metrics\": {\"doc-count\": 0}\n" +
- " }}}\n" +
- "}}";
- assertEquals(expected, json.toString(2));
+ assertEquals("""
+ {
+ "cluster" : {
+ "foo" : {
+ "node" : {
+ "1" : {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "initializing",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 5
+ }
+ },
+ "3" : {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "up",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 8
+ }
+ }
+ }
+ },
+ "bar" : {
+ "node" : {
+ "2" : {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "down",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 0
+ }
+ }
+ }
+ }
+ }
+ }""",
+ json.toPrettyString());
}
{
- JSONObject json = executeOkJsonRequest(
- new HttpRequest().setPath("/cluster/v2").addUrlOption("recursive", "1"));
- String expected =
- "{\"cluster\": {\n" +
- " \"foo\": {\"node\": {\n" +
- " \"1\": {\"link\": \"\\/cluster\\/v2\\/foo\\/1\"},\n" +
- " \"3\": {\"link\": \"\\/cluster\\/v2\\/foo\\/3\"}\n" +
- " }},\n" +
- " \"bar\": {\"node\": {\"2\": {\"link\": \"\\/cluster\\/v2\\/bar\\/2\"}}}\n" +
- "}}";
- // Verify that the actual link does not contain backslash. It's just an artifact of
- // jettison json output.
- assertEquals("/cluster/v2/foo/1",
- json.getJSONObject("cluster").getJSONObject("foo").getJSONObject("node")
- .getJSONObject("1").getString("link"));
- assertEquals(expected, json.toString(2));
+ JsonNode json = executeOkJsonRequest(new HttpRequest().setPath("/cluster/v2").addUrlOption("recursive", "1"));
+ assertEquals("""
+ {
+ "cluster" : {
+ "foo" : {
+ "node" : {
+ "1" : {
+ "link" : "/cluster/v2/foo/1"
+ },
+ "3" : {
+ "link" : "/cluster/v2/foo/3"
+ }
+ }
+ },
+ "bar" : {
+ "node" : {
+ "2" : {
+ "link" : "/cluster/v2/bar/2"
+ }
+ }
+ }
+ }
+ }""",
+ json.toPrettyString());
}
{
- JSONObject json = executeOkJsonRequest(
- new HttpRequest().setPath("/cluster/v2/foo").addUrlOption("recursive", "1"));
- String expected =
- "{\"node\": {\n" +
- " \"1\": {\n" +
- " \"attributes\": {\"group\": \"mygroup\"},\n" +
- " \"state\": {\"current\": {\n" +
- " \"state\": \"initializing\",\n" +
- " \"reason\": \"\"\n" +
- " }},\n" +
- " \"metrics\": {\"doc-count\": 5}\n" +
- " },\n" +
- " \"3\": {\n" +
- " \"attributes\": {\"group\": \"mygroup\"},\n" +
- " \"state\": {\"current\": {\n" +
- " \"state\": \"up\",\n" +
- " \"reason\": \"\"\n" +
- " }},\n" +
- " \"metrics\": {\"doc-count\": 8}\n" +
- " }\n" +
- "}}";
- assertEquals(expected, json.toString(2));
+ JsonNode json = executeOkJsonRequest( new HttpRequest().setPath("/cluster/v2/foo").addUrlOption("recursive", "1"));
+ assertEquals("""
+ {
+ "node" : {
+ "1" : {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "initializing",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 5
+ }
+ },
+ "3" : {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "up",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 8
+ }
+ }
+ }
+ }""", json.toPrettyString());
}
{
- JSONObject json = executeOkJsonRequest(
- new HttpRequest().setPath("/cluster/v2/foo").addUrlOption("recursive", "false"));
- String expected =
- "{\"node\": {\n" +
- " \"1\": {\"link\": \"\\/cluster\\/v2\\/foo\\/1\"},\n" +
- " \"3\": {\"link\": \"\\/cluster\\/v2\\/foo\\/3\"}\n" +
- "}}";
- assertEquals(expected, json.toString(2));
+ JsonNode json = executeOkJsonRequest( new HttpRequest().setPath("/cluster/v2/foo").addUrlOption("recursive", "false"));
+ assertEquals("""
+ {
+ "node" : {
+ "1" : {
+ "link" : "/cluster/v2/foo/1"
+ },
+ "3" : {
+ "link" : "/cluster/v2/foo/3"
+ }
+ }
+ }""", json.toPrettyString());
}
}
private String retireAndExpectHttp200Response(Optional<String> responseWait) throws Exception {
- JSONObject json = new JSONObject()
- .put("state", new JSONObject()
- .put("current", new JSONObject()
- .put("state", "retired")
- .put("reason", "No reason")))
- .put("condition", "FORCE");
- if (responseWait.isPresent()) {
- json.put("response-wait", responseWait.get());
- }
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current").put("state", "retired").put("reason", "No reason");
+ json.put("condition", "FORCE");
+ responseWait.ifPresent(wait -> json.put("response-wait", wait));
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
@@ -227,44 +307,56 @@ public class StateRestAPITest {
void testSetNodeState() throws Exception {
setupDummyStateApi();
{
- JSONObject json = new JSONObject().put("state", new JSONObject()
- .put("current", new JSONObject()
- .put("state", "retired")
- .put("reason", "No reason")));
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current").put("state", "retired").put("reason", "No reason");
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
}
{
- JSONObject json = executeOkJsonRequest(new HttpRequest().setPath("/cluster/v2/foo/3"));
- String expected = "{\n"
- + " \"attributes\": {\"group\": \"mygroup\"},\n"
- + " \"state\": {\"current\": {\n"
- + " \"state\": \"retired\",\n"
- + " \"reason\": \"No reason\"\n"
- + " }},\n"
- + " \"metrics\": {\"doc-count\": 8}\n"
- + "}";
- assertEquals(expected, json.toString(2), json.toString(2));
+ JsonNode json = executeOkJsonRequest(new HttpRequest().setPath("/cluster/v2/foo/3"));
+ assertEquals("""
+ {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "retired",
+ "reason" : "No reason"
+ }
+ },
+ "metrics" : {
+ "doc-count" : 8
+ }
+ }""",
+ json.toPrettyString());
}
{
- JSONObject json = new JSONObject().put("state", new JSONObject()
- .put("current", new JSONObject()));
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current");
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(200, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));
}
{
- JSONObject json = executeOkJsonRequest(new HttpRequest().setPath("/cluster/v2/foo/3"));
- String expected = "{\n"
- + " \"attributes\": {\"group\": \"mygroup\"},\n"
- + " \"state\": {\"current\": {\n"
- + " \"state\": \"up\",\n"
- + " \"reason\": \"\"\n"
- + " }},\n"
- + " \"metrics\": {\"doc-count\": 8}\n"
- + "}";
- assertEquals(expected, json.toString(2), json.toString(2));
+ JsonNode json = executeOkJsonRequest(new HttpRequest().setPath("/cluster/v2/foo/3"));
+ assertEquals("""
+ {
+ "attributes" : {
+ "group" : "mygroup"
+ },
+ "state" : {
+ "current" : {
+ "state" : "up",
+ "reason" : ""
+ }
+ },
+ "metrics" : {
+ "doc-count" : 8
+ }
+ }""",
+ json.toPrettyString());
}
}
@@ -273,19 +365,21 @@ public class StateRestAPITest {
setupDummyStateApi();
{
String result = retireAndExpectHttp200Response(Optional.of("wait-until-cluster-acked"));
- assertEquals(result,
- "JSON: {\n" +
- " \"wasModified\": true,\n" +
- " \"reason\": \"DummyStateAPI wait-until-cluster-acked call\"\n" +
- "}");
+ assertEquals("""
+ JSON: {
+ "wasModified" : true,
+ "reason" : "DummyStateAPI wait-until-cluster-acked call"
+ }""",
+ result);
}
{
String result = retireAndExpectHttp200Response(Optional.of("no-wait"));
- assertEquals(result,
- "JSON: {\n" +
- " \"wasModified\": true,\n" +
- " \"reason\": \"DummyStateAPI no-wait call\"\n" +
- "}");
+ assertEquals("""
+ JSON: {
+ "wasModified" : true,
+ "reason" : "DummyStateAPI no-wait call"
+ }""",
+ result);
}
}
@@ -293,11 +387,12 @@ public class StateRestAPITest {
void set_node_state_response_wait_type_is_cluster_acked_by_default() throws Exception {
setupDummyStateApi();
String result = retireAndExpectHttp200Response(Optional.empty());
- assertEquals(result,
- "JSON: {\n" +
- " \"wasModified\": true,\n" +
- " \"reason\": \"DummyStateAPI wait-until-cluster-acked call\"\n" +
- "}");
+ assertEquals("""
+ JSON: {
+ "wasModified" : true,
+ "reason" : "DummyStateAPI wait-until-cluster-acked call"
+ }""",
+ result);
}
@Test
@@ -314,7 +409,7 @@ public class StateRestAPITest {
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/1234"));
assertEquals(404, result.getHttpReturnCode(), result.toString(true));
assertEquals("No such resource 'foo/1234'.", result.getHttpReturnCodeDescription(), result.toString(true));
- String expected = "{\"message\":\"No such resource 'foo\\/1234'.\"}";
+ String expected = "{\"message\":\"No such resource 'foo/1234'.\"}";
assertEquals(expected, result.getContent().toString());
}
}
@@ -442,7 +537,7 @@ public class StateRestAPITest {
void testInvalidJsonInSetStateRequest() throws Exception {
setupDummyStateApi();
{
- JSONObject json = new JSONObject();
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("Content of HTTP request had invalid data", result.getHttpReturnCodeDescription(), result.toString(true));
@@ -450,7 +545,7 @@ public class StateRestAPITest {
assertTrue(result.getContent().toString().contains("Set state requests must contain a state object"), result.toString(true));
}
{
- JSONObject json = new JSONObject().put("state", 5);
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory()).put("state", 5);
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("Content of HTTP request had invalid data", result.getHttpReturnCodeDescription(), result.toString(true));
@@ -458,8 +553,8 @@ public class StateRestAPITest {
assertTrue(result.getContent().toString().contains("value of state is not a json object"), result.toString(true));
}
{
- JSONObject json = new JSONObject().put("state", new JSONObject()
- .put("current", 5));
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").put("current", 5);
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("Content of HTTP request had invalid data", result.getHttpReturnCodeDescription(), result.toString(true));
@@ -467,8 +562,8 @@ public class StateRestAPITest {
assertTrue(result.getContent().toString().contains("value of state->current is not a json object"), result.toString(true));
}
{
- JSONObject json = new JSONObject().put("state", new JSONObject()
- .put("current", new JSONObject().put("state", 5)));
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current").put("state", 5);
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("Content of HTTP request had invalid data", result.getHttpReturnCodeDescription(), result.toString(true));
@@ -476,8 +571,8 @@ public class StateRestAPITest {
assertTrue(result.getContent().toString().contains("value of state->current->state is not a string"), result.toString(true));
}
{
- JSONObject json = new JSONObject().put("state", new JSONObject()
- .put("current", new JSONObject().put("state", "down").put("reason", 5)));
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current").put("state", "down").put("reason", 5);
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("Content of HTTP request had invalid data", result.getHttpReturnCodeDescription(), result.toString(true));
@@ -486,24 +581,27 @@ public class StateRestAPITest {
}
{
String result = retireAndExpectHttp400Response("Non existing condition", "no-wait");
- assertEquals(result,
- "JSON: {\"message\": \"Invalid value for condition: 'Non existing condition', expected one of 'force', 'safe'\"}");
+ assertEquals("""
+ JSON: {
+ "message" : "Invalid value for condition: 'Non existing condition', expected one of 'force', 'safe'"
+ }""",
+ result);
}
{
String result = retireAndExpectHttp400Response("FORCE", "banana");
- assertEquals(result,
- "JSON: {\"message\": \"Invalid value for response-wait: 'banana', expected one of 'wait-until-cluster-acked', 'no-wait'\"}");
+ assertEquals("""
+ JSON: {
+ "message" : "Invalid value for response-wait: 'banana', expected one of 'wait-until-cluster-acked', 'no-wait'"
+ }""",
+ result);
}
}
private String retireAndExpectHttp400Response(String condition, String responseWait) throws Exception {
- JSONObject json = new JSONObject()
- .put("state", new JSONObject()
- .put("current", new JSONObject()
- .put("state", "retired")
- .put("reason", "No reason")))
- .put("condition", condition)
- .put("response-wait", responseWait);
+ ObjectNode json = new ObjectNode(mapper.getNodeFactory());
+ json.putObject("state").putObject("current").put("state", "retired").put("reason", "No reason");
+ json.put("condition", condition);
+ json.put("response-wait", responseWait);
HttpResult result = execute(new HttpRequest().setPath("/cluster/v2/foo/3").setPostContent(json));
assertEquals(400, result.getHttpReturnCode(), result.toString(true));
assertEquals("application/json", result.getHeader("Content-Type"), result.toString(true));