/*
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.serializer;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.util.ReflectionTestUtils.*;
import static org.springframework.util.ObjectUtils.*;
import java.io.IOException;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.support.NullValue;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.TypeFactory;
/**
* @author Christoph Strobl
*/
public class GenericJackson2JsonRedisSerializerUnitTests {
private static final SimpleObject SIMPLE_OBJECT = new SimpleObject(1L);
private static final ComplexObject COMPLEX_OBJECT = new ComplexObject("steelheart", SIMPLE_OBJECT);
@Test // DATAREDIS-392
public void shouldUseDefaultTyping() {
assertThat(extractTypeResolver(new GenericJackson2JsonRedisSerializer()), notNullValue());
}
@Test // DATAREDIS-392
public void shouldUseDefaultTypingWhenClassPropertyNameIsEmpty() {
TypeResolverBuilder<?> typeResolver = extractTypeResolver(new GenericJackson2JsonRedisSerializer(""));
assertThat((String) getField(typeResolver, "_typeProperty"), is(JsonTypeInfo.Id.CLASS.getDefaultPropertyName()));
}
@Test // DATAREDIS-392
public void shouldUseDefaultTypingWhenClassPropertyNameIsNull() {
TypeResolverBuilder<?> typeResolver = extractTypeResolver(new GenericJackson2JsonRedisSerializer((String) null));
assertThat((String) getField(typeResolver, "_typeProperty"), is(JsonTypeInfo.Id.CLASS.getDefaultPropertyName()));
}
@Test // DATAREDIS-392
public void shouldUseDefaultTypingWhenClassPropertyNameIsProvided() {
TypeResolverBuilder<?> typeResolver = extractTypeResolver(new GenericJackson2JsonRedisSerializer("firefight"));
assertThat((String) getField(typeResolver, "_typeProperty"), is("firefight"));
}
@Test // DATAREDIS-392
public void serializeShouldReturnEmptyByteArrayWhenSouceIsNull() {
assertThat(new GenericJackson2JsonRedisSerializer().serialize(null), is(SerializationUtils.EMPTY_ARRAY));
}
@Test // DATAREDIS-392
public void deserializeShouldReturnNullWhenSouceIsNull() {
assertThat(new GenericJackson2JsonRedisSerializer().deserialize(null), nullValue());
}
@Test // DATAREDIS-392
public void deserializeShouldReturnNullWhenSouceIsEmptyArray() {
assertThat(new GenericJackson2JsonRedisSerializer().deserialize(SerializationUtils.EMPTY_ARRAY), nullValue());
}
@Test // DATAREDIS-392
public void deserializeShouldBeAbleToRestoreSimpleObjectAfterSerialization() {
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
assertThat((SimpleObject) serializer.deserialize(serializer.serialize(SIMPLE_OBJECT)), is(SIMPLE_OBJECT));
}
@Test // DATAREDIS-392
public void deserializeShouldBeAbleToRestoreComplexObjectAfterSerialization() {
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
assertThat((ComplexObject) serializer.deserialize(serializer.serialize(COMPLEX_OBJECT)), is(COMPLEX_OBJECT));
}
@Test(expected = SerializationException.class) // DATAREDIS-392
public void serializeShouldThrowSerializationExceptionProcessingError() throws JsonProcessingException {
ObjectMapper objectMapperMock = mock(ObjectMapper.class);
when(objectMapperMock.writeValueAsBytes(any())).thenThrow(new JsonGenerationException("nightwielder"));
new GenericJackson2JsonRedisSerializer(objectMapperMock).serialize(SIMPLE_OBJECT);
}
@Test(expected = SerializationException.class) // DATAREDIS-392
public void deserializeShouldThrowSerializationExceptionProcessingError() throws IOException {
ObjectMapper objectMapperMock = mock(ObjectMapper.class);
when(objectMapperMock.readValue(Mockito.any(byte[].class), Mockito.any(Class.class)))
.thenThrow(new JsonMappingException("conflux"));
new GenericJackson2JsonRedisSerializer(objectMapperMock).deserialize(new byte[] { 1 });
}
@Test // DATAREDIS-553
public void shouldSerializeNullValueSoThatItCanBeDeserializedWithDefaultTypingEnabled() {
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
NullValue nv = BeanUtils.instantiateClass(NullValue.class);
byte[] serializedValue = serializer.serialize(nv);
assertThat(serializedValue, is(notNullValue()));
Object deserializedValue = serializer.deserialize(serializedValue);
assertThat(deserializedValue, is(instanceOf(NullValue.class)));
}
private TypeResolverBuilder<?> extractTypeResolver(GenericJackson2JsonRedisSerializer serializer) {
ObjectMapper mapper = (ObjectMapper) getField(serializer, "mapper");
return mapper.getSerializationConfig().getDefaultTyper(TypeFactory.defaultInstance().constructType(Object.class));
}
static class ComplexObject {
public String stringValue;
public SimpleObject simpleObject;
public ComplexObject() {}
public ComplexObject(String stringValue, SimpleObject simpleObject) {
this.stringValue = stringValue;
this.simpleObject = simpleObject;
}
@Override
public int hashCode() {
return nullSafeHashCode(stringValue) + nullSafeHashCode(simpleObject);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ComplexObject)) {
return false;
}
ComplexObject other = (ComplexObject) obj;
return nullSafeEquals(this.stringValue, other.stringValue)
&& nullSafeEquals(this.simpleObject, other.simpleObject);
}
}
static class SimpleObject {
public Long longValue;
public SimpleObject() {}
public SimpleObject(Long longValue) {
this.longValue = longValue;
}
@Override
public int hashCode() {
return nullSafeHashCode(this.longValue);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof SimpleObject)) {
return false;
}
SimpleObject other = (SimpleObject) obj;
return nullSafeEquals(this.longValue, other.longValue);
}
}
}