Merge branch 'master' into 48-documentation-and-semantic-defintion-of-accpetmatch-functionality

This commit is contained in:
Renzo Kottmann 2020-06-20 17:49:39 +02:00
commit a670b227ed
24 changed files with 381 additions and 421 deletions

3
.gitignore vendored
View file

@ -36,3 +36,6 @@ src/generated
.settings
.vscode
*.code-workspace
# Testing stuff
xrechnung

View file

@ -5,16 +5,36 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## next version (unreleased)
# Added
### Added
- Support java.xml.transform.Source/java.xml.transform.StreamSource as Input
# Changed
### Changed
- Inputs are NOT read into memory (e.g. Byte-Array) prior processing within the validator. This reduces memory consumption.
## UNRELEASED
### Fixed
- Validator was creating invalid createReportInput xml in case of no scenrio match
## 1.2.0
### Added
- Provide access to schematron result through [Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java)
- *Result#getFailedAsserts()* returns a list of failed asserts found by schematron
- *Result#isSchematronValid()* convinience access to evaluate whether schematron was processed without any *FailedAsserts*
### Changed
- *Result#getAcceptRecommendation()* does not _only_ work when _acceptMatch_ is configured in the scenario
- schema correctness is a precondition, if the checked instance is not valid, this evaluates to _REJECTED_
- if _acceptMatch_ is configured, the result is based on the boolean result of the xpath expression evaluated against the generated report
- if *no* _acceptMatch_ is configured, the result is based on evaluation of schema and schematron correctness
- _UNDEFINED_ is only returned, when processing is stopped somehow
- *Result#isAcceptable()* can now evaluate to true, when no _acceptMatch_ is configured (see above)
## 1.1.3
### Fixed

View file

@ -66,6 +66,10 @@ java -jar validationtool-<version>-standalone.jar --help
A concrete example with a specific validator configuration can be found on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
### Application User Interface (API / embedded usage)
The validator can also be used in own Java Applications via the API. Details can be [found here](./docs/api.md).
### Daemon-Mode
You can also start the validator as an HTTP-Server. Just start it in _Daemon-Mode_ with the `-D` option.
@ -86,6 +90,4 @@ You can HTTP-POST to `/` and the response will return the report document as de
Additionally there is the GET `/health` endpoint which can be used by monitoring systems.
### Application User Interface (embedded usage)
The validator can also be used in own Java Applications via the API. Details can be [found here](./docs/api.md).

View file

