Repository: olingo-odata4 Updated Branches: refs/heads/master 5e21bb2ba -> 03ab69c49
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/03ab69c4/lib/server-test/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java b/lib/server-test/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java new file mode 100644 index 0000000..cfe9be4 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/netty/server/core/ODataNettyHandlerImplTest.java @@ -0,0 +1,1259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.netty.server.core; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; +import org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider; +import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet; +import org.apache.olingo.commons.api.edmx.EdmxReference; +import org.apache.olingo.commons.api.ex.ODataException; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.netty.server.api.ODataNetty; +import org.apache.olingo.netty.server.api.ODataNettyHandler; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.ODataServerError; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.batch.BatchFacade; +import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionComplexProcessor; +import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionEntityProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.ActionPrimitiveProcessor; +import org.apache.olingo.server.api.processor.ActionVoidProcessor; +import org.apache.olingo.server.api.processor.BatchProcessor; +import org.apache.olingo.server.api.processor.ComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.ComplexProcessor; +import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor; +import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor; +import org.apache.olingo.server.api.processor.CountPrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityCollectionProcessor; +import org.apache.olingo.server.api.processor.EntityProcessor; +import org.apache.olingo.server.api.processor.ErrorProcessor; +import org.apache.olingo.server.api.processor.MediaEntityProcessor; +import org.apache.olingo.server.api.processor.MetadataProcessor; +import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor; +import org.apache.olingo.server.api.processor.PrimitiveProcessor; +import org.apache.olingo.server.api.processor.PrimitiveValueProcessor; +import org.apache.olingo.server.api.processor.Processor; +import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; +import org.apache.olingo.server.api.processor.ReferenceProcessor; +import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.tecsvc.provider.ContainerProvider; +import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; +import org.junit.Test; + +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; + +public class ODataNettyHandlerImplTest { + + private static final String BASE_URI = "http://localhost/odata"; + + @Test + public void serviceDocumentNonDefault() throws Exception { + final ServiceDocumentProcessor processor = mock(ServiceDocumentProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor) + .readServiceDocument(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + final ODataResponse response = dispatch(HttpMethod.GET, "/", processor); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response.getStatusCode()); + + verify(processor).readServiceDocument( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + // We support HEAD now too + final ServiceDocumentProcessor processor2 = mock(ServiceDocumentProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor2) + .readServiceDocument(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "/", processor2); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response2.getStatusCode()); + + verify(processor2).readServiceDocument( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "/", processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, "/", processor); + dispatchMethodNotAllowed(HttpMethod.PUT, "/", processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, "/", processor); + } + + @Test + public void serviceDocumentDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "/", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + String ct = response.getHeader(HttpHeader.CONTENT_TYPE); + assertThat(ct, containsString("application/json")); + assertThat(ct, containsString("odata.metadata=minimal")); + + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + + assertThat(doc, containsString("\"@odata.context\":\"$metadata\"")); + assertThat(doc, containsString("\"value\":")); + + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "/", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response2.getStatusCode()); + assertNull(response2.getHeader(HttpHeader.CONTENT_TYPE)); + assertNull(response2.getContent()); + } + + @Test + public void serviceDocumentRedirect() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "", null); + assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), response.getStatusCode()); + assertEquals(BASE_URI + "/", response.getHeader(HttpHeader.LOCATION)); + + final ODataResponse responseHead = dispatch(HttpMethod.HEAD, "", null); + assertEquals(HttpStatusCode.TEMPORARY_REDIRECT.getStatusCode(), responseHead.getStatusCode()); + assertEquals(BASE_URI + "/", responseHead.getHeader(HttpHeader.LOCATION)); + } + + @Test + public void metadataNonDefault() throws Exception { + final MetadataProcessor processor = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor) + .readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response.getStatusCode()); + + verify(processor).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + // We support HEAD now too + final MetadataProcessor processor2 = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 100, Locale.ENGLISH)).when(processor2) + .readMetadata(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "$metadata", processor2); + assertEquals(HttpStatusCode.CONTINUE.getStatusCode(), response2.getStatusCode()); + + verify(processor2).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.PUT, "$metadata", processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, "$metadata", processor); + } + + @Test + public void metadataDefault() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + assertEquals(ContentType.APPLICATION_XML.toContentTypeString(), response.getHeader(HttpHeader.CONTENT_TYPE)); + + assertNotNull(response.getContent()); + assertThat(IOUtils.toString(response.getContent()), + containsString("<edmx:Edmx Version=\"4.0\"")); + + final ODataResponse response2 = dispatch(HttpMethod.HEAD, "$metadata", null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response2.getStatusCode()); + assertNull(response2.getHeader(HttpHeader.CONTENT_TYPE)); + assertNull(response2.getContent()); + } + + @Test + public void maxVersionNone() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null); + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void maxVersionSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null, + HttpHeader.ODATA_MAX_VERSION, ODataServiceVersion.V40.toString(), null); + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void maxVersionNotSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", null, + HttpHeader.ODATA_MAX_VERSION, ODataServiceVersion.V30.toString(), null); + + assertEquals(ODataServiceVersion.V40.toString(), response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=xml", null, null, null); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationNotSupported() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=not/Supported", null, null, null); + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode()); + } + + @Test + public void contentNegotiationNotSupported2() { + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", "$format=notSupported", null, null, null); + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode()); + } + + @Test + public void unregisteredProcessor() { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrim", null); + assertEquals(HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseNotFound() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "NotFound", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseBadRequest() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrim('122')", null); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void uriParserExceptionWithFormatQueryJson() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=json", "", "", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatQueryJsonAndMore() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=json&$top=3", "", "", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatJsonAcceptAtom() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=json", + HttpHeader.ACCEPT, ContentType.APPLICATION_ATOM_XML.toContentTypeString(), null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatQueryAtom() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=atom", "", "", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatQueryAtomAndTop() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=atom&$top=19", "", "", null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatAtomAcceptJson() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=atom", + HttpHeader.ACCEPT, ContentType.APPLICATION_JSON.toContentTypeString(), null); + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void uriParserExceptionWithFormatQueryInvali() throws Exception { + final ODataResponse response = dispatch(HttpMethod.GET, "ESAllPrims", "$format=somenotvalid", "", "", null); + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), response.getStatusCode()); + assertEquals("application/json;odata.metadata=minimal", + response.getHeader(HttpHeader.CONTENT_TYPE)); + } + + @Test + public void applicationExceptionInProcessorMessage() throws Exception { + final String ODATA_ERRORCODE = "425"; + final String ORIGINAL_MESSAGE = "original message"; + final String LOCALIZED_MESSAGE = "localized message"; + MetadataProcessor processor = mock(MetadataProcessor.class); + + ODataApplicationException oDataApplicationException = + new ODataApplicationException(ORIGINAL_MESSAGE, 425, Locale.ENGLISH, ODATA_ERRORCODE) { + private static final long serialVersionUID = 1L; + + @Override + public String getLocalizedMessage() { + return LOCALIZED_MESSAGE; + } + }; + + doThrow(oDataApplicationException).when(processor).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); + InputStream contentStream = response.getContent(); + String responseContent = IOUtils.toString(contentStream, Charset.forName("UTF-8")); + // does the response contain the localized message and the status code? + boolean isMessage = responseContent.contains(LOCALIZED_MESSAGE) && responseContent.contains(ODATA_ERRORCODE); + // test if message is localized + assertEquals(true, isMessage); + // test if the original is hold + assertEquals(ORIGINAL_MESSAGE, oDataApplicationException.getMessage()); + } + + @Test + public void applicationExceptionInProcessor() throws Exception { + MetadataProcessor processor = mock(MetadataProcessor.class); + doThrow(new ODataApplicationException("msg", 425, Locale.ENGLISH)).when(processor).readMetadata( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + final ODataResponse response = dispatch(HttpMethod.GET, "$metadata", processor); + assertEquals(425, response.getStatusCode()); + } + + @Test + public void uriParserExceptionResultsInRightResponseEdmCause() throws Exception { + final OData odata = OData.newInstance(); + final ServiceMetadata serviceMetadata = odata.createServiceMetadata( + new CsdlAbstractEdmProvider() { + @Override + public CsdlEntitySet getEntitySet(final FullQualifiedName entityContainer, final String entitySetName) + throws ODataException { + throw new ODataException("msg"); + } + }, + Collections.<EdmxReference> emptyList()); + + ODataRequest request = new ODataRequest(); + request.setMethod(HttpMethod.GET); + request.setRawODataPath("EdmException"); + + final ODataResponse response = + new ODataNettyHandlerImpl(odata, serviceMetadata).process(request); + assertNotNull(response); + assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + } + + @Test + public void dispatchBatch() throws Exception { + final String uri = "$batch"; + final BatchProcessor processor = mock(BatchProcessor.class); + + dispatch(HttpMethod.POST, uri, null, HttpHeader.CONTENT_TYPE, ContentType.MULTIPART_MIXED.toContentTypeString(), + processor); + verify(processor).processBatch(any(BatchFacade.class), any(ODataRequest.class), any(ODataResponse.class)); + + dispatchMethodNotAllowed(HttpMethod.GET, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchEntitySet() throws Exception { + final String uri = "ESAllPrim"; + final EntityCollectionProcessor processor = mock(EntityCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchEntitySetCount() throws Exception { + final String uri = "ESAllPrim/$count"; + final CountEntityCollectionProcessor processor = mock(CountEntityCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).countEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchCountWithNavigation() throws Exception { + final CountEntityCollectionProcessor processor = mock(CountEntityCollectionProcessor.class); + String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$count"; + dispatch(HttpMethod.GET, uri, processor); + + verify(processor).countEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchFunction() throws Exception { + EntityProcessor entityProcessor = mock(EntityProcessor.class); + dispatch(HttpMethod.GET, "FICRTETKeyNav()", entityProcessor); + verify(entityProcessor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + EntityCollectionProcessor entityCollectionProcessor = mock(EntityCollectionProcessor.class); + dispatch(HttpMethod.GET, "FICRTCollESTwoKeyNavParam(ParameterInt16=123)", entityCollectionProcessor); + verify(entityCollectionProcessor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + final String entityCountUri = "FICRTCollESTwoKeyNavParam(ParameterInt16=123)/$count"; + final CountEntityCollectionProcessor entityCountProcessor = mock(CountEntityCollectionProcessor.class); + dispatch(HttpMethod.GET, entityCountUri, entityCountProcessor); + verify(entityCountProcessor).countEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, entityCountUri, entityCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, entityCountUri, entityCountProcessor); + + PrimitiveProcessor primitiveProcessor = mock(PrimitiveProcessor.class); + dispatch(HttpMethod.GET, "FICRTString()", primitiveProcessor); + verify(primitiveProcessor).readPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + // FINRTInt16 is not composable so /$value is not allowed + final String valueUri = "FINRTInt16()/$value"; + final PrimitiveValueProcessor primitiveValueProcessor = mock(PrimitiveValueProcessor.class); + dispatchMethodWithError(HttpMethod.GET, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.POST, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.PATCH, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.PUT, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.DELETE, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + dispatchMethodWithError(HttpMethod.HEAD, valueUri, primitiveValueProcessor, HttpStatusCode.BAD_REQUEST); + + final String primitiveCollectionUri = "FICRTCollString()"; + PrimitiveCollectionProcessor primitiveCollectionProcessor = mock(PrimitiveCollectionProcessor.class); + dispatch(HttpMethod.GET, primitiveCollectionUri, primitiveCollectionProcessor); + verify(primitiveCollectionProcessor).readPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.POST, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCollectionUri, primitiveCollectionProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, primitiveCollectionUri, primitiveCollectionProcessor); + + final String primitiveCountUri = "FICRTCollString()/$count"; + final CountPrimitiveCollectionProcessor primitiveCountProcessor = mock(CountPrimitiveCollectionProcessor.class); + dispatch(HttpMethod.GET, primitiveCountUri, primitiveCountProcessor); + verify(primitiveCountProcessor).countPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, primitiveCountUri, primitiveCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, primitiveCountUri, primitiveCountProcessor); + + ComplexProcessor complexProcessor = mock(ComplexProcessor.class); + dispatch(HttpMethod.GET, "FICRTCTTwoPrim()", complexProcessor); + verify(complexProcessor).readComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + ComplexCollectionProcessor complexCollectionProcessor = mock(ComplexCollectionProcessor.class); + dispatch(HttpMethod.GET, "FICRTCollCTTwoPrim()", complexCollectionProcessor); + verify(complexCollectionProcessor).readComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + final String complexCountUri = "FICRTCollCTTwoPrim()/$count"; + final CountComplexCollectionProcessor complexCountProcessor = mock(CountComplexCollectionProcessor.class); + dispatch(HttpMethod.GET, complexCountUri, complexCountProcessor); + verify(complexCountProcessor).countComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.POST, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, complexCountUri, complexCountProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, complexCountUri, complexCountProcessor); + + final String mediaUri = "FICRTESMedia(ParameterInt16=1)/$value"; + final MediaEntityProcessor mediaProcessor = mock(MediaEntityProcessor.class); + dispatch(HttpMethod.GET, mediaUri, mediaProcessor); + verify(mediaProcessor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.POST, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, mediaUri, mediaProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, mediaUri, mediaProcessor); + } + + @Test + public void dispatchAction() throws Exception { + final ActionPrimitiveProcessor primitiveProcessor = mock(ActionPrimitiveProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_STRING, primitiveProcessor); + verify(primitiveProcessor).processActionPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.GET, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT_STRING, primitiveProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, ContainerProvider.AIRT_STRING, primitiveProcessor); + + ActionPrimitiveCollectionProcessor primitiveCollectionProcessor = mock(ActionPrimitiveCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_STRING_TWO_PARAM, primitiveCollectionProcessor); + verify(primitiveCollectionProcessor).processActionPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionComplexProcessor complexProcessor = mock(ActionComplexProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTCT_TWO_PRIM_PARAM, complexProcessor); + verify(complexProcessor).processActionComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionComplexCollectionProcessor complexCollectionProcessor = mock(ActionComplexCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_CT_TWO_PRIM_PARAM, complexCollectionProcessor); + verify(complexCollectionProcessor).processActionComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityProcessor entityProcessor = mock(ActionEntityProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTET_TWO_KEY_TWO_PRIM_PARAM, entityProcessor); + verify(entityProcessor).processActionEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityCollectionProcessor entityCollectionProcessor = mock(ActionEntityCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_ET_KEY_NAV_PARAM, entityCollectionProcessor); + verify(entityCollectionProcessor).processActionEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityProcessor entityProcessorEs = mock(ActionEntityProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRTES_ALL_PRIM_PARAM, entityProcessorEs); + verify(entityProcessorEs).processActionEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + ActionEntityCollectionProcessor entityCollectionProcessorEs = mock(ActionEntityCollectionProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT_COLL_ES_ALL_PRIM_PARAM, entityCollectionProcessorEs); + verify(entityCollectionProcessorEs).processActionEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + final ActionVoidProcessor voidProcessor = mock(ActionVoidProcessor.class); + dispatch(HttpMethod.POST, ContainerProvider.AIRT, voidProcessor); + verify(voidProcessor).processActionVoid( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + dispatchMethodNotAllowed(HttpMethod.GET, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.PATCH, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.PUT, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.DELETE, ContainerProvider.AIRT, voidProcessor); + dispatchMethodNotAllowed(HttpMethod.HEAD, ContainerProvider.AIRT, voidProcessor); + } + + @Test + public void dispatchEntity() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatch(HttpMethod.POST, "ESAllPrim", processor); + verify(processor).createEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + + @Test + public void dispatchSingleton() throws Exception { + final String uri = "SI"; + final EntityProcessor processor = mock(EntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchSingletonMedia() throws Exception { + final String uri = "SIMedia/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + } + + @Test + public void dispatchSingletonNavigation() throws Exception { + final String uri = "SINav/NavPropertyETTwoKeyNavOne"; + final String sigletonNavUri = "ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/NavPropertySINav"; + final String sigletonManyNavUri = "SINav/NavPropertyETTwoKeyNavMany"; + final EntityProcessor processor = mock(EntityProcessor.class); + final EntityCollectionProcessor collectionProcessor = mock(EntityCollectionProcessor.class); + + dispatch(HttpMethod.GET, sigletonNavUri, processor); + verify(processor).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, sigletonNavUri, processor); + verify(processor).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, sigletonNavUri, processor); + verify(processor, times(2)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, sigletonNavUri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonNavUri, processor); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor, times(2)).readEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor, times(3)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(4)).updateEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + + dispatch(HttpMethod.GET, sigletonManyNavUri, collectionProcessor); + verify(collectionProcessor).readEntityCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, sigletonManyNavUri, processor); + + dispatchMethodNotAllowed(HttpMethod.PUT, sigletonManyNavUri, processor); + + dispatch(HttpMethod.POST, sigletonManyNavUri, processor); + verify(processor).createEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + + dispatchMethodNotAllowed(HttpMethod.DELETE, sigletonManyNavUri, processor); + } + + @Test + public void dispatchMedia() throws Exception { + final String uri = "ESMedia(1)/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.POST, "ESMedia", processor); + verify(processor).createMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchValueOnNoMedia() throws Exception { + final String uri = "ESAllPrim(1)/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verifyZeroInteractions(processor); + + dispatch(HttpMethod.POST, uri, processor); + verifyZeroInteractions(processor); + + dispatch(HttpMethod.PUT, uri, processor); + verifyZeroInteractions(processor); + + dispatch(HttpMethod.DELETE, uri, processor); + verifyZeroInteractions(processor); + + dispatch(HttpMethod.HEAD, uri, processor); + verifyZeroInteractions(processor); + } + + @Test + public void dispatchMediaWithNavigation() throws Exception { + /* + * In Java we decided that any kind of navigation will be accepted. This means that a $value on a media resource + * must be dispatched as well + */ + final String uri = "ESKeyNav(1)/NavPropertyETMediaOne/$value"; + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readMediaEntity( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne", processor); + + dispatchMethodNotAllowed(HttpMethod.POST, "ESKeyNav(1)/NavPropertyETMediaOne/$value", processor); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class), any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteMediaEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchMediaDeleteIndirect() throws Exception { + final MediaEntityProcessor processor = mock(MediaEntityProcessor.class); + dispatch(HttpMethod.DELETE, "ESMedia(1)", processor); + + verify(processor).deleteEntity(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + dispatchMethodNotAllowed(HttpMethod.HEAD, "ESMedia(1)", processor); + } + + @Test + public void dispatchPrimitiveProperty() throws Exception { + final String uri = "ESAllPrim(0)/PropertyString"; + final PrimitiveProcessor processor = mock(PrimitiveProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updatePrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updatePrimitive( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitive(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchPrimitivePropertyValue() throws Exception { + final String uri = "ESAllPrim(0)/PropertyString/$value"; + final PrimitiveValueProcessor processor = mock(PrimitiveValueProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitiveValue(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, null, HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString(), + processor); + verify(processor).updatePrimitiveValue( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitiveValue(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchPrimitiveCollectionProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyString"; + final PrimitiveCollectionProcessor processor = mock(PrimitiveCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readPrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updatePrimitiveCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deletePrimitiveCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchPrimitiveCollectionPropertyCount() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyString/$count"; + final CountPrimitiveCollectionProcessor processor = mock(CountPrimitiveCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).countPrimitiveCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchComplexProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/PropertyComp"; + final ComplexProcessor processor = mock(ComplexProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateComplex( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteComplex(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchComplexCollectionProperty() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyComp"; + final ComplexCollectionProcessor processor = mock(ComplexCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor).updateComplexCollection( + any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), any(ContentType.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uri, processor); + verify(processor).deleteComplexCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchComplexCollectionPropertyCount() throws Exception { + final String uri = "ESMixPrimCollComp(7)/CollPropertyComp/$count"; + final CountComplexCollectionProcessor processor = mock(CountComplexCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).countComplexCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.DELETE, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + } + + @Test + public void dispatchReference() throws Exception { + final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimOne/$ref"; + final String uriMany = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final String singletonUri = "SINav/NavPropertyETKeyNavOne/$ref"; + final String singletonUriMany = "SINav/NavPropertyETTwoKeyNavMany/$ref"; + final String singleUri = "SINav/$ref"; + final ReferenceProcessor processor = mock(ReferenceProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, uri, processor); + verify(processor).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, uri, processor); + verify(processor, times(2)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, uri, processor); + + dispatch(HttpMethod.POST, uriMany, processor); + verify(processor).createReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, uriMany, "$id=ESTwoPrim(1)", null, null, processor); + verify(processor).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + + //singleton URIs + + dispatch(HttpMethod.GET, singletonUri, processor); + verify(processor, times(2)).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, singletonUri, processor); + verify(processor, times(3)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, singletonUri, processor); + verify(processor, times(4)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, singletonUri, processor); + + dispatch(HttpMethod.GET, singleUri, processor); + verify(processor, times(3)).readReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PATCH, singleUri, processor); + verify(processor, times(5)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.PUT, singleUri, processor); + verify(processor, times(6)).updateReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.POST, singleUri, processor); + + dispatch(HttpMethod.POST, singletonUriMany, processor); + verify(processor, times(2)).createReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatch(HttpMethod.DELETE, singletonUriMany, "$id=ESTwoPrim(1)", null, null, processor); + verify(processor, times(2)).deleteReference(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class)); + + dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUriMany, processor); + } + + @Test + public void dispatchReferenceCollection() throws Exception { + final String uri = "ESAllPrim(0)/NavPropertyETTwoPrimMany/$ref"; + final String singletonUri = "SINav/NavPropertyETTwoKeyNavMany/$ref"; + final ReferenceCollectionProcessor processor = mock(ReferenceCollectionProcessor.class); + + dispatch(HttpMethod.GET, uri, processor); + verify(processor).readReferenceCollection(any(ODataRequest.class), any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, uri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, uri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, uri, processor); + + //singleton ref + dispatch(HttpMethod.GET, singletonUri, processor); + verify(processor, times(2)).readReferenceCollection(any(ODataRequest.class), + any(ODataResponse.class), any(UriInfo.class), + any(ContentType.class)); + + dispatchMethodNotAllowed(HttpMethod.PATCH, singletonUri, processor); + dispatchMethodNotAllowed(HttpMethod.PUT, singletonUri, processor); + dispatchMethodNotAllowed(HttpMethod.HEAD, singletonUri, processor); + } + + @Test + public void noRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + final ODataResponse response = dispatch(HttpMethod.POST, "ESAllPrim", null, + HttpHeader.CONTENT_TYPE, null, processor); + verifyZeroInteractions(processor); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void illegalRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + final ODataResponse response = dispatch(HttpMethod.POST, "ESAllPrim", null, + HttpHeader.CONTENT_TYPE, "*/*", processor); + verifyZeroInteractions(processor); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + } + + @Test + public void unsupportedRequestContentType() throws Exception { + EntityProcessor processor = mock(EntityProcessor.class); + ErrorProcessor errorProcessor = mock(ErrorProcessor.class); + dispatch(HttpMethod.POST, "ESAllPrim", null, HttpHeader.CONTENT_TYPE, "some/unsupported", errorProcessor); + verifyZeroInteractions(processor); + verify(errorProcessor).processError(any(ODataRequest.class), any(ODataResponse.class), + any(ODataServerError.class), + any(ContentType.class)); + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final String query, + final String headerName, final String headerValue, final Processor processor) { + ODataRequest request = new ODataRequest(); + request.setMethod(method); + request.setRawBaseUri(BASE_URI); + if (path.isEmpty()) { + request.setRawRequestUri(BASE_URI); + } + request.setRawODataPath(path); + request.setRawQueryPath(query); + + if (headerName != null) { + request.addHeader(headerName, Collections.singletonList(headerValue)); + } + + if (headerName != HttpHeader.CONTENT_TYPE) { + request.addHeader(HttpHeader.CONTENT_TYPE, Collections.singletonList( + ContentType.JSON.toContentTypeString())); + } + + final ODataNetty odata = ODataNetty.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList()); + + ODataNettyHandlerImpl handler = new ODataNettyHandlerImpl(odata, metadata); + + if (processor != null) { + handler.register(processor); + } + + final ODataResponse response = handler.process(request); + assertNotNull(response); + return response; + } + + private ODataResponse dispatch(final HttpMethod method, final String path, final Processor processor) { + return dispatch(method, path, null, null, null, processor); + } + + private void dispatchMethodNotAllowed(final HttpMethod method, final String path, final Processor processor) { + final ODataResponse response = dispatch(method, path, processor); + assertEquals(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatusCode()); + assertNotNull(response.getContent()); + } + + private void dispatchMethodWithError(final HttpMethod method, final String path, final Processor processor, + final HttpStatusCode statusCode) { + final ODataResponse response = dispatch(method, path, processor); + assertEquals(statusCode.getStatusCode(), response.getStatusCode()); + assertNotNull(response.getContent()); + } + + @Test + public void testNettyReqResp_GetMethod() { + MetadataProcessor processor = mock(MetadataProcessor.class); + final ODataNetty odata = ODataNetty.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList()); + + ODataNettyHandler handler = odata.createNettyHandler(metadata); + + handler.register(processor); + DefaultFullHttpRequest nettyRequest = mock(DefaultFullHttpRequest.class); + io.netty.handler.codec.http.HttpMethod httpMethod = mock(io.netty.handler.codec.http.HttpMethod.class); + when(httpMethod.name()).thenReturn("GET"); + when(nettyRequest.method()).thenReturn(httpMethod); + HttpVersion httpVersion = mock(HttpVersion.class); + when(httpVersion.text()).thenReturn("HTTP/1.0"); + when(nettyRequest.protocolVersion()).thenReturn(httpVersion); + when(nettyRequest.uri()).thenReturn("/odata.svc/$metadata"); + HttpHeaders headers = mock(HttpHeaders.class); + headers.add("Accept", "application/atom+xml"); + Set<String> set = new HashSet<String>(); + set.add("Accept"); + when(headers.names()).thenReturn(set); + when(nettyRequest.headers()).thenReturn(headers); + when(nettyRequest.content()).thenReturn(Unpooled.buffer()); + + DefaultFullHttpResponse nettyResponse = mock(DefaultFullHttpResponse.class); + when(nettyResponse.status()).thenReturn(HttpResponseStatus.OK); + when(nettyResponse.headers()).thenReturn(headers); + + when(nettyResponse.content()).thenReturn(Unpooled.buffer()); + + Map<String, String> requestParams = new HashMap<String, String>(); + requestParams.put("contextPath", "/odata.svc"); + handler.processNettyRequest(nettyRequest, nettyResponse, requestParams); + + assertEquals(HttpStatusCode.OK.getStatusCode(), nettyResponse.status().OK.code()); + } + + @Test + public void testNettyReqResp_POSTMethod() { + EntityProcessor processor = mock(EntityProcessor.class); + final ODataNetty odata = ODataNetty.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList()); + + ODataNettyHandler handler = odata.createNettyHandler(metadata); + + handler.register(processor); + HttpRequest nettyRequest = mock(DefaultFullHttpRequest.class); + io.netty.handler.codec.http.HttpMethod httpMethod = mock(io.netty.handler.codec.http.HttpMethod.class); + when(httpMethod.name()).thenReturn("POST"); + when(nettyRequest.method()).thenReturn(httpMethod); + HttpVersion httpVersion = mock(HttpVersion.class); + when(httpVersion.text()).thenReturn("HTTP/1.0"); + when(nettyRequest.protocolVersion()).thenReturn(httpVersion); + when(nettyRequest.uri()).thenReturn("/odata.svc/ESAllPrim"); + HttpHeaders headers = mock(HttpHeaders.class); + headers.set("Content-Type", "application/json"); + Set<String> set = new HashSet<String>(); + set.add("Content-Type"); + when(headers.names()).thenReturn(set); + List<String> headerValues = new ArrayList<String>(); + headerValues.add("application/json"); + when(headers.getAll("Content-Type")).thenReturn(headerValues); + when(nettyRequest.headers()).thenReturn(headers); + String content = "{\"@odata.context\": \"$metadata#ESAllPrim/$entity\"," + + "\"PropertyInt16\": 32767," + + "\"PropertyString\": \"First Resource &&&- positive values\"," + + "\"PropertyBoolean\": true," + + "\"PropertyByte\": 255," + + "\"PropertySByte\": 127," + + "\"PropertyInt32\": 2147483647," + + "\"PropertyInt64\": 9223372036854775807," + + "\"PropertySingle\": 17900000," + + "\"PropertyDouble\": -179000," + + "\"PropertyDecimal\": 34," + + "\"PropertyBinary\": \"ASNFZ4mrze8=\"," + + "\"PropertyDate\": \"2012-12-03\"," + + "\"PropertyDateTimeOffset\": \"2012-12-03T07:16:23Z\"," + + "\"PropertyDuration\": \"PT6S\"," + + "\"PropertyGuid\": \"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"PropertyTimeOfDay\": \"03:26:05\"}"; + byte[] arr = new byte[content.length()]; + arr = content.getBytes(); + + when(((DefaultFullHttpRequest) nettyRequest).content()).thenReturn(Unpooled.copiedBuffer(arr)); + + HttpResponse nettyResponse = mock(DefaultFullHttpResponse.class); + when(nettyResponse.status()).thenReturn(HttpResponseStatus.CREATED); + when(nettyResponse.headers()).thenReturn(headers); + + when(((DefaultFullHttpResponse) nettyResponse).content()).thenReturn(Unpooled.buffer()); + + Map<String, String> requestParams = new HashMap<String, String>(); + requestParams.put("contextPath", "/odata.svc"); + handler.processNettyRequest(nettyRequest, nettyResponse, requestParams); + + assertEquals(HttpStatusCode.CREATED.getStatusCode(), nettyResponse.status().CREATED.code()); + } +}
