diff --git a/kie-dmn/kie-dmn-core/pom.xml b/kie-dmn/kie-dmn-core/pom.xml index ab4f01aae28..cfa0dea7c8b 100644 --- a/kie-dmn/kie-dmn-core/pom.xml +++ b/kie-dmn/kie-dmn-core/pom.xml @@ -276,5 +276,35 @@ true + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + generate-test-resources + + unpack + + + + + org.kie + kie-dmn-test-resources + ${project.version} + jar + tests + true + ${project.build.directory}/test-classes/org/kie/dmn/core + **/*.dmn + + + + + + + diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java index 203c44f32af..b335f3a7fd1 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java @@ -18,7 +18,9 @@ */ package org.kie.dmn.core.impl; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -30,6 +32,7 @@ import javax.xml.namespace.QName; import org.drools.kiesession.rulebase.InternalKnowledgeBase; +import org.kie.api.builder.Message; import org.kie.dmn.api.core.DMNContext; import org.kie.dmn.api.core.DMNDecisionResult; import org.kie.dmn.api.core.DMNMessage; @@ -111,6 +114,7 @@ public DMNResult evaluateAll(DMNModel model, DMNContext context) { Objects.requireNonNull(model, () -> MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "model")); Objects.requireNonNull(context, () -> MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "context")); boolean performRuntimeTypeCheck = performRuntimeTypeCheck(model); + identifyDecisionErrors(model); DMNResultImpl result = createResult( model, context ); DMNRuntimeEventManagerUtils.fireBeforeEvaluateAll( eventManager, model, result ); // the engine should evaluate all Decisions belonging to the "local" model namespace, not imported decision explicitly. @@ -148,6 +152,7 @@ public DMNResult evaluateByName( DMNModel model, DMNContext context, String... d if (decisionNames.length == 0) { throw new IllegalArgumentException(MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_EMPTY, "decisionNames")); } + identifyDecisionErrors(model, decisionNames); final DMNResultImpl result = createResult( model, context ); for (String name : decisionNames) { evaluateByNameInternal( model, context, result, name ); @@ -184,6 +189,7 @@ public DMNResult evaluateById( DMNModel model, DMNContext context, String... dec if (decisionIds.length == 0) { throw new IllegalArgumentException(MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_EMPTY, "decisionIds")); } + identifyDecisionErrors(model, decisionIds); final DMNResultImpl result = createResult( model, context ); for ( String id : decisionIds ) { evaluateByIdInternal( model, context, result, id ); @@ -754,6 +760,15 @@ private static String getDependencyIdentifier(DMNNode callerNode, DMNNode node) } + private static void identifyDecisionErrors(DMNModel model, String... decisions) { + List errorMessages = model.getMessages(DMNMessage.Severity.ERROR); + List identifiedErrors = errorMessages.stream().map(Message::getText) + .filter(messages -> decisions.length == 0 || Arrays.stream(decisions).anyMatch(messages::contains)).collect(Collectors.toList()); + if (!identifiedErrors.isEmpty()) { + throw new IllegalStateException(String.join(", ", identifiedErrors)); + } + } + public boolean performRuntimeTypeCheck(DMNModel model) { Objects.requireNonNull(model, () -> MsgUtil.createMessage(Msg.PARAM_CANNOT_BE_NULL, "model")); return overrideRuntimeTypeCheck || ((DMNModelImpl) model).isRuntimeTypeCheck(); diff --git a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/internal/utils/DMNRuntimeBuilderTest.java b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/internal/utils/DMNRuntimeBuilderTest.java index f10cec99ede..1ed6dc20f8e 100644 --- a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/internal/utils/DMNRuntimeBuilderTest.java +++ b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/internal/utils/DMNRuntimeBuilderTest.java @@ -18,13 +18,23 @@ */ package org.kie.dmn.core.internal.utils; +import java.io.File; import java.util.Collections; +import org.drools.util.FileUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.kie.api.io.Resource; +import org.kie.dmn.api.core.DMNContext; +import org.kie.dmn.api.core.DMNModel; +import org.kie.dmn.api.core.DMNResult; +import org.kie.dmn.api.core.DMNRuntime; +import org.kie.dmn.core.api.DMNFactory; import org.kie.dmn.core.impl.DMNRuntimeImpl; +import org.kie.internal.io.ResourceFactory; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; class DMNRuntimeBuilderTest { @@ -43,4 +53,198 @@ void buildFromConfiguration() { .fromResources(Collections.emptyList()).getOrElseThrow(RuntimeException::new); assertThat(retrieved).isNotNull(); } + + @Test + void fromDefaultsMultipleDecisionWithoutInputDataReference() { + File modelFile = FileUtils.getFile("Invalid_decisions_model.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_BDC29BCF-B5DC-4AD7-8A5F-43DC08780F97"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_1A4BD262-7672-4887-9F25-986EE5277D16"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + String errorMessage = "DMN: Error compiling FEEL expression 'Person Age >= 18' for name 'Can Drive?' on node 'Can Drive?': syntax error near 'Age' (DMN id: _563E78C7-EFD1-4109-9F30-B14922EF68DF, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateAll(dmnModel, context); + }).withMessage(errorMessage); + } + + @Test + void evaluateMultipleDecisionModel() { + File modelFile = FileUtils.getFile("MultipleDecision.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_CADD03FC-4ABD-46D2-B631-E7FDE384D6D7"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_54AA2CFA-2374-4FCE-8F16-B594DFF87EBE"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + DMNResult dmnResult = dmnRuntime.evaluateAll(dmnModel, context); + assertThat(dmnResult).isNotNull(); + } + + @Test + void evaluateWrongDecisionWithoutInputDataReferencesByName() { + File modelFile = FileUtils.getFile("Invalid_decisions_model.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_BDC29BCF-B5DC-4AD7-8A5F-43DC08780F97"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_1A4BD262-7672-4887-9F25-986EE5277D16"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + String errorMessage = "DMN: Error compiling FEEL expression 'Person Age >= 18' for name 'Can Drive?' on node 'Can Drive?': syntax error near 'Age' (DMN id: _563E78C7-EFD1-4109-9F30-B14922EF68DF, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateByName(dmnModel, context, "Can Drive"); + }).withMessage(errorMessage); + } + + @Test + void evaluateRightDecisionWithoutInputDataReferencesByName() { + File modelFile = FileUtils.getFile("Invalid_decisions_model.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_BDC29BCF-B5DC-4AD7-8A5F-43DC08780F97"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_1A4BD262-7672-4887-9F25-986EE5277D16"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + DMNResult dmnResult = dmnRuntime.evaluateByName(dmnModel, context, "Can Vote?"); + assertThat(dmnResult).isNotNull(); + } + + @Test + void evaluateWrongDecisionWithoutInputDataReferencesById() { + File modelFile = FileUtils.getFile("Invalid_decisions_model.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_BDC29BCF-B5DC-4AD7-8A5F-43DC08780F97"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_1A4BD262-7672-4887-9F25-986EE5277D16"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + String errorMessage = "DMN: Error compiling FEEL expression 'Person Age >= 18' for name 'Can Drive?' on node 'Can Drive?': syntax error near 'Age' (DMN id: _563E78C7-EFD1-4109-9F30-B14922EF68DF, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateById(dmnModel, context, "_563E78C7-EFD1-4109-9F30-B14922EF68DF"); + }).withMessage(errorMessage); + } + + @Test + void evaluateRightDecisionWithoutInputDataReferencesById() { + File modelFile = FileUtils.getFile("Invalid_decisions_model.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_BDC29BCF-B5DC-4AD7-8A5F-43DC08780F97"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_1A4BD262-7672-4887-9F25-986EE5277D16"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + DMNResult dmnResult = dmnRuntime.evaluateById(dmnModel, context, "_7ACCB8BC-A382-4530-B8EE-AD32D187FD8B"); + assertThat(dmnResult).isNotNull(); + } + + @Test + void evaluateDecisionWithInvalidFeelError() { + File modelFile = FileUtils.getFile("InvalidFeel.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_9DF86C49-C80A-4744-9F50-BCE65A89C98C"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_33900B8B-73DD-4D1E-87E9-F6C3FE534B43"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set("Person Age", 24); + String errorMessage = "DMN: Error compiling FEEL expression 'Person Age >?= 18' for name 'Can Drive' on node 'Can Drive': Unknown variable '?' (DMN id: _F477B6E0-C617-4087-9648-DE25A711C5F9, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateByName(dmnModel, context, "Can Drive"); + }).withMessage(errorMessage); + + } + + @Test + void evaluateMultipleErrorDecision() { + File modelFile = FileUtils.getFile("MultipleErrorDecision.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_36ADF828-4BE5-41E1-8808-6245D13C6AB4"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_45A15AF7-9910-4EAD-B249-8AE218B3BF43"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + String errorMessage = "DMN: Error compiling FEEL expression 'Age + 20.?>' for name 'ContextEntry-1' on node 'Can Vote?': syntax error near '+' (DMN id: _B7D17199-0568-40EE-94D0-FDFAB0E97868, Error compiling the referenced FEEL expression) , DMN: Error compiling FEEL expression 'if Age > 25 \"YES\" elsesss \"NO\"' for name 'Can Vote?' on node 'Can Vote?': syntax error near '\"YES\"' (DMN id: _59E71393-14B3-405D-A0B4-3C1E6562823F, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateByName(dmnModel, context, "Can Vote"); + }).withMessage(errorMessage); + } + + @Test + void evaluateMultipleErrorModel() { + File modelFile = FileUtils.getFile("MultipleError.dmn"); + assertThat(modelFile).isNotNull().exists(); + Resource modelResource = ResourceFactory.newFileResource(modelFile); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults().buildConfiguration() + .fromResources(Collections.singletonList(modelResource)).getOrElseThrow(RuntimeException::new); + assertThat(dmnRuntime).isNotNull(); + String nameSpace = "https://kie.org/dmn/_231A34DE-33C6-4787-A51F-228C910D5EAF"; + + final DMNModel dmnModel = dmnRuntime.getModel( + nameSpace, + "DMN_DC99A8C4-4524-407D-B3D1-577442AED995"); + assertThat(dmnModel).isNotNull(); + DMNContext context = DMNFactory.newContext(); + context.set( "Person Age", 24 ); + String errorMessage = "DMN: Error compiling FEEL expression 'Person Age >= 18' for name 'Can Vote?' on node 'Can Vote?': syntax error near 'Age' (DMN id: _E3EF0CCA-0F1E-42B1-8C65-124D77C07E38, Error compiling the referenced FEEL expression) , DMN: Error compiling FEEL expression 'Person Age >=18' for name 'Can Drive?' on node 'Can Drive?': syntax error near 'Age' (DMN id: _B2F31CDD-29D1-4C20-93B8-8FB8E11E1FFC, Error compiling the referenced FEEL expression) "; + assertThatIllegalStateException().isThrownBy(() -> { + dmnRuntime.evaluateByName(dmnModel, context, "Can Vote?", "Can Drive?"); + }).withMessage(errorMessage); + } + } \ No newline at end of file diff --git a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/InvalidFeel.dmn b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/InvalidFeel.dmn new file mode 100644 index 00000000000..b7b58f6ae11 --- /dev/null +++ b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/InvalidFeel.dmn @@ -0,0 +1,54 @@ + + + + + + + + + + + + + Person Age >?= 18 + + + + + + + + 190 + + + + + + + + + + + + + + + + diff --git a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/Invalid_decisions_model.dmn b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/Invalid_decisions_model.dmn new file mode 100644 index 00000000000..75383661572 --- /dev/null +++ b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/Invalid_decisions_model.dmn @@ -0,0 +1,71 @@ + + + + + + + + + + Can Drive + + Person Age >= 18 + + + Person Age >= 18 + + + + + + + + + Person Age>=18 + + + + + + + + 190 + + + 190 + + + + + + + + + + + + + + + + + + + diff --git a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleError.dmn b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleError.dmn new file mode 100644 index 00000000000..caef1591de9 --- /dev/null +++ b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleError.dmn @@ -0,0 +1,59 @@ + + + + + + + + + + Person Age >= 18 + + + + + + Person Age >=18 + + + + + + + + 190 + + + 190 + + + + + + + + + + + + + + + diff --git a/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleErrorDecision.dmn b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleErrorDecision.dmn new file mode 100644 index 00000000000..bfe20aedbc7 --- /dev/null +++ b/kie-dmn/kie-dmn-test-resources/src/test/resources/invalid_models/DMNv1_5/MultipleErrorDecision.dmn @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + Age + 20.?> + + + + + if Age > 25 "YES" elsesss "NO" + + + + + + + + + + 120 + + + 190 + + + 190 + + + + + + + + + + + + + + + + diff --git a/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_5/MultipleDecision.dmn b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_5/MultipleDecision.dmn new file mode 100644 index 00000000000..c85554c46d7 --- /dev/null +++ b/kie-dmn/kie-dmn-test-resources/src/test/resources/valid_models/DMNv1_5/MultipleDecision.dmn @@ -0,0 +1,73 @@ + + + + + + + + + + + + + Person Age >= 18 + + + + + + + + + Person Age>=18 + + + + + + + + 190 + + + 190 + + + + + + + + + + + + + + + + + + + + + + +