package com.temenos.interaction.core.rim; /* * #%L * interaction-core * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.apache.commons.lang.StringUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.temenos.interaction.core.MultivaluedMapImpl; import com.temenos.interaction.core.web.RequestContext; @RunWith(PowerMockRunner.class) @PrepareForTest(HeaderHelper.class) public class TestHeaderHelper { @Before public void setup() { // initialise the thread local request context with requestUri and baseUri RequestContext ctx = new RequestContext("http://localhost/myservice.svc", "/baseuri/", null); RequestContext.setRequestContext(ctx); } @Test public void testOptionsAllowHeader() { SortedSet<String> validNextStates = new TreeSet<String>(); validNextStates.add("SEE"); validNextStates.add("HISTORY"); validNextStates.add("AUTHORISE"); validNextStates.add("REVERSE"); validNextStates.add("DELETE"); validNextStates.add("INPUT"); validNextStates.add("GET"); validNextStates.add("HEAD"); validNextStates.add("OPTIONS"); Response r = HeaderHelper.allowHeader(Response.ok(), validNextStates).build(); assertEquals("AUTHORISE, DELETE, GET, HEAD, HISTORY, INPUT, OPTIONS, REVERSE, SEE", r.getMetadata().getFirst("Allow")); } @Test public void testOptionsNoAllowHeader() { Response r = HeaderHelper.allowHeader(Response.ok(), null).build(); assertNull(r.getMetadata().getFirst("Allow")); } @Test public void testOptionsNoValidStates() { Response r = HeaderHelper.allowHeader(Response.ok(), new HashSet<String>()).build(); assertEquals("", r.getMetadata().getFirst("Allow")); } @Test public void testLocation() { Response r = HeaderHelper.locationHeader(Response.ok(), "/path").build(); assertEquals("/path", r.getMetadata().getFirst("Location")); } @Test public void testLocationNull() { Response r = HeaderHelper.locationHeader(Response.ok(), null).build(); assertNull(r.getMetadata().getFirst("Location")); } @Test public void testLocationWithQueryParam(){ MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("transactionId", "101"); Response r = HeaderHelper.locationHeader(Response.ok(), "/path?customerName=Jack", values).build(); assertThat((String)r.getMetadata().getFirst("Location"), containsString("?customerName=Jack&transactionId=101")); } @Test public void testLocationWithoutQueryParam(){ MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("transactionId", "101"); Response r = HeaderHelper.locationHeader(Response.ok(), "/path", values).build(); assertThat((String)r.getMetadata().getFirst("Location"), containsString("?transactionId=101")); } @Test public void testEtag() { Response r = HeaderHelper.etagHeader(Response.ok(), "ABCDEFG").build(); assertEquals("ABCDEFG", r.getMetadata().getFirst(HttpHeaders.ETAG)); } @Test public void testEtagNull() { Response r = HeaderHelper.etagHeader(Response.ok(), null).build(); assertNull(r.getMetadata().getFirst(HttpHeaders.ETAG)); } @Test public void testEtagNotSpecified() { Response r = Response.ok().build(); assertNull(r.getMetadata().getFirst(HttpHeaders.ETAG)); } @Test public void testEtagEmpty() { Response r = HeaderHelper.etagHeader(Response.ok(), "").build(); assertNull(r.getMetadata().getFirst(HttpHeaders.ETAG)); } @Test public void testEncodeQueryParameters(){ MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("customerName", "Jack"); values.add("customerName", "Jill"); values.add("transaction", "101"); String queryParam = HeaderHelper.encodeMultivalueQueryParameters(values); assertThat(queryParam, allOf( containsString("customerName=Jack"), containsString("customerName=Jill"), containsString("transaction=101") )); assertThat(StringUtils.countMatches(queryParam, "&"), equalTo(2)); assertThat(StringUtils.countMatches(queryParam, "?"), equalTo(0)); } @Test public void testEncodeQueryParametersDropsDuplicateKeyValues(){ MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("customerName", "Jack"); values.add("customerName", "Jack"); values.add("transaction", "101"); values.add("transaction", "102"); String queryParam = HeaderHelper.encodeMultivalueQueryParameters(values); assertThat(queryParam, allOf( containsString("customerName=Jack"), containsString("transaction=101"), containsString("transaction=102") )); assertThat(StringUtils.countMatches(queryParam, "customerName=Jack"), equalTo(1)); assertThat(StringUtils.countMatches(queryParam, "&"), equalTo(2)); } @Test public void testEncodeQueryParametersWithHttpEntities(){ MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("customerNam=", "J&ck"); values.add("trans&ction", "!0!"); String queryParam = HeaderHelper.encodeMultivalueQueryParameters(values); assertThat(queryParam, allOf( containsString("customerNam%3D=J%26ck"), containsString("trans%26ction=%210%21") )); assertThat(StringUtils.countMatches(queryParam, "&"), equalTo(1)); } @Test public void testEncodeQueryParametersWithoutAnyQueryParams(){ assertThat( HeaderHelper.encodeMultivalueQueryParameters( new MultivaluedMapImpl<String>() ), equalTo("") ); } @Test(expected = RuntimeException.class) public void testEncodeQueryParametersURLEncoderThrowsException() throws Exception { PowerMockito.spy(HeaderHelper.class); PowerMockito.doThrow(new UnsupportedEncodingException()).when( HeaderHelper.class, "encodeQueryParameter", anyString() ); MultivaluedMap<String, String> values = new MultivaluedMapImpl<String>(); values.add("customerNam=", "J&ck"); values.add("customerName", "Jack"); values.add("customerName", "Jill"); values.add("trans&ction", "!0!"); HeaderHelper.encodeMultivalueQueryParameters(values); } @Test public void testEncodeQueryParametersWithMultivaluedMapContainingEmptyLists(){ HashMap<String, List<String>> values = new MultivaluedMapImpl<String>(); values.put("customerName", new ArrayList<String>()); values.put("transaction", new ArrayList<String>()); values.put("item", new ArrayList<String>(Arrays.asList(new String[]{"apple"}))); values.put("money", new ArrayList<String>()); MultivaluedMap<String, String> reinterpretedMap = new MultivaluedMapImpl<String>(); reinterpretedMap.putAll(values); String queryParam = HeaderHelper.encodeMultivalueQueryParameters(reinterpretedMap); assertThat(queryParam, containsString("item=apple")); assertThat(StringUtils.countMatches(queryParam, "&"), equalTo(3)); } @Test public void testGetFirstHeader() { HttpHeaders httpHeadersMock = Mockito.mock(HttpHeaders.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { String header = (String)invocationOnMock.getArguments()[0]; return buildHeaders().get(header); } }).when(httpHeadersMock).getRequestHeader(anyString()); assertNull(HeaderHelper.getFirstHeader(null, null)); assertEquals("value0", HeaderHelper.getFirstHeader(httpHeadersMock, "header0")); assertNull(HeaderHelper.getFirstHeader(httpHeadersMock, "HEADER0")); assertNull(HeaderHelper.getFirstHeader(httpHeadersMock, "Header0")); } @Test public void testGetFirstHeaderCaseInsensitive() { HttpHeaders httpHeadersMock = Mockito.mock(HttpHeaders.class); when(httpHeadersMock.getRequestHeaders()).thenReturn(buildHeaders()); assertNull(HeaderHelper.getFirstHeaderCaseInsensitive(null, null)); assertEquals("value0", HeaderHelper.getFirstHeaderCaseInsensitive(httpHeadersMock, "header0")); assertEquals("value0", HeaderHelper.getFirstHeaderCaseInsensitive(httpHeadersMock, "HEADER0")); assertEquals("value0", HeaderHelper.getFirstHeaderCaseInsensitive(httpHeadersMock, "Header0")); } private MultivaluedMap<String,String> buildHeaders() { MultivaluedMap<String, String> headers = new MultivaluedMapImpl<>(); List<String> values = new ArrayList<>(); values.add("value0"); values.add("value1"); values.add("value2"); headers.put("header0", values); return headers; } }