/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF 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. */ package org.apache.wiki.workflow; import java.security.Principal; import java.util.Collection; import java.util.List; import java.util.Properties; import junit.framework.TestCase; import org.apache.wiki.PageManager; import org.apache.wiki.TestEngine; import org.apache.wiki.WikiContext; import org.apache.wiki.api.engine.FilterManager; import org.apache.wiki.api.exceptions.FilterException; import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.api.filters.BasicPageFilter; import org.apache.wiki.auth.Users; import org.apache.wiki.auth.WikiPrincipal; public class ApprovalWorkflowTest extends TestCase { WorkflowBuilder m_builder; TestEngine m_engine; WorkflowManager m_wm; DecisionQueue m_dq; protected void setUp() throws Exception { super.setUp(); Properties props = TestEngine.getTestProperties(); // Explicitly turn on Admin approvals for page saves and our sample approval workflow props.put("jspwiki.approver.workflow.saveWikiPage", "Admin"); props.put( "jspwiki.approver.workflow.approvalWorkflow", Users.JANNE ); // Start the wiki engine m_engine = new TestEngine(props); m_wm = m_engine.getWorkflowManager(); m_dq = m_wm.getDecisionQueue(); m_builder = WorkflowBuilder.getBuilder( m_engine ); } public void testBuildApprovalWorkflow() throws WikiException { Principal submitter = new WikiPrincipal( "Submitter" ); String workflowApproverKey = "workflow.approvalWorkflow"; Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); String decisionKey = "decision.saveWikiPage"; Fact[] facts = new Fact[3]; facts[0] = new Fact("fact1",new Integer(1)); facts[1] = new Fact("fact2","A factual String"); facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); Task completionTask = new TestPrepTask( "task.saveWikiPage" ); String rejectedMessageKey = "notification.saveWikiPage.reject"; Workflow w = m_builder.buildApprovalWorkflow(submitter, workflowApproverKey, prepTask, decisionKey, facts, completionTask, rejectedMessageKey); w.setWorkflowManager( m_engine.getWorkflowManager() ); // Check to see if the workflow built correctly assertFalse( w.isStarted() || w.isCompleted() || w.isAborted() ); assertNull( w.getCurrentStep() ); assertEquals( "workflow.approvalWorkflow", w.getMessageKey() ); assertEquals( Workflow.CREATED, w.getCurrentState() ); assertEquals( new WikiPrincipal("Submitter"), w.getOwner() ); assertEquals( m_engine.getWorkflowManager(), w.getWorkflowManager() ); assertEquals( 0, w.getHistory().size() ); // Our dummy "task complete" attributes should still be null assertNull( w.getAttribute( "task.preSaveWikiPage") ); assertNull( w.getAttribute( "task.saveWikiPage") ); // Start the workflow w.start(); // Presave complete attribute should be set now, and current step should be Decision Step decision = w.getCurrentStep(); assertTrue( decision instanceof Decision ); assertEquals( 2, w.getHistory().size() ); assertEquals( prepTask, w.getHistory().get( 0 ) ); assertTrue( w.getHistory().get( 1 ) instanceof Decision ); assertNotNull( w.getAttribute( "task.preSaveWikiPage") ); assertEquals( new WikiPrincipal( Users.JANNE ), decision.getActor() ); assertEquals( decisionKey, decision.getMessageKey() ); List decisionFacts = ((Decision)decision).getFacts(); assertEquals( 3, decisionFacts.size() ); assertEquals( facts[0], decisionFacts.get(0) ); assertEquals( facts[1], decisionFacts.get(1) ); assertEquals( facts[2], decisionFacts.get(2) ); // Check that our predecessor/successor relationships are ok assertEquals( decision, prepTask.getSuccessor( Outcome.STEP_COMPLETE ) ); assertEquals( null, prepTask.getSuccessor( Outcome.STEP_ABORT ) ); assertEquals( null, prepTask.getSuccessor( Outcome.STEP_CONTINUE ) ); assertEquals( null, decision.getSuccessor( Outcome.DECISION_ACKNOWLEDGE ) ); assertEquals( null, decision.getSuccessor( Outcome.DECISION_HOLD ) ); assertEquals( null, decision.getSuccessor( Outcome.DECISION_REASSIGN ) ); assertEquals( completionTask, decision.getSuccessor( Outcome.DECISION_APPROVE ) ); // The "deny" notification should use the right key Step notification = decision.getSuccessor( Outcome.DECISION_DENY ); assertNotNull( notification ); assertEquals( rejectedMessageKey, notification.getMessageKey() ); assertTrue( notification instanceof SimpleNotification ); // Now, approve the Decision and everything should complete ((Decision)decision).decide( Outcome.DECISION_APPROVE ); assertTrue( w.isCompleted() ); assertNull( w.getCurrentStep() ); assertEquals( 3, w.getHistory().size() ); assertEquals( completionTask, w.getHistory().get( 2 ) ); assertTrue( completionTask.isCompleted() ); assertEquals( Outcome.STEP_COMPLETE, completionTask.getOutcome() ); } public void testBuildApprovalWorkflowDeny() throws WikiException { Principal submitter = new WikiPrincipal( "Submitter" ); String workflowApproverKey = "workflow.approvalWorkflow"; Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); String decisionKey = "decision.saveWikiPage"; Fact[] facts = new Fact[3]; facts[0] = new Fact("fact1",new Integer(1)); facts[1] = new Fact("fact2","A factual String"); facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); Task completionTask = new TestPrepTask( "task.saveWikiPage" ); String rejectedMessageKey = "notification.saveWikiPage.reject"; Workflow w = m_builder.buildApprovalWorkflow(submitter, workflowApproverKey, prepTask, decisionKey, facts, completionTask, rejectedMessageKey); w.setWorkflowManager( m_engine.getWorkflowManager() ); // Start the workflow w.start(); // Now, deny the Decision and the submitter should see a notification Step step = w.getCurrentStep(); assertTrue( step instanceof Decision ); Decision decision = (Decision)step; decision.decide( Outcome.DECISION_DENY ); assertFalse( w.isCompleted() ); // Check that the notification is ok, then acknowledge it step = w.getCurrentStep(); assertTrue( step instanceof SimpleNotification ); assertEquals( rejectedMessageKey, step.getMessageKey() ); SimpleNotification notification = (SimpleNotification)step; notification.acknowledge(); // Workflow should be complete now assertTrue( w.isCompleted() ); assertNull( w.getCurrentStep() ); assertEquals( 3, w.getHistory().size() ); assertEquals( notification, w.getHistory().get( 2 ) ); } public void testSaveWikiPageWithApproval() throws WikiException { // Create a sample test page and try to save it String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); String text = "This is a test!"; try { m_engine.saveTextAsJanne(pageName, text); } catch ( DecisionRequiredException e ) { // Swallow exception, because it is expected... } // How do we know the workflow works? Well, first of all the page shouldn't exist yet... assertFalse( m_engine.pageExists(pageName)); // Second, GroupPrincipal Admin should see a Decision in its queue Collection decisions = m_dq.getActorDecisions( m_engine.adminSession() ); assertEquals(1, decisions.size()); // Now, approve the decision and it should go away, and page should appear. Decision decision = (Decision)decisions.iterator().next(); decision.decide(Outcome.DECISION_APPROVE); assertTrue( m_engine.pageExists(pageName)); decisions = m_dq.getActorDecisions( m_engine.adminSession() ); assertEquals(0, decisions.size()); // Delete the page we created m_engine.deletePage( pageName ); } public void testSaveWikiPageWithRejection() throws WikiException { // Create a sample test page and try to save it String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); String text = "This is a test!"; try { m_engine.saveTextAsJanne(pageName, text); } catch ( DecisionRequiredException e ) { // Swallow exception, because it is expected... } // How do we know the workflow works? Well, first of all the page shouldn't exist yet... assertFalse( m_engine.pageExists(pageName)); // ...and there should be a Decision in GroupPrincipal Admin's queue Collection decisions = m_dq.getActorDecisions( m_engine.adminSession() ); assertEquals(1, decisions.size()); // Now, DENY the decision and the page should still not exist... Decision decision = (Decision)decisions.iterator().next(); decision.decide(Outcome.DECISION_DENY); assertFalse( m_engine.pageExists(pageName) ); // ...but there should also be a notification decision in Janne's queue decisions = m_dq.getActorDecisions( m_engine.janneSession() ); assertEquals(1, decisions.size()); decision = (Decision)decisions.iterator().next(); assertEquals(PageManager.SAVE_REJECT_MESSAGE_KEY, decision.getMessageKey()); // Once Janne disposes of the notification, his queue should be empty decision.decide(Outcome.DECISION_ACKNOWLEDGE); decisions = m_dq.getActorDecisions( m_engine.janneSession() ); assertEquals(0, decisions.size()); } public void testSaveWikiPageWithException() throws WikiException { // Add a PageFilter that rejects all save attempts FilterManager fm = m_engine.getFilterManager(); fm.addPageFilter( new AbortFilter(), 0 ); // Create a sample test page and try to save it String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); String text = "This is a test!"; try { m_engine.saveTextAsJanne(pageName, text); } catch ( WikiException e ) { assertTrue( e instanceof FilterException ); assertEquals( "Page save aborted.", e.getMessage() ); return; } fail( "Page save should have thrown a FilterException, but didn't." ); } /** * Sample "prep task" that sets an attribute in the workflow indicating * that it ran successfully, */ public static class TestPrepTask extends Task { private static final long serialVersionUID = 1L; public TestPrepTask( String messageKey ) { super( messageKey ); } public Outcome execute() throws WikiException { getWorkflow().setAttribute( getMessageKey(), "Completed" ); setOutcome( Outcome.STEP_COMPLETE ); return Outcome.STEP_COMPLETE; } } /** * Dummy PageFilter that always throws a FilterException during preSave operations. */ public static class AbortFilter extends BasicPageFilter { public String preSave(WikiContext wikiContext, String content) throws FilterException { throw new FilterException( "Page save aborted." ); } } }