Perforce Chronicle 2012.2/486814
API Documentation

Workflow_Test_TransitionTest Class Reference

Test the workflow state model. More...

List of all members.

Public Member Functions

 testAccessors ()
 Test get(id|label|worfklow) methods.
 testAreConditionsMetFor ()
 Test areConditionsMetFor() method.
 testGetActions ()
 Test getting actions from a transition object.
 testGetConditions ()
 Test getting conditions from a transition object.
 testInvokeActionsOn ()
 Test invokeActions() method.
 testSetGetFromToState ()
 Test (setFrom|getFrom|setTo|getTo)State() methods.

Protected Member Functions

 _recordsTransitionRunner (array $records, Workflow_Model_Transition $transition)
 Helper function to filter records array by allowed transitions evaluated in their context.

Detailed Description

Test the workflow state model.

Copyright:
2011-2012 Perforce Software. All rights reserved
License:
Please see LICENSE.txt in top-level folder of this distribution.
Version:
2012.2/486814

Member Function Documentation

Workflow_Test_TransitionTest::_recordsTransitionRunner ( array $  records,
Workflow_Model_Transition transition 
) [protected]

Helper function to filter records array by allowed transitions evaluated in their context.

Returns array with keys of records where conditions are met for given transition in record context.

Parameters:
array$recordsrecords to filter by condition.
Workflow_Model_Transition$transitiontransition to evaluate in record context.
Returns:
array filtered array with records allowed by transtion.
    {
        $filteredRecords = array_filter(
            $records,
            function ($record) use ($transition)
            {
                return $transition->areConditionsMetFor($record);
            }
        );

        return array_keys($filteredRecords);
    }
Workflow_Test_TransitionTest::testAccessors ( )

Test get(id|label|worfklow) methods.

Other accessors are tested in separate functions.

    {
        $workflow   = new Workflow_Model_Workflow;
        $transition = new Workflow_Model_Transition(
            array(
                'label'     => 'sample transition',
                'fromState' => Workflow_Model_State::create(
                    array('workflow' => $workflow, 'label' => 'foo')
                ),
                'toState'   => Workflow_Model_State::create(
                    array('workflow' => $workflow, 'label' => 'bar')
                )
            )
        );

        // id
        $this->assertSame(
            null,
            $transition->getId(),
            "Expected transition id."
        );

        // label
        $this->assertSame(
            'sample transition',
            $transition->getLabel(),
            "Expected transition label name."
        );

        // workflow
        $this->assertSame(
            $workflow,
            $transition->getWorkflow(),
            "Expected transition workflow."
        );

        // ensure exception is thrown if try to access workflow that has not been set
        $transitionNoWorkflow = new Workflow_Model_Transition(array('label' => 'foo'));
        try {
            $transitionNoWorkflow->getWorkflow();
            $this->fail("Unexpected success when getting non-existing workflow");
        } catch (Workflow_Exception $e) {
            // expected exception
        }                
    }
Workflow_Test_TransitionTest::testAreConditionsMetFor ( )

