/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo Framework
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.model.internal.definitionconverter;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.test.util.ReflectionTestUtils.getField;
import static org.springframework.test.util.ReflectionTestUtils.setField;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.test.util.ReflectionTestUtils;
import com.google.common.collect.Lists;
import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.model.CustomHook;
import com.qcadoo.model.TransactionMockAwareTest;
import com.qcadoo.model.Utils;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.FieldDefinition;
import com.qcadoo.model.api.types.BelongsToType;
import com.qcadoo.model.api.types.HasManyType;
import com.qcadoo.model.api.types.ManyToManyType;
import com.qcadoo.model.api.types.TreeType;
import com.qcadoo.model.internal.DataDefinitionImpl;
import com.qcadoo.model.internal.DataDefinitionServiceImpl;
import com.qcadoo.model.internal.FieldDefinitionImpl;
import com.qcadoo.model.internal.api.DataAccessService;
import com.qcadoo.model.internal.api.EntityHookDefinition;
import com.qcadoo.model.internal.api.FieldHookDefinition;
import com.qcadoo.model.internal.api.InternalDataDefinition;
import com.qcadoo.model.internal.api.InternalDataDefinitionService;
import com.qcadoo.model.internal.api.ModelXmlToClassConverter;
import com.qcadoo.model.internal.classconverter.ModelXmlToClassConverterImpl;
import com.qcadoo.model.internal.types.BelongsToEntityType;
import com.qcadoo.model.internal.types.BooleanType;
import com.qcadoo.model.internal.types.DateTimeType;
import com.qcadoo.model.internal.types.DateType;
import com.qcadoo.model.internal.types.DecimalType;
import com.qcadoo.model.internal.types.DictionaryType;
import com.qcadoo.model.internal.types.EnumType;
import com.qcadoo.model.internal.types.HasManyEntitiesType;
import com.qcadoo.model.internal.types.IntegerType;
import com.qcadoo.model.internal.types.PasswordType;
import com.qcadoo.model.internal.types.PriorityType;
import com.qcadoo.model.internal.types.StringType;
import com.qcadoo.model.internal.types.TextType;
import com.qcadoo.model.internal.types.TreeEntitiesType;
import com.qcadoo.model.internal.validators.CustomEntityValidator;
import com.qcadoo.model.internal.validators.CustomValidator;
import com.qcadoo.model.internal.validators.LengthValidator;
import com.qcadoo.model.internal.validators.RangeValidator;
import com.qcadoo.model.internal.validators.RegexValidator;
import com.qcadoo.model.internal.validators.RequiredValidator;
import com.qcadoo.model.internal.validators.ScaleValidator;
import com.qcadoo.model.internal.validators.UniqueValidator;
import com.qcadoo.model.internal.validators.UnscaledValueValidator;
public class ModelXmlToDefinitionConverterTest extends TransactionMockAwareTest {
private static ModelXmlToDefinitionConverterImpl modelXmlToDefinitionConverter;
private static InternalDataDefinitionService dataDefinitionService;
private static DataAccessService dataAccessService;
private static ApplicationContext applicationContext;
private static TranslationService translationService;
private static InternalDataDefinition dataDefinition;
private static Collection<DataDefinition> dataDefinitions;
private static ModelXmlToClassConverter modelXmlToClassConverter;
@BeforeClass
public static void init() throws Exception {
applicationContext = mock(ApplicationContext.class);
dataAccessService = mock(DataAccessService.class);
translationService = mock(TranslationService.class);
dataDefinitionService = new DataDefinitionServiceImpl();
modelXmlToDefinitionConverter = new ModelXmlToDefinitionConverterImpl();
setField(modelXmlToDefinitionConverter, "dataDefinitionService", dataDefinitionService);
setField(modelXmlToDefinitionConverter, "dataAccessService", dataAccessService);
setField(modelXmlToDefinitionConverter, "applicationContext", applicationContext);
setField(modelXmlToDefinitionConverter, "translationService", translationService);
given(applicationContext.getBean(CustomHook.class)).willReturn(new CustomHook());
modelXmlToClassConverter = new ModelXmlToClassConverterImpl();
((ModelXmlToClassConverterImpl) modelXmlToClassConverter).setBeanClassLoader(ClassLoader.getSystemClassLoader());
dataDefinitions = performConvert(Utils.FULL_FIRST_ENTITY_XML_RESOURCE, Utils.FULL_SECOND_ENTITY_XML_RESOURCE,
Utils.FULL_THIRD_ENTITY_XML_RESOURCE, Utils.OTHER_FIRST_ENTITY_XML_RESOURCE,
Utils.OTHER_SECOND_ENTITY_XML_RESOURCE);
for (DataDefinition dd : dataDefinitions.toArray(new DataDefinition[dataDefinitions.size()])) {
if (dd.getName().equals("firstEntity") && dd.getPluginIdentifier().equals("full")) {
dataDefinition = (InternalDataDefinition) dd;
}
}
}
@Test
public final void shouldThrowExceptionForFieldWhichCanNotBeBothCopyableAndUnique() {
// when & then
try {
performConvert(Utils.UNIQUE_COPYABLE_BROKEN_ENTITY_XML_RESOURCE);
Assert.fail();
} catch (IllegalStateException ise) {
assertTrue(ise.getMessage().contains("Unique field can not have the copyable attribute set to true."));
assertTrue(ise.getMessage().contains("#other_uniqueCopyableBrokenEntity.uniqueAndCopyableInteger"));
}
}
@Test
public final void shouldBuildDataDefinitionWithFieldWhichIsAllowedToBeBothCopyableAndUnique() {
// when
DataDefinition convertedDataDefinition = performConvert(Utils.UNIQUE_COPYABLE_ENTITY_XML_RESOURCE).iterator().next();
// then
for (String fieldName : Lists.newArrayList("stringField", "textField")) {
FieldDefinition fieldDefinition = convertedDataDefinition.getField(fieldName);
assertNotNull(String.format("Field '%s' is missing", fieldName), fieldDefinition);
assertTrue(String.format("Field '%s' should be unique", fieldName), fieldDefinition.isUnique());
assertTrue(String.format("Field '%s' should be copyable", fieldName), fieldDefinition.getType().isCopyable());
}
}
private static Collection<DataDefinition> performConvert(final Resource... resources) {
modelXmlToClassConverter.convert(resources);
return modelXmlToDefinitionConverter.convert(resources);
}
@Test
public void shouldParseXml() {
assertNotNull(dataDefinition);
}
@Test
public void shouldSetDataDefinitionAttributes() {
assertEquals(5, dataDefinitions.size());
assertEquals("firstEntity", dataDefinition.getName());
assertEquals("com.qcadoo.model.beans.full.FullFirstEntity", dataDefinition.getFullyQualifiedClassName());
assertEquals("com.qcadoo.model.beans.full.FullFirstEntity", dataDefinition.getInstanceForEntity().getClass()
.getCanonicalName());
assertEquals("full", dataDefinition.getPluginIdentifier());
assertEquals("com.qcadoo.model.beans.full.FullFirstEntity", dataDefinition.getClassForEntity().getCanonicalName());
assertFalse(dataDefinition.isDeletable());
assertFalse(dataDefinition.isInstertable());
assertTrue(dataDefinition.isUpdatable());
}
@Test
public void shouldSetEntityValidators() {
assertEquals(1, ((DataDefinitionImpl) dataDefinition).getValidators().size());
EntityHookDefinition validator = ((DataDefinitionImpl) dataDefinition).getValidators().get(0);
assertThat(validator, instanceOf(CustomEntityValidator.class));
testHookDefinition(validator, "entityHook", CustomHook.class, "validate");
}
@Test
public void shouldSetFields() {
assertNotNull(dataDefinition.getField("fieldInteger"));
assertThat(dataDefinition.getField("fieldInteger").getType(), instanceOf(IntegerType.class));
assertNotNull(dataDefinition.getField("fieldString"));
assertThat(dataDefinition.getField("fieldString").getType(), instanceOf(StringType.class));
assertNotNull(dataDefinition.getField("fieldText"));
assertThat(dataDefinition.getField("fieldText").getType(), instanceOf(TextType.class));
assertNotNull(dataDefinition.getField("fieldDecimal"));
assertThat(dataDefinition.getField("fieldDecimal").getType(), instanceOf(DecimalType.class));
assertNotNull(dataDefinition.getField("fieldDatetime"));
assertThat(dataDefinition.getField("fieldDatetime").getType(), instanceOf(DateTimeType.class));
assertNotNull(dataDefinition.getField("fieldDate"));
assertThat(dataDefinition.getField("fieldDate").getType(), instanceOf(DateType.class));
assertNotNull(dataDefinition.getField("fieldBoolean"));
assertThat(dataDefinition.getField("fieldBoolean").getType(), instanceOf(BooleanType.class));
assertNotNull(dataDefinition.getField("fieldSecondEntity"));
assertThat(dataDefinition.getField("fieldSecondEntity").getType(), instanceOf(BelongsToEntityType.class));
assertEquals("other", ((BelongsToType) (dataDefinition.getField("fieldSecondEntity")).getType()).getDataDefinition()
.getPluginIdentifier());
assertEquals("secondEntity", ((BelongsToType) (dataDefinition.getField("fieldSecondEntity")).getType())
.getDataDefinition().getName());
assertFalse(((BelongsToType) (dataDefinition.getField("fieldSecondEntity")).getType()).isLazyLoading());
assertNotNull(dataDefinition.getField("fieldSecondEntity2"));
assertThat(dataDefinition.getField("fieldSecondEntity2").getType(), instanceOf(BelongsToEntityType.class));
assertEquals("other", ((BelongsToType) (dataDefinition.getField("fieldSecondEntity2")).getType()).getDataDefinition()
.getPluginIdentifier());
assertEquals("secondEntity", ((BelongsToType) (dataDefinition.getField("fieldSecondEntity2")).getType())
.getDataDefinition().getName());
assertTrue(((BelongsToType) (dataDefinition.getField("fieldSecondEntity2")).getType()).isLazyLoading());
assertNotNull(dataDefinition.getField("fieldHasMany"));
assertThat(dataDefinition.getField("fieldHasMany").getType(), instanceOf(HasManyEntitiesType.class));
assertEquals("fieldFirstEntity", ((HasManyType) (dataDefinition.getField("fieldHasMany")).getType()).getJoinFieldName());
assertEquals("full", getField(dataDefinition.getField("fieldHasMany").getType(), "pluginIdentifier"));
assertEquals("thirdEntity", getField(dataDefinition.getField("fieldHasMany").getType(), "entityName"));
assertEquals(HasManyType.Cascade.NULLIFY, getField(dataDefinition.getField("fieldHasMany").getType(), "cascade"));
assertNotNull(dataDefinition.getField("fieldManyToMany"));
assertThat(dataDefinition.getField("fieldManyToMany").getType(), instanceOf(ManyToManyType.class));
assertEquals("thirdEntity", ((ManyToManyType) (dataDefinition.getField("fieldManyToMany")).getType()).getDataDefinition()
.getName());
assertEquals("full", getField(dataDefinition.getField("fieldManyToMany").getType(), "pluginIdentifier"));
assertEquals("thirdEntity", getField(dataDefinition.getField("fieldManyToMany").getType(), "entityName"));
assertEquals(ManyToManyType.Cascade.DELETE, getField(dataDefinition.getField("fieldManyToMany").getType(), "cascade"));
assertNotNull(dataDefinition.getField("fieldTree"));
assertThat(dataDefinition.getField("fieldTree").getType(), instanceOf(TreeEntitiesType.class));
assertEquals("fieldFirstEntity", ((TreeType) (dataDefinition.getField("fieldTree")).getType()).getJoinFieldName());
assertEquals("full", getField(dataDefinition.getField("fieldTree").getType(), "pluginIdentifier"));
assertEquals("secondEntity", getField(dataDefinition.getField("fieldTree").getType(), "entityName"));
assertEquals(TreeType.Cascade.DELETE, getField(dataDefinition.getField("fieldTree").getType(), "cascade"));
assertNotNull(dataDefinition.getField("fieldEnum"));
assertThat(dataDefinition.getField("fieldEnum").getType(), instanceOf(EnumType.class));
assertThat(((EnumType) dataDefinition.getField("fieldEnum").getType()).values(Locale.ENGLISH).keySet(),
hasItems("one", "two", "three"));
assertNotNull(dataDefinition.getField("fieldDictionary"));
assertThat(dataDefinition.getField("fieldDictionary").getType(), instanceOf(DictionaryType.class));
assertEquals("categories", getField(dataDefinition.getField("fieldDictionary").getType(), "dictionary"));
assertThat(dataDefinition.getField("fieldPassword").getType(), instanceOf(PasswordType.class));
assertFalse(dataDefinition.getField("fieldInteger").isReadOnly());
assertTrue(dataDefinition.getField("fieldText").isReadOnly());
assertThat(dataDefinition.getField("createDate").getType(), instanceOf(DateTimeType.class));
assertThat(dataDefinition.getField("updateDate").getType(), instanceOf(DateTimeType.class));
assertThat(dataDefinition.getField("createUser").getType(), instanceOf(StringType.class));
assertThat(dataDefinition.getField("updateUser").getType(), instanceOf(StringType.class));
assertNotNull(dataDefinition.getField("createDate"));
assertNotNull(dataDefinition.getField("updateDate"));
assertNotNull(dataDefinition.getField("createUser"));
assertNotNull(dataDefinition.getField("updateUser"));
}
@Test
public void shouldBeActivable() throws Exception {
assertTrue(dataDefinition.isActivable());
}
@Test
public void shouldDefineIdentifierExpression() throws Exception {
assertEquals("#fieldString", dataDefinition.getIdentifierExpression());
}
@Test
public void shouldDefineFieldExpression() throws Exception {
assertEquals("#fString", dataDefinition.getField("fieldStringWithExpression").getExpression());
}
@Test
public void shouldSetPersistentFlag() throws Exception {
assertFalse(dataDefinition.getField("fieldStringNotPersistent").isPersistent());
assertTrue(dataDefinition.getField("fieldString").isPersistent());
}
@Test
public void shouldSetFieldValidators() {
assertFalse(dataDefinition.getField("fieldText").isRequired());
assertFalse(dataDefinition.getField("fieldText").isUnique());
assertTrue(dataDefinition.getField("fieldInteger").isRequired());
assertTrue(dataDefinition.getField("fieldInteger").isUnique());
assertEquals(0, ((FieldDefinitionImpl) dataDefinition.getField("fieldDatetime")).getValidators().size());
assertEquals(6, ((FieldDefinitionImpl) dataDefinition.getField("fieldInteger")).getValidators().size());
List<FieldHookDefinition> validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldInteger")).getValidators();
assertThat(validators.get(0), instanceOf(RequiredValidator.class));
assertThat(validators.get(1), instanceOf(UniqueValidator.class));
assertThat(validators.get(2), instanceOf(CustomValidator.class));
testHookDefinition(validators.get(2), "fieldHook", CustomHook.class, "validateField");
assertThat(validators.get(3), instanceOf(LengthValidator.class));
assertEquals(1, getField(validators.get(3), "min"));
assertNull(getField(validators.get(3), "is"));
assertEquals(3, getField(validators.get(3), "max"));
assertThat(validators.get(4), instanceOf(UnscaledValueValidator.class));
assertEquals(2, getField(validators.get(4), "min"));
assertNull(getField(validators.get(4), "is"));
assertEquals(4, getField(validators.get(4), "max"));
assertThat(validators.get(5), instanceOf(RangeValidator.class));
assertEquals(18, getField(validators.get(5), "from"));
assertEquals(null, getField(validators.get(5), "to"));
assertEquals(false, getField(validators.get(5), "inclusively"));
validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldString")).getValidators();
assertEquals(4, validators.size());
assertThat(validators.get(3), instanceOf(RegexValidator.class));
assertEquals("d??p", getField(validators.get(3), "regex"));
validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldStringWithoutValidators")).getValidators();
assertEquals(1, validators.size());
assertThat(validators.get(0), instanceOf(LengthValidator.class));
assertEquals(255, ReflectionTestUtils.getField(validators.get(0), "max"));
validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldDecimal")).getValidators();
assertThat(validators.get(0), instanceOf(ScaleValidator.class));
assertEquals(2, getField(validators.get(0), "min"));
assertNull(getField(validators.get(0), "is"));
assertEquals(4, getField(validators.get(0), "max"));
assertThat(validators.get(1), instanceOf(UnscaledValueValidator.class));
assertNull(getField(validators.get(1), "min"));
assertEquals(2, getField(validators.get(1), "is"));
assertNull(getField(validators.get(1), "max"));
validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldDecimalOnlyWithScale")).getValidators();
assertThat(validators.get(0), instanceOf(ScaleValidator.class));
assertEquals(2, getField(validators.get(0), "min"));
assertNull(getField(validators.get(0), "is"));
assertEquals(4, getField(validators.get(0), "max"));
validators = ((FieldDefinitionImpl) dataDefinition.getField("fieldDecimalWithoutValidators")).getValidators();
assertEquals(2, validators.size());
}
@Test
public void shouldSetPriorityField() {
assertNotNull(dataDefinition.getPriorityField());
assertEquals("fieldPriority", dataDefinition.getPriorityField().getName());
assertEquals("fieldInteger", ((PriorityType) dataDefinition.getPriorityField().getType()).getScopeFieldDefinition()
.getName());
assertTrue((dataDefinition).isPrioritizable());
}
@Test
public void shouldSetHooks() {
DataDefinitionImpl dataDefImpl = (DataDefinitionImpl) dataDefinition;
testListHookDefinition(dataDefImpl.getCreateHooks(), CustomHook.class, "createHook");
testListHookDefinition(dataDefImpl.getUpdateHooks(), CustomHook.class, "updateHook");
testListHookDefinition(dataDefImpl.getSaveHooks(), CustomHook.class, "hook");
testListHookDefinition(dataDefImpl.getCopyHooks(), CustomHook.class, "copyHook");
testListHookDefinition(dataDefImpl.getDeleteHooks(), CustomHook.class, "deleteHook");
}
private void testListHookDefinition(final List<EntityHookDefinition> hooks, final Class<?> hookBeanClass,
final String hookMethodName) {
assertEquals(1, hooks.size());
assertThat(getField(hooks.get(0), "bean"), instanceOf(hookBeanClass));
assertEquals(hookMethodName, ((Method) getField(hooks.get(0), "method")).getName());
}
private void testHookDefinition(final Object object, final String hookFieldName, final Class<?> hookBeanClass,
final String hookMethodName) {
Object hook = getField(object, hookFieldName);
assertNotNull(hook);
assertThat(getField(hook, "bean"), instanceOf(hookBeanClass));
assertEquals(hookMethodName, ((Method) getField(hook, "method")).getName());
}
}