001/**
002 * Copyright 2005-2015 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krms.framework;
017
018import static junit.framework.Assert.assertFalse;
019import static junit.framework.Assert.assertNotNull;
020import static junit.framework.Assert.assertTrue;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.junit.Before;
030import org.junit.Test;
031import org.kuali.rice.krms.api.engine.EngineResults;
032import org.kuali.rice.krms.api.engine.ExecutionOptions;
033import org.kuali.rice.krms.api.engine.ExecutionFlag;
034import org.kuali.rice.krms.api.engine.Facts;
035import org.kuali.rice.krms.api.engine.SelectionCriteria;
036import org.kuali.rice.krms.api.engine.Term;
037import org.kuali.rice.krms.api.engine.TermResolver;
038import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
039import org.kuali.rice.krms.framework.engine.Action;
040import org.kuali.rice.krms.framework.engine.Agenda;
041import org.kuali.rice.krms.framework.engine.AgendaTreeEntry;
042import org.kuali.rice.krms.framework.engine.BasicAgenda;
043import org.kuali.rice.krms.framework.engine.BasicAgendaTree;
044import org.kuali.rice.krms.framework.engine.BasicAgendaTreeEntry;
045import org.kuali.rice.krms.framework.engine.BasicContext;
046import org.kuali.rice.krms.framework.engine.BasicRule;
047import org.kuali.rice.krms.framework.engine.ComparableTermBasedProposition;
048import org.kuali.rice.krms.framework.engine.expression.ComparisonOperator;
049import org.kuali.rice.krms.framework.engine.Context;
050import org.kuali.rice.krms.framework.engine.ContextProvider;
051import org.kuali.rice.krms.framework.engine.Proposition;
052import org.kuali.rice.krms.framework.engine.ProviderBasedEngine;
053import org.kuali.rice.krms.framework.engine.ResultLogger;
054import org.kuali.rice.krms.framework.engine.Rule;
055import org.kuali.rice.krms.framework.engine.expression.ComparisonOperatorServiceImpl;
056
057public class AgendaTest {
058        private static final ResultLogger LOG = ResultLogger.getInstance();
059
060        // totalCostTerm will resolve to the Integer value 5
061    private ComparisonOperator operatorGreaterThan;
062        private Proposition trueProp;
063        private Proposition falseProp;
064
065        @Before
066        public void setUp() {
067        operatorGreaterThan = ComparisonOperator.GREATER_THAN;
068        operatorGreaterThan.setComparisonOperatorService(ComparisonOperatorServiceImpl.getInstance());
069        trueProp = new ComparableTermBasedProposition(operatorGreaterThan, totalCostTerm, Integer.valueOf(1));
070        falseProp = new ComparableTermBasedProposition(operatorGreaterThan, totalCostTerm, Integer.valueOf(1000));
071
072                ActionMock.resetActionsFired();
073        }
074
075
076        @Test
077        public void testAllRulesAgenda() {
078
079                Rule rule1 = new BasicRule("r1", trueProp, Collections.<Action>singletonList(new ActionMock("a1")));
080                Rule rule2 = new BasicRule("r2", falseProp, Collections.<Action>singletonList(new ActionMock("a2")));
081                Rule rule3 = new BasicRule("r3", trueProp, Collections.<Action>singletonList(new ActionMock("a3")));
082                
083                AgendaTreeEntry entry1 = new BasicAgendaTreeEntry(rule1);
084                AgendaTreeEntry entry2 = new BasicAgendaTreeEntry(rule2);
085                AgendaTreeEntry entry3 = new BasicAgendaTreeEntry(rule3);
086                BasicAgendaTree agendaTree = new BasicAgendaTree(entry1, entry2, entry3); 
087                Agenda agenda = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree);
088                
089                execute(agenda);
090
091                assertTrue(ActionMock.actionFired("a1"));
092                assertFalse(ActionMock.actionFired("a2"));
093                assertTrue(ActionMock.actionFired("a3"));
094        }
095        
096        @Test
097        public void testIfTrueSubAgenda() {
098
099                Rule rule1 = new BasicRule("r1", trueProp, Collections.<Action>singletonList(new ActionMock("a1")));
100                Rule rule2 = new BasicRule("r2", falseProp, Collections.<Action>singletonList(new ActionMock("a2")));
101                Rule subRule1 = new BasicRule("r1s1", trueProp, Collections.<Action>singletonList(new ActionMock("a3")));
102                
103                BasicAgendaTree subAgendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(subRule1));
104                BasicAgendaTree agendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule1, subAgendaTree1, null)); 
105                Agenda agenda1 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree1);
106                
107                execute(agenda1);
108
109                assertTrue(ActionMock.actionFired("a1"));
110                assertTrue(ActionMock.actionFired("a3"));
111                
112                // RESET
113                ActionMock.resetActionsFired();
114                
115                BasicAgendaTree subAgendaTree2 = new BasicAgendaTree(new BasicAgendaTreeEntry(subRule1));
116                BasicAgendaTree agendaTree2 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule2, subAgendaTree2, null)); 
117                Agenda agenda2 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree2);
118                
119                execute(agenda2);
120
121                assertFalse(ActionMock.actionFired("a2"));
122                assertFalse(ActionMock.actionFired("a3"));
123        }
124
125        @Test
126        public void testIfFalseSubAgenda() {
127
128                Rule rule1 = new BasicRule("r1", trueProp, Collections.<Action>singletonList(new ActionMock("a1")));
129                Rule rule2 = new BasicRule("r2", falseProp, Collections.<Action>singletonList(new ActionMock("a2")));
130                Rule subRule1 = new BasicRule("r1s1", trueProp, Collections.<Action>singletonList(new ActionMock("a3")));
131                
132                BasicAgendaTree subAgendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(subRule1));
133                BasicAgendaTree agendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule1, null, subAgendaTree1)); 
134                Agenda agenda1 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree1);
135                
136                execute(agenda1);
137
138                assertTrue(ActionMock.actionFired("a1"));
139                assertFalse(ActionMock.actionFired("a3"));
140                
141                // RESET
142                ActionMock.resetActionsFired();
143                
144                BasicAgendaTree subAgendaTree2 = new BasicAgendaTree(new BasicAgendaTreeEntry(subRule1));
145                BasicAgendaTree agendaTree2 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule2, null, subAgendaTree2)); 
146                Agenda agenda2 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree2);
147                
148                execute(agenda2);
149
150                assertFalse(ActionMock.actionFired("a2"));
151                assertTrue(ActionMock.actionFired("a3"));
152        }
153        
154        @Test
155        public void testAfterAgenda() {
156
157                Rule rule1 = new BasicRule("r1", trueProp, Collections.<Action>singletonList(new ActionMock("a1")));
158                Rule rule2 = new BasicRule("r2", falseProp, Collections.<Action>singletonList(new ActionMock("a2")));
159                Rule subRule1 = new BasicRule("r1s1", trueProp, Collections.<Action>singletonList(new ActionMock("a3")));
160                
161                BasicAgendaTree agendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule1), new BasicAgendaTreeEntry(subRule1)); 
162                Agenda agenda1 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree1);
163                
164                execute(agenda1);
165
166                assertTrue(ActionMock.actionFired("a1"));
167                assertTrue(ActionMock.actionFired("a3"));
168                
169                // RESET
170                ActionMock.resetActionsFired();
171                
172                BasicAgendaTree agendaTree2 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule2), new BasicAgendaTreeEntry(subRule1)); 
173                Agenda agenda2 = new BasicAgenda(Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"), agendaTree2);
174                
175                execute(agenda2);
176
177                assertFalse(ActionMock.actionFired("a2"));
178                assertTrue(ActionMock.actionFired("a3"));
179        }
180
181    /**
182     * Make sure agenda qualifier matching is based on the provided qualifiers
183     * see https://jira.kuali.org/browse/KULRICE-6098
184     */
185    public void testQualifierMissingFromAgenda() {
186        // RESET
187                ActionMock.resetActionsFired();
188
189        Rule rule1 = new BasicRule("r1", trueProp, Collections.<Action>singletonList(new ActionMock("a1")));
190
191        BasicAgendaTree agendaTree1 = new BasicAgendaTree(new BasicAgendaTreeEntry(rule1));
192        Agenda agenda1 = new BasicAgenda(Collections.<String, String>emptyMap(), agendaTree1);
193
194        // this shouldn't select any agendas, so no rules will really get executed
195        execute(agenda1, Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"));
196
197        // Expected: the agenda didn't get selected, so the action didn't fire
198        assertFalse("the agenda should not have been selected and executed", ActionMock.actionFired("a1"));
199    }
200
201    /**
202     * execute the engine against a trivial context containing the given agenda.
203     * a default agenda qualifier of Event=test will be used.
204     * @param agenda
205     */
206    private void execute(Agenda agenda) {
207        execute(agenda, Collections.singletonMap(AgendaDefinition.Constants.EVENT, "test"));
208    }
209
210    /**
211     * execute the engine against a trivial context containing the given agenda.
212     * the given agenda qualifier will be used.
213     * @param agenda
214     * @param agendaQualifiers
215     */
216        private void execute(Agenda agenda, Map<String, String> agendaQualifiers) {
217                Map<String, String> contextQualifiers = new HashMap<String, String>();
218                contextQualifiers.put("docTypeName", "Proposal");
219
220                List<TermResolver<?>> testResolvers = new ArrayList<TermResolver<?>>();
221                testResolvers.add(testResolver);
222                
223                Context context = new BasicContext(Arrays.asList(agenda), testResolvers);
224                ContextProvider contextProvider = new ManualContextProvider(context);
225                
226                SelectionCriteria selectionCriteria = SelectionCriteria.createCriteria(null, contextQualifiers,
227                agendaQualifiers);
228                
229                ProviderBasedEngine engine = new ProviderBasedEngine();
230                engine.setContextProvider(contextProvider);
231
232                // Set execution options to log execution
233                ExecutionOptions executionOptions = new ExecutionOptions().setFlag(ExecutionFlag.LOG_EXECUTION, true);
234                
235                EngineResults results = engine.execute(selectionCriteria, Facts.EMPTY_FACTS, executionOptions);
236                assertNotNull(results);
237        }
238        
239        private static final Term totalCostTerm = new Term("totalCost");
240        
241        private static final TermResolver<Integer> testResolver = new TermResolverMock<Integer>(totalCostTerm.getName(), 10);
242}