blob: 5bbdd5dd75d5f950056bfe0e230aa42e40f834b5 [file] [log] [blame]
/*
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." );
}
}
}