package com.tinify;
import com.google.gson.Gson;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
import mockit.*;
import org.apache.commons.codec.binary.Base64;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.lang.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class ClientTest {
Client subject;
MockWebServer server;
String key = "key";
@Before
public void setup() throws IOException {
Logger.getLogger(MockWebServer.class.getName()).setLevel(Level.WARNING);
server = new MockWebServer();
server.start();
subject = new Client(key);
new MockUp<HttpUrl>()
{
@Mock
@SuppressWarnings("unused")
HttpUrl parse(String input)
{
return new HttpUrl.Builder()
.scheme("http")
.host(server.getHostName())
.port(server.getPort())
.encodedPath("/shrink")
.build();
}
};
}
@After
public void tearDown() throws IOException {
server.shutdown();
}
public void enqueuShrink() {
server.enqueue(new MockResponse()
.setResponseCode(201)
.addHeader("Location", "https://api.tinify.com/foo.png")
.addHeader("Compression-Count", 12));
}
@Test
public void requestWhenValidShouldIssueRequest() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink");
String credentials = new String(Base64.encodeBase64(("api:" + key).getBytes()));
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals("Basic " + credentials, request.getHeader("Authorization"));
}
@Test
public void requestWhenValidShouldIssueRequestToEndpoint() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink");
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals("/shrink", request.getPath());
}
@Test
public void requestWhenValidShouldIssueRequestWithMethod() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink");
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals("POST", request.getMethod());
}
@Test
public void requestWhenValidShouldReturnResponse() throws Exception, InterruptedException, IOException, URISyntaxException {
enqueuShrink();
byte[] body = Files.readAllBytes(
Paths.get(getClass().getResource("/voormedia.png").toURI()));
assertEquals("https://api.tinify.com/foo.png",
subject.request(Client.Method.POST, "/shrink", body).header("Location"));
}
@Test
public void requestWhenValidShouldIssueRequestWithoutBodyWhenOptionsAreEmpty() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.GET, "/shrink", new Options());
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals(0, request.getBody().size());
}
@Test
public void requestWhenValidShouldIssueRequestWithoutContentTypeWhenOptionsAreEmpty() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.GET, "/shrink", new Options());
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals(null, request.getHeader("Content-Type"));
}
@Test
public void requestWhenValidShouldIssueRequestWithJSONBody() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink", new Options().with("hello", "world"));
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
Gson gson = new Gson();
assertEquals("world", gson.fromJson(request.getBody().readUtf8(), HashMap.class).get("hello"));
assertEquals("application/json; charset=utf-8", request.getHeader("Content-Type"));
}
@Test
public void requestWhenValidShouldIssueRequestWithUserAgent() throws Exception, InterruptedException {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink", new byte[]{});
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals(Client.USER_AGENT, request.getHeader("User-Agent"));
}
@Test
public void requestWhenValidShouldUpdateCompressionCount() throws Exception {
enqueuShrink();
subject.request(Client.Method.POST, "/shrink", new byte[]{});
assertEquals(12, Tinify.compressionCount());
}
@Test
public void requestWhenValidWithAppIdShouldIssueRequestWithUserAgent() throws Exception, InterruptedException {
enqueuShrink();
Client client = new Client(key, "TestApp/0.1");
client.request(Client.Method.POST, "/shrink");
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals(Client.USER_AGENT + " TestApp/0.1", request.getHeader("User-Agent"));
}
@Test
public void requestWhenValidWithProxyShouldIssueRequestWithProxyAuthorization() throws Exception, InterruptedException {
server.enqueue(new MockResponse().setResponseCode(407));
enqueuShrink();
Client client = new Client(key, null, "http://user:pass@" + server.getHostName() + ":" + server.getPort());
client.request(Client.Method.POST, "/shrink");
RecordedRequest request1 = server.takeRequest(5, TimeUnit.SECONDS);
RecordedRequest request2 = server.takeRequest(5, TimeUnit.SECONDS);
assertEquals("Basic dXNlcjpwYXNz", request2.getHeader("Proxy-Authorization"));
}
@Test
public void requestWithTimeoutOnceShouldReturnResponse() throws Exception {
new MockUp<Call>() {
int count = 1;
@Mock
public Response execute(Invocation inv) throws IOException {
if (count == 0) {
return inv.proceed();
} else {
count--;
throw new java.net.SocketTimeoutException("SocketTimeoutException");
}
}
};
server.enqueue(new MockResponse()
.setResponseCode(201)
.setBody(""));
assertEquals(201, new Client(key).request(Client.Method.POST, "/shrink").code());
}
@Test(expected = ConnectionException.class)
public void requestWithTimeoutRepeatedlyShouldThrowConnectionException() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new java.net.SocketTimeoutException("SocketTimeoutException");
}
};
new Client(key).request(Client.Method.POST, "http://shrink");
}
@Test
public void requestWithTimeoutRepeatedlyShouldThrowExceptionWithMessage() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new java.net.SocketTimeoutException("SocketTimeoutException");
}
};
try {
new Client(key).request(Client.Method.POST, "http://shrink");
fail("Expected an Exception to be thrown");
} catch (ConnectionException e) {
assertEquals("Error while connecting: SocketTimeoutException", e.getMessage());
}
}
@Test
public void requestWithSocketErrorOnceShouldReturnResponse() throws Exception {
new MockUp<Call>() {
int count = 1;
@Mock
public Response execute(Invocation inv) throws IOException {
if (count == 0) {
return inv.proceed();
} else {
count--;
throw new java.net.UnknownHostException("UnknownHostException");
}
}
};
server.enqueue(new MockResponse()
.setResponseCode(201)
.setBody(""));
assertEquals(201, new Client(key).request(Client.Method.POST, "/shrink").code());
}
@Test(expected = ConnectionException.class)
public void requestWithSocketErrorRepeatedlyShouldThrowConnectionException() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new java.net.UnknownHostException("UnknownHostException");
}
};
new Client(key).request(Client.Method.POST, "http://shrink");
}
@Test
public void requestWithSocketErrorRepeatedlyShouldThrowExceptionWithMessage() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new java.net.UnknownHostException("UnknownHostException");
}
};
try {
new Client(key).request(Client.Method.POST, "http://shrink");
fail("Expected an Exception to be thrown");
} catch (ConnectionException e) {
assertEquals("Error while connecting: UnknownHostException", e.getMessage());
}
}
@Test
public void requestWithUnexpectedExceptionOnceShouldReturnResponse() throws Exception {
new MockUp<Call>() {
int count = 1;
@Mock
public Response execute(Invocation inv) throws IOException {
if (count == 0) {
return inv.proceed();
} else {
count--;
throw new RuntimeException("Some exception");
}
}
};
server.enqueue(new MockResponse()
.setResponseCode(201)
.setBody(""));
assertEquals(201, new Client(key).request(Client.Method.POST, "/shrink").code());
}
@Test(expected = ConnectionException.class)
public void requestWithUnexpectedExceptionRepeatedlyShouldThrowConnectionException() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new RuntimeException("Some exception");
}
};
new Client(key).request(Client.Method.POST, "http://shrink");
}
@Test
public void requestWithUnexpectedExceptionRepeatedlyShouldThrowExceptionWithMessage() throws Exception {
new MockUp<Call>() {
@Mock
public Response execute(Invocation inv) throws IOException {
throw new RuntimeException("Some exception");
}
};
try {
new Client(key).request(Client.Method.POST, "http://shrink");
fail("Expected an Exception to be thrown");
} catch (ConnectionException e) {
assertEquals("Error while connecting: Some exception", e.getMessage());
}
}
@Test
public void requestWithServerErrorOnceShouldReturnResponse() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(584)
.setBody("{'error':'InternalServerError','message':'Oops!'}"));
server.enqueue(new MockResponse()
.setResponseCode(201)
.setBody(""));
assertEquals(201, new Client(key).request(Client.Method.POST, "/shrink").code());
}
@Test(expected = ServerException.class)
public void requestWithServerErrorRepeatedlyShouldThrowServerException() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(584)
.setBody("{'error':'InternalServerError','message':'Oops!'}"));
server.enqueue(new MockResponse()
.setResponseCode(584)
.setBody("{'error':'InternalServerError','message':'Oops!'}"));
new Client(key).request(Client.Method.POST, "/shrink");
}
@Test
public void requestWithServerErrorRepeatedlyShouldThrowExceptionWithMessage() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(584)
.setBody("{'error':'InternalServerError','message':'Oops!'}"));
server.enqueue(new MockResponse()
.setResponseCode(584)
.setBody("{'error':'InternalServerError','message':'Oops!'}"));
try {
new Client(key).request(Client.Method.POST, "/shrink");
fail("Expected an Exception to be thrown");
} catch (Exception e) {
assertEquals("Oops! (HTTP 584/InternalServerError)", e.getMessage());
}
}
@Test
public void requestWithBadServerResponseOnceShouldReturnResponse() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody("<!-- this is not json -->"));
server.enqueue(new MockResponse()
.setResponseCode(201)
.setBody(""));
assertEquals(201, new Client(key).request(Client.Method.POST, "/shrink").code());
}
@Test(expected = ServerException.class)
public void requestWithBadServerResponseRepeatedlyShouldThrowServerException() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody("<!-- this is not json -->"));
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody("<!-- this is not json -->"));
new Client(key).request(Client.Method.POST, "/shrink");
}
@Test
public void requestWithBlankServerResponseRepeatedlyShouldThrowServerException() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody(""));
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody(""));
try {
new Client(key).request(Client.Method.POST, "/shrink");
fail("Expected an Exception to be thrown");
} catch (Exception e) {
assertEquals("Error while parsing response: received empty body (HTTP 543/ParseError)", e.getMessage());
}
}
@Test
public void requestWithBadServerResponseRepeatedlyShouldThrowExceptionWithMessage() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody("<!-- this is not json -->"));
server.enqueue(new MockResponse()
.setResponseCode(543)
.setBody("<!-- this is not json -->"));
try {
new Client(key).request(Client.Method.POST, "/shrink");
fail("Expected an Exception to be thrown");
} catch (Exception e) {
assertEquals("Error while parsing response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ (HTTP 543/ParseError)", e.getMessage());
}
}
@Test(expected = ClientException.class)
public void requestWithClientErrorShouldThrowClientException() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(492)
.setBody("{'error':'BadRequest','message':'Oops!'}"));
new Client(key).request(Client.Method.POST, "/shrink");
}
@Test
public void requestWithClientErrorShouldThrowExceptionWithMessage() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(492)
.setBody("{'error':'BadRequest','message':'Oops!'}"));
try {
new Client(key).request(Client.Method.POST, "/shrink");
fail("Expected an Exception to be thrown");
} catch (Exception e) {
assertEquals("Oops! (HTTP 492/BadRequest)", e.getMessage());
}
}
@Test(expected = AccountException.class)
public void requestWithBadCredentialsShouldThrowAccountException() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(401)
.setBody("{'error':'Unauthorized','message':'Oops!'}"));
new Client(key).request(Client.Method.POST, "/shrink");
}
@Test
public void requestWithBadCredentialsShouldThrowExceptionWithMessage() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(401)
.setBody("{'error':'Unauthorized','message':'Oops!'}"));
try {
new Client(key).request(Client.Method.POST, "/shrink");
fail("Expected an Exception to be thrown");
} catch (Exception e) {
assertEquals("Oops! (HTTP 401/Unauthorized)", e.getMessage());
}
}
}