Test areConditionsMetFor() method.

    {
        // create records for conditions context
        $recordA = P4Cms_Record::create(
            array(
                'field1' => 'this field has bogus mime type',
                'field2' => 'abc123 foo bar',
            )
        );
        $recordA->setFieldMetadata('field1', array('type' => 'foo', 'mimeType' => 'bogus'));

        $recordB = P4Cms_Record::create(
            array(
                'field1' => 'this field has text mime type',
                'fieldY' => 'xyz123 baz',
                'fieldZ' => 'qwerty baz'
            )
        );
        $recordB->setFieldMetadata('field1', array('mimeType' => 'text/ascii'));

        $recordC = P4Cms_Record::create(
            array(
                'dim'     => '100x200 foo units',
            )
        );

        // define tests
        $tests = array(
            array(
                'conditions'        => array(),
                'expectedRecords'   => array('a', 'b', 'c'),
                'message'           => __LINE__ . ': no conditions'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'string'    => 'field'
                    )
                ),
                'expectedRecords'   => array('b'),
                'message'           => __LINE__ . ': single contains condition'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'string'    => '1'
                    ),
                    'contains'      => array(
                        'string'    => 'foo'
                    )
                ),
                'expectedRecords'   => array('a', 'c'),
                'message'           => __LINE__ . ': two contains conditions'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'pattern'   => '/./'
                    ),
                    'false'
                ),
                'expectedRecords'   => array(),
                'message'           => __LINE__ . ': false and contains conditions'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'pattern'   => '/./'
                    ),
                    'false'         => array(
                        'negate'    => true
                    )
                ),
                'expectedRecords'   => array('a', 'b', 'c'),
                'message'           => __LINE__ . ': negate false and contains conditions'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'pattern'   => '/\dx/'
                    )
                ),
                'expectedRecords'   => array('c'),
                'message'           => __LINE__ . ': regex contains condition'
            ),
            array(
                'conditions'        => array(
                    'contains'      => array(
                        'pattern'   => '/./',
                        'fields'    => array('dim', 'fieldY', 'fieldZ', 'fieldA')
                    ),
                    'contains'      => array(
                        'string'    => 't',
                        'fields'    => array('dim', 'field*')
                    )
                ),
                'expectedRecords'   => array('c'),
                'message'           => __LINE__ . ': two contains condition with literal/regex patterns'
            )
        );

        // create records array for the runner
        $records = array('a' => $recordA, 'b' => $recordB, 'c' => $recordC);

        // run tests
        foreach ($tests as $test) {
            $transition = new Workflow_Model_Transition(array('conditions' => $test['conditions']));
            $this->assertSame(
                $test['expectedRecords'],
                $this->_recordsTransitionRunner($records, $transition),
                'Line ' . $test['message']
            );
        }

        // test publish content permission
        $workflow = Workflow_Model_Workflow::create(
            array(
                'states'    => array(
                    'foo'   => array(
                        'transitions'   => array(
                            'published' => array(
                                'label' => 'publish'
                            )
                        )
                    ),
                    'published'         => array(),
                )
            )
        );
        $transition = $workflow->getStateModel('foo')->getTransitionModel('published');

        // verify that publishing a content is denied if no active user
        P4Cms_User::clearActive();
        $this->assertFalse(
            $transition->areConditionsMetFor(new P4Cms_Content),
            "Expected false value if no active user."
        );

        // verify that publishing a content is denied if active user has not publish content permission
        $joe = P4Cms_User::create(
            array(
                'id'        => 'joe',
                'fullName'  => 'Mr Joe',
                'email'     => 'joe@email.com'
            )
        )->save();
        $acl = P4Cms_Acl::fetchActive();
        $acl->installDefaults()->save();
        $role = P4Cms_Acl_Role::create(array('id' => 'foo', 'users' => array('joe')))->save();
        $acl->addRole($role);
        P4Cms_User::setActive($joe);

        $this->assertFalse(
            $transition->areConditionsMetFor(new P4Cms_Content),
            "Expected false value if active user doesn't have permission to publish a content."
        );
        // verify that publish content permission is evaluated only for content records
        $this->assertTrue(
            $transition->areConditionsMetFor(new P4Cms_Record),
            "Expected true value for generic records."
        );

        // add publish content permission to the user and verify that conditions are met
        $acl->allow($role, 'content', 'publish');
        $this->assertTrue(
            $transition->areConditionsMetFor(new P4Cms_Content),
            "Expected true value if active user has permission to publish a content."
        );
    }
Workflow_Test_TransitionTest::testGetActions ( )

