summaryrefslogtreecommitdiffstats
path: root/vespaclient-container-plugin
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2020-09-29 20:50:28 +0200
committerJon Marius Venstad <venstad@gmail.com>2020-09-30 10:23:35 +0200
commit85bffffb34ea959576baeef0affd73ea803fc93a (patch)
treeb0c496b35b67a41fc7cd311964e11fb46b0a3155 /vespaclient-container-plugin
parent574989151329e44cd49b510b8e53aa822ec2b413 (diff)
More response tests — some more try-catch
Diffstat (limited to 'vespaclient-container-plugin')
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java32
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/DocumentOperationExecutorMock.java3
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java402
3 files changed, 259 insertions, 178 deletions
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
index 08a1b16e527..ab221274634 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
@@ -244,12 +244,19 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
},
// TODO jonmv: make streaming — first doc indicates 200 OK anyway — unless session dies, which is a semi-200 anyway
document -> {
- synchronized (monitor) { // Putting things into the slime is not thread safe, so need synchronization.
- SlimeUtils.copyObject(SlimeUtils.jsonToSlime(JsonWriter.toByteArray(document)).get(),
- documents.addObject());
+ try {
+ synchronized (monitor) { // Putting things into the slime is not thread safe, so need synchronization.
+ SlimeUtils.copyObject(SlimeUtils.jsonToSlime(JsonWriter.toByteArray(document)).get(),
+ documents.addObject());
+ }
+ }
+ // TODO jonmv: This shouldn't happen much, but ... expose errors too?
+ catch (RuntimeException e) {
+ log.log(WARNING, "Exception serializing document in document/v1 visit response", e);
}
});
}
+
private ContentChannel getDocument(HttpRequest request, DocumentPath path, ResponseHandler handler) {
DocumentId id = path.id();
DocumentOperationParameters parameters = parameters();
@@ -259,13 +266,18 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
parameters,
new OperationContext((type, message) -> handleError(request, type, message, responseRoot(request, id), handler),
document -> {
- Cursor root = responseRoot(request, id);
- document.map(JsonWriter::toByteArray)
- .map(SlimeUtils::jsonToSlime)
- .ifPresent(doc -> SlimeUtils.copyObject(doc.get().field("fields"), root.setObject("fields")));
- respond(document.isPresent() ? 200 : 404,
- root,
- handler);
+ try {
+ Cursor root = responseRoot(request, id);
+ document.map(JsonWriter::toByteArray)
+ .map(SlimeUtils::jsonToSlime)
+ .ifPresent(doc -> SlimeUtils.copyObject(doc.get().field("fields"), root.setObject("fields")));
+ respond(document.isPresent() ? 200 : 404,
+ root,
+ handler);
+ }
+ catch (Exception e) {
+ serverError(request, new RuntimeException(e), handler);
+ }
}));
return ignoredContent;
}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/DocumentOperationExecutorMock.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/DocumentOperationExecutorMock.java
index 95c90e1a4ca..3d350adab87 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/DocumentOperationExecutorMock.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/DocumentOperationExecutorMock.java
@@ -50,6 +50,9 @@ public class DocumentOperationExecutorMock implements DocumentOperationExecutor
@Override
public String routeToCluster(String cluster) {
+ if ("throw-me".equals(cluster))
+ throw new IllegalArgumentException(cluster);
+
return "route-to-" + cluster;
}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
index b13af7fd067..82d0a530600 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
@@ -7,24 +7,17 @@ import com.yahoo.document.Document;
import com.yahoo.document.DocumentGet;
import com.yahoo.document.DocumentPut;
import com.yahoo.document.DocumentRemove;
-import com.yahoo.document.DocumentType;
import com.yahoo.document.DocumentTypeManager;
-import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.TestAndSetCondition;
import com.yahoo.document.config.DocumentmanagerConfig;
-import com.yahoo.document.datatypes.StringFieldValue;
-import com.yahoo.document.restapi.DocumentOperationExecutor;
import com.yahoo.document.restapi.DocumentOperationExecutor.Group;
import com.yahoo.document.restapi.DocumentOperationExecutor.VisitorOptions;
import com.yahoo.document.restapi.DocumentOperationExecutorMock;
import com.yahoo.document.restapi.resource.DocumentV1ApiHandler.DocumentOperationParser;
-import com.yahoo.document.update.FieldUpdate;
import com.yahoo.documentapi.DocumentAccessParams;
import com.yahoo.documentapi.local.LocalDocumentAccess;
import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.http.HttpRequest;
import com.yahoo.metrics.simple.MetricReceiver;
-import com.yahoo.prelude.fastsearch.StringField;
import com.yahoo.searchdefinition.derived.Deriver;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.SlimeUtils;
@@ -40,8 +33,13 @@ import java.util.Optional;
import static com.yahoo.document.restapi.DocumentOperationExecutor.ErrorType.BAD_REQUEST;
import static com.yahoo.document.restapi.DocumentOperationExecutor.ErrorType.ERROR;
+import static com.yahoo.document.restapi.DocumentOperationExecutor.ErrorType.OVERLOAD;
+import static com.yahoo.document.restapi.DocumentOperationExecutor.ErrorType.PRECONDITION_FAILED;
+import static com.yahoo.document.restapi.DocumentOperationExecutor.ErrorType.TIMEOUT;
import static com.yahoo.documentapi.DocumentOperationParameters.parameters;
import static com.yahoo.jdisc.http.HttpRequest.Method.DELETE;
+import static com.yahoo.jdisc.http.HttpRequest.Method.OPTIONS;
+import static com.yahoo.jdisc.http.HttpRequest.Method.PATCH;
import static com.yahoo.jdisc.http.HttpRequest.Method.POST;
import static com.yahoo.jdisc.http.HttpRequest.Method.PUT;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -88,176 +86,244 @@ public class DocumentV1ApiTest {
@Test
public void testResponses() {
- try (RequestHandlerTestDriver driver = new RequestHandlerTestDriver(handler)) {
- // GET at non-existent path returns 404 with available paths
- var response = driver.sendRequest("http://localhost/document/v1/not-found");
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/not-found\"," +
- " \"message\": \"Nothing at '/document/v1/not-found'. Available paths are:\\n" +
- "/document/v1/\\n" +
- "/document/v1/{namespace}/{documentType}/docid/\\n" +
- "/document/v1/{namespace}/{documentType}/group/{group}/\\n" +
- "/document/v1/{namespace}/{documentType}/number/{number}/\\n" +
- "/document/v1/{namespace}/{documentType}/docid/{docid}\\n" +
- "/document/v1/{namespace}/{documentType}/group/{group}/{docid}\\n" +
- "/document/v1/{namespace}/{documentType}/number/{number}/{docid}\"" +
- "}",
- response.readAll());
- assertEquals("application/json; charset=UTF-8", response.getResponse().headers().getFirst("Content-Type"));
- assertEquals(404, response.getStatus());
+ RequestHandlerTestDriver driver = new RequestHandlerTestDriver(handler);
+ // GET at non-existent path returns 404 with available paths
+ var response = driver.sendRequest("http://localhost/document/v1/not-found");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/not-found\"," +
+ " \"message\": \"Nothing at '/document/v1/not-found'. Available paths are:\\n" +
+ "/document/v1/\\n" +
+ "/document/v1/{namespace}/{documentType}/docid/\\n" +
+ "/document/v1/{namespace}/{documentType}/group/{group}/\\n" +
+ "/document/v1/{namespace}/{documentType}/number/{number}/\\n" +
+ "/document/v1/{namespace}/{documentType}/docid/{docid}\\n" +
+ "/document/v1/{namespace}/{documentType}/group/{group}/{docid}\\n" +
+ "/document/v1/{namespace}/{documentType}/number/{number}/{docid}\"" +
+ "}",
+ response.readAll());
+ assertEquals("application/json; charset=UTF-8", response.getResponse().headers().getFirst("Content-Type"));
+ assertEquals(404, response.getStatus());
- // GET at root is a visit. Numeric parameters have an upper bound.
- response = driver.sendRequest("http://localhost/document/v1?cluster=lackluster&bucketSpace=default&wantedDocumentCount=1025&concurrency=123" +
- "&selection=all%20the%20things&fieldSet=[id]&continuation=token");
- executor.lastVisitContext().document(doc1);
- executor.lastVisitContext().document(doc2);
- executor.lastVisitContext().document(doc3);
- executor.lastVisitContext().success(Optional.of("token"));
- assertSameJson("{" +
- " \"pathId\": \"/document/v1\"," +
- " \"documents\": [" +
- " {" +
- " \"id\": \"id:space:music::one\"," +
- " \"fields\": {" +
- " \"artist\": \"Tom Waits\"" +
- " }" +
- " }," +
- " {" +
- " \"id\": \"id:space:music:n=1:two\"," +
- " \"fields\": {" +
- " \"artist\": \"Asa-Chan & Jun-Ray\"" +
- " }" +
- " }," +
- " {" +
- " \"id\": \"id:space:music:g=a:three\"," +
- " \"fields\": {}" +
- " }" +
- " ]," +
- " \"continuation\": \"token\"" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- assertEquals(VisitorOptions.builder().cluster("lackluster").bucketSpace("default").wantedDocumentCount(1024)
- .concurrency(100).selection("all the things").fieldSet("[id]").continuation("token").build(),
- executor.lastOptions());
+ // GET at root is a visit. Numeric parameters have an upper bound.
+ response = driver.sendRequest("http://localhost/document/v1?cluster=lackluster&bucketSpace=default&wantedDocumentCount=1025&concurrency=123" +
+ "&selection=all%20the%20things&fieldSet=[id]&continuation=token");
+ executor.lastVisitContext().document(doc1);
+ executor.lastVisitContext().document(doc2);
+ executor.lastVisitContext().document(doc3);
+ executor.lastVisitContext().success(Optional.of("token"));
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1\"," +
+ " \"documents\": [" +
+ " {" +
+ " \"id\": \"id:space:music::one\"," +
+ " \"fields\": {" +
+ " \"artist\": \"Tom Waits\"" +
+ " }" +
+ " }," +
+ " {" +
+ " \"id\": \"id:space:music:n=1:two\"," +
+ " \"fields\": {" +
+ " \"artist\": \"Asa-Chan & Jun-Ray\"" +
+ " }" +
+ " }," +
+ " {" +
+ " \"id\": \"id:space:music:g=a:three\"," +
+ " \"fields\": {}" +
+ " }" +
+ " ]," +
+ " \"continuation\": \"token\"" +
+ "}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+ assertEquals(VisitorOptions.builder().cluster("lackluster").bucketSpace("default").wantedDocumentCount(1024)
+ .concurrency(100).selection("all the things").fieldSet("[id]").continuation("token").build(),
+ executor.lastOptions());
- // GET with namespace and document type is a restricted visit.
- response = driver.sendRequest("http://localhost/document/v1/space/music/docid");
- executor.lastVisitContext().error(BAD_REQUEST, "nope");
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/docid\"," +
- " \"documents\": []," +
- " \"message\": \"nope\"" +
- "}",
- response.readAll());
- assertEquals(400, response.getStatus());
- assertEquals(VisitorOptions.builder().namespace("space").documentType("music").build(),
- executor.lastOptions());
+ // GET with namespace and document type is a restricted visit.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/docid");
+ executor.lastVisitContext().error(BAD_REQUEST, "nope");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/docid\"," +
+ " \"documents\": []," +
+ " \"message\": \"nope\"" +
+ "}",
+ response.readAll());
+ assertEquals(400, response.getStatus());
+ assertEquals(VisitorOptions.builder().namespace("space").documentType("music").build(),
+ executor.lastOptions());
- // GET with namespace, document type and group is a restricted visit.
- response = driver.sendRequest("http://localhost/document/v1/space/music/group/best");
- executor.lastVisitContext().error(ERROR, "error");
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/group/best\"," +
- " \"documents\": []," +
- " \"message\": \"error\"" +
- "}",
- response.readAll());
- assertEquals(500, response.getStatus());
- assertEquals(VisitorOptions.builder().namespace("space").documentType("music").group(Group.of("best")).build(),
- executor.lastOptions());
+ // GET with namespace, document type and group is a restricted visit.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/group/best");
+ executor.lastVisitContext().error(ERROR, "error");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/group/best\"," +
+ " \"documents\": []," +
+ " \"message\": \"error\"" +
+ "}",
+ response.readAll());
+ assertEquals(500, response.getStatus());
+ assertEquals(VisitorOptions.builder().namespace("space").documentType("music").group(Group.of("best")).build(),
+ executor.lastOptions());
- // GET with namespace, document type and number is a restricted visit.
- response = driver.sendRequest("http://localhost/document/v1/space/music/number/123");
- executor.lastVisitContext().success(Optional.empty());
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/number/123\"," +
- " \"documents\": []" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- assertEquals(VisitorOptions.builder().namespace("space").documentType("music").group(Group.of(123)).build(),
- executor.lastOptions());
+ // GET with namespace, document type and number is a restricted visit.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/123");
+ executor.lastVisitContext().success(Optional.empty());
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/123\"," +
+ " \"documents\": []" +
+ "}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+ assertEquals(VisitorOptions.builder().namespace("space").documentType("music").group(Group.of(123)).build(),
+ executor.lastOptions());
- // GET with full document ID is a document get operation which returns 404 when no document is found
- response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?cluster=lackluster&fieldSet=go");
- executor.lastOperationContext().success(Optional.empty());
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/docid/one\"," +
- " \"id\": \"id:space:music::one\"" +
- "}",
- response.readAll());
- assertEquals(404, response.getStatus());
- assertEquals(new DocumentGet(doc1.getId()), executor.lastOperation());
- assertEquals(parameters().withRoute("route-to-lackluster").withFieldSet("go"), executor.lastParameters());
+ // GET with full document ID is a document get operation which returns 404 when no document is found
+ response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?cluster=lackluster&fieldSet=go");
+ executor.lastOperationContext().success(Optional.empty());
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/docid/one\"," +
+ " \"id\": \"id:space:music::one\"" +
+ "}",
+ response.readAll());
+ assertEquals(404, response.getStatus());
+ assertEquals(new DocumentGet(doc1.getId()), executor.lastOperation());
+ assertEquals(parameters().withRoute("route-to-lackluster").withFieldSet("go"), executor.lastParameters());
- // GET with full document ID is a document get operation.
- response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?");
- executor.lastOperationContext().success(Optional.of(doc1));
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/docid/one\"," +
- " \"id\": \"id:space:music::one\"," +
- " \"fields\": {" +
- " \"artist\": \"Tom Waits\"" +
- " }" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- assertEquals(new DocumentGet(doc1.getId()), executor.lastOperation());
- assertEquals(parameters(), executor.lastParameters());
+ // GET with full document ID is a document get operation.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?");
+ executor.lastOperationContext().success(Optional.of(doc1));
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/docid/one\"," +
+ " \"id\": \"id:space:music::one\"," +
+ " \"fields\": {" +
+ " \"artist\": \"Tom Waits\"" +
+ " }" +
+ "}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+ assertEquals(new DocumentGet(doc1.getId()), executor.lastOperation());
+ assertEquals(parameters(), executor.lastParameters());
- // POST with a document payload is a document put operation.
- response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?condition=test%20it", POST,
- "{" +
- " \"fields\": {" +
- " \"artist\": \"Asa-Chan & Jun-Ray\"" +
- " }" +
- "}");
- executor.lastOperationContext().success(Optional.empty());
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
- " \"id\": \"id:space:music:n=1:two\"" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- DocumentPut put = new DocumentPut(doc2);
- put.setCondition(new TestAndSetCondition("test it"));
- assertEquals(put, executor.lastOperation());
- assertEquals(parameters(), executor.lastParameters());
+ // GET with not encoded / in user specified part of document id is a 404
+ response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one/two/three");
+ response.readAll(); // Must drain body.
+ assertEquals(404, response.getStatus());
- // PUT with a document update payload is a document update operation.
- response = driver.sendRequest("http://localhost/document/v1/space/music/group/a/three?create=true", PUT,
- "{" +
- " \"fields\": {" +
- " \"artist\": { \"assign\": \"Lisa Ekdahl\" }" +
- " }" +
- "}");
- executor.lastOperationContext().success(Optional.empty());
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/group/a/three\"," +
- " \"id\": \"id:space:music:g=a:three\"" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- DocumentUpdate update = new DocumentUpdate(manager.getDocumentType("music"), "id:space:music:g=a:three");
- update.addFieldUpdate(FieldUpdate.createAssign(manager.getDocumentType("music").getField("artist"),
- new StringFieldValue("Lisa Ekdahl")));
- update.setCreateIfNonExistent(true);
- assertEquals(update, executor.lastOperation());
- assertEquals(parameters(), executor.lastParameters());
+ // POST with a document payload is a document put operation.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?condition=test%20it", POST,
+ "{" +
+ " \"fields\": {" +
+ " \"artist\": \"Asa-Chan & Jun-Ray\"" +
+ " }" +
+ "}");
+ executor.lastOperationContext().success(Optional.empty());
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"id\": \"id:space:music:n=1:two\"" +
+ "}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+ DocumentPut put = new DocumentPut(doc2);
+ put.setCondition(new TestAndSetCondition("test it"));
+ assertEquals(put, executor.lastOperation());
+ assertEquals(parameters(), executor.lastParameters());
- // DELETE with full document ID is a document remove operation.
- response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?route=route", DELETE);
- executor.lastOperationContext().success(Optional.empty());
- assertSameJson("{" +
- " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
- " \"id\": \"id:space:music:n=1:two\"" +
- "}",
- response.readAll());
- assertEquals(200, response.getStatus());
- assertEquals(new DocumentRemove(doc2.getId()), executor.lastOperation());
- assertEquals(parameters().withRoute("route"), executor.lastParameters());
- }
+ // POST with illegal payload is a 400
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?condition=test%20it", POST,
+ "{" +
+ " ┻━┻︵ \\(°□°)/ ︵ ┻━┻" +
+ "}");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"message\": \"Unexpected character ('┻' (code 9531 / 0x253b)): was expecting double-quote to start field name\\n at [Source: com.yahoo.jdisc.handler.UnsafeContentInputStream@50ecde95; line: 1, column: 7]\"" +
+ "}",
+ response.readAll());
+ assertEquals(400, response.getStatus());
+
+ // PUT on a unknown document type is a 400
+ response = driver.sendRequest("http://localhost/document/v1/space/house/group/a/three?create=true", PUT,
+ "{" +
+ " \"fields\": {" +
+ " \"artist\": { \"assign\": \"Lisa Ekdahl\" }" +
+ " }" +
+ "}");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/house/group/a/three\"," +
+ " \"message\": \"Document type house does not exist\"" +
+ "}",
+ response.readAll());
+ assertEquals(400, response.getStatus());
+
+ // DELETE with full document ID is a document remove operation.
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?route=route", DELETE);
+ executor.lastOperationContext().success(Optional.empty());
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"id\": \"id:space:music:n=1:two\"" +
+ "}",
+ response.readAll());
+ assertEquals(200, response.getStatus());
+ assertEquals(new DocumentRemove(doc2.getId()), executor.lastOperation());
+ assertEquals(parameters().withRoute("route"), executor.lastParameters());
+
+ // GET with non-existent cluster is a 400
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?cluster=throw-me");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"message\": \"throw-me\"" +
+ "}",
+ response.readAll());
+ assertEquals(400, response.getStatus());
+
+ // TIMEOUT is a 504
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two");
+ executor.lastOperationContext().error(TIMEOUT, "timeout");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"id\": \"id:space:music:n=1:two\"," +
+ " \"message\": \"timeout\"" +
+ "}",
+ response.readAll());
+ assertEquals(504, response.getStatus());
+
+ // OVERLOAD is a 429
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two");
+ executor.lastOperationContext().error(OVERLOAD, "overload");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"id\": \"id:space:music:n=1:two\"," +
+ " \"message\": \"overload\"" +
+ "}",
+ response.readAll());
+ assertEquals(429, response.getStatus());
+
+ // PRECONDITION_FAILED is a 412
+ response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two");
+ executor.lastOperationContext().error(PRECONDITION_FAILED, "no dice");
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/number/1/two\"," +
+ " \"id\": \"id:space:music:n=1:two\"," +
+ " \"message\": \"no dice\"" +
+ "}",
+ response.readAll());
+ assertEquals(412, response.getStatus());
+
+ // OPTIONS gets options
+ response = driver.sendRequest("https://localhost/document/v1/space/music/docid/one", OPTIONS);
+ assertEquals("", response.readAll());
+ assertEquals(204, response.getStatus());
+ assertEquals("GET,POST,PUT,DELETE", response.getResponse().headers().getFirst("Allow"));
+
+ // PATCH is not allowed
+ response = driver.sendRequest("https://localhost/document/v1/space/music/docid/one", PATCH);
+ assertSameJson("{" +
+ " \"pathId\": \"/document/v1/space/music/docid/one\"," +
+ " \"message\": \"'PATCH' not allowed at '/document/v1/space/music/docid/one'. Allowed methods are: GET, POST, PUT, DELETE\"" +
+ "}",
+ response.readAll());
+ assertEquals(405, response.getStatus());
+
+ driver.close();
}
void assertSameJson(String expected, String actual) {