package org.molgenis.data.validation.meta;
import junit.framework.Assert;
import org.molgenis.data.DataService;
import org.molgenis.data.EntityManager;
import org.molgenis.data.MolgenisDataException;
import org.molgenis.data.Sort;
import org.molgenis.data.meta.AttributeType;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.validation.MolgenisValidationException;
import org.molgenis.data.validation.meta.AttributeValidator.ValidationMode;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.molgenis.data.Sort.Direction.ASC;
import static org.molgenis.data.meta.AttributeType.*;
import static org.molgenis.data.meta.model.AttributeMetadata.ATTRIBUTE_META_DATA;
import static org.testng.Assert.assertEquals;
public class AttributeValidatorTest
{
private AttributeValidator attributeValidator;
private DataService dataService;
@BeforeMethod
public void beforeMethod()
{
dataService = mock(DataService.class);
EntityManager entityManager = mock(EntityManager.class);
attributeValidator = new AttributeValidator(dataService, entityManager);
}
@Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Invalid characters in: \\[invalid.name\\] Only letters \\(a-z, A-Z\\), digits \\(0-9\\), underscores \\(_\\) and hashes \\(#\\) are allowed.")
public void validateAttributeInvalidName()
{
Attribute attr = makeMockAttribute("invalid.name");
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test
public void validateMappedByValidEntity()
{
String entityName = "entityName";
EntityType refEntity = when(mock(EntityType.class).getName()).thenReturn(entityName).getMock();
Attribute attr = makeMockAttribute("attrName");
when(attr.getRefEntity()).thenReturn(refEntity);
String mappedByAttrName = "mappedByAttrName";
Attribute mappedByAttr = when(mock(Attribute.class).getName()).thenReturn(mappedByAttrName).getMock();
when(mappedByAttr.getDataType()).thenReturn(XREF);
when(attr.getMappedBy()).thenReturn(mappedByAttr);
when(refEntity.getAttribute(mappedByAttrName)).thenReturn(mappedByAttr);
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "mappedBy attribute \\[mappedByAttrName\\] is not part of entity \\[entityName\\].")
public void validateMappedByInvalidEntity()
{
String entityName = "entityName";
EntityType refEntity = when(mock(EntityType.class).getName()).thenReturn(entityName).getMock();
Attribute attr = makeMockAttribute("attrName");
when(attr.getRefEntity()).thenReturn(refEntity);
String mappedByAttrName = "mappedByAttrName";
Attribute mappedByAttr = when(mock(Attribute.class).getName()).thenReturn(mappedByAttrName).getMock();
when(mappedByAttr.getDataType()).thenReturn(XREF);
when(attr.getMappedBy()).thenReturn(mappedByAttr);
when(refEntity.getAttribute(mappedByAttrName)).thenReturn(null);
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Invalid mappedBy attribute \\[mappedByAttrName\\] data type \\[STRING\\].")
public void validateMappedByInvalidDataType()
{
String entityName = "entityName";
EntityType refEntity = when(mock(EntityType.class).getName()).thenReturn(entityName).getMock();
Attribute attr = makeMockAttribute("attrName");
when(attr.getRefEntity()).thenReturn(refEntity);
String mappedByAttrName = "mappedByAttrName";
Attribute mappedByAttr = when(mock(Attribute.class).getName()).thenReturn(mappedByAttrName).getMock();
when(mappedByAttr.getDataType()).thenReturn(STRING); // invalid type
when(attr.getMappedBy()).thenReturn(mappedByAttr);
when(refEntity.getAttribute(mappedByAttrName)).thenReturn(null);
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test
public void validateOrderByValid()
{
String entityName = "entityName";
EntityType refEntity = when(mock(EntityType.class).getName()).thenReturn(entityName).getMock();
Attribute attr = makeMockAttribute("attrName");
when(attr.getRefEntity()).thenReturn(refEntity);
String mappedByAttrName = "mappedByAttrName";
Attribute mappedByAttr = when(mock(Attribute.class).getName()).thenReturn(mappedByAttrName).getMock();
when(mappedByAttr.getDataType()).thenReturn(XREF);
when(attr.getMappedBy()).thenReturn(mappedByAttr);
when(refEntity.getAttribute(mappedByAttrName)).thenReturn(mappedByAttr);
when(attr.getOrderBy()).thenReturn(new Sort(mappedByAttrName, ASC));
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test(expectedExceptions = MolgenisDataException.class, expectedExceptionsMessageRegExp = "Unknown entity \\[entityName\\] attribute \\[fail\\] referred to by entity \\[test\\] attribute \\[attrName\\] sortBy \\[fail,ASC\\]")
public void validateOrderByInvalidRefAttribute()
{
String entityName = "entityName";
EntityType refEntity = when(mock(EntityType.class).getName()).thenReturn(entityName).getMock();
Attribute attr = makeMockAttribute("attrName");
EntityType entity = mock(EntityType.class);
when(entity.getName()).thenReturn("test");
when(attr.getEntityType()).thenReturn(entity);
when(attr.getRefEntity()).thenReturn(refEntity);
String mappedByAttrName = "mappedByAttrName";
Attribute mappedByAttr = when(mock(Attribute.class).getName()).thenReturn(mappedByAttrName).getMock();
when(mappedByAttr.getDataType()).thenReturn(XREF);
when(attr.getMappedBy()).thenReturn(mappedByAttr);
when(refEntity.getAttribute(mappedByAttrName)).thenReturn(mappedByAttr);
when(attr.getOrderBy()).thenReturn(new Sort("fail", ASC));
attributeValidator.validate(attr, ValidationMode.ADD);
}
@Test(dataProvider = "disallowedTransitionProvider", expectedExceptions = MolgenisDataException.class)
public void testDisallowedTransition(Attribute currentAttr, Attribute newAttr)
{
when(dataService.findOneById(ATTRIBUTE_META_DATA, newAttr.getIdentifier(), Attribute.class))
.thenReturn(currentAttr);
attributeValidator.validate(newAttr, ValidationMode.UPDATE);
}
@Test(dataProvider = "allowedTransitionProvider")
public void testAllowedTransition(Attribute currentAttr, Attribute newAttr)
{
when(dataService.findOneById(ATTRIBUTE_META_DATA, newAttr.getIdentifier(), Attribute.class))
.thenReturn(currentAttr);
}
@Test
public void testDefaultValueDate()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test");
when(attr.getDataType()).thenReturn(AttributeType.DATE);
try
{
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
catch (MolgenisDataException actual)
{
assertEquals(actual.getCause().getMessage(), "Unparseable date: \"test\"");
}
}
@Test
public void testDefaultValueDateValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("01-01-2016");
when(attr.getDataType()).thenReturn(AttributeType.DATE);
attributeValidator.validateDefaultValue(attr);
}
@Test
public void testDefaultValueDateTime()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test");
when(attr.getDataType()).thenReturn(AttributeType.DATE_TIME);
try
{
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
catch (MolgenisDataException actual)
{
assertEquals(actual.getCause().getMessage(), "Unparseable date: \"test\"");
}
}
@Test
public void testDefaultValueDateTimeValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("2016-10-10T12:00:10+0000");
when(attr.getDataType()).thenReturn(AttributeType.DATE_TIME);
attributeValidator.validateDefaultValue(attr);
}
@Test
public void testDefaultValueHyperlink()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test^");
when(attr.getDataType()).thenReturn(AttributeType.HYPERLINK);
try
{
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
catch (MolgenisDataException actual)
{
assertEquals(actual.getMessage(), "Default value [test^] is not a valid hyperlink.");
}
}
@Test
public void testDefaultValueHyperlinkValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("http://www.molgenis.org");
when(attr.getDataType()).thenReturn(AttributeType.HYPERLINK);
attributeValidator.validateDefaultValue(attr);
}
@Test
public void testDefaultValueEnum()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test");
when(attr.getEnumOptions()).thenReturn(Arrays.asList("a", "b", "c"));
when(attr.getDataType()).thenReturn(AttributeType.ENUM);
try
{
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
catch (MolgenisDataException actual)
{
assertEquals(actual.getMessage(),
"Invalid default value [test] for enum [null] value must be one of [a, b, c]");
}
}
@Test
public void testDefaultValueEnumValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("b");
when(attr.getEnumOptions()).thenReturn(Arrays.asList("a", "b", "c"));
when(attr.getDataType()).thenReturn(AttributeType.ENUM);
attributeValidator.validateDefaultValue(attr);
}
@Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Invalid default value \\[test\\] for data type \\[INT\\]")
public void testDefaultValueInt1()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test");
when(attr.getDataType()).thenReturn(AttributeType.INT);
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
@Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Invalid default value \\[1.0\\] for data type \\[INT\\]")
public void testDefaultValueInt2()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("1.0");
when(attr.getDataType()).thenReturn(AttributeType.INT);
attributeValidator.validateDefaultValue(attr);
Assert.fail();
}
@Test
public void testDefaultValueIntValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("123456");
when(attr.getDataType()).thenReturn(AttributeType.INT);
attributeValidator.validateDefaultValue(attr);
}
@Test(expectedExceptions = MolgenisValidationException.class, expectedExceptionsMessageRegExp = "Invalid default value \\[test\\] for data type \\[LONG\\]")
public void testDefaultValueLong()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("test");
when(attr.getDataType()).thenReturn(AttributeType.LONG);
attributeValidator.validateDefaultValue(attr);
}
@Test
public void testDefaultValueLongValid()
{
Attribute attr = mock(Attribute.class);
when(attr.getDefaultValue()).thenReturn("123456");
when(attr.getDataType()).thenReturn(AttributeType.LONG);
attributeValidator.validateDefaultValue(attr);
}
@DataProvider(name = "allowedTransitionProvider")
private static Object[][] allowedTransitionProvider()
{
Attribute currentAttr1 = makeMockAttribute("attr1");
Attribute currentAttr2 = makeMockAttribute("attr2");
Attribute currentAttr3 = makeMockAttribute("attr3");
when(currentAttr1.getDataType()).thenReturn(BOOL);
when(currentAttr2.getDataType()).thenReturn(CATEGORICAL);
when(currentAttr3.getDataType()).thenReturn(COMPOUND);
Attribute newAttr1 = makeMockAttribute("attr1");
Attribute newAttr2 = makeMockAttribute("attr2");
Attribute newAttr3 = makeMockAttribute("attr3");
when(newAttr1.getDataType()).thenReturn(INT);
when(newAttr2.getDataType()).thenReturn(INT);
when(newAttr3.getDataType()).thenReturn(INT);
return new Object[][] { { currentAttr1, newAttr1 }, { currentAttr2, newAttr2 }, { currentAttr3, newAttr3 } };
}
@DataProvider(name = "disallowedTransitionProvider")
private static Object[][] disallowedTransitionProvider()
{
Attribute currentAttr1 = makeMockAttribute("attr1");
Attribute currentAttr2 = makeMockAttribute("attr2");
Attribute currentAttr3 = makeMockAttribute("attr3");
when(currentAttr1.getDataType()).thenReturn(BOOL);
when(currentAttr2.getDataType()).thenReturn(CATEGORICAL);
when(currentAttr3.getDataType()).thenReturn(COMPOUND);
Attribute newAttr1 = makeMockAttribute("attr1");
Attribute newAttr2 = makeMockAttribute("attr2");
Attribute newAttr3 = makeMockAttribute("attr3");
when(newAttr1.getDataType()).thenReturn(ONE_TO_MANY);
when(newAttr2.getDataType()).thenReturn(HYPERLINK);
when(newAttr3.getDataType()).thenReturn(FILE);
return new Object[][] { { currentAttr1, newAttr1 }, { currentAttr2, newAttr2 }, { currentAttr3, newAttr3 } };
}
private static Attribute makeMockAttribute(String name)
{
Attribute attr = mock(Attribute.class);
when(attr.getName()).thenReturn(name);
when(attr.getIdentifier()).thenReturn(name);
return attr;
}
}