Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
fix(#1675): Update value data type validation checks
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewdunsdon committed Jul 17, 2020
1 parent 1facc82 commit 670eb1d
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.common.whitelist.WeightedElement;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.atomic.AtomicConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.ConstraintValidator;
Expand Down Expand Up @@ -50,43 +49,6 @@ ValidationResult fieldTypeMustMatchValueType(T dto, FieldType expectedFieldType)
return ValidationResult.success();
}

ValidationResult fieldTypeMustMatchValueType(T dto, Object value)
{
if (value == null)
{
return ValidationResult.failure("Values must be specified" + getErrorInfo(dto));
}
FieldType fieldType = FieldValidator.getSpecificFieldType(getField(dto.field)).getFieldType();
if (value instanceof Boolean && fieldType != FieldType.BOOLEAN)
{
return ValidationResult.failure(String.format("Value %s must be a boolean%s", ValidationResult.quote(value), getErrorInfo(dto)));
}
if (!(value instanceof Number || value instanceof String && isNumber((String)value) ||
value instanceof WeightedElement && isNumber((String)((WeightedElement) value).element())) &&
fieldType == FieldType.NUMERIC)
{
return ValidationResult.failure(String.format("Value %s must be a number%s", ValidationResult.quote(value), getErrorInfo(dto)));
}
if (value instanceof Number && fieldType != FieldType.NUMERIC)
{
return ValidationResult.failure(String.format("Value %s must be a string%s", ValidationResult.quote(value), getErrorInfo(dto)));
}
return ValidationResult.success();
}

private static boolean isNumber(String s)
{
try
{
Double.parseDouble(s);
return true;
} catch (NumberFormatException | NullPointerException e)
{
return false;
}
}


