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

Commit

Permalink
Merge pull request #1688 from matthewdunsdon/feature/improveErrorMess…
Browse files Browse the repository at this point in the history
…ages

Improve validation messages identified
  • Loading branch information
cuthullu authored Aug 27, 2020
2 parents 2a0f0bb + 8f5a0dd commit 61313f3
Show file tree
Hide file tree
Showing 68 changed files with 3,495 additions and 746 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

package com.scottlogic.datahelix.generator.common.profile;

import com.scottlogic.datahelix.generator.common.RandomNumberGenerator;
import com.scottlogic.datahelix.generator.common.ValidationException;
import com.scottlogic.datahelix.generator.common.util.NumberUtils;

import com.scottlogic.datahelix.generator.common.util.defaults.NumericDefaults;
import com.scottlogic.datahelix.generator.common.RandomNumberGenerator;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
Expand All @@ -39,16 +39,13 @@ public NumericGranularity(int decimalPlaces) {
public static NumericGranularity create(Object granularity)
{
BigDecimal asNumber = NumberUtils.coerceToBigDecimal(granularity);
if (asNumber == null)
{
throw new ValidationException("Can't interpret granularity expression: " + granularity);
if (asNumber == null) {
throw new ValidationException(String.format("Can't interpret numeric granularity expression: %s", ValidationResult.quote(granularity)));
}
if (asNumber.compareTo(BigDecimal.ONE) > 0)
{
if (asNumber.compareTo(BigDecimal.ONE) > 0) {
throw new ValidationException("Numeric granularity must be <= 1");
}
if (!asNumber.equals(BigDecimal.ONE.scaleByPowerOfTen(-asNumber.scale())))
{
if (!asNumber.equals(BigDecimal.ONE.scaleByPowerOfTen(-asNumber.scale()))) {
throw new ValidationException("Numeric granularity must be fractional power of ten");
}
return new NumericGranularity(asNumber.scale());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.scottlogic.datahelix.generator.common.profile;

import java.util.Arrays;
import java.util.Optional;

import static com.scottlogic.datahelix.generator.common.util.Defaults.DEFAULT_DATE_FORMATTING;
import static com.scottlogic.datahelix.generator.common.util.Defaults.DEFAULT_TIME_FORMATTING;
Expand Down Expand Up @@ -56,11 +57,11 @@ public FieldType getFieldType()
return fieldType;
}

public static StandardSpecificFieldType from(String type) {
public static Optional<StandardSpecificFieldType> from(String type)
{
return Arrays.stream(StandardSpecificFieldType.values())
.filter(sft -> sft.type.equals(type))
.findAny()
.orElseThrow(() -> new IllegalStateException("No data types with type " + type));
.findAny();
}

public String getDefaultFormatting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -77,6 +78,20 @@ public static ValidationResult combine(Stream<ValidationResult> validationResult
});
return new ValidationResult(isValid.stream().allMatch(o -> o), errors);
}

public static String quote(Object value)
{
if (value instanceof Collection) {
Collection<Object> values = (Collection<Object>) value;
return values.stream().map(ValidationResult::quote).collect(Collectors.joining(", "));
} else if (value instanceof String) {
return String.format("'%s'", ((String) value).replace("'", "\\'"));
} else if (value == null) {
return "NULL";
} else {
return value.toString();
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.core.builders;

import com.scottlogic.datahelix.generator.core.config.detail.CombinationStrategyType;
import com.scottlogic.datahelix.generator.core.config.detail.DataGenerationType;
import com.scottlogic.datahelix.generator.core.config.detail.MonitorType;
import com.scottlogic.datahelix.generator.core.config.detail.VisualiserLevel;
import com.scottlogic.datahelix.generator.core.generation.GenerationConfigSource;
import com.scottlogic.datahelix.generator.core.generation.TestGenerationConfigSource;

import java.io.File;
import java.nio.file.Path;

public class GenerationConfigSourceBuilder
{
private final DataGenerationType generationType;
private final CombinationStrategyType combinationStrategyType;
private final Long maxRows;
private final boolean infiniteOutput;
private final MonitorType monitorType;
private final VisualiserLevel visualiserLevel;
private final Path visualiserOutputFolder;

private GenerationConfigSourceBuilder(DataGenerationType generationType,
CombinationStrategyType combinationStrategyType,
Long maxRows,
boolean infiniteOutput,
MonitorType monitorType,
VisualiserLevel visualiserLevel,
Path visualiserOutputFolder)
{
this.generationType = generationType;
this.combinationStrategyType = combinationStrategyType;
this.maxRows = maxRows;
this.infiniteOutput = infiniteOutput;
this.monitorType = monitorType;
this.visualiserLevel = visualiserLevel;
this.visualiserOutputFolder = visualiserOutputFolder;
}

public GenerationConfigSourceBuilder()
{
this(DataGenerationType.RANDOM,
CombinationStrategyType.MINIMAL,
null,
false,
MonitorType.QUIET,
VisualiserLevel.OFF,
new File("mockFilePath").toPath());
}

public GenerationConfigSource build()
{
return new TestGenerationConfigSource(generationType,
combinationStrategyType,
maxRows,
infiniteOutput,
monitorType,
visualiserLevel,
visualiserOutputFolder);
}

public GenerationConfigSourceBuilder withCombinationStrategyType(CombinationStrategyType combinationStrategyType)
{
return new GenerationConfigSourceBuilder(generationType,
combinationStrategyType,
maxRows,
infiniteOutput,
monitorType,
visualiserLevel,
visualiserOutputFolder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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.core.generation;

import com.scottlogic.datahelix.generator.core.config.detail.CombinationStrategyType;
import com.scottlogic.datahelix.generator.core.config.detail.DataGenerationType;
import com.scottlogic.datahelix.generator.core.config.detail.MonitorType;
import com.scottlogic.datahelix.generator.core.config.detail.VisualiserLevel;

import java.nio.file.Path;

public class TestGenerationConfigSource implements GenerationConfigSource
{

private DataGenerationType generationType;
private CombinationStrategyType combinationStrategyType;
private Long maxRows;
private boolean infiniteOutput;
private MonitorType monitorType;
private VisualiserLevel visualiserLevel;
private Path visualiserOutputFolder;

public TestGenerationConfigSource(DataGenerationType generationType,
CombinationStrategyType combinationStrategyType,
Long maxRows,
boolean infiniteOutput,
MonitorType monitorType,
VisualiserLevel visualiserLevel,
Path visualiserOutputFolder)
{
this.generationType = generationType;
this.combinationStrategyType = combinationStrategyType;
this.maxRows = maxRows;
this.infiniteOutput = infiniteOutput;
this.monitorType = monitorType;
this.visualiserLevel = visualiserLevel;
this.visualiserOutputFolder = visualiserOutputFolder;
}

@Override
public DataGenerationType getGenerationType()
{
return generationType;
}

@Override
public CombinationStrategyType getCombinationStrategyType()
{
return combinationStrategyType;
}

@Override
public Long getMaxRows()
{
return maxRows;
}

@Override
public boolean getInfiniteOutput()
{
return infiniteOutput;
}

@Override
public MonitorType getMonitorType()
{
return monitorType;
}

@Override
public VisualiserLevel getVisualiserLevel()
{
return visualiserLevel;
}

@Override
public Path getVisualiserOutputFolder()
{
return visualiserOutputFolder;
}
}
3 changes: 2 additions & 1 deletion orchestrator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ dependencies {
testCompile "com.shazam:shazamcrest:${SHAZAMCREST_VERSION}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${JUNIT_JUPITER_VERSION}"
testCompile group: "junit", name: "junit", version: "${JUNIT_4_VERSION}"

testCompile project(':core').sourceSets.test.output
testCompile project(':profile').sourceSets.test.output

testImplementation("org.junit.jupiter:junit-jupiter:${JUNIT_JUPITER_VERSION}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Feature: User can create data across multiple fields for all combinations availa
And bar is in set:
| 2 |
| null |
Then the profile is invalid with error "Values must be specified | Field: bar | Constraint: inSet"
Then the profile is invalid with error "Values must be specified | Field: 'bar' | Constraint: 'inSet'"

Scenario: Running an exhaustive combination strategy should be successful
Given the following non nullable fields exist:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Feature: User can specify that a value is equalTo a required value
Given there is a non nullable field foo
And foo has type "string"
And foo is equal to null
Then the profile is invalid with error "Values must be specified | Field: foo | Constraint: equalTo"
Then the profile is invalid with error "Values must be specified | Field: 'foo' | Constraint: 'equalTo'"

Scenario: Running an 'equalTo' of an invalid date value should fail with an error message
Given there is a non nullable field foo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,22 @@ Feature: User can specify that a field value belongs to a set of predetermined o
| "Scotland" | "Edinburgh" | 2 | "two" |
| "Wales" | "Cardiff" | 1 | "one" |
| "Wales" | "Cardiff" | 2 | "two" |


Scenario: Running an 'inMap' with text a restriction
Given the following non nullable fields exist:
| HomeNation |
| Population |
And the file "testFile" contains the following data:
| Country | Population |
| England | 56286961 |
| Scotland | 5463300 |
| Wales | ~3150000 |
| Northern Ireland | TBC |
And HomeNation has type "string"
And Population has type "integer"
And HomeNation is from Country in testFile
And Population is from Population in testFile
But the profile is invalid with errors:
| Value '~3150000' must be a number \| Constraint: 'inMap' |
| Value 'TBC' must be a number \| Constraint: 'inMap' |
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Feature: User can specify that a field value belongs to a set of predetermined o
And foo is in set:
| null |
| 1 |
Then the profile is invalid with error "Values must be specified | Field: foo | Constraint: inSet"
Then the profile is invalid with error "Values must be specified | Field: 'foo' | Constraint: 'inSet'"
And no data is created

Scenario: Running an 'inSet' request that includes multiples of the same entry should be successful.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,4 @@ Feature: User can specify that a datetime date is after, but not equal to, a spe

Scenario: Running a 'after' request that specifies the highest valid system date should be unsuccessful
Given foo is after 10000-01-01T00:00:00.000Z
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: foo | Constraint: after"
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: 'foo' | Constraint: 'after'"
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Feature: User can specify that a datetime date is more than, or the same as, a s

Scenario: Running afterOrAt request that includes datetime field with date and time (YYYY-MM-DDTHH:MM:SS) values that has invalid year should fail
Given foo is after or at 0000-01-10T00:00:00.000Z
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: foo | Constraint: afterOrAt"
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: 'foo' | Constraint: 'afterOrAt'"
And no data is created

Scenario: Running afterOrAt request that includes datetime field with date and time (YYYY-MM-DDTHH:MM:SS) values that has leap year
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,4 @@ Feature: User can specify that a datetime date is lower than, but not equal to,

Scenario: Running a 'before' request that specifies the highest valid system date should be unsuccessful
Given foo is before 0000-01-01T00:00:00.000Z
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: foo | Constraint: before"
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: 'foo' | Constraint: 'before'"
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Feature: User can specify that a datetime date is lower than, or the same as, a

Scenario: Running beforeOrAt request that includes datetime field with date and time (YYYY-MM-DDTHH:MM:SS) values that has invalid year should fail
Given foo is before or at 0000-01-10T00:00:00.000Z
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: foo | Constraint: beforeOrAt"
Then the profile is invalid with error containing "Dates must be between 0001-01-01T00:00:00.000Z and 9999-12-31T23:59:59.999Z | Field: 'foo' | Constraint: 'beforeOrAt'"
And no data is created

Scenario: Running beforeOrAt request against a non-contradicting beforeOrAt constraint should be successful
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,5 @@ Feature: User can specify that datetime fields are granular to a certain unit

Scenario: Applying an invalid datetime granularTo constraint fails with an appropriate error
Given foo is granular to "decades"
Then the profile is invalid with error "Granularity decades is not supported | Field: foo | Constraint: granularTo"
Then the profile is invalid with error "Granularity 'decades' is not supported | Field: 'foo' | Constraint: 'granularTo'"
And no data is created
Loading

0 comments on commit 61313f3

Please sign in to comment.