package com.mixer.api.test.unit.http; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.mixer.api.MixerAPI; import com.mixer.api.http.MixerHttpClient; import com.mixer.api.resource.MixerUser; import com.mixer.api.resource.user.JSONWebToken; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; import org.apache.http.protocol.HttpContext; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.UnsupportedEncodingException; import static com.mixer.api.http.MixerHttpClient.CSRF_TOKEN_HEADER; import static com.mixer.api.http.MixerHttpClient.X_JWT_HEADER; import static org.mockito.Matchers.any; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; public class MixerHttpClientTest { private HttpClient defaultHttpClient; private MixerHttpClient mixerHttpClient; private MixerAPI mixerAPI; @Before public void setup() { } @Test public void itHandlesCSRF() { mixerAPI = new MixerAPI("clientId"); defaultHttpClient = mock(HttpClient.class); // This is done because Mockito is not able to mock methods that are called within the class' // Constructor. class MockableMixerHttpClient extends MixerHttpClient { private MockableMixerHttpClient(MixerAPI mixer) { super(mixer, "clientId"); } @Override protected HttpClient buildHttpClient() { return defaultHttpClient; } } mixerHttpClient = new MockableMixerHttpClient(mixerAPI); HttpResponse csrfResponse = prepareResponse(461, "{\"error\":\"Invalid or missing CSRF header, see details here: <https://dev.mixer.pro/rest.html#csrf>\",\"statusCode\":461}"); csrfResponse.addHeader(new BasicHeader(CSRF_TOKEN_HEADER, "abc123")); csrfResponse.setEntity(mock(HttpEntity.class)); HttpResponse okResponse = prepareResponse(200, "{\"id\":314,\"userId\":344,\"token\":\"Mixer\",\"online\":false,\"featured\":false,\"partnered\":false,\"transcodingProfileId\":1,\"suspended\":false,\"name\":\"Weekly Updates and more!\"}"); okResponse.addHeader(new BasicHeader(CSRF_TOKEN_HEADER, "abc123")); try { doReturn(csrfResponse) .doReturn(okResponse) .when(defaultHttpClient) .execute(any(HttpUriRequest.class), isNull(HttpContext.class)); } catch (IOException e) { Assert.fail(); } Futures.addCallback(mixerHttpClient.get("/channels/314", MixerUser.class, null), new FutureCallback<MixerUser>() { @Override public void onSuccess(MixerUser mixerUser) { Assert.assertEquals(mixerHttpClient.getCsrfToken(), "abc123"); } @Override public void onFailure(Throwable throwable) { Assert.fail(); } }); } private HttpResponse prepareResponse(int expectedResponseStatus, String expectedResponseBody) { HttpResponse response = new BasicHttpResponse(new BasicStatusLine( new ProtocolVersion("HTTP", 1, 1), expectedResponseStatus, "")); response.setStatusCode(expectedResponseStatus); try { response.setEntity(new StringEntity(expectedResponseBody)); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e); } return response; } @Test public void itHandlesJWT() { mixerAPI = new MixerAPI("clientId"); defaultHttpClient = mock(HttpClient.class); final String jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjU3NDk5NSwiYnR5cCI6ImdyYW50IiwiZ3JvdXBzIjpbeyJpZCI6MSwibmFtZSI6IlVzZXIiLCJVc2VyR3JvdXAiOnsiZ3JvdXBJZCI6MSwidXNlcklkIjo1NzQ5OTV9fV0sImlhdCI6MTQ4MTE0NDQ0NywiZXhwIjoxNTEyNjgwNDQ3LCJpc3MiOiJCZWFtIiwianRpIjoiVGhpcyBpc24ndCBhIHJlYWwgc2Vzc2lvbi4ifQ==.HkL5xq-eivwCk5OgczgIu5s_NFFxdQAKH9Jfb906aT4"; final JSONWebToken.Group jwtGroup = new JSONWebToken.Group(); jwtGroup.id = 1L; jwtGroup.name = "User"; final JSONWebToken jwt = new JSONWebToken(); jwt.exp = 1512680447L; jwt.iat = 1481144447L; jwt.iss = "Mixer"; jwt.jti = "This isn't a real session."; jwt.sub = 574995L; jwt.btyp = "grant"; jwt.groups = new JSONWebToken.Group[]{jwtGroup}; // This is done because Mockito is not able to mock methods that are called within the class' // Constructor. class MockableMixerHttpClient extends MixerHttpClient { private MockableMixerHttpClient(MixerAPI mixer) { super(mixer, "clientId"); } @Override protected HttpClient buildHttpClient() { return defaultHttpClient; } } mixerHttpClient = new MockableMixerHttpClient(mixerAPI); HttpResponse okResponse = prepareResponse(200, "{\"id\":314,\"userId\":344,\"token\":\"Mixer\",\"online\":false,\"featured\":false,\"partnered\":false,\"transcodingProfileId\":1,\"suspended\":false,\"name\":\"Weekly Updates and more!\"}"); okResponse.addHeader(new BasicHeader(X_JWT_HEADER, jwtString)); try { doReturn(okResponse).when(defaultHttpClient).execute(any(HttpUriRequest.class), isNull(HttpContext.class)); } catch (IOException e) { Assert.fail(); } Futures.addCallback(mixerHttpClient.get("/channels/314", MixerUser.class, null), new FutureCallback<MixerUser>() { @Override public void onSuccess(MixerUser mixerUser) { Assert.assertEquals(mixerHttpClient.getJWTString(), jwtString); Assert.assertEquals(mixerHttpClient.getJwt(), jwt); } @Override public void onFailure(Throwable throwable) { Assert.fail(); } }); } }