ErrorReportConfigurationTest.java
package com.fasterxml.jackson.core;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.io.ContentReference;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
/**
* Unit tests for class {@link ErrorReportConfiguration}.
*
* @since 2.16
*/
class ErrorReportConfigurationTest
extends JUnit5TestBase
{
/*
/**********************************************************
/* Unit Tests
/**********************************************************
*/
private final int DEFAULT_CONTENT_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_RAW_CONTENT_LENGTH;
private final int DEFAULT_ERROR_LENGTH = ErrorReportConfiguration.DEFAULT_MAX_ERROR_TOKEN_LENGTH;
private final ErrorReportConfiguration DEFAULTS = ErrorReportConfiguration.defaults();
@Test
void normalBuild()
{
ErrorReportConfiguration config = ErrorReportConfiguration.builder()
.maxErrorTokenLength(1004)
.maxRawContentLength(2008)
.build();
assertEquals(1004, config.getMaxErrorTokenLength());
assertEquals(2008, config.getMaxRawContentLength());
}
@Test
void zeroLengths()
{
// boundary tests, because we throw error on negative values
ErrorReportConfiguration config = ErrorReportConfiguration.builder()
.maxErrorTokenLength(0)
.maxRawContentLength(0)
.build();
assertEquals(0, config.getMaxErrorTokenLength());
assertEquals(0, config.getMaxRawContentLength());
}
@Test
void invalidMaxErrorTokenLength()
{
ErrorReportConfiguration.Builder builder = ErrorReportConfiguration.builder();
try {
builder.maxErrorTokenLength(-1);
fail("Should not reach here as exception is expected");
} catch (IllegalArgumentException ex) {
verifyException(ex, "Value of maxErrorTokenLength");
verifyException(ex, "cannot be negative");
}
try {
builder.maxRawContentLength(-1);
fail("Should not reach here as exception is expected");
} catch (IllegalArgumentException ex) {
verifyException(ex, "Value of maxRawContentLength");
verifyException(ex, "cannot be negative");
}
}
@Test
void defaults()
{
// default value
assertEquals(DEFAULT_ERROR_LENGTH, DEFAULTS.getMaxErrorTokenLength());
assertEquals(DEFAULT_CONTENT_LENGTH, DEFAULTS.getMaxRawContentLength());
// equals
assertEquals(ErrorReportConfiguration.defaults(), ErrorReportConfiguration.defaults());
}
@Test
void overrideDefaultErrorReportConfiguration()
{
// (1) override with null, will be no change
ErrorReportConfiguration.overrideDefaultErrorReportConfiguration(null);
try {
ErrorReportConfiguration nullDefaults = ErrorReportConfiguration.defaults();
assertEquals(DEFAULT_ERROR_LENGTH, nullDefaults.getMaxErrorTokenLength());
assertEquals(DEFAULT_CONTENT_LENGTH, nullDefaults.getMaxRawContentLength());
// (2) override with other value that actually changes default values
ErrorReportConfiguration.overrideDefaultErrorReportConfiguration(ErrorReportConfiguration.builder()
.maxErrorTokenLength(10101)
.maxRawContentLength(20202)
.build());
ErrorReportConfiguration overrideDefaults = ErrorReportConfiguration.defaults();
assertEquals(10101, overrideDefaults.getMaxErrorTokenLength());
assertEquals(20202, overrideDefaults.getMaxRawContentLength());
} finally {
// (3) revert back to default values
// IMPORTANT : make sure to revert back, otherwise other tests will be affected
ErrorReportConfiguration.overrideDefaultErrorReportConfiguration(ErrorReportConfiguration.builder()
.maxErrorTokenLength(DEFAULT_ERROR_LENGTH)
.maxRawContentLength(DEFAULT_CONTENT_LENGTH)
.build());
}
}
@Test
void rebuild()
{
ErrorReportConfiguration config = ErrorReportConfiguration.builder().build();
ErrorReportConfiguration rebuiltConfig = config.rebuild().build();
assertEquals(config.getMaxErrorTokenLength(), rebuiltConfig.getMaxErrorTokenLength());
assertEquals(config.getMaxRawContentLength(), rebuiltConfig.getMaxRawContentLength());
}
@Test
void builderConstructorWithErrorReportConfiguration()
{
ErrorReportConfiguration configA = ErrorReportConfiguration.builder()
.maxErrorTokenLength(1234)
.maxRawContentLength(5678)
.build();
ErrorReportConfiguration configB = configA.rebuild().build();
assertEquals(configA.getMaxErrorTokenLength(), configB.getMaxErrorTokenLength());
assertEquals(configA.getMaxRawContentLength(), configB.getMaxRawContentLength());
}
@Test
void withJsonLocation() throws Exception
{
// Truncated result
_verifyJsonLocationToString("abc", 2, "\"ab\"[truncated 1 chars]");
// Exact length
_verifyJsonLocationToString("abc", 3, "\"abc\"");
// Enough length
_verifyJsonLocationToString("abc", 4, "\"abc\"");
}
@Test
void withJsonFactory() throws Exception
{
// default
_verifyJsonProcessingExceptionSourceLength(500,
ErrorReportConfiguration.builder().build());
// default
_verifyJsonProcessingExceptionSourceLength(500,
ErrorReportConfiguration.defaults());
// shorter
_verifyJsonProcessingExceptionSourceLength(499,
ErrorReportConfiguration.builder()
.maxRawContentLength(DEFAULT_CONTENT_LENGTH - 1).build());
// longer
_verifyJsonProcessingExceptionSourceLength(501,
ErrorReportConfiguration.builder()
.maxRawContentLength(DEFAULT_CONTENT_LENGTH + 1).build());
// zero
_verifyJsonProcessingExceptionSourceLength(0,
ErrorReportConfiguration.builder()
.maxRawContentLength(0).build());
}
@Test
void expectedTokenLengthWithConfigurations()
throws Exception
{
// default
_verifyErrorTokenLength(263,
ErrorReportConfiguration.builder().build());
// default
_verifyErrorTokenLength(263,
ErrorReportConfiguration.defaults());
// shorter
_verifyErrorTokenLength(63,
ErrorReportConfiguration.builder()
.maxErrorTokenLength(DEFAULT_ERROR_LENGTH - 200).build());
// longer
_verifyErrorTokenLength(463,
ErrorReportConfiguration.builder()
.maxErrorTokenLength(DEFAULT_ERROR_LENGTH + 200).build());
// zero
_verifyErrorTokenLength(9,
ErrorReportConfiguration.builder()
.maxErrorTokenLength(0).build());
// negative value fails
try {
_verifyErrorTokenLength(9,
ErrorReportConfiguration.builder()
.maxErrorTokenLength(-1).build());
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.contains("Value of maxErrorTokenLength")
.contains("cannot be negative");
}
// null is not allowed, throws NPE
try {
_verifyErrorTokenLength(263,
null);
} catch (NullPointerException e) {
// no-op
}
}
@Test
void nonPositiveErrorTokenConfig()
{
// Zero should be ok
ErrorReportConfiguration.builder().maxErrorTokenLength(0).build();
// But not -1
try {
ErrorReportConfiguration.builder().maxErrorTokenLength(-1).build();
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage())
.contains("Value of maxErrorTokenLength")
.contains("cannot be negative");
}
}
@Test
void nullSetterThrowsException() {
try {
newStreamFactory().setErrorReportConfiguration(null);
fail();
} catch (NullPointerException npe) {
assertThat(npe).hasMessage("Cannot pass null ErrorReportConfiguration");
}
}
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
private void _verifyJsonProcessingExceptionSourceLength(int expectedRawContentLength, ErrorReportConfiguration erc)
throws Exception
{
// Arrange
JsonFactory factory = streamFactoryBuilder()
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
.errorReportConfiguration(erc)
.build();
// Make JSON input too long so it can be cutoff
int tooLongContent = 50 * DEFAULT_CONTENT_LENGTH;
String inputWithDynamicLength = _buildBrokenJsonOfLength(tooLongContent);
// Act
try (JsonParser parser = factory.createParser(inputWithDynamicLength)) {
parser.nextToken();
parser.nextToken();
fail("Should not reach");
} catch (JsonProcessingException e) {
// Assert
String prefix = "(String)\"";
String suffix = "\"[truncated 12309 chars]";
// The length of the source description should be [ prefix + expected length + suffix ]
int expectedLength = prefix.length() + expectedRawContentLength + suffix.length();
int actualLength = e.getLocation().sourceDescription().length();
assertEquals(expectedLength, actualLength);
assertThat(e.getMessage())
.contains("Unrecognized token '")
.contains("was expecting (JSON");
}
}
private void _verifyJsonLocationToString(String rawSrc, int rawContentLength, String expectedMessage)
{
ErrorReportConfiguration erc = ErrorReportConfiguration.builder()
.maxRawContentLength(rawContentLength)
.build();
ContentReference reference = ContentReference.construct(true, rawSrc, 0, rawSrc.length(), erc);
assertEquals(
"[Source: (String)" + expectedMessage + "; line: 1, column: 1]",
new JsonLocation(reference, 10L, 10L, 1, 1).toString());
}
private void _verifyErrorTokenLength(int expectedTokenLen, ErrorReportConfiguration errorReportConfiguration)
throws Exception
{
JsonFactory jf3 = streamFactoryBuilder()
.errorReportConfiguration(errorReportConfiguration)
.build();
_testWithMaxErrorTokenLength(expectedTokenLen,
// creating arbitrary number so that token reaches max len, but not over-do it
50 * DEFAULT_ERROR_LENGTH, jf3);
}
private void _testWithMaxErrorTokenLength(int expectedSize, int tokenLen, JsonFactory factory)
throws Exception
{
String inputWithDynamicLength = _buildBrokenJsonOfLength(tokenLen);
try (JsonParser parser = factory.createParser(inputWithDynamicLength)) {
parser.nextToken();
parser.nextToken();
} catch (JsonProcessingException e) {
assertThat(e.getLocation().getCharOffset()).isEqualTo(expectedSize);
assertThat(e.getMessage()).contains("Unrecognized token");
}
}
private String _buildBrokenJsonOfLength(int len)
{
StringBuilder sb = new StringBuilder("{\"key\":");
for (int i = 0; i < len; i++) {
sb.append("a");
}
sb.append("!}");
return sb.toString();
}
}