ValidationResult fieldMustBeValid(T dto)
{
String fieldName = dto.field;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.atomic;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.atomic.EqualToConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.ValueTypeValidator;

import java.util.List;

Expand All @@ -33,8 +36,10 @@ public EqualToConstraintValidator(List<FieldDTO> fields)
public final ValidationResult validate(EqualToConstraintDTO dto)
{
ValidationResult fieldMustBeValid = fieldMustBeValid(dto);
if(!fieldMustBeValid.isSuccess) return fieldMustBeValid;
return fieldTypeMustMatchValueType(dto, dto.value);
if (!fieldMustBeValid.isSuccess) return fieldMustBeValid;

FieldType fieldType = FieldValidator.getSpecificFieldType(getField(dto.field)).getFieldType();
return new ValueTypeValidator(fieldType, getErrorInfo(dto)).validate(dto.value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.atomic;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.atomic.GranularToConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.ValueTypeValidator;

import java.util.List;

Expand All @@ -33,9 +36,11 @@ public GranularToConstraintValidator(List<FieldDTO> fields)
public final ValidationResult validate(GranularToConstraintDTO dto)
{
ValidationResult fieldMustBeValid = fieldMustBeValid(dto);
if(!fieldMustBeValid.isSuccess) return fieldMustBeValid;
ValidationResult valueMustBeValid = fieldTypeMustMatchValueType(dto, dto.value);
if(!valueMustBeValid.isSuccess) return valueMustBeValid;
if (!fieldMustBeValid.isSuccess) return fieldMustBeValid;

FieldType fieldType = FieldValidator.getSpecificFieldType(getField(dto.field)).getFieldType();
ValidationResult valueMustBeValid = new ValueTypeValidator(fieldType, getErrorInfo(dto)).validate(dto.value);
if (!valueMustBeValid.isSuccess) return valueMustBeValid;

return validateGranularity(dto, dto.field, dto.value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.atomic;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.atomic.InSetConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.ValueTypeValidator;

import java.util.List;

Expand Down Expand Up @@ -47,6 +50,8 @@ private ValidationResult valuesMustBeSpecified(InSetConstraintDTO dto)

private ValidationResult fieldTypeMustBeValid(InSetConstraintDTO dto)
{
return ValidationResult.combine(dto.values.stream().map(v -> fieldTypeMustMatchValueType(dto, v)));
FieldType fieldType = FieldValidator.getSpecificFieldType(getField(dto.field)).getFieldType();
ValueTypeValidator valueTypeValidator = new ValueTypeValidator(fieldType, getErrorInfo(dto));
return ValidationResult.combine(dto.values.stream().map(valueTypeValidator::validate));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* 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 com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.common.validators.Validator;
import com.scottlogic.datahelix.generator.common.whitelist.WeightedElement;

public class ValueTypeValidator implements Validator<Object>
{
private final FieldType expectedFieldType;
private final String errorInfo;

public ValueTypeValidator(FieldType expectedFieldType, String errorInfo)
{
this.expectedFieldType = expectedFieldType;
this.errorInfo = errorInfo;
}

@Override
public final ValidationResult validate(Object value)
{
if (value == null)
{
return ValidationResult.failure("Values must be specified" + errorInfo);
}
if (value instanceof WeightedElement)
{
return validate(((WeightedElement) value).element());
}

switch (expectedFieldType)
{
case BOOLEAN:
return value instanceof Boolean
? ValidationResult.success()
: ValidationResult.failure(String.format("Value %s must be a boolean%s", ValidationResult.quote(value), errorInfo));
case NUMERIC:
return value instanceof Number || value instanceof String && isNumber((String) value)
? ValidationResult.success()
: ValidationResult.failure(String.format("Value %s must be a number%s", ValidationResult.quote(value), errorInfo));
default:
return value instanceof String ? ValidationResult.success() : ValidationResult.failure(String.format("Value %s must be a string%s", ValidationResult.quote(value), errorInfo));
}
}

private static boolean isNumber(String s)
{
try
{
Double.parseDouble(s);
return true;
} catch (NumberFormatException | NullPointerException e)
{
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,6 @@ public void validateEqualToConstraint_withInvalidData_fails()
// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
assertThat(validationResult.errors, hasItem("Value true must be a boolean | Field: 'text' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value true must be a string | Field: 'text' | Constraint: 'equalTo'"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ public void validateInSetConstraint_withInvalidData_fails()
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(2));
assertThat(validationResult.errors, hasItem("Value 1.1 must be a string | Field: 'text' | Constraint: 'inSet'"));
// TODO: This error message is not helpful or correct
assertThat(validationResult.errors, hasItem("Value true must be a boolean | Field: 'text' | Constraint: 'inSet'"));
assertThat(validationResult.errors, hasItem("Value true must be a string | Field: 'text' | Constraint: 'inSet'"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,10 @@
*/
package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities;

import com.scottlogic.datahelix.generator.common.profile.StandardSpecificFieldType;
import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.creation.FieldDTOBuilder;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.atomic.EqualToConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.atomic.EqualToConstraintValidator;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static com.scottlogic.datahelix.generator.profile.creation.AtomicConstraintDTOBuilder.atomicConstraintDTO;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.iterableWithSize;
import static org.junit.Assert.assertFalse;
Expand All @@ -36,21 +27,13 @@

public class ValueTypeValidatorTests
{
private final List<FieldDTO> fields = Arrays.asList
(
FieldDTOBuilder.fieldDTO("text", StandardSpecificFieldType.STRING).build(),
FieldDTOBuilder.fieldDTO("decimal", StandardSpecificFieldType.DECIMAL).build(),
FieldDTOBuilder.fieldDTO("boolean", StandardSpecificFieldType.BOOLEAN).build()
);
public static final String ERROR_INFO = " | error info";

@Test
public void validateValueType_withStringTypeAndValidData_succeeds()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("text").buildEqualTo("test");

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.STRING, ERROR_INFO).validate("test");

// Assert
assertTrue(validationResult.isSuccess);
Expand All @@ -59,42 +42,32 @@ public void validateValueType_withStringTypeAndValidData_succeeds()
@Test
public void validateValueType_withStringTypeAndNumericValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("text").buildEqualTo(1.1);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.STRING, ERROR_INFO).validate(1.1);

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
assertThat(validationResult.errors, hasItem("Value 1.1 must be a string | Field: 'text' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value 1.1 must be a string | error info"));
}

@Test
public void validateValueType_withStringTypeAndBooleanValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("text").buildEqualTo(true);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.STRING, ERROR_INFO).validate(true);

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
// TODO: This error message is not helpful or correct
assertThat(validationResult.errors, hasItem("Value true must be a boolean | Field: 'text' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value true must be a string | error info"));
}

@Test
public void validateValueType_withNumericTypeAndValidData_succeeds()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("decimal").buildEqualTo(1.1);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.NUMERIC, ERROR_INFO).validate(1.1);

// Assert
assertTrue(validationResult.isSuccess);
Expand All @@ -103,42 +76,32 @@ public void validateValueType_withNumericTypeAndValidData_succeeds()
@Test
public void validateValueType_withNumericTypeAndStringValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("decimal").buildEqualTo("text");

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.NUMERIC, ERROR_INFO).validate("text");

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
assertThat(validationResult.errors, hasItem("Value 'text' must be a number | Field: 'decimal' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value 'text' must be a number | error info"));
}

@Test
public void validateValueType_withNumericTypeAndBooleanValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("decimal").buildEqualTo(true);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.NUMERIC, ERROR_INFO).validate(true);

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
// TODO: This error message is not helpful or correct
assertThat(validationResult.errors, hasItem("Value true must be a boolean | Field: 'decimal' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value true must be a number | error info"));
}

@Test
public void validateValueType_withBooleanTypeAndValidData_succeeds()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("boolean").buildEqualTo(true);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.BOOLEAN, ERROR_INFO).validate(true);

// Assert
assertTrue(validationResult.isSuccess);
Expand All @@ -147,33 +110,24 @@ public void validateValueType_withBooleanTypeAndValidData_succeeds()
@Test
public void validateValueType_withBooleanTypeAndNumericValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("boolean").buildEqualTo(1.1);

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.BOOLEAN, ERROR_INFO).validate(1.1);

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
// TODO: This error message is not helpful or correct
assertThat(validationResult.errors, hasItem("Value 1.1 must be a string | Field: 'boolean' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value 1.1 must be a boolean | error info"));
}

// TODO: validation should fail, but currently succeeds
@Disabled
@Test
public void validateValueType_withBooleanTypeAndStringValue_fails()
{
// Arrange
EqualToConstraintDTO dto = atomicConstraintDTO("boolean").buildEqualTo("text");

// Act
ValidationResult validationResult = new EqualToConstraintValidator(fields).validate(dto);
ValidationResult validationResult = new ValueTypeValidator(FieldType.BOOLEAN, ERROR_INFO).validate("text");

// Assert
assertFalse(validationResult.isSuccess);
assertThat(validationResult.errors, iterableWithSize(1));
assertThat(validationResult.errors, hasItem("Value 'text' must be a boolean | Field: 'boolean' | Constraint: 'equalTo'"));
assertThat(validationResult.errors, hasItem("Value 'text' must be a boolean | error info"));
}
}

0 comments on commit 670eb1d

Please sign in to comment.