diff options
Diffstat (limited to 'vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi')
3 files changed, 164 insertions, 12 deletions
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java index 5021899e30b..4c04070cec4 100644 --- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java +++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/OperationHandlerImplTest.java @@ -145,6 +145,10 @@ public class OperationHandlerImplTest { return new RestUri(new URI("http://localhost/document/v1/namespace/document-type/docid/")); } + private static RestUri apiRootVisitUri() throws Exception { + return new RestUri(new URI("http://localhost/document/v1/")); + } + private static RestUri dummyGetUri() throws Exception { return new RestUri(new URI("http://localhost/document/v1/namespace/document-type/docid/foo")); } @@ -166,6 +170,7 @@ public class OperationHandlerImplTest { try { OperationHandlerImpl handler = fixture.createHandler(); handler.visit(dummyVisitUri(), "", emptyVisitOptions()); + fail("Exception expected"); } catch (RestApiException e) { assertThat(e.getResponse().getStatus(), is(500)); assertThat(renderRestApiExceptionAsString(e), containsString("Timed out")); @@ -192,14 +197,19 @@ public class OperationHandlerImplTest { assertThat(fixture.assignedParameters.get().getSessionTimeoutMs(), is((long)OperationHandlerImpl.VISIT_TIMEOUT_MS)); } - private static VisitorParameters generatedParametersFromVisitOptions(OperationHandler.VisitOptions options) throws Exception { + private static VisitorParameters generatedVisitParametersFrom(RestUri restUri, String documentSelection, + OperationHandler.VisitOptions options) throws Exception { OperationHandlerImplFixture fixture = new OperationHandlerImplFixture(); OperationHandlerImpl handler = fixture.createHandler(); - handler.visit(dummyVisitUri(), "", options); + handler.visit(restUri, documentSelection, options); return fixture.assignedParameters.get(); } + private static VisitorParameters generatedParametersFromVisitOptions(OperationHandler.VisitOptions options) throws Exception { + return generatedVisitParametersFrom(dummyVisitUri(), "", options); + } + @Test public void document_type_is_mapped_to_correct_bucket_space() throws Exception { OperationHandlerImplFixture fixture = new OperationHandlerImplFixture(); @@ -218,6 +228,7 @@ public class OperationHandlerImplTest { try { OperationHandlerImpl handler = fixture.createHandler(); handler.visit(dummyVisitUri(), "", emptyVisitOptions()); + fail("Exception expected"); } catch (RestApiException e) { assertThat(e.getResponse().getStatus(), is(400)); String errorMsg = renderRestApiExceptionAsString(e); @@ -304,4 +315,89 @@ public class OperationHandlerImplTest { verify(fixture.mockSyncSession).get(any(), eq("donald,duck"), any()); } + @Test + public void api_root_visit_uri_requires_cluster_set() throws Exception { + OperationHandlerImplFixture fixture = new OperationHandlerImplFixture(); + OperationHandlerImpl handler = fixture.createHandler(); + try { + handler.visit(apiRootVisitUri(), "", emptyVisitOptions()); + fail("Exception expected"); + } catch (RestApiException e) { + assertThat(e.getResponse().getStatus(), is(400)); + assertThat(renderRestApiExceptionAsString(e), containsString( + "MISSING_CLUSTER Must set 'cluster' parameter to a valid content cluster id " + + "when visiting at a root /document/v1/ level")); + } + } + + @Test + public void api_root_visiting_propagates_request_route() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", optionsBuilder().cluster("foo").build()); + assertEquals("[Storage:cluster=foo;clusterconfigid=configId]", parameters.getRoute().toString()); + } + + @Test + public void api_root_visiting_targets_default_bucket_space_by_default() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", optionsBuilder().cluster("foo").build()); + assertEquals("default", parameters.getBucketSpace()); + } + + @Test + public void api_root_visiting_can_explicitly_specify_bucket_space() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", + optionsBuilder().cluster("foo").bucketSpace("global").build()); + assertEquals("global", parameters.getBucketSpace()); + } + + @Test + public void api_root_visiting_throws_exception_on_unknown_bucket_space_name() throws Exception { + try { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", + optionsBuilder().cluster("foo").bucketSpace("langbein").build()); + } catch (RestApiException e) { + assertThat(e.getResponse().getStatus(), is(400)); + assertThat(renderRestApiExceptionAsString(e), containsString( + "UNKNOWN_BUCKET_SPACE Bucket space 'langbein' is not a known bucket space " + + "(expected 'default' or 'global')")); + } + } + + @Test + public void api_root_visiting_has_empty_document_selection_by_default() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", optionsBuilder().cluster("foo").build()); + assertEquals("", parameters.getDocumentSelection()); + } + + @Test + public void api_root_visiting_propagates_provided_document_selection() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "baz.blarg", optionsBuilder().cluster("foo").build()); + // Note: syntax correctness of selection is checked and enforced by RestApi + assertEquals("baz.blarg", parameters.getDocumentSelection()); + } + + @Test + public void api_root_visiting_uses_all_fieldset_by_default() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", optionsBuilder().cluster("foo").build()); + assertEquals("[all]", parameters.getFieldSet()); + } + + @Test + public void api_root_visiting_propagates_provided_fieldset() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(apiRootVisitUri(), "", + optionsBuilder().cluster("foo").fieldSet("zoidberg:[document]").build()); + assertEquals("zoidberg:[document]", parameters.getFieldSet()); + } + + @Test + public void namespace_and_doctype_augmented_selection_has_parenthesized_selection_sub_expression() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(dummyVisitUri(), "1 != 2", optionsBuilder().cluster("foo").build()); + assertEquals("((1 != 2) and document-type and (id.namespace=='namespace'))", parameters.getDocumentSelection()); + } + + @Test + public void namespace_and_doctype_visit_without_selection_does_not_contain_selection_sub_expression() throws Exception { + VisitorParameters parameters = generatedVisitParametersFrom(dummyVisitUri(), "", optionsBuilder().cluster("foo").build()); + assertEquals("document-type and (id.namespace=='namespace')", parameters.getDocumentSelection()); + } + } diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java index 297576f1bef..5f44b06ab77 100644 --- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java +++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/MockedOperationHandler.java @@ -24,7 +24,9 @@ public class MockedOperationHandler implements OperationHandler { + documentSelection + "'" + options.wantedDocumentCount.map(n -> String.format(", min docs returned: %d", n)).orElse("") + options.fieldSet.map(s -> String.format(", field set: '%s'", s)).orElse("") - + options.concurrency.map(n -> String.format(", concurrency: %d", n)).orElse("")); + + options.concurrency.map(n -> String.format(", concurrency: %d", n)).orElse("") + + options.bucketSpace.map(s -> String.format(", bucket space: '%s'", s)).orElse("") + + options.cluster.map(s -> String.format(", cluster: '%s'", s)).orElse("")); } @Override diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java index 6b7f708e090..5e3aa05130d 100644 --- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java +++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/RestApiTest.java @@ -28,6 +28,8 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; +import java.util.function.Function; +import java.util.function.Supplier; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; @@ -288,19 +290,47 @@ public class RestApiTest { } } - private String performV1RestCall(String pathSuffix) { + private static String defaultPathPrefix() { + return "namespace/document-type/"; + } + + private String performV1RestCall(String pathPrefix, String pathSuffix, Function<Request, HttpRequestBase> methodOp) { try { - Request request = new Request(String.format("http://localhost:%s/document/v1/namespace/document-type/%s", - getFirstListenPort(), pathSuffix)); - HttpGet get = new HttpGet(request.getUri()); - return doRest(get); + Request request = new Request(String.format("http://localhost:%s/document/v1/%s%s", + getFirstListenPort(), pathPrefix, pathSuffix)); + HttpRequestBase restOp = methodOp.apply(request); + return doRest(restOp); } catch (Exception e) { throw new RuntimeException(e); } } + private String performV1GetRestCall(String pathSuffix) { + return performV1RestCall(defaultPathPrefix(), pathSuffix, (request) -> new HttpGet(request.getUri())); + } + + private void doTestRootPathNotAccepted(Function<Request, HttpRequestBase> methodOpFactory) { + String output = performV1RestCall("", "", methodOpFactory); + assertThat(output, containsString("Root /document/v1/ requests only supported for HTTP GET")); + } + + @Test + public void root_api_path_not_accepted_for_http_put() { + doTestRootPathNotAccepted((request) -> new HttpPut(request.getUri())); + } + + @Test + public void root_api_path_not_accepted_for_http_post() { + doTestRootPathNotAccepted((request) -> new HttpPost(request.getUri())); + } + + @Test + public void root_api_path_not_accepted_for_http_delete() { + doTestRootPathNotAccepted((request) -> new HttpDelete(request.getUri())); + } + private void assertResultingDocumentSelection(String suffix, String expected) { - String output = performV1RestCall(suffix); + String output = performV1GetRestCall(suffix); assertThat(output, containsString(String.format("doc selection: '%s'", expected))); } @@ -321,7 +351,7 @@ public class RestApiTest { } private void assertNumericIdFailsParsing(String id) { - String output = performV1RestCall(String.format("number/%s", encoded(id))); + String output = performV1GetRestCall(String.format("number/%s", encoded(id))); assertThat(output, containsString("Failed to parse numeric part of selection URI")); } @@ -335,7 +365,7 @@ public class RestApiTest { @Test public void non_text_group_string_character_returns_error() { - String output = performV1RestCall(String.format("group/%s", encoded("\u001f"))); + String output = performV1GetRestCall(String.format("group/%s", encoded("\u001f"))); assertThat(output, containsString("Failed to parse group part of selection URI; contains invalid text code point U001F")); } @@ -362,7 +392,7 @@ public class RestApiTest { } private void assertDocumentSelectionFailsParsing(String expression) { - String output = performV1RestCall(String.format("number/1234?selection=%s", encoded(expression))); + String output = performV1GetRestCall(String.format("number/1234?selection=%s", encoded(expression))); assertThat(output, containsString("Failed to parse expression given in 'selection' parameter. Must be a complete and valid sub-expression.")); } @@ -416,6 +446,30 @@ public class RestApiTest { } @Test + public void root_api_visit_cluster_parameter_is_propagated() throws IOException { + Request request = new Request(String.format("http://localhost:%s/document/v1/?cluster=vaffel", getFirstListenPort())); + HttpGet get = new HttpGet(request.getUri()); + String rest = doRest(get); + assertThat(rest, containsString("cluster: 'vaffel'")); + } + + @Test + public void root_api_visit_selection_parameter_is_propagated() throws IOException { + Request request = new Request(String.format("http://localhost:%s/document/v1/?cluster=foo&selection=yoshi", getFirstListenPort())); + HttpGet get = new HttpGet(request.getUri()); + String rest = doRest(get); + assertThat(rest, containsString("doc selection: 'yoshi'")); + } + + @Test + public void root_api_visit_bucket_space_parameter_is_propagated() throws IOException { + Request request = new Request(String.format("http://localhost:%s/document/v1/?cluster=foo&bucketSpace=global", getFirstListenPort())); + HttpGet get = new HttpGet(request.getUri()); + String rest = doRest(get); + assertThat(rest, containsString("bucket space: 'global'")); + } + + @Test public void invalid_visit_concurrency_parameter_returns_error_response() throws IOException { Request request = new Request(String.format("http://localhost:%s/document/v1/namespace/document-type/docid/?concurrency=badgers", getFirstListenPort())); HttpGet get = new HttpGet(request.getUri()); |