diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java index d6e91f5..1e3c8a6 100644 --- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java @@ -16,6 +16,8 @@ import javax.xml.validation.Schema; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; +import lombok.AccessLevel; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import de.kosit.validationtool.api.Configuration; @@ -37,6 +39,7 @@ import net.sf.saxon.s9api.Processor; * @author Andreas Penski */ @Slf4j +@Getter(AccessLevel.PACKAGE) public class ConfigurationBuilder { private final List scenarios = new ArrayList<>(); @@ -66,18 +69,33 @@ public class ConfigurationBuilder { return this; } - public ConfigurationBuilder date(final LocalDate localDate) { - this.date = localDate.toString(); - return this; - } public ConfigurationBuilder name(final String name) { this.name = name; return this; } + /** + * Sets the date for this configuration. + * + * @param date the date + * @return this + */ + public ConfigurationBuilder date(final LocalDate date) { + if (date != null) { + this.date = date.toString(); + } + return this; + } + + /** + * Sets the date for this configuration. + * + * @param date the date + * @return this + */ public ConfigurationBuilder date(final Date date) { - return date(LocalDate.ofEpochDay(date.getTime())); + return date(date != null ? LocalDate.ofEpochDay(date.getTime()) : null); } public ConfigurationBuilder with(final ScenarioBuilder scenarioBuilder) { diff --git a/src/main/java/de/kosit/validationtool/config/Keys.java b/src/main/java/de/kosit/validationtool/config/Keys.java index 6670e71..bd37c5c 100644 --- a/src/main/java/de/kosit/validationtool/config/Keys.java +++ b/src/main/java/de/kosit/validationtool/config/Keys.java @@ -5,16 +5,19 @@ package de.kosit.validationtool.config; * * @author Andreas Penski */ -public class Keys { +public final class Keys { /** * The actual scenarios file location as used with {@link ConfigurationLoader}. */ public static final String SCENARIOS_FILE = "scenarios_file"; - /** * The actual scenarios configuration represented as serializable tree. This either loaded from file or build manually * via {@link ConfigurationBuilder} */ public static final String SCENARIO_DEFINITION = "scenario_definition"; + + private Keys() { + // hide + } } diff --git a/src/main/java/de/kosit/validationtool/config/ReportBuilder.java b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java index 1c69053..b512877 100644 --- a/src/main/java/de/kosit/validationtool/config/ReportBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java @@ -38,7 +38,7 @@ public class ReportBuilder implements Builder, String> build(final ContentRepository repository) { if (this.executable == null && this.source == null) { - return createError("Must supply source location and/or executable"); + return createError(String.format("Must supply source location and/or executable for report '%s'", this.name)); } final CreateReportType object = createObject(); Result, String> result; diff --git a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java index 18f6536..898f1a9 100644 --- a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.tuple.Pair; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import de.kosit.validationtool.impl.ContentRepository; import de.kosit.validationtool.impl.Scenario; @@ -37,6 +38,7 @@ import net.sf.saxon.s9api.XPathExecutable; * @author Andreas Penski */ @RequiredArgsConstructor +@Slf4j public class ScenarioBuilder implements Builder { private static int nameCount = 0; @@ -45,9 +47,9 @@ public class ScenarioBuilder implements Builder { private final Map namespaces = new HashMap<>(); - private final XPathBuilder matchConfig = new XPathBuilder(); + private final XPathBuilder matchConfig = new XPathBuilder("match"); - private final XPathBuilder acceptConfig = new XPathBuilder(); + private final XPathBuilder acceptConfig = new XPathBuilder("accept"); @Getter(AccessLevel.PACKAGE) private final String name; @@ -207,13 +209,17 @@ public class ScenarioBuilder implements Builder { private void buildAccept(final ContentRepository repository, final List errors, final Scenario scenario) { this.acceptConfig.setNamespaces(this.namespaces); - final Result result = this.acceptConfig.build(repository); - if (result.isValid()) { - scenario.setAcceptExecutable(result.getObject()); - scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath()); - this.namespaces.putAll(this.acceptConfig.getNamespaces()); + if (this.acceptConfig.isAvailable()) { + final Result result = this.acceptConfig.build(repository); + if (result.isValid()) { + scenario.setAcceptExecutable(result.getObject()); + scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath()); + this.namespaces.putAll(this.acceptConfig.getNamespaces()); + } else { + errors.addAll(result.getErrors()); + } } else { - errors.addAll(result.getErrors()); + log.debug("No accept configuration available"); } } diff --git a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java index 49dd722..ca51060 100644 --- a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java @@ -37,7 +37,7 @@ public class SchemaBuilder implements Builder, String> build(final ContentRepository repository) { if (this.schema == null && this.schemaLocation == null) { - return createError("Must supply schema location and/or schema"); + return createError(String.format("Must supply source location and/or executable for schema '%s'", this.name)); } Result, String> result; try { diff --git a/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java index ad74f43..73c187b 100644 --- a/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java @@ -38,7 +38,7 @@ public class SchematronBuilder implements Builder, String> build(final ContentRepository repository) { if (this.executable == null && this.source == null) { - return createError("Must supply source location and/or executable"); + return createError(String.format("Must supply source location and/or executable for schematron '%s'", this.name)); } final ValidateWithSchematron object = createObject(); Result, String> result; diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java index 464d745..d8d53b0 100644 --- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java +++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java @@ -1,5 +1,7 @@ package de.kosit.validationtool.config; +import static org.apache.commons.lang3.ObjectUtils.isNotEmpty; + import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -11,7 +13,9 @@ import org.apache.commons.lang3.ArrayUtils; import lombok.AccessLevel; import lombok.Data; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import de.kosit.validationtool.impl.ContentRepository; import de.kosit.validationtool.impl.model.Result; @@ -23,11 +27,15 @@ import net.sf.saxon.s9api.XPathExecutable; * * @author Andreas Penski */ +@RequiredArgsConstructor @Data +@Slf4j class XPathBuilder implements Builder { private static final String[] IGNORED_PREFIXES = new String[] { "xsd" }; + private final String name; + private String xpath; private XPathExecutable executable; @@ -44,19 +52,30 @@ class XPathBuilder implements Builder { public String getXPath() { return this.xpath == null && this.executable != null ? this.executable.getUnderlyingExpression().getInternalExpression().toString() : this.xpath; + } + public boolean isAvailable() { + return this.executable != null || isNotEmpty(this.xpath); } @Override public Result build(final ContentRepository repository) { - if (this.executable == null && this.xpath == null) { - return createError("No configuration for xpath expression found"); + if (!isAvailable()) { + return createError(String.format("No configuration for %s xpath expression found", this.name)); } - if (this.executable == null) { - this.executable = repository.createXPath(this.xpath, this.namespaces); - } else { - this.xpath = extractExpression(); - this.namespaces = extractNamespaces(); + try { + if (this.executable == null) { + this.executable = repository.createXPath(this.xpath, this.namespaces); + + } else { + this.xpath = extractExpression(); + this.namespaces = extractNamespaces(); + } + } catch (final IllegalStateException e) { + final String msg = String.format("Error creating %s xpath", this.name, e); + log.error(msg, e); + return new Result<>(Collections.singletonList(msg)); + } return new Result<>(this.executable); } diff --git a/src/main/java/de/kosit/validationtool/impl/DateFactory.java b/src/main/java/de/kosit/validationtool/impl/DateFactory.java index 4665c97..0c9fe9a 100644 --- a/src/main/java/de/kosit/validationtool/impl/DateFactory.java +++ b/src/main/java/de/kosit/validationtool/impl/DateFactory.java @@ -3,23 +3,25 @@ package de.kosit.validationtool.impl; import java.util.Date; import java.util.GregorianCalendar; -import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; +import lombok.SneakyThrows; + /** * @author Andreas Penski */ public class DateFactory { + private DateFactory() { + // hide + } + + @SneakyThrows public static XMLGregorianCalendar createTimestamp() { - try { final GregorianCalendar cal = new GregorianCalendar(); cal.setTime(new Date()); return DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); - } catch (final DatatypeConfigurationException e) { - throw new IllegalStateException("Can not create timestamp", e); - } } } diff --git a/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java new file mode 100644 index 0000000..86df9be --- /dev/null +++ b/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java @@ -0,0 +1,88 @@ +package de.kosit.validationtool.config; + +import static de.kosit.validationtool.config.ConfigurationBuilder.report; +import static de.kosit.validationtool.config.ConfigurationBuilder.schematron; +import static de.kosit.validationtool.config.SimpleConfigTest.createSimpleConfiguration; +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.time.LocalDate; +import java.util.Date; + +import org.hamcrest.Matchers; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +/** + * Test {@link ConfigurationBuilder}. + * + * @author Andreas Penski + */ +public class ConfigurationBuilderTest { + + public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); + + @Rule + public ExpectedException exceptions = ExpectedException.none(); + + @Test + public void testNoConfiguration() { + this.exceptions.expect(IllegalStateException.class); + new ConfigurationBuilder().build(); + } + + @Test + public void testNoFallback() { + this.exceptions.expect(IllegalStateException.class); + this.exceptions.expectMessage(Matchers.containsString("fallback")); + final ConfigurationBuilder builder = createSimpleConfiguration(); + builder.with((FallbackBuilder) null); + builder.build(); + } + + @Test + public void testNoSchema() { + this.exceptions.expect(IllegalStateException.class); + this.exceptions.expectMessage(Matchers.containsString("schema")); + final ConfigurationBuilder builder = createSimpleConfiguration(); + builder.getScenarios().get(0).validate((SchemaBuilder) null); + builder.build(); + } + + @Test + public void testInvalidSchematron() { + this.exceptions.expect(IllegalStateException.class); + this.exceptions.expectMessage(Matchers.containsString("schematron")); + final ConfigurationBuilder builder = createSimpleConfiguration(); + builder.getScenarios().get(0).validate(schematron("invalid").source(URI.create("DoesNotExist"))); + builder.build(); + } + + @Test + public void testInsufficientSchematron() { + this.exceptions.expect(IllegalStateException.class); + this.exceptions.expectMessage(Matchers.containsString("schematron")); + final ConfigurationBuilder builder = createSimpleConfiguration(); + builder.getScenarios().get(0).validate(schematron("invalid")); + builder.build(); + } + + @Test + public void testNoReport() { + this.exceptions.expect(IllegalStateException.class); + this.exceptions.expectMessage(Matchers.containsString("report")); + final ConfigurationBuilder builder = createSimpleConfiguration(); + builder.getScenarios().get(0).with(report("invalid")); + builder.build(); + } + + @Test + public void testDate() { + assertThat(createSimpleConfiguration().date(EPOCH).build().getDate()).isEqualTo("1970-01-01"); + assertThat(createSimpleConfiguration().date(new Date(EPOCH.toEpochDay())).build().getDate()).isEqualTo("1970-01-01"); + assertThat(createSimpleConfiguration().date((Date) null).build().getDate()).isEqualTo(LocalDate.now().toString()); + assertThat(createSimpleConfiguration().date((LocalDate) null).build().getDate()).isEqualTo(LocalDate.now().toString()); + } + +} diff --git a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java new file mode 100644 index 0000000..e6b9d13 --- /dev/null +++ b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java @@ -0,0 +1,74 @@ +package de.kosit.validationtool.config; + +import static de.kosit.validationtool.config.SimpleConfigTest.createScenarioConfiguration; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import de.kosit.validationtool.impl.Helper.Simple; +import de.kosit.validationtool.impl.Scenario; +import de.kosit.validationtool.impl.model.Result; + +/** + * Test {@link ScenarioBuilder}. + * + * @author Andreas Penski + */ +public class ScenarioBuilderTest { + + @Rule + public ExpectedException exceptions = ExpectedException.none(); + + @Test + public void simpleValid() { + final Result result = createScenarioConfiguration().build(Simple.createContentRepository()); + assertThat(result.isValid()).isTrue(); + assertThat(result.getObject().getConfiguration()).isNotNull(); + } + + @Test + public void testNoSchema() { + final ScenarioBuilder builder = createScenarioConfiguration(); + builder.validate((SchemaBuilder) null); + final Result result = builder.build(Simple.createContentRepository()); + assertThat(result.isValid()).isFalse(); + assertThat(result.getErrors()).anyMatch(e -> e.contains("schema")); + } + + @Test + public void testNoMatch() { + final ScenarioBuilder builder = createScenarioConfiguration(); + builder.match((String) null); + final Result result = builder.build(Simple.createContentRepository()); + assertThat(result.isValid()).isFalse(); + assertThat(result.getErrors()).anyMatch(e -> e.contains("match")); + } + + @Test + public void testInvalidMatch() { + final ScenarioBuilder builder = createScenarioConfiguration(); + builder.match("/////"); + final Result result = builder.build(Simple.createContentRepository()); + assertThat(result.isValid()).isFalse(); + assertThat(result.getErrors()).anyMatch(e -> e.contains("match")); + } + + @Test + public void testNoAccept() { + final ScenarioBuilder builder = createScenarioConfiguration(); + builder.acceptWith((String) null); + final Result result = builder.build(Simple.createContentRepository()); + assertThat(result.isValid()).isTrue(); + } + + @Test + public void testInvalidAccept() { + final ScenarioBuilder builder = createScenarioConfiguration(); + builder.acceptWith("/////"); + final Result result = builder.build(Simple.createContentRepository()); + assertThat(result.isValid()).isFalse(); + assertThat(result.getErrors()).anyMatch(e -> e.contains("accept")); + } +} diff --git a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java index 1fadac4..d6f50c2 100644 --- a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java +++ b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java @@ -25,25 +25,26 @@ public class SimpleConfigTest { @Test public void testSimpleWithApi() { //@formatter:off - final Configuration config = - Configuration.create().name("Simple-API") - .with(scenario("simple") - .validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd"))) - .with(report("Report für eRechnung").source("report.xsl")) - .acceptWith("count(//test:rejected) = 0") - .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput") - .declareNamespace("rpt", "http://validator.kosit.de/test-report") - .declareNamespace("test", "http://validator.kosit.de/test-sample") - .match("/test:simple") -// .description("awesome api") - ) - .with(fallback().name("default").source("report.xsl")) - - .resolvingMode(ResolvingMode.STRICT_RELATIVE) - .useRepository(Simple.REPOSITORY_URI).build(); + final Configuration config = createSimpleConfiguration().build(); //@formatter:on final DefaultCheck check = new DefaultCheck(config); final Result result = check.checkInput(InputFactory.read(Simple.SIMPLE_VALID)); assertThat(result).isNotNull(); } + + static ConfigurationBuilder createSimpleConfiguration() { + return Configuration.create().name("Simple-API").with(createScenarioConfiguration() + // .description("awesome api") + ).with(fallback().name("default").source("report.xsl")) + + .resolvingMode(ResolvingMode.STRICT_RELATIVE).useRepository(Simple.REPOSITORY_URI); + } + + static ScenarioBuilder createScenarioConfiguration() { + return scenario("simple").validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd"))) + .with(report("Report für eRechnung").source("report.xsl")).acceptWith("count(//test:rejected) = 0") + .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput") + .declareNamespace("rpt", "http://validator.kosit.de/test-report") + .declareNamespace("test", "http://validator.kosit.de/test-sample").match("/test:simple"); + } }