Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use getRawStatusCode to avoid getting an exception on fake status code 999, convert it to 406 Not Acceptable #3497

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add test with unsupported HttpStatus code
sebr72 authored and sbrunner committed Nov 4, 2024

Verified

This commit was signed with the committer’s verified signature.
bdraco J. Nick Koston
commit acc16dcec9b96de0597fd440890b3b66db41ac4d
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ public FeaturesParser(
}

@VisibleForTesting
static final CoordinateReferenceSystem parseCoordinateReferenceSystem(
static CoordinateReferenceSystem parseCoordinateReferenceSystem(
final MfClientHttpRequestFactory requestFactory,
final JSONObject geojson,
final boolean forceLongitudeFirst) {
@@ -108,7 +108,8 @@ static final CoordinateReferenceSystem parseCoordinateReferenceSystem(
}
}
} else {
LOGGER.warn("Unable to load linked CRS from geojson: \n{}", crsJson);
LOGGER.warn(
"Unsupported link type {} in linked CRS from geojson: \n{}", linkType, crsJson);
}
} else {
code.append(getProperty(crsJson, "code"));
@@ -147,7 +148,6 @@ private static String getProperty(final JSONObject crsJson, final String nameCod
* @param template the template
* @param features what to parse
* @return the feature collection
* @throws IOException
*/
public final SimpleFeatureCollection autoTreat(final Template template, final String features)
throws IOException {
@@ -189,7 +189,6 @@ public final SimpleFeatureCollection treatStringAsURL(
*
* @param geoJsonString what to parse
* @return the feature collection
* @throws IOException
*/
public final SimpleFeatureCollection treatStringAsGeoJson(final String geoJsonString)
throws IOException {
@@ -231,7 +230,7 @@ private SimpleFeatureType createFeatureType(@Nonnull final String geojsonData) {
builder.setName("GeosjonFeatureType");
final JSONArray features = geojson.getJSONArray("features");

if (features.length() == 0) {
if (features.isEmpty()) {
// do not try to build the feature type if there are no features
return null;
}
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ public static Optional<Style> loadStyleAsURI(
final ClientHttpRequestFactory clientHttpRequestFactory,
final String styleRef,
final Function<byte[], @Nullable Optional<Style>> loadFunction) {
int statusCode;
int httpStatusCode;
final byte[] input;

URI uri;
@@ -47,13 +47,13 @@ public static Optional<Style> loadStyleAsURI(
try {
final ClientHttpRequest request = clientHttpRequestFactory.createRequest(uri, HttpMethod.GET);
try (ClientHttpResponse response = request.execute()) {
statusCode = response.getRawStatusCode();
httpStatusCode = response.getRawStatusCode();
input = IOUtils.toByteArray(response.getBody());
}
} catch (Exception e) {
return Optional.empty();
}
if (statusCode == HttpStatus.OK.value()) {
if (httpStatusCode == HttpStatus.OK.value()) {
return loadFunction.apply(input);
} else {
return Optional.empty();
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.mapfish.print.processor.jasper;

import com.google.common.annotations.VisibleForTesting;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
@@ -29,7 +30,7 @@ public final class HttpImageResolver implements TableColumnConverter<BufferedIma
private static final int IMAGE_SIZE = 48;
private Pattern urlExtractor = Pattern.compile("(.*)");
private int urlGroup = 1;
private final BufferedImage defaultImage;
@VisibleForTesting final BufferedImage defaultImage;

/** Constructor. */
public HttpImageResolver() {
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.annotations.VisibleForTesting;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
@@ -161,7 +162,14 @@ private void createTasks(
if (icons != null && icons.length > 0) {
for (URL icon : icons) {
tasks.add(
new IconTask(icon, dpi, context, level, tempTaskDirectory, clientHttpRequestFactory));
new IconTask(
icon,
dpi,
context,
level,
tempTaskDirectory,
clientHttpRequestFactory,
metricRegistry));
}
}
if (legendAttributes.classes != null) {
@@ -269,7 +277,8 @@ protected void extraValidation(
// no checks needed
}

private synchronized BufferedImage getMissingImage() {
@VisibleForTesting
synchronized BufferedImage getMissingImage() {
if (this.missingImage == null) {
this.missingImage =
new BufferedImage(
@@ -311,7 +320,7 @@ public static final class Output {
/** The datasource for the legend object in the report. */
public final JRTableModelDataSource legendDataSource;

/** The path to the compiled subreport. */
/** The path to the compiled sub-report. */
public final String legendSubReport;

/** The number of rows in the legend. */
@@ -343,28 +352,33 @@ public Object[] call() {
}
}

private final class IconTask implements Callable<Object[]> {
@VisibleForTesting
final class IconTask implements Callable<Object[]> {

private final URL icon;
private final double iconDPI;
private final ExecutionContext context;
private final MfClientHttpRequestFactory clientHttpRequestFactory;
private final int level;
private final File tempTaskDirectory;
private final MetricRegistry metricRegistry;

private IconTask(
@VisibleForTesting
IconTask(
final URL icon,
final double iconDPI,
final ExecutionContext context,
final int level,
final File tempTaskDirectory,
final MfClientHttpRequestFactory clientHttpRequestFactory) {
final MfClientHttpRequestFactory clientHttpRequestFactory,
final MetricRegistry metricRegistry) {
this.icon = icon;
this.iconDPI = iconDPI;
this.context = context;
this.level = level;
this.clientHttpRequestFactory = clientHttpRequestFactory;
this.tempTaskDirectory = tempTaskDirectory;
this.metricRegistry = metricRegistry;
}

@Override
@@ -389,8 +403,7 @@ private BufferedImage loadImage(final URI uri) {
this.context.stopIfCanceled();
final ClientHttpRequest request =
this.clientHttpRequestFactory.createRequest(uri, HttpMethod.GET);
try (Timer.Context ignored =
LegendProcessor.this.metricRegistry.timer(metricName).time()) {
try (Timer.Context ignored = metricRegistry.timer(metricName).time()) {
try (ClientHttpResponse httpResponse = request.execute()) {
if (httpResponse.getRawStatusCode() == HttpStatus.OK.value()) {
image = ImageIO.read(httpResponse.getBody());
@@ -409,7 +422,7 @@ private BufferedImage loadImage(final URI uri) {

if (image == null) {
image = getMissingImage();
LegendProcessor.this.metricRegistry.counter(metricName + ".error").inc();
metricRegistry.counter(metricName + ".error").inc();
}

return image;
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.File;
@@ -36,6 +37,10 @@ public class FeaturesParserTest extends AbstractMapfishSpringTest {

private static final String EXAMPLE_GEOJSONFILE =
"geojson/geojson-inconsistent-attributes-2.json";
private static final String WKT =
"GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\""
+ ",SPHEROID[\"WGS_1984\",6378137,298.257223563]]"
+ ",PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]]";
@Autowired private TestHttpClientFactory requestFactory;
@Autowired private ConfigurationFactory configurationFactory;

@@ -131,19 +136,32 @@ public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
@Test
@DirtiesContext
public void testParseCRSLinkEsriWkt() {
final MockClientHttpResponse clientHttpResponse =
new MockClientHttpResponse(WKT.getBytes(Constants.DEFAULT_CHARSET), HttpStatus.OK);

CoordinateReferenceSystem crs = parseCoordinateReferenceSystemFromResponse(clientHttpResponse);
assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
}

@Test
@DirtiesContext
public void testParseCRSLinkEsriWktWithUnsupportedErrorCode() {
final MockClientHttpResponse clientHttpResponse =
new MockClientHttpResponse(WKT.getBytes(Constants.DEFAULT_CHARSET), 999);

CoordinateReferenceSystem crs = parseCoordinateReferenceSystemFromResponse(clientHttpResponse);
assertSame(DefaultEngineeringCRS.GENERIC_2D, crs);
}

private CoordinateReferenceSystem parseCoordinateReferenceSystemFromResponse(
final MockClientHttpResponse clientHttpResponse) {
requestFactory.registerHandler(
input -> input != null && input.getHost().equals("spatialreference.org"),
new TestHttpClientFactory.Handler() {
@Override
public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
String wkt =
"GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298"
+ ".257223563]],"
+ "PRIMEM[\"Greenwich\","
+ "0],UNIT[\"Degree\",0.017453292519943295]]";
MockClientHttpRequest mockClientHttpRequest = new MockClientHttpRequest();
mockClientHttpRequest.setResponse(
new MockClientHttpResponse(wkt.getBytes(Constants.DEFAULT_CHARSET), HttpStatus.OK));
mockClientHttpRequest.setResponse(clientHttpResponse);
return mockClientHttpRequest;
}
});
@@ -161,10 +179,11 @@ public MockClientHttpRequest handleRequest(URI uri, HttpMethod httpMethod) {
CoordinateReferenceSystem crs =
FeaturesParser.parseCoordinateReferenceSystem(this.requestFactory, crsJSON, false);
assertNotNull(crs);
assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
return crs;
}

public void testParseCRSLinkProj4() {
@Test
public void testUnsupportedLinkTypeProj4() {
JSONObject crsJSON =
new JSONObject(
"{\"crs\":{\n"
@@ -179,7 +198,7 @@ public void testParseCRSLinkProj4() {
CoordinateReferenceSystem crs =
FeaturesParser.parseCoordinateReferenceSystem(this.requestFactory, crsJSON, false);
assertNotNull(crs);
assertNotSame(DefaultEngineeringCRS.GENERIC_2D, crs);
assertSame(DefaultEngineeringCRS.GENERIC_2D, crs);
}

@Test
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.mapfish.print.map.image;

import static org.junit.Assert.assertTrue;

import com.codahale.metrics.MetricRegistry;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import org.mapfish.print.Constants;
import org.mapfish.print.attribute.map.MapfishMapContext;
import org.mapfish.print.http.MfClientHttpRequestFactory;
import org.mapfish.print.map.AbstractLayerParams;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpResponse;

public class AbstractSingleImageLayerTest {

@Test
public void testFetchImage() throws IOException {
MapfishMapContext mapContext = new MapfishMapContext(null, null, 100, 100, false, true);
MockClientHttpRequest mockClientHttpRequest = new MockClientHttpRequest();
final int unsupportedStatusCode = 999;
mockClientHttpRequest.setResponse(
new MockClientHttpResponse(
"SomeResponse".getBytes(Constants.DEFAULT_CHARSET), unsupportedStatusCode));
AbstractLayerParams layerParams = new AbstractLayerParams();
layerParams.failOnError = true;
AbstractSingleImageLayer layer = new AbstractSingleImageLayerTestImpl(layerParams);

try {
layer.fetchImage(mockClientHttpRequest, mapContext);
Assert.fail("Did not throw exception with unsupported status code");
} catch (RuntimeException e) {
assertTrue(e.getMessage().contains(String.valueOf(unsupportedStatusCode)));
}
}

private static class AbstractSingleImageLayerTestImpl extends AbstractSingleImageLayer {

public AbstractSingleImageLayerTestImpl(AbstractLayerParams layerParams) {
super(null, null, layerParams, new MetricRegistry(), null);
}

@Override
protected BufferedImage loadImage(
MfClientHttpRequestFactory requestFactory, MapfishMapContext transformer) {
return null;
}

@Override
public RenderType getRenderType() {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.mapfish.print.map.style;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.function.Function;
import org.geotools.api.style.Style;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;

public class ParserPluginUtilsTest {

@Test
public void loadStyleAsURITest_ValidURI() throws IOException, URISyntaxException {
int httpStatusCode = HttpStatus.OK.value();
Style style = mock(Style.class);
Optional<Style> actualStyle = testLoadingStyleWithStatusCode(style, httpStatusCode);

assertTrue(actualStyle.isPresent());
assertEquals(style, actualStyle.get());
}

private static Optional<Style> testLoadingStyleWithStatusCode(
final Style style, final int httpStatusCode) throws IOException, URISyntaxException {
ClientHttpRequestFactory factory = mock(ClientHttpRequestFactory.class);
ClientHttpRequest request = mock(ClientHttpRequest.class);
ClientHttpResponse response = mock(ClientHttpResponse.class);

Function<byte[], Optional<Style>> function = bytes -> Optional.of(style);

when(factory.createRequest(new URI("http://valid.uri"), HttpMethod.GET)).thenReturn(request);
when(request.execute()).thenReturn(response);
when(response.getRawStatusCode()).thenReturn(httpStatusCode);
when(response.getBody())
.thenReturn(
new ByteArrayInputStream(
"This is dummy style data".getBytes(Charset.defaultCharset())));

return ParserPluginUtils.loadStyleAsURI(factory, "http://valid.uri", function);
}

@Test
public void loadStyleAsURITest_InvalidStatusCode() throws IOException, URISyntaxException {
int httpStatusCode = 999;
Style style = mock(Style.class);
Optional<Style> actualStyle = testLoadingStyleWithStatusCode(style, httpStatusCode);

assertFalse(actualStyle.isPresent());
}

@Test
public void loadStyleAsURITest_InValidURI() {
ClientHttpRequestFactory factory = mock(ClientHttpRequestFactory.class);
Style style = mock(Style.class);

Function<byte[], Optional<Style>> function = bytes -> Optional.of(style);

Optional<Style> actualStyle =
ParserPluginUtils.loadStyleAsURI(factory, "invalid|uri", function);
assertTrue(actualStyle.isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.mapfish.print.map.tiled;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.codahale.metrics.MetricRegistry;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import org.junit.Test;
import org.mapfish.print.PrintException;
import org.mapfish.print.processor.AbstractProcessor;
import org.mapfish.print.processor.Processor;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;

public class SingleTileLoaderTaskTest {

@Test
public void testCompute() throws IOException, URISyntaxException {
final int unsupportedStatusCode = 200;
ClientHttpRequest tileRequest = mock(ClientHttpRequest.class);
CoverageTask.SingleTileLoaderTask task = prepareTest(unsupportedStatusCode, tileRequest);

CoverageTask.Tile singleTile = task.compute();
assertNotNull(singleTile);
verify(tileRequest, times(3)).getURI();
}

private CoverageTask.SingleTileLoaderTask prepareTest(
int unsupportedStatusCode, ClientHttpRequest tileRequest)
throws IOException, URISyntaxException {
BufferedImage errorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Processor.ExecutionContext context = new AbstractProcessor.Context(new HashMap<>());
MetricRegistry registry = new MetricRegistry();

// mocking ClientHttpRequest
ClientHttpResponse clientHttpResponse = getMockResponse(unsupportedStatusCode);
when(tileRequest.execute()).thenReturn(clientHttpResponse);
when(tileRequest.getURI()).thenReturn(new URI("http://localhost"));

return new CoverageTask.SingleTileLoaderTask(
tileRequest, errorImage, 0, 0, true, registry, context);
}

@Test
public void testComputeWithUnsupportedStatusCode() throws IOException, URISyntaxException {
final int unsupportedStatusCode = 999;
ClientHttpRequest tileRequest = mock(ClientHttpRequest.class);
CoverageTask.SingleTileLoaderTask task = prepareTest(unsupportedStatusCode, tileRequest);

try {
task.compute();
fail("Did not throw exception with unsupported status code");
} catch (PrintException e) {
assertTrue(e.getCause().getMessage().contains(String.valueOf(unsupportedStatusCode)));
verify(tileRequest, times(4)).getURI();
}
}

private ClientHttpResponse getMockResponse(int rawStatusCode) throws IOException {
ClientHttpResponse response = mock(ClientHttpResponse.class);
when(response.getRawStatusCode()).thenReturn(rawStatusCode);
when(response.getStatusCode())
.thenThrow(new RuntimeException("Unsupported status code has no HttpStatus"));
when(response.getBody()).thenReturn(new ByteArrayInputStream(new byte[0]));
when(response.getHeaders()).thenReturn(new org.springframework.http.HttpHeaders());
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package org.mapfish.print.processor.jasper;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import org.junit.Test;
import org.mapfish.print.AbstractMapfishSpringTest;
import org.mapfish.print.http.MfClientHttpRequestFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpResponse;

public class HttpImageResolverTest {
@Test
public void testResolve() throws IOException {
int statusCode = HttpStatus.OK.value();
HttpImageResolver httpImageResolver = new HttpImageResolver();

BufferedImage result = resolveImageWithStatusCode(statusCode, httpImageResolver);

// Assert that image is not the default one
assertNotEquals(httpImageResolver.defaultImage, result);
}

private BufferedImage resolveImageWithStatusCode(
int statusCode, HttpImageResolver httpImageResolver) throws IOException {
String testUriString = "https://test.com/image.png";
// Mock request factory and request
MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
MockClientHttpRequest requestMock = new MockClientHttpRequest();
when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);

MockClientHttpResponse responseMock = buildMockResponse(statusCode);
try {
when(requestFactoryMock.createRequest(
new URI(testUriString), org.springframework.http.HttpMethod.GET))
.thenReturn(requestMock);
requestMock.setResponse(responseMock);
} catch (Exception e) {
fail("Exception thrown: " + e.getMessage());
}

httpImageResolver.setUrlExtractor("(.*)");
httpImageResolver.setUrlGroup(1);

return httpImageResolver.resolve(requestFactoryMock, testUriString);
}

@Test
public void testResolveWithUnsupportedStatus() throws IOException {
int statusCode = 999;
HttpImageResolver httpImageResolver = new HttpImageResolver();

BufferedImage result = resolveImageWithStatusCode(statusCode, httpImageResolver);

// Assert that image is the default one
assertEquals(httpImageResolver.defaultImage, result);
}

private MockClientHttpResponse buildMockResponse(int statusCode) throws IOException {
File file = AbstractMapfishSpringTest.getFile(this.getClass(), "/map-data/tiger-ny.png");
byte[] responseBody = Files.readAllBytes(file.toPath());
ByteArrayInputStream inputStream = new ByteArrayInputStream(responseBody);
MockClientHttpResponse responseMock = new MockClientHttpResponse(inputStream, statusCode);
responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
return responseMock;
}

@Test
public void testInvalidURIResolve() {
String invalidUriString = "htp:/invalid";
MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
HttpImageResolver httpImageResolver = new HttpImageResolver();
httpImageResolver.setUrlExtractor("(.*)");
httpImageResolver.setUrlGroup(1);

// Calls the actual method with an invalid image
BufferedImage result = httpImageResolver.resolve(requestFactoryMock, invalidUriString);

assertEquals(httpImageResolver.defaultImage, result);
}

@Test
public void testInvalidUrlGroupResolve() throws IOException {
String testUriString = "https://test.com/image.png";
MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
MockClientHttpRequest requestMock = new MockClientHttpRequest();
when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);

byte[] responseBody = "Image bytes".getBytes(Charset.defaultCharset());
MockClientHttpResponse responseMock = new MockClientHttpResponse(responseBody, HttpStatus.OK);
responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
try {
when(requestFactoryMock.createRequest(
new URI(testUriString), org.springframework.http.HttpMethod.GET))
.thenReturn(requestMock);
requestMock.setResponse(responseMock);
} catch (Exception e) {
fail("Exception thrown: " + e.getMessage());
}

HttpImageResolver httpImageResolver = new HttpImageResolver();
httpImageResolver.setUrlExtractor("(.*)");
int urlGroup = 2;
httpImageResolver.setUrlGroup(urlGroup); // Invalid group

try {
// Calls the actual method with an invalid group
httpImageResolver.resolve(requestFactoryMock, testUriString);
fail("Did not throw IndexOutOfBoundsException for unsupported group");
} catch (IndexOutOfBoundsException e) {
assertTrue(e.getMessage().contains("No group " + urlGroup));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.mapfish.print.processor.jasper;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import com.codahale.metrics.MetricRegistry;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import org.junit.Test;
import org.mapfish.print.http.MfClientHttpRequestFactory;
import org.mapfish.print.processor.AbstractProcessor;
import org.mapfish.print.processor.Processor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpResponse;

public class IconTaskTest {

@Test
public void call_iconImageDoesNotExist_returnsMissingImageWhenOk() throws Exception {
int statusCode = HttpStatus.OK.value();
BufferedImage missingImage = new BufferedImage(24, 24, BufferedImage.TYPE_INT_RGB);

LegendProcessor.IconTask iconTask = prepareTest(missingImage, statusCode);

Object[] result = iconTask.call();

assertEquals(missingImage, result[1]);
}

@Test
public void call_iconImageDoesNotExist_returnsMissingImageWithUnsupportedStatus()
throws Exception {
int statusCode = 999;
BufferedImage missingImage = new BufferedImage(24, 24, BufferedImage.TYPE_INT_RGB);

LegendProcessor.IconTask iconTask = prepareTest(missingImage, statusCode);

Object[] result = iconTask.call();

assertEquals(missingImage, result[1]);
}

private static LegendProcessor.IconTask prepareTest(BufferedImage missingImage, int statusCode)
throws URISyntaxException, IOException {
LegendProcessor legendProcessorMock = spy(LegendProcessor.class);
when(legendProcessorMock.getMissingImage()).thenReturn(missingImage);
Processor.ExecutionContext context = new AbstractProcessor.Context(new HashMap<>());
URL icon = mock(URL.class);
when(icon.toURI()).thenReturn(new URI("http://localhost/icon.png"));
when(icon.getProtocol()).thenReturn("http");
MfClientHttpRequestFactory requestFactoryMock = mock(MfClientHttpRequestFactory.class);
MockClientHttpRequest requestMock = new MockClientHttpRequest();
when(requestFactoryMock.createRequest(any(URI.class), any())).thenReturn(requestMock);

byte[] responseBody = "Image bytes".getBytes(Charset.defaultCharset());
MockClientHttpResponse responseMock = new MockClientHttpResponse(responseBody, statusCode);
responseMock.getHeaders().setContentType(MediaType.IMAGE_PNG);
when(requestFactoryMock.createRequest(
new URI("testUriString"), org.springframework.http.HttpMethod.GET))
.thenReturn(requestMock);
requestMock.setResponse(responseMock);

return legendProcessorMock
.new IconTask(icon, 96, context, 1, null, requestFactoryMock, new MetricRegistry());
}
}