blob: 167fa7ea51b94841c66e6b5a2fe5238006320632 [file] [log] [blame]
/*
* 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.server.core;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyListOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
import org.apache.olingo.server.api.serializer.RepresentationType;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
import org.junit.Test;
public class ContentNegotiatorTest {
static final private String ACCEPT_CASE_MIN = ContentType.JSON.toContentTypeString();
static final private String ACCEPT_CASE_MIN_UTF8 = "application/json;charset=UTF-8;odata.metadata=minimal";
static final private String ACCEPT_CASE_MIN_UTF81 = "application/json;charset=utf-8;odata.metadata=minimal";
static final private String ACCEPT_CASE_ISO_8859_1 = "application/json;charset=ISO-8859-1";
static final private String ACCEPT_CASE_FULL = ContentType.JSON_FULL_METADATA.toContentTypeString();
static final private String ACCEPT_CASE_NONE = ContentType.JSON_NO_METADATA.toContentTypeString();
static final private String ACCEPT_CASE_MIN_UTF8_IEEE754 =
"application/json;charset=UTF-8;odata.metadata=minimal;IEEE754Compatible=true";
static final private String ACCEPT_CASE_MIN_IEEE754 = ACCEPT_CASE_MIN + ";IEEE754Compatible=true";
String ACCEPT_CASE_MIN_IEEE754_1 = ACCEPT_CASE_MIN + ";IEEE754Compatible=false";
static final private String ACCEPT_CASE_MIN_IEEE754_FAIL = ACCEPT_CASE_MIN + ";IEEE754Compatible=xyz";
static final private String ACCEPT_CASE_JSONQ = "application/json;q=0.2";
static final private String ACCEPT_CASE_XML = ContentType.APPLICATION_XML.toContentTypeString();
static final private String ACCEPT_CASE_JSON = ContentType.APPLICATION_JSON.toContentTypeString();
static final private String ACCEPT_CASE_WILDCARD1 = "*/*";
static final private String ACCEPT_CASE_WILDCARD2 = "application/*";
static final private String ACCEPT_CASE_JSON_IEEE754 = ACCEPT_CASE_JSON + ";IEEE754Compatible=true";
static final private String ACCEPT_CASE_MULTIPART_MIXED = ContentType.MULTIPART_MIXED.toContentTypeString();
//@formatter:off (Eclipse formatter)
//CHECKSTYLE:OFF (Maven checkstyle)
String[][] casesServiceDocument = {
/* expected $format accept modified content types */
{ ACCEPT_CASE_MIN, null, null, null },
{ ACCEPT_CASE_MIN, "json", null, null },
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_JSONQ, null },
{ ACCEPT_CASE_NONE, ACCEPT_CASE_NONE, null, null },
{ "a/a", "a/a", null, "a/a" },
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_JSONQ, null },
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD1, null },
{ ACCEPT_CASE_MIN, null, ACCEPT_CASE_WILDCARD2, null },
{ ACCEPT_CASE_MIN, null, null, ACCEPT_CASE_MIN },
{ "a/a", "a/a", null, "a/a,b/b" },
{ "a/a;x=y", "a/a", ACCEPT_CASE_WILDCARD1, "a/a;x=y" },
{ "a/a;v=w;x=y", null, "a/a;x=y", "a/a;b=c,a/a;v=w;x=y" },
{ "a/a;v=w;x=y", "a/a;x=y", null, "a/a;b=c,a/a;v=w;x=y" },
{ ACCEPT_CASE_MIN, "json", ACCEPT_CASE_MIN, null },
{ ACCEPT_CASE_FULL, null, ACCEPT_CASE_FULL, ACCEPT_CASE_FULL },
{ ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null },
{ ACCEPT_CASE_MIN_IEEE754, null, ACCEPT_CASE_MIN_IEEE754, null },
{ ACCEPT_CASE_MIN_UTF8_IEEE754, null, ACCEPT_CASE_MIN_UTF8_IEEE754, null },
{ ACCEPT_CASE_MIN_IEEE754, ACCEPT_CASE_MIN_IEEE754, ACCEPT_CASE_MIN , null },
{ ACCEPT_CASE_XML, "xml", null, null },
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_XML, null },
{ ACCEPT_CASE_MIN_IEEE754_1, null, ACCEPT_CASE_MIN_IEEE754_1, null }
};
String[][] casesMetadata = {
/* expected $format accept modified content types */
{ ACCEPT_CASE_XML, null, null, null },
{ ACCEPT_CASE_XML, "xml", null, null },
{ ACCEPT_CASE_XML, "xml", ACCEPT_CASE_XML, null },
{ "a/a", "a/a", null, "a/a" },
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_XML, null },
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_WILDCARD1, null },
{ ACCEPT_CASE_XML, null, ACCEPT_CASE_WILDCARD2, null },
{ "a/a", "a/a", null, "a/a,b/b" },
{ "a/a;x=y", "a/a", ACCEPT_CASE_WILDCARD1, "a/a;x=y" },
{ ACCEPT_CASE_JSON, "json", ACCEPT_CASE_JSON_IEEE754, null },
{ ACCEPT_CASE_JSON, "json", ACCEPT_CASE_WILDCARD1, null },
{ ACCEPT_CASE_JSON, "application/json",ACCEPT_CASE_JSON_IEEE754, null },
{ ACCEPT_CASE_JSON_IEEE754,null, ACCEPT_CASE_JSON_IEEE754, null },
{ ACCEPT_CASE_JSON, null, ACCEPT_CASE_JSON, null }
};
String[][] casesMetadataFail = {
/* expected $format accept modified content types */
{ "Unsupported $format = json;IEEE754Compatible=true","json;IEEE754Compatible=true", null, null},
{ "Unsupported $format = json;charset=ISO-8859-1","json;charset=ISO-8859-1", null, null},
{ "Unsupported or illegal Accept header value: json;"
+ "charset=ISO-8859-1 != [application/xml, application/json]",null,
"json;charset=ISO-8859-1", null},
{ "Unsupported $format = application/json;charset=ISO-8859-1",
"application/json;charset=ISO-8859-1",null, null},
};
String[][] casesFail = {
/* expected $format accept modified content types */
{ null, "xxx/yyy", null, null },
{ null, "a/a", null, "b/b" },
{ null, "a/a;x=y", null, "a/a;v=w" },
{ null, null, "a/a;x=y", "a/a;v=w" },
{ null, null, "*", null },
{ null, "a/b;charset=ISO-8859-1", null, "a/b" },
{ null, null, "a/b;charset=ISO-8859-1", "a/b" },
{ null, null, null, "text/plain" },
{ null, "xxx", null, null },
{ null, null, ACCEPT_CASE_MIN_IEEE754_FAIL,null },
{ null, null, "application/json;charset=utf<8",null },
{ null, null, "application/json;charset=utf-8;q=<",null},
{ null, null, "application/json;charset=utf-8,application/json;q=1<",null},
{ null, null, "application/json;charset=utf-8,abc",null}
};
String[][] casesAcceptCharset = {
/* expected $format accept modified content types acceptCharset*/
{ ACCEPT_CASE_MIN_UTF8, null, null, null, "utf-8" },
{ ACCEPT_CASE_MIN_UTF8, "json", ACCEPT_CASE_MIN_UTF8, null, "utf-8" },
{ ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_ISO_8859_1, null, "utf-8" },
{ ACCEPT_CASE_MIN_UTF81, null, ACCEPT_CASE_ISO_8859_1, null, "utf-8" },
{ ACCEPT_CASE_MIN_UTF81, null, "application/json;charset=abc", null, "utf-8" },
{ ACCEPT_CASE_MIN_UTF8, null, "application/json;charset=utf-8", null, null },
{ ACCEPT_CASE_MIN_UTF8, null, "application/json;charset=utf8", null, null },
{ ACCEPT_CASE_MIN_UTF8, null, "application/json;charset=utf8;q=0.8", null, null }
};
String[][] casesAcceptCharsetFail = {
/* expected $format accept modified content types acceptCharset*/
{ null, null, null, null, "ISO-8859-1" },
{ null, "json", ACCEPT_CASE_MIN_UTF8, null, "abc" },
{ null, null, ACCEPT_CASE_ISO_8859_1, null, "utf<8" },
{ null, null, ACCEPT_CASE_MIN_UTF8, null, "utf-8;abc=xyz"},
{ null, null, ACCEPT_CASE_MIN_UTF8, null, "utf-8;q=1<" },
{ null, null, ACCEPT_CASE_MIN_UTF8, null, "utf-8;<" },
{ null, null, ACCEPT_CASE_ISO_8859_1, null, null },
{ null, null, "application/json;charset=abc", null, null },
{ null, null, "application/json;charset=utf-8;q", null, null },
{ null, null, "application/json;charset=utf-8;abc=xyz", null, null },
{ null, null, "application/json;charset=utf-8;q<", null, null },
{ null, null, "application/json;charset=utf<8", null, null },
{ null, "json;charset=abc",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "json;charset=utf<8",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "json;charset=utf-8;abc=xyz",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "json;charset=utf-8;q=1<",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "json;charset=utf-8;q='",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "application/json;abc=xyz",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "application/json;charset=utf<8",ACCEPT_CASE_MIN_UTF8, null, null },
{ null, "application/json;charset=abc",ACCEPT_CASE_MIN_UTF8, null, null }
};
//CHECKSTYLE:ON
//@formatter:on
@Test
public void serviceDocumentSingleCase() throws Exception {
testContentNegotiation(
new String[] { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_MIN_UTF8, null },
RepresentationType.SERVICE);
}
@Test
public void serviceDocument() throws Exception {
for (String[] useCase : casesServiceDocument) {
testContentNegotiation(useCase, RepresentationType.SERVICE);
}
}
@Test
public void metadataSingleCase() throws Exception {
testContentNegotiation(new String[] { ACCEPT_CASE_XML, null, null, null }, RepresentationType.METADATA);
}
@Test
public void metadataJson() throws Exception {
testContentNegotiation(new String[] { ACCEPT_CASE_JSON,
"application/json", null, null }, RepresentationType.METADATA);
}
@Test
public void metadata() throws Exception {
for (String[] useCase : casesMetadata) {
testContentNegotiation(useCase, RepresentationType.METADATA);
}
}
@Test
public void entityCollectionFail() throws Exception {
for (String[] useCase : casesFail) {
try {
testContentNegotiation(useCase, RepresentationType.COLLECTION_ENTITY);
fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!");
} catch (final AcceptHeaderContentNegotiatorException e) {
// Expected Exception
} catch (final ContentNegotiatorException e) {
// Expected Exception
} catch (final IllegalArgumentException e) {
// Expected Exception
}
}
}
@Test
public void metadataFail() throws Exception {
for (String[] useCase : casesMetadataFail) {
try {
testContentNegotiation(useCase, RepresentationType.METADATA);
fail("Unsupported $format = " + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!");
} catch (final ContentNegotiatorException e) {
// Expected Exception
}
}
}
@Test
public void checkSupport() throws Exception {
ContentNegotiator.checkSupport(ContentType.JSON, null, RepresentationType.ENTITY);
ContentNegotiator.checkSupport(ContentType.TEXT_PLAIN, null, RepresentationType.VALUE);
try {
ContentNegotiator.checkSupport(ContentType.APPLICATION_SVG_XML, null, RepresentationType.ENTITY);
fail("Exception expected.");
} catch (final ContentNegotiatorException e) {
assertEquals(ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPE, e.getMessageKey());
}
ContentNegotiator.checkSupport(ContentType.create("a/b"), createCustomContentTypeSupport("a/b"),
RepresentationType.ENTITY);
ContentNegotiator.checkSupport(ContentType.create(ContentType.create("a/b"), "c", "d"),
createCustomContentTypeSupport("a/b"),
RepresentationType.ENTITY);
try {
ContentNegotiator.checkSupport(ContentType.create("a/b"), createCustomContentTypeSupport("a/b;c=d"),
RepresentationType.ENTITY);
fail("Exception expected.");
} catch (final ContentNegotiatorException e) {
assertEquals(ContentNegotiatorException.MessageKeys.UNSUPPORTED_CONTENT_TYPE, e.getMessageKey());
}
}
private void testContentNegotiation(final String[] useCase, final RepresentationType representationType)
throws Exception {
FormatOption formatOption = null;
if (useCase[1] != null) {
formatOption = mock(FormatOption.class);
when(formatOption.getFormat()).thenReturn(useCase[1]);
}
ODataRequest request = new ODataRequest();
if (useCase[2] != null) {
request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2]));
}
if (useCase.length > 4) {
if (useCase[4] != null) {
request.addHeader(HttpHeader.ACCEPT_CHARSET, Arrays.asList(useCase[4]));
}
}
final CustomContentTypeSupport customContentTypeSupport = useCase[3] == null ? null :
createCustomContentTypeSupport(useCase[3]);
final ContentType requestedContentType = ContentNegotiator.doContentNegotiation(
formatOption, request, customContentTypeSupport, representationType);
assertNotNull(requestedContentType);
if (useCase[0] != null) {
assertEquals(ContentType.create(useCase[0]), requestedContentType);
}
}
private CustomContentTypeSupport createCustomContentTypeSupport(final String contentTypeString) {
final String[] contentTypes = contentTypeString.split(",");
List<ContentType> types = new ArrayList<ContentType>();
for (String contentType : contentTypes) {
types.add(ContentType.create(contentType));
}
CustomContentTypeSupport customContentTypeSupport = mock(CustomContentTypeSupport.class);
when(customContentTypeSupport.modifySupportedContentTypes(
anyListOf(ContentType.class), any(RepresentationType.class)))
.thenReturn(types);
return customContentTypeSupport;
}
@Test
public void testAcceptCharset() throws Exception {
for (String[] useCase : casesAcceptCharset) {
testContentNegotiation(useCase, RepresentationType.ENTITY);
}
}
@Test
public void testAcceptCharsetFail() throws Exception {
for (String[] useCase : casesAcceptCharsetFail) {
try {
testContentNegotiation(useCase, RepresentationType.ENTITY);
fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!");
} catch (final AcceptHeaderContentNegotiatorException e) {
// Expected Exception
} catch (final ContentNegotiatorException e) {
// Expected Exception
} catch (final IllegalArgumentException e) {
// Expected Exception
}
}
}
@Test
public void testSupportedTypes() throws ContentNegotiatorException, IllegalArgumentException {
assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"),
createCustomContentTypeSupport("a/b"), RepresentationType.ENTITY));
assertFalse(ContentNegotiator.isSupported(ContentType.create("a/b"),
createCustomContentTypeSupport("x/y"), RepresentationType.ENTITY));
assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"),
createCustomContentTypeSupport("a/b"), RepresentationType.BATCH));
assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"),
createCustomContentTypeSupport("a/b"), RepresentationType.BINARY));
}
@Test
public void checBatchkSupport() throws Exception {
testContentNegotiation(new String[] { ACCEPT_CASE_MULTIPART_MIXED, null, ACCEPT_CASE_MULTIPART_MIXED, null },
RepresentationType.BATCH);
}
}