Test getting actions from a transition object.

    {
        $transition = new Workflow_Model_Transition(
            array(
                'actions' => array(
                    'bogus',
                    'noop',
                    'noop'       => array('foo' => 'bar'),
                    'other'      => array('action' => 'noop', 'foo' => 'bar'),
                    'another'    => array(
                        'action'    => 'noop',
                        'foo'       => 'bar',
                        'options'   => array('foo' => 'baz')
                    )
                )
            )
        );

        $actions = $transition->getActions();
        $this->assertTrue(is_array($actions));
        $this->assertSame(4, count($actions));

        // ensure all actions are action objects.
        foreach ($actions as $action) {
            $this->assertTrue($action instanceof Workflow_ActionInterface);
        }

        // verify options were loaded correctly.
        $this->assertSame(array(), $actions[0]->getOptions());
        $this->assertSame(array('foo' => 'bar'), $actions[1]->getOptions());
        $this->assertSame(array('foo' => 'bar'), $actions[2]->getOptions());
        $this->assertSame(array('foo' => 'baz'), $actions[3]->getOptions());
    }
Workflow_Test_TransitionTest::testGetConditions ( )

Test getting conditions from a transition object.

    {
        $transition = new Workflow_Model_Transition(
            array(
                'conditions' => array(
                    'bogus',
                    'false',
                    'false'     => array('foo' => 'bar'),
                    'other'     => array('condition' => 'false', 'foo' => 'bar'),
                    'another'   => array(
                        'condition' => 'false',
                        'foo'       => 'bar',
                        'options'   => array('foo' => 'baz')
                    )
                )
            )
        );

        $conditions = $transition->getConditions();
        $this->assertTrue(is_array($conditions));
        $this->assertSame(4, count($conditions));

        // ensure all conditions are condition objects.
        foreach ($conditions as $condition) {
            $this->assertTrue($condition instanceof Workflow_ConditionInterface);
        }

        // verify options were loaded correctly.
        $this->assertSame(array(), $conditions[0]->getOptions());
        $this->assertSame(array('foo' => 'bar'), $conditions[1]->getOptions());
        $this->assertSame(array('foo' => 'bar'), $conditions[2]->getOptions());
        $this->assertSame(array('foo' => 'baz'), $conditions[3]->getOptions());
    }
Workflow_Test_TransitionTest::testInvokeActionsOn ( )

Test invokeActions() method.

    {
        // define test suite (test with sendEmail action)
        $tests = array(
            array(
                'actions'           => array(),
                'expected'          => array(
                    'emailsSent'    => 0
                ),
                'message'           => __LINE__ . ': no actions'
            ),
            array(
                'actions'           => array(
                    'email'         => array(
                        'action'    => 'sendEmail',
                        'noize'     => array(),
                        'options'   => array()
                    )
                ),
                'expected'          => array(
                    'emailsSent'    => 0
                ),
                'message'           => __LINE__ . ': no recipient set'
            ),
            array(
                'actions'           => array(
                    'email'         => array(
                        'action'    => 'sendEmail',
                        'noize'     => 'foo-bar-baz',
                        'options'   => array('to' => 'foo@email.com')
                    )                    
                ),
                'expected'          => array(
                    'emailsSent'    => 1,
                    'recipients'    => array('foo@email.com')
                ),
                'message'           => __LINE__ . ': 1 email'
            ),
            array(
                'actions'           => array(
                    'email1'        => array(
                        'action'    => 'sendEmail',
                        'noize'     => '123abc',
                        'options'   => array('to' => array('foo@email.com', 'bar@email.com'))
                    ),
                    'email2'        => array(
                        'action'    => 'sendEmail',
                        'noize'     => 'xyz',
                        'options'   => array('to' => 'abc@xyz.com')
                    )
                ),
                'expected'          => array(
                    'emailsSent'    => 2,
                    'recipients'    => array('foo@email.com,bar@email.com', 'abc@xyz.com')
                ),
                'message'           => __LINE__ . ': 2 emails'
            ),
            array(
                'actions'           => array(
                    'email1'        => array(
                        'action'    => 'sendEmail',
                        'noize'     => null,
                        'options'   => array('to' => array('foo@abc.com'))
                    ),
                    'email2'        => array(
                        'action'    => 'sendEmail',
                        'noize'     => 'xyz',
                        'options'   => array()
                    )
                ),
                'expected'          => array(
                    'emailsSent'    => 1,
                    'recipients'    => array('foo@abc.com')
                ),
                'message'           => __LINE__ . ': 2 emails, but 1 missing recipient'
            ),
        );

        // set custom mail transport to actually not sent any real emails, but
        // check emails registered by sent() action instead
        $transport = new Workflow_Test_MailTransport;
        Zend_Mail::setDefaultTransport($transport);

        // prepare workflow transition model
        $workflow   = new Workflow_Model_Workflow;
        $transition = new Workflow_Model_Transition(
            array(
                'fromState' => Workflow_Model_State::create(
                    array('workflow' => $workflow, 'label' => 'foo')
                ),
                'toState' => Workflow_Model_State::create(
                    array('workflow' => $workflow, 'label' => 'bar')
                )
            )
        );        

        // run tests
        foreach ($tests as $test) {
            $transport->reset();
            $transition->setValue('actions', $test['actions']);
            $transition->invokeActionsOn(new P4Cms_Record);

            // get sent emails register
            $sentEmails = $transport->getSentMails();

            // check number of emails sent
            $this->assertSame(
                $test['expected']['emailsSent'],
                count($sentEmails),
                "Expected number of sent emails."
            );

            // if there were emails sent, check recipients
            if (count($sentEmails)) {
                foreach (array_values($sentEmails) as $index => $email) {
                    $this->assertSame(
                        $test['expected']['recipients'][$index],
                        $email['to'],
                        "Expected email recipient match. Line: " . $test['message']
                    );
                }
            }
        }
    }
