/*
* Copyright 2011-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.mapping.model;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.hamcrest.CoreMatchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentEntitySpec;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.Person;
import org.springframework.data.mapping.context.SampleMappingContext;
import org.springframework.data.mapping.context.SamplePersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.test.util.ReflectionTestUtils;
/**
* Unit test for {@link BasicPersistentEntity}.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
@Rule public ExpectedException exception = ExpectedException.none();
@Mock T property, anotherProperty;
@Test
public void assertInvariants() {
PersistentEntitySpec.assertInvariants(createEntity(Person.class));
}
@Test(expected = IllegalArgumentException.class)
public void rejectsNullTypeInformation() {
new BasicPersistentEntity<Object, T>(null);
}
@Test(expected = IllegalArgumentException.class)
public void rejectsNullProperty() {
createEntity(Person.class, null).addPersistentProperty(null);
}
@Test
public void returnsNullForTypeAliasIfNoneConfigured() {
PersistentEntity<Entity, T> entity = createEntity(Entity.class);
assertThat(entity.getTypeAlias()).isEqualTo(Alias.NONE);
}
@Test
public void returnsTypeAliasIfAnnotated() {
PersistentEntity<AliasedEntity, T> entity = createEntity(AliasedEntity.class);
assertThat(entity.getTypeAlias()).isEqualTo(Alias.of("foo"));
}
@Test // DATACMNS-50
@SuppressWarnings("unchecked")
public void considersComparatorForPropertyOrder() {
BasicPersistentEntity<Person, T> entity = createEntity(Person.class,
Comparator.comparing(PersistentProperty::getName));
T lastName = (T) Mockito.mock(PersistentProperty.class);
when(lastName.getName()).thenReturn("lastName");
T firstName = (T) Mockito.mock(PersistentProperty.class);
when(firstName.getName()).thenReturn("firstName");
T ssn = (T) Mockito.mock(PersistentProperty.class);
when(ssn.getName()).thenReturn("ssn");
entity.addPersistentProperty(lastName);
entity.addPersistentProperty(firstName);
entity.addPersistentProperty(ssn);
entity.verify();
List<T> properties = (List<T>) ReflectionTestUtils.getField(entity, "properties");
assertThat(properties).hasSize(3);
Iterator<T> iterator = properties.iterator();
assertThat(entity.getPersistentProperty("firstName")).hasValue(iterator.next());
assertThat(entity.getPersistentProperty("lastName")).hasValue(iterator.next());
assertThat(entity.getPersistentProperty("ssn")).hasValue(iterator.next());
}
@Test // DATACMNS-186
public void addingAndIdPropertySetsIdPropertyInternally() {
MutablePersistentEntity<Person, T> entity = createEntity(Person.class);
assertThat(entity.getIdProperty()).isNotPresent();
when(property.isIdProperty()).thenReturn(true);
entity.addPersistentProperty(property);
assertThat(entity.getIdProperty()).hasValue(property);
}
@Test // DATACMNS-186
public void rejectsIdPropertyIfAlreadySet() {
MutablePersistentEntity<Person, T> entity = createEntity(Person.class);
when(property.isIdProperty()).thenReturn(true);
when(anotherProperty.isIdProperty()).thenReturn(true);
entity.addPersistentProperty(property);
exception.expect(MappingException.class);
entity.addPersistentProperty(anotherProperty);
}
@Test // DATACMNS-365
public void detectsPropertyWithAnnotation() {
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
Optional<SamplePersistentProperty> property = entity.getPersistentProperty(LastModifiedBy.class);
assertThat(property).hasValueSatisfying(it -> assertThat(it.getName()).isEqualTo("field"));
property = entity.getPersistentProperty(CreatedBy.class);
assertThat(property).hasValueSatisfying(it -> assertThat(it.getName()).isEqualTo("property"));
assertThat(entity.getPersistentProperty(CreatedDate.class)).isNotPresent();
}
@Test // DATACMNS-596
public void returnsBeanWrapperForPropertyAccessor() {
assumeThat(System.getProperty("java.version"), CoreMatchers.startsWith("1.6"));
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
Entity value = new Entity();
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value);
assertThat(accessor).isInstanceOf(BeanWrapper.class);
assertThat(accessor.getBean()).isEqualTo(value);
}
@Test // DATACMNS-809
public void returnsGeneratedPropertyAccessorForPropertyAccessor() {
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
Entity value = new Entity();
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value);
assertThat(accessor).isNotInstanceOf(BeanWrapper.class);
assertThat(accessor.getClass().getName()).contains("_Accessor_");
assertThat(accessor.getBean()).isEqualTo(value);
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-596
public void rejectsNullBeanForPropertyAccessor() {
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
entity.getPropertyAccessor(null);
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-596
public void rejectsNonMatchingBeanForPropertyAccessor() {
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
entity.getPropertyAccessor("foo");
}
@Test // DATACMNS-597
public void supportsSubtypeInstancesOnPropertyAccessorLookup() {
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context.getRequiredPersistentEntity(Entity.class);
assertThat(entity.getPropertyAccessor(new Subtype())).isNotNull();
}
@Test // DATACMNS-825
public void returnsTypeAliasIfAnnotatedUsingComposedAnnotation() {
PersistentEntity<AliasEntityUsingComposedAnnotation, T> entity = createEntity(
AliasEntityUsingComposedAnnotation.class);
assertThat(entity.getTypeAlias()).isEqualTo(Alias.of("bar"));
}
@Test // DATACMNS-866
public void invalidBeanAccessCreatesDescriptiveErrorMessage() {
PersistentEntity<Entity, T> entity = createEntity(Entity.class);
exception.expectMessage(Entity.class.getName());
exception.expectMessage(Object.class.getName());
entity.getPropertyAccessor(new Object());
}
@Test // DATACMNS-934, DATACMNS-867
public void rejectsNullAssociation() {
MutablePersistentEntity<Entity, T> entity = createEntity(Entity.class);
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> entity.addAssociation(null));
}
private <S> BasicPersistentEntity<S, T> createEntity(Class<S> type) {
return createEntity(type, null);
}
private <S> BasicPersistentEntity<S, T> createEntity(Class<S> type, Comparator<T> comparator) {
return new BasicPersistentEntity<>(ClassTypeInformation.from(type), Optional.ofNullable(comparator));
}
@TypeAlias("foo")
static class AliasedEntity {
}
static class Entity {
@LastModifiedBy String field;
String property;
/**
* @return the property
*/
@CreatedBy
public String getProperty() {
return property;
}
}
@Retention(RetentionPolicy.RUNTIME)
@TypeAlias("foo")
static @interface ComposedTypeAlias {
@AliasFor(annotation = TypeAlias.class, attribute = "value")
String name() default "bar";
}
@ComposedTypeAlias
static class AliasEntityUsingComposedAnnotation {}
static class Subtype extends Entity {}
}