<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_User
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_User
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_User
 */
class Core_UserTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Core_User
     */
    protected $_object;

    protected function setUp()
    {
        $this->_object = new Core_User;
        $identityDiscoveryMock = $this->getMock('Core_User_IdentityDiscovery_Interface');
        $this->_object->setIdentityDiscovery($identityDiscoveryMock);
    }

    protected function tearDown()
    {
        Core_Application::resetInstance();
    }

    /**
     * @covers Core_User::setSecurity
     * @covers Core_User::getSecurity
     */
    public function testCanSetAndGetSecurity()
    {
        $security = $this->getMock('Core_Security');
        $this->_object->setSecurity($security);
        $this->assertSame($security, $this->_object->getSecurity());
    }

    /**
     * @covers Core_User::setIdentityDiscovery
     * @covers Core_User::getIdentityDiscovery
     */
    public function testCanSetAndGetIdentityDiscovery()
    {
        $identityDiscovery = $this->getMock('Core_User_IdentityDiscovery_Interface');
        $this->_object->setIdentityDiscovery($identityDiscovery);
        $this->assertSame($identityDiscovery, $this->_object->getIdentityDiscovery());
    }

    /**
     * @covers Core_User::getIdentityDiscovery
     */
    public function testCallingGetIdentityDiscoveryWithoutBeingSetThrowsARuntimeException()
    {
        $this->setExpectedException('RuntimeException');

        $object = new Core_User;
        $object->getIdentityDiscovery();
    }

    /**
     * @covers Core_User::getSecurity
     */
    public function testIfNoSecurityInstanceIsSetGetSecurityWillPullItFromApplicationInstanceContainer()
    {
        $security   = $this->getMock('Core_Security');
        $container  = $this->_mockContainer('security', $security);
        Core_Application::getInstance()->setContainer($container);
        $this->assertSame($security, $this->_object->getSecurity());
    }

    /**
     * @covers Core_User::clearIdentity
     * @covers Core_User::setAuth
     * @covers Core_User::_getAuth
     */
    public function testProxyCallForClearingIdentity()
    {
        $this->_mockZendAuthForProxyCall('clearIdentity', null);

        $this->assertNull($this->_object->clearIdentity());
    }

    /**
     * @covers Core_User::hasIdentity
     * @covers Core_User::setAuth
     * @covers Core_User::_getAuth
     */
    public function testProxyCallForHasIdentity()
    {
        $this->markTestIncomplete('TODO');
        
        $this->_mockZendAuthForProxyCall('hasIdentity', true);

        $this->assertTrue($this->_object->hasIdentity());
    }

    /**
     * @covers Core_User::getIdentity
     * @covers Core_User::setAuth
     * @covers Core_User::_getAuth
     */
    public function testProxyCallForGetIdentity()
    {
        $userMock = $this->getMock('Core_User_Model_Interface');
        $this->_mockZendAuthForProxyCall('getIdentity', $userMock);

        $this->assertSame($userMock, $this->_object->getIdentity());
    }

    /**
     * @covers Core_User::getIdentity
     * @covers Core_User::setAuth
     * @covers Core_User::_getAuth
     */
    public function testGetIdentityWhenIdentityIsProxy()
    {
        $this->markTestIncomplete('TODO');

        $this->_mockZendAuthForProxyCall('getIdentity', 123);

        $userMock = $this->getMock('Core_User_Model_Interface');
        $this->_object->getIdentityDiscovery()
            ->expects($this->once())
            ->method('find')
            ->with($this->equalTo(123))
            ->will($this->returnValue($userMock));

        $this->assertSame($userMock, $this->_object->getIdentity());
    }

    /**
     * @covers Core_User::getIdentity
     */
    public function testCallingGetIdentityWhenIdentityIsNeitherIntOrInterfaceThrowsException()
    {
        $this->setExpectedException('InvalidArgumentException');

        $this->_mockZendAuthForProxyCall('getIdentity', array());

        $this->_object->getIdentity();
    }

    /**
     * @covers Core_User::authenticate
     * @covers Core_User::setAuth
     * @covers Core_User::_getAuth
     */
    public function testProxyCallForAuthenticate()
    {
        $this->markTestIncomplete('TODO');

        $this->_mockZendAuthForProxyCall('authenticate', true);

        $authAdapterMock = $this->getMock('Core_User_AuthAdapter_Doctrine');
        $this->assertTrue($this->_object->authenticate($authAdapterMock));
    }

    /**
     * @covers Core_User::setStorage
     * @covers Core_User::getStorage
     * @covers Core_User::setAuth
     */
    public function testCanSetAndGetStorage()
    {
        $zendAuthMock = $this->getMockBuilder('Zend_Auth')
            ->disableOriginalConstructor()
            ->setMethods(array('setStorage', 'getStorage'))
            ->getMock();

        $this->_object->setAuth($zendAuthMock);

        $storageMock = $this->getMock('Zend_Auth_Storage_NonPersistent');

        $zendAuthMock->expects($this->once())
            ->method('getStorage')
            ->will($this->returnValue($storageMock));

        $this->_object->setStorage($storageMock);
        $this->assertSame($storageMock, $this->_object->getStorage());
    }

    /**
     * @covers Core_User::isAllowed
     */
    public function testIsAllowedReturnsFalseIfUserIsAnonymous()
    {
        $this->markTestIncomplete('TODO');
        
        $object = $this->getMock('Core_User', array('hasIdentity'));
        $object
            ->expects($this->once())
            ->method('hasIdentity')
            ->will($this->returnValue(false));

        $this->assertFalse($object->isAllowed());
    }

    /**
     * @covers Core_User::isAllowed
     */
    public function testIsAllowedReturnsTrueIfUserIsNotAnonymousAndAtLeastOneRoleIsAllowedResource()
    {
        $this->markTestIncomplete('TODO');
        
        $object = $this->getMock('Core_User', array('hasIdentity', 'getIdentity'));
        $object
            ->expects($this->once())
            ->method('hasIdentity')
            ->will($this->returnValue(true));
        $user = $this->getMock('Core_User_Model_Interface');
        $user
            ->expects($this->once())
            ->method('getRoles')
            ->will($this->returnValue(array('role1', 'role2')));
        $object
            ->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue($user));
        $security = $this->getMock('Core_Security', array('isAllowed'));
        $security
            ->expects($this->exactly(2)) // 2 roles
            ->method('isAllowed')
            ->will($this->returnValueMap(array(array('role1', null, null, false), array('role2', null, null, true))));
        $object->setSecurity($security);

        $this->assertTrue($object->isAllowed());
    }

    /**
     * @covers Core_User::isLoggedIn
     */
    public function testCallingIsLoggedInCallsHasIdentity()
    {
        $object = $this->getMock('Core_User', array('hasIdentity'));
        $object
            ->expects($this->once())
            ->method('hasIdentity')
            ->will($this->returnValue(true));

        $this->assertTrue($object->isLoggedIn());
    }

    /**
     * @covers Core_User::__call
     */
    public function testGetIdentityDataViaMagicCallReturnsNullForUnsetValue()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(null));

        $this->assertNull($object->getUsername());
    }

    /**
     * @covers Core_User::__call
     */
    public function testGetIdentityDataViaMagicCallReturnsSetValue()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(array('username' => 'some-username')));

        $this->assertEquals('some-username', $object->getUsername());
    }

    /**
     * @covers Core_User::__call
     */
    public function testGetIdentityDataViaMagicCallReturnsSetValueWhenIdentityIsObject()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(new IdentityObject));

        $this->assertEquals('some-username', $object->getUsername());
    }

    /**
     * @covers Core_User::__get
     */
    public function testGetIdentityDataViaMagicGetReturnsNullForUnsetValue()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(null));

        $this->assertNull($object->username);
    }

    /**
     * @covers Core_User::__get
     */
    public function testGetIdentityDataViaMagicGetReturnsSetValue()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(array('username' => 'some-username')));

        $this->assertEquals('some-username', $object->username);
    }

    /**
     * @covers Core_User::__get
     */
    public function testGetIdentityDataViaMagicGetReturnsSetValueWhenIdentityIsObject()
    {
        $object = $this->getMock('Core_user', array('getIdentity'));
        $object->expects($this->once())
            ->method('getIdentity')
            ->will($this->returnValue(new IdentityObject));

        $this->assertEquals('some-username', $object->username);
    }

    protected function _mockZendAuthForProxyCall($proxyMethod, $proxyResponse)
    {
        $zendAuthMock = $this->getMockBuilder('Zend_Auth')
            ->disableOriginalConstructor()
            ->setMethods(array($proxyMethod))
            ->getMock();

        $this->_object->setAuth($zendAuthMock);

        $zendAuthMock->expects($this->once())
            ->method($proxyMethod)
            ->will($this->returnValue($proxyResponse));

        return $this->_object;
    }

    protected function _mockContainer($name, $value)
    {
        // mock the container
        $container = $this->getMock('Core_Yadif_Container', array('getComponent'));
        $container
            ->expects($this->once())
            ->method('getComponent')
            ->with($this->equalTo($name))
            ->will($this->returnValue($value));

        return $container;
    }
}

class IdentityObject
{
    public function getUsername()
    {
        return 'some-username';
    }
}
