| /* |
| 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 org.apache.wiki.TestEngine; |
| import org.apache.wiki.api.core.Context; |
| import org.apache.wiki.api.exceptions.FilterException; |
| import org.apache.wiki.api.exceptions.WikiException; |
| import org.apache.wiki.api.filters.BasePageFilter; |
| import org.apache.wiki.auth.Users; |
| import org.apache.wiki.auth.WikiPrincipal; |
| import org.apache.wiki.filters.FilterManager; |
| import org.apache.wiki.pages.PageManager; |
| import org.junit.jupiter.api.Assertions; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| import java.security.Principal; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Properties; |
| |
| public class ApprovalWorkflowTest { |
| |
| WorkflowBuilder m_builder; |
| TestEngine m_engine; |
| WorkflowManager m_wm; |
| DecisionQueue m_dq; |
| |
| @BeforeEach |
| public void setUp() throws Exception { |
| final 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 ); |
| } |
| |
| |
| @Test |
| public void testBuildApprovalWorkflow() throws WikiException { |
| final Principal submitter = new WikiPrincipal( "Submitter" ); |
| final String workflowApproverKey = "workflow.approvalWorkflow"; |
| final Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); |
| final String decisionKey = "decision.saveWikiPage"; |
| final Fact[] facts = new Fact[3]; |
| facts[0] = new Fact("fact1", 1 ); |
| facts[1] = new Fact("fact2","A factual String"); |
| facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); |
| final Task completionTask = new TestPrepTask( "task.saveWikiPage" ); |
| final String rejectedMessageKey = "notification.saveWikiPage.reject"; |
| |
| final 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 |
| Assertions.assertFalse( w.isStarted() || w.isCompleted() || w.isAborted() ); |
| Assertions.assertNull( w.getCurrentStep() ); |
| Assertions.assertEquals( "workflow.approvalWorkflow", w.getMessageKey() ); |
| Assertions.assertEquals( Workflow.CREATED, w.getCurrentState() ); |
| Assertions.assertEquals( new WikiPrincipal("Submitter"), w.getOwner() ); |
| Assertions.assertEquals( m_engine.getWorkflowManager(), w.getWorkflowManager() ); |
| Assertions.assertEquals( 0, w.getHistory().size() ); |
| |
| // Our dummy "task complete" attributes should still be null |
| Assertions.assertNull( w.getAttribute( "task.preSaveWikiPage") ); |
| Assertions.assertNull( w.getAttribute( "task.saveWikiPage") ); |
| |
| // Start the workflow |
| w.start(); |
| |
| // Presave complete attribute should be set now, and current step should be Decision |
| final Step decision = w.getCurrentStep(); |
| Assertions.assertTrue( decision instanceof Decision ); |
| Assertions.assertEquals( 2, w.getHistory().size() ); |
| Assertions.assertEquals( prepTask, w.getHistory().get( 0 ) ); |
| Assertions.assertTrue( w.getHistory().get( 1 ) instanceof Decision ); |
| Assertions.assertNotNull( w.getAttribute( "task.preSaveWikiPage") ); |
| Assertions.assertEquals( new WikiPrincipal( Users.JANNE ), decision.getActor() ); |
| Assertions.assertEquals( decisionKey, decision.getMessageKey() ); |
| final List< Fact > decisionFacts = ((Decision)decision).getFacts(); |
| Assertions.assertEquals( 3, decisionFacts.size() ); |
| Assertions.assertEquals( facts[0], decisionFacts.get(0) ); |
| Assertions.assertEquals( facts[1], decisionFacts.get(1) ); |
| Assertions.assertEquals( facts[2], decisionFacts.get(2) ); |
| |
| // Check that our predecessor/successor relationships are ok |
| Assertions.assertEquals( decision, prepTask.getSuccessor( Outcome.STEP_COMPLETE ) ); |
| Assertions.assertNull( prepTask.getSuccessor( Outcome.STEP_ABORT ) ); |
| Assertions.assertNull( prepTask.getSuccessor( Outcome.STEP_CONTINUE ) ); |
| Assertions.assertNull( decision.getSuccessor( Outcome.DECISION_ACKNOWLEDGE ) ); |
| Assertions.assertNull( decision.getSuccessor( Outcome.DECISION_HOLD ) ); |
| Assertions.assertNull( decision.getSuccessor( Outcome.DECISION_REASSIGN ) ); |
| Assertions.assertEquals( completionTask, decision.getSuccessor( Outcome.DECISION_APPROVE ) ); |
| |
| // The "deny" notification should use the right key |
| final Step notification = decision.getSuccessor( Outcome.DECISION_DENY ); |
| Assertions.assertNotNull( notification ); |
| Assertions.assertEquals( rejectedMessageKey, notification.getMessageKey() ); |
| Assertions.assertTrue( notification instanceof SimpleNotification ); |
| |
| // Now, approve the Decision and everything should complete |
| ((Decision)decision).decide( Outcome.DECISION_APPROVE ); |
| Assertions.assertTrue( w.isCompleted() ); |
| Assertions.assertNull( w.getCurrentStep() ); |
| Assertions.assertEquals( 3, w.getHistory().size() ); |
| Assertions.assertEquals( completionTask, w.getHistory().get( 2 ) ); |
| Assertions.assertTrue( completionTask.isCompleted() ); |
| Assertions.assertEquals( Outcome.STEP_COMPLETE, completionTask.getOutcome() ); |
| } |
| |
| @Test |
| public void testBuildApprovalWorkflowDeny() throws WikiException { |
| final Principal submitter = new WikiPrincipal( "Submitter" ); |
| final String workflowApproverKey = "workflow.approvalWorkflow"; |
| final Task prepTask = new TestPrepTask( "task.preSaveWikiPage" ); |
| final String decisionKey = "decision.saveWikiPage"; |
| final Fact[] facts = new Fact[3]; |
| facts[0] = new Fact("fact1", 1 ); |
| facts[1] = new Fact("fact2","A factual String"); |
| facts[2] = new Fact("fact3",Outcome.DECISION_ACKNOWLEDGE); |
| final Task completionTask = new TestPrepTask( "task.saveWikiPage" ); |
| final String rejectedMessageKey = "notification.saveWikiPage.reject"; |
| |
| final 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(); |
| Assertions.assertTrue( step instanceof Decision ); |
| final Decision decision = (Decision)step; |
| decision.decide( Outcome.DECISION_DENY ); |
| Assertions.assertFalse( w.isCompleted() ); |
| |
| // Check that the notification is ok, then acknowledge it |
| step = w.getCurrentStep(); |
| Assertions.assertTrue( step instanceof SimpleNotification ); |
| Assertions.assertEquals( rejectedMessageKey, step.getMessageKey() ); |
| final SimpleNotification notification = (SimpleNotification)step; |
| notification.acknowledge(); |
| |
| // Workflow should be complete now |
| Assertions.assertTrue( w.isCompleted() ); |
| Assertions.assertNull( w.getCurrentStep() ); |
| Assertions.assertEquals( 3, w.getHistory().size() ); |
| Assertions.assertEquals( notification, w.getHistory().get( 2 ) ); |
| } |
| |
| @Test |
| public void testSaveWikiPageWithApproval() throws WikiException { |
| // Create a sample test page and try to save it |
| final String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); |
| final String text = "This is a test!"; |
| try { |
| m_engine.saveTextAsJanne( pageName, text ); |
| } catch( final 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... |
| Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); |
| |
| // Second, GroupPrincipal Admin should see a Decision in its queue |
| Collection< Decision > decisions = m_dq.getActorDecisions( m_engine.adminSession() ); |
| Assertions.assertEquals(1, decisions.size()); |
| |
| // Now, approve the decision and it should go away, and page should appear. |
| final Decision decision = decisions.iterator().next(); |
| decision.decide(Outcome.DECISION_APPROVE); |
| Assertions.assertTrue( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); |
| decisions = m_dq.getActorDecisions( m_engine.adminSession() ); |
| Assertions.assertEquals(0, decisions.size()); |
| |
| // Delete the page we created |
| m_engine.getManager( PageManager.class ).deletePage( pageName ); |
| } |
| |
| @Test |
| public void testSaveWikiPageWithRejection() throws WikiException { |
| // Create a sample test page and try to save it |
| final String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); |
| final String text = "This is a test!"; |
| try { |
| m_engine.saveTextAsJanne( pageName, text ); |
| } catch( final 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... |
| Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName)); |
| |
| // ...and there should be a Decision in GroupPrincipal Admin's queue |
| Collection< Decision > decisions = m_dq.getActorDecisions( m_engine.adminSession() ); |
| Assertions.assertEquals(1, decisions.size()); |
| |
| // Now, DENY the decision and the page should still not exist... |
| Decision decision = decisions.iterator().next(); |
| decision.decide(Outcome.DECISION_DENY); |
| Assertions.assertFalse( m_engine.getManager( PageManager.class ).wikiPageExists(pageName) ); |
| |
| // ...but there should also be a notification decision in Janne's queue |
| decisions = m_dq.getActorDecisions( m_engine.janneSession() ); |
| Assertions.assertEquals(1, decisions.size()); |
| decision = decisions.iterator().next(); |
| Assertions.assertEquals(WorkflowManager.WF_WP_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() ); |
| Assertions.assertEquals(0, decisions.size()); |
| } |
| |
| @Test |
| public void testSaveWikiPageWithException() { |
| // Add a PageFilter that rejects all save attempts |
| final FilterManager fm = m_engine.getFilterManager(); |
| fm.addPageFilter( new AbortFilter(), 0 ); |
| |
| // Create a sample test page and try to save it |
| final String pageName = "SaveWikiPageWorkflow-Test" + System.currentTimeMillis(); |
| final String text = "This is a test!"; |
| try |
| { |
| m_engine.saveTextAsJanne(pageName, text); |
| } |
| catch ( final WikiException e ) |
| { |
| Assertions.assertTrue( e instanceof FilterException ); |
| Assertions.assertEquals( "Page save aborted.", e.getMessage() ); |
| return; |
| } |
| Assertions.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( final String messageKey ) |
| { |
| super( messageKey ); |
| } |
| |
| @Override |
| public Outcome execute() { |
| 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 BasePageFilter { |
| @Override |
| public String preSave( final Context wikiContext, final String content ) throws FilterException { |
| throw new FilterException( "Page save aborted." ); |
| } |
| } |
| |
| } |