@ -4,7 +4,8 @@ The Validator offers an API which allows you to integrate Validator in your own
## Dependency Management
Currently, we *do not* deploy to Maven Central or similar. Hence you need to build and optionally deploy the Validator artifacts to your own shared repository (see for example [Maven Documentation](https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html)).
Currently, we *do not* deploy to Maven Central or similar. Hence you need to build and optionally deploy the Validator artifacts to your own
shared (or local) repository (see for example [Maven Documentation](https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html)).
### Maven
@ -28,7 +29,8 @@ dependencies {
## Usage
Prerequisite for use is a valid [scenario definition](configurations.md) and the a folder with all necessary artifacts for validation (repository) either on the filesystem or on the classpath.
Prerequisite for use is a valid [scenario definition](configurations.md) and the a folder with all necessary artifacts for validation
(repository) either on the filesystem or on the classpath.
The following example demonstrates loading scenario.xml and whole configuration from classpath and validating one XML document:
@ -81,14 +83,19 @@ public class StandardExample {
}
```
The `Result` interface has more methods to retrieve details about XSD validation errors and Schematron messages.
The `Result` interface has convenience methods to retrieve details about XSD validation errors and Schematron messages and other processing results. See
[Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java) for details.
Initializing all XML artifacts and XSLT-executables is expensive. The `Check` instance is *threadsafe* and keeps all artifacts. Therefore, we recommend the re-use of an `Check` instance.
* Batch use is serial and *not parallel*
Initializing all XML artifacts and XSLT-executables is expensive. The `Check` instance is *threadsafe* and keeps all artifacts. Therefore,
we recommend the re-use of an `Check` instance.
The only input `de.kosit.validationtool.api.Input` which can be created by various methods of `de.kosit.validationtool.api.InputFactory`.
The `InputFactory` calculates a hash sum for each Input which is also written to the Report. _SHA-256_ from the JDK is the default algorithm. It can be changed using the `read`-methods of `InputFactory`.
The `InputFactory` calculates a hash sum for each Input which is also written to the Report. _SHA-256_ from the JDK is the default algorithm.
It can be changed using the `read`-methods of `InputFactory`.
The main interface [Check.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Check.java)
allows using a batch interface (processing list of [Inputs](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Input.java)).
However, there is no parallel processing implemented at the moment.
## Accept Recommendation and Accept Match
@ -96,14 +103,21 @@ A tri-state Object `AcceptRecommendation` can be retrieved from the `Result` usi
The three defined states are:
1. `UNDEFINED` i.e. the evaluation of the overall validation could not be computed.
2. `ACCEPTABLE` i.e. the recommendation is to accept input based on the evaluation of the overall validation.
3. `REJECT` i.e. the recommendation is to reject input based on the evaluation of the overall validation.
1. `ACCEPTABLE` i.e. the recommendation is to accept input based on the evaluation of the overall validation.
1. `REJECT` i.e. the recommendation is to reject input based on the evaluation of the overall validation.
1. `UNDEFINED` i.e. the evaluation of the overall validation could not be computed (overall processing is incomplete)
By default it is `UNDEFINED`.
The accept recommendation is based on either:
1. schema and schematron validation results
1. if configured based on _acceptMatch_ configuration of the scenario (see below)
### Accept Match in Scenario Configuration
For your own configuration you can add an `acceptMatch` element in each scenario. It can contain in XPATH expression over your own defined `Report` to compute a boolean. An XPATH expression evaluating to true will lead to an `ACCEPTABLE` amd otherwise to a `REJECT` recommendation.
For your own configuration you can add an `acceptMatch` element in each scenario. It can contain in XPATH expression over your own
defined `Report` to compute a boolean value. An XPATH expression evaluating to true will lead to an `ACCEPTABLE` and otherwise to a `REJECT`
recommendation.
This allows to have own control over what validation result is to be considered acceptable for your own application context.
This allows to have control over what validation result is to be considered _acceptable_ for your own application context. E.g. you can
overrule schematron validation errors with _acceptMatch_ configuration and consider certain errors as _acceptable_. Nevertheless you can *not*
overrule schema errors with accept match.

View file

@ -6,7 +6,7 @@
<name>KoSIT XML Prüftool Implementierung</name>
<groupId>de.kosit</groupId>
<version>1.2.0-SNAPSHOT</version>
<version>1.3.0-SNAPSHOT</version>
<artifactId>validationtool</artifactId>
<description>KoSIT XML Validator against XSD and Schematron based on defined scenarios.</description>

View file

@ -2,6 +2,7 @@ package de.kosit.validationtool.api;
import java.util.List;
import org.oclc.purl.dsdl.svrl.FailedAssert;
import org.oclc.purl.dsdl.svrl.SchematronOutput;
import org.w3c.dom.Document;
@ -69,6 +70,13 @@ public interface Result {
*/
List<SchematronOutput> getSchematronResult();
/**
* Returns {@link org.oclc.purl.dsdl.svrl.FailedAssert FailedAsserts} of a schematron evaluation.
*
* @return list of {@link org.oclc.purl.dsdl.svrl.FailedAssert FailedAsserts}, if any, empty list otherwise
*/
List<FailedAssert> getFailedAsserts();
/**
* Liefert ein true, wenn keine Schema-Violations vorhanden sind.
*
@ -82,4 +90,11 @@ public interface Result {
* @return true wenn well-formed
*/
boolean isWellformed();
/**
* Returns true, if schematron has been checked and the result does not contain any {@link FailedAssert FailedAsserts}.
*
* @return true, if valid
*/
boolean isSchematronValid();
}

View file

@ -127,12 +127,19 @@ public class DefaultResult implements Result {
*
* @return die {@link FailedAssert}
*/
@Override
public List<FailedAssert> getFailedAsserts() {
return filterSchematronResult(FailedAssert.class);
}
private <T> List<T> filterSchematronResult(final Class<T> type) {
return getSchematronResult().stream().filter(type::isInstance).map(type::cast).collect(Collectors.toList());
return getSchematronResult() != null
? getSchematronResult().stream().filter(type::isInstance).map(type::cast).collect(Collectors.toList())
: Collections.emptyList();
}
@Override
public boolean isSchematronValid() {
return getSchematronResult() != null && getFailedAsserts().isEmpty();
}
}

View file

@ -164,6 +164,7 @@ public class ScenarioRepository {
private ScenarioType createFallback() {
final ScenarioType t = new ScenarioType();
t.setFallback(true);
t.setName("Fallback-Scenario");
t.setMatch("count(/)<0");
final CreateReportType reportType = new CreateReportType();

View file

@ -70,6 +70,11 @@ public abstract class BaseScenario {
private ResourceType resourceType;
}
@XmlTransient
@Getter
@Setter
private boolean fallback;
private XPathExecutable matchExecutable;
private XPathExecutable acceptExecutable;

View file

@ -2,15 +2,19 @@ package de.kosit.validationtool.impl.tasks;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import org.oclc.purl.dsdl.svrl.FailedAssert;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.AcceptRecommendation;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XPathSelector;
/**
* Berechnet die Akzeptanz-Empfehlung gemäß konfigurierten 'acceptMatch' des aktuellen Szenarios.
* Computes a {@link AcceptRecommendation} for this instance. This is either based on an 'acceptMatch'-configuration of
* the active scenario or based on overall evaluation about schema and semantic (schematron) correctness of the
*
* @author Andreas Penski
*/
@ -20,23 +24,49 @@ public class ComputeAcceptanceAction implements CheckAction {
@Override
public void check(final Bag results) {
if (preCondtionsMatch(results)) {
final String acceptMatch = results.getScenarioSelectionResult().getObject().getAcceptMatch();
if (isNotBlank(acceptMatch)) {
if (results.getSchemaValidationResult().isValid() && isNotBlank(acceptMatch)) {
evaluateAcceptanceMatch(results);
} else {
evaluateSchemaAndSchematron(results);
}
} else {
results.setAcceptStatus(AcceptRecommendation.REJECT);
}
}
private void evaluateSchemaAndSchematron(final Bag results) {
if (results.getSchemaValidationResult().isValid() && isSchematronValid(results)) {
results.setAcceptStatus(AcceptRecommendation.ACCEPTABLE);
} else {
results.setAcceptStatus(AcceptRecommendation.REJECT);
}
}
private boolean isSchematronValid(final Bag results) {
return !hasSchematronErrors(results);
}
private boolean hasSchematronErrors(final Bag results) {
return results.getReportInput().getValidationResultsSchematron().stream().map(e -> e.getResults().getSchematronOutput())
.flatMap(e -> e.getActivePatternAndFiredRuleAndFailedAssert().stream()).anyMatch(FailedAssert.class::isInstance);
}
private static void evaluateAcceptanceMatch(final Bag results) {
try {
final XPathSelector selector = results.getScenarioSelectionResult().getObject().getAcceptSelector();
selector.setContextItem(results.getReport());
results.setAcceptStatus(selector.effectiveBooleanValue() ? AcceptRecommendation.ACCEPTABLE : AcceptRecommendation.REJECT);
} catch (final Exception e) {
log.error("Fehler bei Evaluierung des Accept-Status: {}", e.getMessage(), e);
}
} catch (final SaxonApiException e) {
final String msg = "Error evaluating accept recommendation: %s";
log.error(msg);
results.addProcessingError(msg);
}
}
@Override
public boolean isSkipped(final Bag results) {
return results.getReport() == null;
private static boolean preCondtionsMatch(final Bag results) {
return results.getReport() != null && results.getSchemaValidationResult() != null && results.getScenarioSelectionResult() != null;
}
}

View file

@ -20,6 +20,7 @@
package de.kosit.validationtool.impl.tasks;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.impl.ScenarioRepository;
import de.kosit.validationtool.impl.model.Result;
@ -35,6 +36,7 @@ import net.sf.saxon.s9api.XdmNode;
* @author Andreas Penski
*/
@RequiredArgsConstructor
@Slf4j
public class ScenarioSelectionAction implements CheckAction {
private final ScenarioRepository repository;
@ -47,16 +49,21 @@ public class ScenarioSelectionAction implements CheckAction {
if (results.getParserResult().isValid()) {
scenarioTypeResult = determineScenario(results.getParserResult().getObject());
} else {
scenarioTypeResult = new Result<>(repository.getFallbackScenario());
scenarioTypeResult = new Result<>(this.repository.getFallbackScenario());
}
results.setScenarioSelectionResult(scenarioTypeResult);
if (!scenarioTypeResult.getObject().isFallback()) {
report.setScenario(scenarioTypeResult.getObject());
log.info("Schenario {} identified for {}", scenarioTypeResult.getObject().getName(), results.getInput().getName());
} else {
log.error("No valid schenario configuration found for {}", results.getInput().getName());
}
}
private Result<ScenarioType, String> determineScenario(final XdmNode document) {
final Result<ScenarioType, String> result = this.repository.selectScenario(document);
if (result.isInvalid()) {
return new Result<>(repository.getFallbackScenario());
return new Result<>(this.repository.getFallbackScenario());
}
return result;
}

View file

@ -138,7 +138,7 @@ public class CommandlineApplicationTest {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r",
Paths.get(Simple.REPOSITORY).toString(), Paths.get(Simple.EXAMPLES).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains("Processing 5 object(s) completed");
assertThat(this.commandLine.getErrorOutput()).contains("Processing 6 object(s) completed");
}
@Test

View file

@ -22,7 +22,6 @@ package de.kosit.validationtool.cmd;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Collectors;
@ -34,6 +33,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@ -43,7 +43,6 @@ import de.kosit.validationtool.impl.tasks.CheckAction;
*/
public class ExtractHtmlActionTest {
private static final URL REPORT = SerializeReportActionTest.class.getResource("/examples/results/report.xml");
private ExtractHtmlContentAction action;
@ -51,24 +50,24 @@ public class ExtractHtmlActionTest {
@Before
public void setup() throws IOException {
tmpDirectory = Files.createTempDirectory("checktool");
action = new ExtractHtmlContentAction(Helper.loadTestRepository(), tmpDirectory);
this.tmpDirectory = Files.createTempDirectory("checktool");
this.action = new ExtractHtmlContentAction(Helper.loadTestRepository(), this.tmpDirectory);
}
@After
public void tearDown() throws IOException {
FileUtils.deleteDirectory(tmpDirectory.toFile());
FileUtils.deleteDirectory(this.tmpDirectory.toFile());
}
@Test
public void testSimple() throws IOException {
CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(REPORT));
assertThat(action.isSkipped(b)).isTrue();
b.setReport(Helper.load(REPORT));
action.check(b);
assertThat(action.isSkipped(b)).isFalse();
action.check(b);
final CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(Simple.SIMPLE_VALID));
assertThat(this.action.isSkipped(b)).isTrue();
b.setReport(Helper.load(Simple.SIMPLE_VALID.toURL()));
this.action.check(b);
assertThat(this.action.isSkipped(b)).isFalse();
this.action.check(b);
assertThat(b.isStopped()).isFalse();
assertThat(Files.list(tmpDirectory).collect(Collectors.toList())).hasSize(1);
assertThat(Files.list(this.tmpDirectory).collect(Collectors.toList())).hasSize(1);
}
}

View file

@ -21,8 +21,7 @@ package de.kosit.validationtool.cmd;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import org.junit.After;
import org.junit.Before;
@ -30,6 +29,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@ -37,34 +37,33 @@ import de.kosit.validationtool.impl.tasks.CheckAction;
*/
public class PrintReportActionTest {
private static final URL REPORT = SerializeReportActionTest.class.getResource("/examples/results/report.xml");
private CommandLine commandLine;
private PrintReportAction action;
@Before
public void setup() throws IOException {
commandLine = new CommandLine();
commandLine.activate();
action = new PrintReportAction();
public void setup() {
this.commandLine = new CommandLine();
this.commandLine.activate();
this.action = new PrintReportAction();
}
@After
public void tearDownd() throws IOException {
commandLine.deactivate();
public void tearDown() {
this.commandLine.deactivate();
}
@Test
public void testSimpleSerialize() {
CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(REPORT));
b.setReport(Helper.load(REPORT));
assertThat(action.isSkipped(b)).isFalse();
action.check(b);
public void testSimpleSerialize() throws MalformedURLException {
final CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(Simple.SIMPLE_VALID));
b.setReport(Helper.load(Simple.SIMPLE_VALID.toURL()));
assertThat(this.action.isSkipped(b)).isFalse();
this.action.check(b);
assertThat(b.isStopped()).isFalse();
assertThat(commandLine.getOutput()).isNotEmpty();
// assertThat(commandLine.getOutput()).contains("<?xml version=\"1.0\" ");
// assertThat(commandLine.getErrorOutput()).isEmpty();
assertThat(this.commandLine.getOutput()).isNotEmpty();
assertThat(this.commandLine.getOutput()).contains("<?xml version=\"1.0\" ");
assertThat(this.commandLine.getErrorOutput()).isEmpty();
}
}

View file

@ -22,7 +22,7 @@ package de.kosit.validationtool.cmd;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -33,6 +33,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@ -40,7 +41,6 @@ import de.kosit.validationtool.impl.tasks.CheckAction;
*/
public class SerializeReportActionTest {
private static final URL REPORT = SerializeReportActionTest.class.getResource("/examples/results/report.xml");
private Path tmpDirectory;
@ -48,31 +48,31 @@ public class SerializeReportActionTest {
@Before
public void setup() throws IOException {
tmpDirectory = Files.createTempDirectory("checktool");
action = new SerializeReportAction(tmpDirectory);
this.tmpDirectory = Files.createTempDirectory("checktool");
this.action = new SerializeReportAction(this.tmpDirectory);
}
@After
public void tearDown() throws IOException {
FileUtils.deleteDirectory(tmpDirectory.toFile());
FileUtils.deleteDirectory(this.tmpDirectory.toFile());
}
@Test
public void testSimpleSerialize() {
CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(REPORT));
assertThat(action.isSkipped(b)).isTrue();
b.setReport(Helper.load(REPORT));
assertThat(action.isSkipped(b)).isFalse();
action.check(b);
public void testSimpleSerialize() throws MalformedURLException {
final CheckAction.Bag b = new CheckAction.Bag(InputFactory.read(Simple.SIMPLE_VALID));
assertThat(this.action.isSkipped(b)).isTrue();
b.setReport(Helper.load(Simple.SIMPLE_VALID.toURL()));
assertThat(this.action.isSkipped(b)).isFalse();
this.action.check(b);
assertThat(b.isStopped()).isFalse();
assertThat(tmpDirectory.toFile().listFiles()).hasSize(1);
assertThat(this.tmpDirectory.toFile().listFiles()).hasSize(1);
}
//ERPT-83
@Test
public void testName(){
final String name = "some.name.with.dots";
CheckAction.Bag b = new CheckAction.Bag(InputFactory.read("ega".getBytes(), name + ".xml"));
final CheckAction.Bag b = new CheckAction.Bag(InputFactory.read("ega".getBytes(), name + ".xml"));
assertThat(b.getName()).isEqualTo(name);
}

View file

@ -22,10 +22,11 @@ package de.kosit.validationtool.impl;
import static de.kosit.validationtool.api.InputFactory.read;
import static de.kosit.validationtool.impl.Helper.Simple.GARBAGE;
import static de.kosit.validationtool.impl.Helper.Simple.NOT_WELLFORMED;
import static de.kosit.validationtool.impl.Helper.Simple.REJECTED;
import static de.kosit.validationtool.impl.Helper.Simple.UNKNOWN;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.net.URISyntaxException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@ -54,7 +55,7 @@ public class DefaultCheckTest {
private DefaultCheck implementation;
@Before
public void setup() throws URISyntaxException {
public void setup() {
final CheckConfiguration d = new CheckConfiguration(Simple.SCENARIOS);
d.setScenarioRepository(new File(Simple.REPOSITORY).toURI());
this.implementation = new DefaultCheck(d);
@ -74,8 +75,8 @@ public class DefaultCheckTest {
final Result doc = this.implementation.checkInput(read(Simple.FOO));
assertThat(doc).isNotNull();
assertThat(doc.getReport()).isNotNull();
assertThat(doc.isAcceptable()).isFalse();
assertThat(doc.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.UNDEFINED);
assertThat(doc.isAcceptable()).isTrue();
assertThat(doc.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@Test
@ -120,6 +121,17 @@ public class DefaultCheckTest {
assertThat(result.isProcessingSuccessful()).isFalse();
}
@Test
public void testNoScenario() {
final Result result = this.implementation.checkInput(read(UNKNOWN));
assertThat(result).isNotNull();
assertThat(result.isWellformed()).isTrue();
assertThat(result.isProcessingSuccessful()).isTrue();
assertThat(result.isSchemaValid()).isFalse();
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT);
assertThat(result.isAcceptable()).isFalse();
}
@Test
public void testNotWellFormed() {
final Result result = this.implementation.checkInput(read(NOT_WELLFORMED));
@ -130,7 +142,19 @@ public class DefaultCheckTest {
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT);
assertThat(result.getReport()).isNotNull();
assertThat(result.getReportDocument()).isNotNull();
System.out.println(Helper.serialize(result.getReportDocument()));
}
@Test
public void testRejectAcceptMatch() {
final Result result = this.implementation.checkInput(read(REJECTED));
assertThat(result).isNotNull();
assertThat(result.isWellformed()).isTrue();
assertThat(result.isSchemaValid()).isTrue();
assertThat(result.isProcessingSuccessful()).isTrue();
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT);
assertThat(result.isAcceptable()).isFalse();
assertThat(result.getReport()).isNotNull();
assertThat(result.getReportDocument()).isNotNull();
}
}

View file

@ -57,6 +57,8 @@ public class Helper {
public static final URI FOO = Simple.ROOT.resolve("input/foo.xml");
public static final URI REJECTED = Simple.ROOT.resolve("input/withManualReject.xml");
public static final URI SCENARIOS = ROOT.resolve("scenarios.xml");
public static final URI REPOSITORY = ROOT.resolve("repository/");

View file

@ -3,7 +3,6 @@ package de.kosit.validationtool.impl;
import static org.assertj.core.api.Assertions.assertThat;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import org.junit.Before;
import org.junit.Test;
@ -19,12 +18,12 @@ import de.kosit.validationtool.impl.Helper.Simple;
*
* @author Andreas Penski
*/
public class SimpleScenarioCheck {
public class SimpleScenarioCheckTest {
private DefaultCheck implementation;
@Before
public void setup() throws URISyntaxException {
public void setup() {
final CheckConfiguration d = new CheckConfiguration(Simple.SCENARIOS);
d.setScenarioRepository(Simple.REPOSITORY);
this.implementation = new DefaultCheck(d);
@ -49,14 +48,17 @@ public class SimpleScenarioCheck {
public void testUnknown() throws MalformedURLException {
final Result result = this.implementation.checkInput(InputFactory.read(Simple.UNKNOWN.toURL()));
assertThat(result).isNotNull();
assertThat(result.isProcessingSuccessful()).isFalse();
assertThat(result.isProcessingSuccessful()).isTrue();
assertThat(result.isAcceptable()).isFalse();
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testWithoutAcceptMatch() throws MalformedURLException {
final Result result = this.implementation.checkInput(InputFactory.read(Simple.FOO.toURL()));
assertThat(result).isNotNull();
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.UNDEFINED);
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
}

View file

@ -0,0 +1,101 @@
package de.kosit.validationtool.impl.tasks;
import static de.kosit.validationtool.impl.tasks.TestBagBuilder.createBag;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
import org.junit.Test;
import de.kosit.validationtool.api.AcceptRecommendation;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
/**
* Tests the 'acceptMatch' functionality.
*
* @author Andreas Penski
*/
public class ComputeAcceptanceActionTest {
private final ComputeAcceptanceAction action = new ComputeAcceptanceAction();
@Test
public void simpleTest() {
final Bag bag = createBag(true, true);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.UNDEFINED);
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@Test
public void testSchemaFailed() {
final Bag bag = createBag(false, true);
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testSchematronFailed() {
final Bag bag = createBag(true, false);
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testValidAcceptMatch() {
final Bag bag = createBag(true, true);
bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@Test
public void testAcceptMatchNotSatisfied() {
final Bag bag = createBag(true, true);
bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 1");
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testAcceptMatchOverridesSchematronErrors() {
final Bag bag = createBag(true, false);
bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@Test
public void testValidAcceptMatchOnSchemaFailed() {
final Bag bag = createBag(false, true);
bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testMissingSchemaCheck() {
final Bag bag = createBag(null, Collections.emptyList());
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@Test
public void testNoSchematronCheck() {
final Bag bag = createBag(true, true);
// remove schematron results
bag.getReportInput().getValidationResultsSchematron().clear();
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@Test
public void testMissingReport() {
final Bag bag = createBag(false, true);
bag.setReport(null);
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
}

View file

@ -1,8 +1,15 @@
package de.kosit.validationtool.impl.tasks;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.oclc.purl.dsdl.svrl.FailedAssert;
import org.oclc.purl.dsdl.svrl.SchematronOutput;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
@ -10,10 +17,15 @@ import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron;
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron.Results;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import de.kosit.validationtool.model.scenarios.ResourceType;
import de.kosit.validationtool.model.scenarios.ScenarioType;
import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema;
import net.sf.saxon.s9api.XdmNode;
/**
* Utilities for creating test objects.
*
@ -26,7 +38,11 @@ public class TestBagBuilder {
}
public static Bag createBag(final Input input, final boolean parse) {
final Bag bag = new Bag(input, new CreateReportInput());
return createBag(input, parse, new CreateReportInput());
}
public static Bag createBag(final Input input, final boolean parse, final CreateReportInput reportInput) {
final Bag bag = new Bag(input, reportInput);
if (parse) {
bag.setParserResult(DocumentParseAction.parseDocument(bag.getInput()));
}
@ -46,4 +62,36 @@ public class TestBagBuilder {
t.initialize(repository, true);
return t;
}
private static XdmNode createReport() {
return DocumentParseAction.parseDocument(InputFactory.read("<some>xml</some>".getBytes(), "someXml")).getObject();
}
static Bag createBag(final boolean schemaValid, final boolean schematronValid) {
final Result<Boolean, XMLSyntaxError> schemaResult = schemaValid ? new Result<>(true)
: new Result<>(Collections.singletonList(new XMLSyntaxError()));
final List<ValidationResultsSchematron> schematronResult = schematronValid ? Collections.emptyList() : createSchematronError();
return createBag(schemaResult, schematronResult);
}
private static List<ValidationResultsSchematron> createSchematronError() {
final ValidationResultsSchematron v = new ValidationResultsSchematron();
final SchematronOutput out = new SchematronOutput();
final FailedAssert f = new FailedAssert();
out.getActivePatternAndFiredRuleAndFailedAssert().add(f);
final Results r = new Results();
r.setSchematronOutput(out);
v.setResults(r);
return Collections.singletonList(v);
}
static Bag createBag(final Result<Boolean, XMLSyntaxError> schemaResult,
final Collection<ValidationResultsSchematron> schematronResult) {
final CreateReportInput reportInput = new CreateReportInput();
reportInput.getValidationResultsSchematron().addAll(schematronResult);
final Bag b = createBag(InputFactory.read("<someXml></someXml>".getBytes(), "someCheck"), true, reportInput);
b.setSchemaValidationResult(schemaResult);
b.setReport(createReport());
return b;
}
}

View file

@ -1,330 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Koordinierungsstelle für IT-Standards (KoSIT) under
~ one or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. KoSIT licenses this file
~ to you 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.
-->
<rep:report xmlns:rep="http://www.xoev.de/de/validator/varl/1 " valid="false" varlVersion="1.0.0">
<rep:engine>
<rep:name>KoSIT POC</rep:name>
</rep:engine>
<rep:timestamp>2017-09-01T13:13:59.055+02:00</rep:timestamp>
<rep:documentIdentification>
<rep:documentHash>
<rep:hashAlgorithm>SHA-256</rep:hashAlgorithm>
<rep:hashValue>4exhW9EJxAbhlZLHZ3mYZ3/hWGG5e6mIpiTAlGTpQ7s=</rep:hashValue>
</rep:documentHash>
<rep:documentReference>
/C:/Developer/source/init/eRechnung-Check/src/test/resources/examples/UBLReady/UBLReady_EU_UBL-NL_20170102_FULL.xml
</rep:documentReference>
</rep:documentIdentification>
<ns2:scenario xmlns:ns2="http://www.xoev.de/de/validator/framework/1/scenarios"
xmlns="http://www.xoev.de/de/validator/framework/1/createreportinput">
<ns2:name>UBL 2.1 Invoice</ns2:name>
<ns2:namespace prefix="invoice">urn:oasis:names:specification:ubl:schema:xsd:Invoice-2</ns2:namespace>
<ns2:match>/invoice:Invoice</ns2:match>
<ns2:validateWithXmlSchema>
<ns2:resource>
<ns2:name>UBL 2.1 Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsdrt/maindoc/UBL-Invoice-2.1.xsd</ns2:location>
</ns2:resource>
</ns2:validateWithXmlSchema>
<ns2:validateWithSchematron>
<ns2:resource>
<ns2:name>BII Rules for Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsl/BIIRULES-UBL-T10.xsl</ns2:location>
</ns2:resource>
</ns2:validateWithSchematron>
<ns2:validateWithSchematron>
<ns2:resource>
<ns2:name>openPEPPOL Rules for Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsl/OPENPEPPOL-UBL-T10.xsl</ns2:location>
</ns2:resource>
</ns2:validateWithSchematron>
<ns2:createReport>
<ns2:resource>
<ns2:name>Report für eRechnung</ns2:name>
<ns2:location>resources/eRechnung/report.xsl</ns2:location>
</ns2:resource>
</ns2:createReport>
</ns2:scenario>
<rep:validationResult>
<rep:validationStepResult id="step_1" valid="true">
<ns2:resource xmlns:ns2="http://www.xoev.de/de/validator/framework/1/scenarios"
xmlns="http://www.xoev.de/de/validator/framework/1/createreportinput">
<ns2:name>UBL 2.1 Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsdrt/maindoc/UBL-Invoice-2.1.xsd</ns2:location>
</ns2:resource>
</rep:validationStepResult>
<rep:validationStepResult id="step_2" valid="false">
<ns2:resource xmlns:ns2="http://www.xoev.de/de/validator/framework/1/scenarios"
xmlns="http://www.xoev.de/de/validator/framework/1/createreportinput">
<ns2:name>BII Rules for Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsl/BIIRULES-UBL-T10.xsl</ns2:location>
</ns2:resource>
<rep:message code="CL-T10-R010" id="message_2.1" level="warning"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AllowanceCharge[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][3]/*:AllowanceChargeReasonCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[CL-T10-R010]-Coded allowance and charge reasons SHOULD belong to the UNCL 4465 code list BII2 subset
</rep:message>
</rep:validationStepResult>
<rep:validationStepResult id="step_3" valid="false">
<ns2:resource xmlns:ns2="http://www.xoev.de/de/validator/framework/1/scenarios"
xmlns="http://www.xoev.de/de/validator/framework/1/createreportinput">
<ns2:name>openPEPPOL Rules for Invoice</ns2:name>
<ns2:location>resources/eRechnung/UBL-2.1/xsl/OPENPEPPOL-UBL-T10.xsl</ns2:location>
</ns2:resource>
<rep:message code="EUGEN-T10-R026" id="message_3.1" level="error"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:DocumentCurrencyCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[EUGEN-T10-R026]-A currency code element MUST have a list identifier attribute 'ISO4217'.
</rep:message>
<rep:message code="EUGEN-T10-R041" id="message_3.2" level="warning"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AccountingSupplierParty[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]">
[EUGEN-T10-R041]-The VAT identifier for the supplier SHOULD be prefixed with country code for companies
with VAT registration in EU countries
</rep:message>
<rep:message code="EUGEN-T10-R054" id="message_3.3" level="warning"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AccountingSupplierParty[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:Party[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:PartyLegalEntity[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:CompanyID[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[EUGEN-T10-R054]-A party legal entity company identifier SHOULD have a scheme identifier attribute.
</rep:message>
<rep:message code="EUGEN-T10-R029" id="message_3.4" level="error"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AllowanceCharge[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][1]/*:AllowanceChargeReasonCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier attribute 'UNCL4465'.
</rep:message>
<rep:message code="EUGEN-T10-R029" id="message_3.5" level="error"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AllowanceCharge[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][2]/*:AllowanceChargeReasonCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier attribute 'UNCL4465'.
</rep:message>
<rep:message code="EUGEN-T10-R029" id="message_3.6" level="error"
xpathLocation="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]/*:AllowanceCharge[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'][3]/*:AllowanceChargeReasonCode[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'][1]">
[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier attribute 'UNCL4465'.
</rep:message>
</rep:validationStepResult>
</rep:validationResult>
<rep:assessment>
<rep:reject>
<rep:explanation>
<html xmlns="http://www.w3.org/1999/xhtml" data-report-type="devreport-reject">
<head>
<title>Pruefbericht der KoSIT</title>
<style>
body{
font-family: Calibri;
width: 230mm;
}
table{
border-collapse: collapse;
width: 100%;
}
table.tbl-errors{
font-size: 10pt;
}
.tbl-errors td{
border: 1px solid lightgray;
padding: 2px;
vertical-align: top;
}
thead{
font-weight: bold;
background-color: #f0f0f0;
padding-top: 6pt;
padding-bottom: 2pt;
}
.tbl-meta td{
padding-right: 1em;
}
tr{
vertical-align: bottom;
border-bottom: 1px solid #c0c0c0;
}
tr.error{
font-weight: bold;
color: red;
}
tr.warning{
font-weight: bold;
}
tr.pos{
font-weight: bold;
}
p.important{
font-weight: bold;
text-align: left;
background-color: #e0e0e0;
padding: 3pt;
}
td.right{
text-align: right
}
</style>
</head>
<body>
<h1>Prüfbericht der KoSIT</h1>
<h2>Angaben zum geprüften Dokument</h2>
<table class="tbl-meta">
<tr>
<td colspan="2">Dokument:</td>
<td colspan="3">
/C:/Developer/source/init/eRechnung-Check/src/test/resources/examples/UBLReady/UBLReady_EU_UBL-NL_20170102_FULL.xml
</td>
</tr>
<tr>
<td colspan="2">Szenario:</td>
<td colspan="3"/>
</tr>
<tr>
<td colspan="2">Zeitpunkt:</td>
<td colspan="3">1.9.2017 13:13:59</td>
</tr>
<tr>
<td colspan="2">Validierungsschritte:</td>
<td>Fehler</td>
<td>Warnung</td>
<td>Information</td>
</tr>
</table>
<p class="important"><b>Konformitätsprüfung:</b>Das geprüfte Dokument enthält 4 Fehler / 3
Warnungen. Es ist <b>nicht konform</b> zu den formalen Vorgaben.
</p>
<table class="tbl-errors">
<thead>
<tr>
<th>Pos</th>
<th>Code</th>
<th>CustomLevel (Level)</th>
<th>Step</th>
<th>Text</th>
</tr>
</thead>
<tbody>
<tr class="warning">
<td>1</td>
<td>CL-T10-R010</td>
<td>warning</td>
<td/>
<td>[CL-T10-R010]-Coded allowance and charge reasons SHOULD belong to the UNCL 4465
code list BII2 subset
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="error">
<td>2</td>
<td>EUGEN-T10-R026</td>
<td>error</td>
<td/>
<td>[EUGEN-T10-R026]-A currency code element MUST have a list identifier attribute
'ISO4217'.
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="warning">
<td>3</td>
<td>EUGEN-T10-R041</td>
<td>warning</td>
<td/>
<td>[EUGEN-T10-R041]-The VAT identifier for the supplier SHOULD be prefixed with
country code for companies with VAT registration in EU countries
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="warning">
<td>4</td>
<td>EUGEN-T10-R054</td>
<td>warning</td>
<td/>
<td>[EUGEN-T10-R054]-A party legal entity company identifier SHOULD have a scheme
identifier attribute.
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="error">
<td>5</td>
<td>EUGEN-T10-R029</td>
<td>error</td>
<td/>
<td>[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier
attribute 'UNCL4465'.
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="error">
<td>6</td>
<td>EUGEN-T10-R029</td>
<td>error</td>
<td/>
<td>[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier
attribute 'UNCL4465'.
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
<tr class="error">
<td>7</td>
<td>EUGEN-T10-R029</td>
<td>error</td>
<td/>
<td>[EUGEN-T10-R029]-An allowance charge reason code MUST have a list identifier
attribute 'UNCL4465'.
</td>
</tr>
<tr>
<td colspan="4"/>
<td/>
</tr>
</tbody>
</table>
<p class="important"><b>Bewertung:</b>Es wird empfohlen das Dokument zurückzuweisen.
</p>
<p class="info">Erstellt mit: KoSIT POC für das InstructionSet
<em/>
vom .
</p>
</body>
</html>
</rep:explanation>
</rep:reject>
</rep:assessment>
</rep:report>

View file

@ -2,4 +2,7 @@
<foo xmlns="http://validator.kosit.de/test-sample">
<inner>asldkfj</inner>
<content>
<rejected/>
</content>
</foo>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<simple xmlns="http://validator.kosit.de/test-sample">
<inner>asldkfj</inner>
<content>
<rejected>directly transferred, so this is part of the report</rejected>
</content>
</simple>

View file

@ -47,7 +47,7 @@
<location>report.xsl</location>
</resource>
</createReport>
<acceptMatch>count(//cri:xmlSyntaxError) = 0</acceptMatch>
<acceptMatch>count(//test:rejected) = 0</acceptMatch>
</scenario>
<scenario>