/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* 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 org.kie.dmn.core;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Map;
import org.junit.Test;
import org.kie.dmn.api.core.DMNContext;
import org.kie.dmn.api.core.DMNMessage;
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.api.core.event.AfterEvaluateDecisionTableEvent;
import org.kie.dmn.api.core.event.DMNEvent;
import org.kie.dmn.api.core.event.DMNRuntimeEventListener;
import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
import org.kie.dmn.core.api.DMNFactory;
import org.kie.dmn.core.util.DMNRuntimeUtil;
import org.kie.dmn.feel.runtime.events.HitPolicyViolationEvent;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
public class DMNDecisionTableRuntimeTest {
@Test
public void testSimpleDecisionTableUniqueHitPolicy() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "0004-simpletable-U.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "https://github.com/kiegroup/kie-dmn", "0004-simpletable-U" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Age", new BigDecimal( 18 ) );
context.set( "RiskCategory", "Medium" );
context.set( "isAffordable", true );
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
DMNContext result = dmnResult.getContext();
assertThat( result.get( "Approval Status" ), is( "Approved" ) );
}
@Test
public void testSimpleDecisionTableUniqueHitPolicySatisfies() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "0004-simpletable-U.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "https://github.com/kiegroup/kie-dmn", "0004-simpletable-U" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Age", new BigDecimal( 18 ) );
context.set( "RiskCategory", "ASD" );
context.set( "isAffordable", false );
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
DMNContext result = dmnResult.getContext();
assertThat( result.get( "Approval Status" ), nullValue() );
assertTrue( dmnResult.getMessages().size() > 0 );
}
@Test
public void testSimpleDecisionTableUniqueHitPolicyNullWarn() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "0004-simpletable-U-noinputvalues.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "https://github.com/kiegroup/kie-dmn", "0004-simpletable-U-noinputvalues" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Age", new BigDecimal( 18 ) );
context.set( "RiskCategory", "ASD" );
context.set( "isAffordable", false );
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
DMNContext result = dmnResult.getContext();
assertThat( result.get( "Approval Status" ), nullValue() );
assertTrue( dmnResult.getMessages().size() > 0 );
assertTrue( dmnResult.getMessages().stream().anyMatch( dm -> dm.getSeverity().equals(DMNMessage.Severity.WARN)
&& dm.getFeelEvent() instanceof HitPolicyViolationEvent
&& dm.getFeelEvent().getSeverity().equals(FEELEvent.Severity.WARN) ) );
}
@Test
public void testDecisionTableWithCalculatedResult() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "calculation1.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/definitions/_77ae284e-ce52-4579-a50f-f3cc584d7f4b", "Calculation1" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "MonthlyDeptPmt", BigDecimal.valueOf( 200 ) );
context.set( "MonthlyPmt", BigDecimal.valueOf( 100 ) );
context.set( "MonthlyIncome", BigDecimal.valueOf( 600 ) );
DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
assertThat( dmnResult.hasErrors(), is( false ) );
DMNContext result = dmnResult.getContext();
assertThat( ((BigDecimal)result.get("Logique de décision 1")).setScale( 1, RoundingMode.CEILING), is( BigDecimal.valueOf( 0.5 ) ) );
}
@Test
public void testDecisionTableMultipleResults() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "car_damage_responsibility.dmn", this.getClass() );
DMNRuntimeEventListener listener = Mockito.mock( DMNRuntimeEventListener.class );
runtime.addListener( listener );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/definitions/_820611e9-c21c-47cd-8e52-5cba2be9f9cc", "Car Damage Responsibility" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Membership Level", "Silver" );
context.set( "Damage Types", "Body" );
context.set( "Responsible", "Driver" );
DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
assertThat( dmnResult.hasErrors(), is( false ) );
DMNContext result = dmnResult.getContext();
assertThat( (Map<String,Object>)result.get("Car Damage Responsibility"), hasEntry( is( "EU Rent" ), is( BigDecimal.valueOf( 40 )) ));
assertThat( (Map<String,Object>)result.get("Car Damage Responsibility"), hasEntry( is( "Renter" ), is( BigDecimal.valueOf( 60 )) ));
assertThat( result.get("Payment method"), is( "Check" ) );
ArgumentCaptor<AfterEvaluateDecisionTableEvent> captor = ArgumentCaptor.forClass(AfterEvaluateDecisionTableEvent.class);
verify( listener, times(2) ).afterEvaluateDecisionTable( captor.capture() );
AfterEvaluateDecisionTableEvent first = captor.getAllValues().get( 0 );
assertThat( first.getMatches(), is( Arrays.asList( 5 ) ) );
assertThat( first.getSelected(), is( Arrays.asList( 5 ) ) );
AfterEvaluateDecisionTableEvent second = captor.getAllValues().get( 1 );
assertThat( second.getMatches(), is( Arrays.asList( 3 ) ) );
assertThat( second.getSelected(), is( Arrays.asList( 3 ) ) );
}
@Test
public void testDecisionTableUniqueHitPolicy() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "BranchDistribution.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/dmn/definitions/_cdf29af2-959b-4004-8271-82a9f5a62147", "Dessin 1" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Branches dispersion", "Province" );
context.set( "Number of Branches", BigDecimal.valueOf( 10 ) );
DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
assertThat( dmnResult.hasErrors(), is( false ) );
DMNContext result = dmnResult.getContext();
System.out.println(result);
assertThat( result.get("Branches distribution"), is( "Medium" ) );
}
@Test
public void testDecisionTableCollectHitPolicy() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "Collect_Hit_Policy.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/definitions/_da1a4dcb-01bf-4dee-9be8-f498bc68178c", "Collect Hit Policy" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Input", 20 );
DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
assertThat( dmnResult.hasErrors(), is( false ) );
DMNContext result = dmnResult.getContext();
assertThat( result.get("Collect"), is( BigDecimal.valueOf( 50 ) ) );
}
@Test
public void testDecisionTableInvalidInputErrorMessage() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "InvalidInput.dmn", this.getClass() );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/dmn/definitions/_cdf29af2-959b-4004-8271-82a9f5a62147", "Dessin 1" );
assertThat( dmnModel, notNullValue() );
DMNContext context = DMNFactory.newContext();
context.set( "Branches dispersion", "Province" );
context.set( "Number of Branches", BigDecimal.valueOf( 10 ) );
DMNResult dmnResult = runtime.evaluateAll(dmnModel, context);
assertThat( dmnResult.hasErrors(), is( true ) );
DMNContext result = dmnResult.getContext();
assertThat( result.isDefined("Branches distribution"), is( false ) );
}
@Test
public void testDecisionTableDefaultValue() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "decisiontable-default-value.dmn", this.getClass() );
DMNRuntimeEventListener listener = Mockito.mock( DMNRuntimeEventListener.class );
runtime.addListener( listener );
DMNModel dmnModel = runtime.getModel( "https://github.com/kiegroup/kie-dmn", "decisiontable-default-value" );
assertThat( dmnModel, notNullValue() );
assertThat( dmnModel.getMessages().toString(), dmnModel.hasErrors(), is(false) );
DMNContext context = DMNFactory.newContext();
context.set( "Age", new BigDecimal( 16 ) );
context.set( "RiskCategory", "Medium" );
context.set( "isAffordable", true );
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
assertThat( dmnResult.getMessages().toString(), dmnResult.hasErrors(), is(false) );
DMNContext result = dmnResult.getContext();
assertThat( result.get( "Approval Status" ), is( "Declined" ) );
ArgumentCaptor<AfterEvaluateDecisionTableEvent> captor = ArgumentCaptor.forClass(AfterEvaluateDecisionTableEvent.class);
verify( listener ).afterEvaluateDecisionTable( captor.capture() );
assertThat( captor.getValue().getMatches(), is( empty() ) );
assertThat( captor.getValue().getSelected(), is( empty() ) );
}
@Test
public void testTwoDecisionTables() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntime( "two_decision_tables.dmn", this.getClass() );
DMNRuntimeEventListener listener = Mockito.mock( DMNRuntimeEventListener.class );
runtime.addListener( listener );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/definitions/_bbb692e7-3d95-407a-bf39-353085bf57f0", "Invocation with two decision table as parameters" );
assertThat( dmnModel, notNullValue() );
assertThat( dmnModel.getMessages().toString(), dmnModel.hasErrors(), is(false) );
DMNContext context = DMNFactory.newContext();
context.set( "Number", 50 );
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
assertThat( dmnResult.getMessages().toString(), dmnResult.hasErrors(), is(false) );
DMNContext result = dmnResult.getContext();
assertThat( (Map<String, Object>)result.get( "Decision Logic 2" ), hasEntry( "the 5 analysis", "A number greater than 5" ) );
assertThat( (Map<String, Object>)result.get( "Decision Logic 2" ), hasEntry( "the 100 analysis", "A number smaller than 100" ) );
ArgumentCaptor<AfterEvaluateDecisionTableEvent> captor = ArgumentCaptor.forClass(AfterEvaluateDecisionTableEvent.class);
verify( listener, times(2) ).afterEvaluateDecisionTable( captor.capture() );
assertThat( captor.getAllValues().get( 0 ).getDecisionTableName(), is( "a" ) );
assertThat( captor.getAllValues().get( 1 ).getDecisionTableName(), is( "b" ) );
}
}