Workflow_Test_TransitionTest::testSetGetFromToState ( )

Test (setFrom|getFrom|setTo|getTo)State() methods.

    {
        $transition = new Workflow_Model_Transition;
        $workflow   = new Workflow_Model_Workflow;
        $fromState  = Workflow_Model_State::create(
            array(
                'label'     => 'from',
                'workflow'  => $workflow
            )
        );
        $toState    = Workflow_Model_State::create(
            array(
                'label'     => 'to',
                'workflow'  => $workflow
            )
        );

        // set/get from state
        $transition->setFromState($fromState);
        $this->assertSame(
            $fromState,
            $transition->getFromState(),
            "Expected transition from state."
        );

        // set/get to state
        $transition->setToState($toState);
        $this->assertSame(
            $toState,
            $transition->getToState(),
            "Expected transition to state."
        );

        // try to set from/to state governed by a different workflow
        try {
            $transition->setToState(
                Workflow_Model_State::create(
                    array(
                        'label'     => 'to',
                        'workflow'  => new Workflow_Model_Workflow
                    )
                )
            );
            $this->fail("Unexpected transition creation - should throw an exception.");
        } catch (Workflow_Exception $e) {
            // expected exception
        }
        try {
            $transition->setFromState(
                Workflow_Model_State::create(
                    array(
                        'label'     => 'from',
                        'workflow'  => new Workflow_Model_Workflow
                    )
                )
            );
            $this->fail("Unexpected transition creation - should throw an exception.");
        } catch (Workflow_Exception $e) {
            // expected exception
        }

        // test exceptions when accessing non-existing states
        $transition = new Workflow_Model_Transition(
            array(
                'toState' => $toState
            )
        );
        try {
            $transition->getFromState();
            $this->fail("Unexpected success when getting non-existing from state");
        } catch (Workflow_Exception $e) {
            // expected exception
        }

        $transition = new Workflow_Model_Transition(
            array(
                'fromState' => $fromState
            )
        );
        try {
            $transition->getToState();
            $this->fail("Unexpected success when getting non-existing to state");
        } catch (Workflow_Exception $e) {
            // expected exception
        }
    }

The documentation for this class was generated from the following file: