<?php
namespace TYPO3\CMS\Core\Tests\Unit\Utility;

/**
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\CMS\Core\Utility\RootlineUtility;

/**
 * Testcase for class \TYPO3\CMS\Core\Utility\RootlineUtility
 *
 * @author Steffen Ritter <steffen.ritter@typo3.org>
 */
class RootlineUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {

	/**
	 * @var RootlineUtility|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject
	 */
	protected $fixture;

	/**
	 * @var \TYPO3\CMS\Frontend\Page\PageRepository|\PHPUnit_Framework_MockObject_MockObject
	 */
	protected $pageContextMock;

	protected function setUp() {
		$this->pageContextMock = $this->getMock('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
		$this->fixture = $this->getAccessibleMock('TYPO3\\CMS\\Core\\Utility\\RootlineUtility', array('enrichWithRelationFields'), array(1, '', $this->pageContextMock));
	}

	protected function tearDown() {
		parent::tearDown();
		RootlineUtility::purgeCaches();
	}

	/***
	 *
	 * 		UTILITY FUNCTIONS
	 *
	 */
	/**
	 * Tests that $subsetCandidate is completely part of $superset
	 * and keys match.
	 *
	 * @see (A ^ B) = A <=> A c B
	 * @param array $subsetCandidate
	 * @param array $superset
	 */
	protected function assertIsSubset(array $subsetCandidate, array $superset) {
		$this->assertSame($subsetCandidate, array_intersect_assoc($subsetCandidate, $superset));
	}

	/***
	 *
	 * 		>TEST CASES
	 *
	 */
	/**
	 * @test
	 */
	public function isMountedPageWithoutMountPointsReturnsFalse() {
		$this->fixture->__construct(1);
		$this->assertFalse($this->fixture->isMountedPage());
	}

	/**
	 * @test
	 */
	public function isMountedPageWithMatchingMountPointParameterReturnsTrue() {
		$this->fixture->__construct(1, '1-99');
		$this->assertTrue($this->fixture->isMountedPage());
	}

	/**
	 * @test
	 */
	public function isMountedPageWithNonMatchingMountPointParameterReturnsFalse() {
		$this->fixture->__construct(1, '99-99');
		$this->assertFalse($this->fixture->isMountedPage());
	}

	/**
	 * @test
	 * @expectedException \RuntimeException
	 */
	public function processMountedPageWithNonMountedPageThrowsException() {
		$this->fixture->__construct(1, '1-99');
		$this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_DEFAULT));
	}

	/**
	 * @test
	 */
	public function processMountedPageWithMountedPageNotThrowsException() {
		$this->fixture->__construct(1, '1-99');
		$this->assertNotEmpty($this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1)));
	}

	/**
	 * @test
	 */
	public function processMountedPageWithMountedPageAddsMountedFromParameter() {
		$this->fixture->__construct(1, '1-99');
		$result = $this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1));
		$this->assertTrue(isset($result['_MOUNTED_FROM']));
		$this->assertSame(1, $result['_MOUNTED_FROM']);
	}

	/**
	 * @test
	 */
	public function processMountedPageWithMountedPageAddsMountPointParameterToReturnValue() {
		$this->fixture->__construct(1, '1-99');
		$result = $this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1));
		$this->assertTrue(isset($result['_MP_PARAM']));
		$this->assertSame('1-99', $result['_MP_PARAM']);
	}

	/**
	 * @test
	 */
	public function processMountedPageForMountPageIsOverlayAddsMountOLParameter() {
		$this->fixture->__construct(1, '1-99');
		$result = $this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 1));
		$this->assertTrue(isset($result['_MOUNT_OL']));
		$this->assertSame(TRUE, $result['_MOUNT_OL']);
	}

	/**
	 * @test
	 */
	public function processMountedPageForMountPageIsOverlayAddsDataInformationAboutMountPage() {
		$this->fixture->__construct(1, '1-99');
		$result = $this->fixture->_call('processMountedPage', array('uid' => 1), array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 1, 'pid' => 5, 'title' => 'TestCase'));
		$this->assertTrue(isset($result['_MOUNT_PAGE']));
		$this->assertSame(array('uid' => 99, 'pid' => 5, 'title' => 'TestCase'), $result['_MOUNT_PAGE']);
	}

	/**
	 * @test
	 */
	public function processMountedPageForMountPageWithoutOverlayReplacesMountedPageWithMountPage() {
		$mountPointPageData = array('uid' => 99, 'doktype' => \TYPO3\CMS\Frontend\Page\PageRepository::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 0);
		$this->fixture->__construct(1, '1-99');
		$result = $this->fixture->_call('processMountedPage', array('uid' => 1), $mountPointPageData);
		$this->assertIsSubset($mountPointPageData, $result);
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsGroupFieldAsLocal() {
		$this->assertFalse($this->fixture->_call('columnHasRelationToResolve', array(
			'type' => 'group'
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsGroupFieldWithMMAsRemote2() {
		$this->assertTrue($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'group',
				'MM' => 'tx_xyz'
			)
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsInlineFieldAsLocal() {
		$this->assertFalse($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'inline'
			)
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsInlineFieldWithForeignKeyAsRemote() {
		$this->assertTrue($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'inline',
				'foreign_field' => 'xyz'
			)
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsInlineFieldWithFMMAsRemote() {
		$this->assertTrue($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'inline',
				'MM' => 'xyz'
			)
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsSelectFieldAsLocal() {
		$this->assertFalse($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'select'
			)
		)));
	}

	/**
	 * @test
	 */
	public function columnHasRelationToResolveDetectsSelectFieldWithMMAsRemote() {
		$this->assertTrue($this->fixture->_call('columnHasRelationToResolve', array(
			'config' => array(
				'type' => 'select',
				'MM' => 'xyz'
			)
		)));
	}

	/**
	 * @test
	 */
	public function getCacheIdentifierContainsAllContextParameters() {
		$this->pageContextMock->sys_language_uid = 8;
		$this->pageContextMock->versioningWorkspaceId = 15;
		$this->pageContextMock->versioningPreview = TRUE;
		$this->fixture->__construct(42, '47-11', $this->pageContextMock);
		$this->assertSame('42_47-11_8_15_1', $this->fixture->getCacheIdentifier());
		$this->pageContextMock->versioningPreview = FALSE;
		$this->fixture->__construct(42, '47-11', $this->pageContextMock);
		$this->assertSame('42_47-11_8_15_0', $this->fixture->getCacheIdentifier());
		$this->pageContextMock->versioningWorkspaceId = 0;
		$this->fixture->__construct(42, '47-11', $this->pageContextMock);
		$this->assertSame('42_47-11_8_0_0', $this->fixture->getCacheIdentifier());
	}

	/**
	 * @test
	 */
	public function getCacheIdentifierReturnsValidIdentifierWithCommasInMountPointParameter() {
		/** @var \TYPO3\CMS\Core\Cache\Frontend\AbstractFrontend $cacheFrontendMock */
		$cacheFrontendMock = $this->getMockForAbstractClass('TYPO3\\CMS\\Core\\Cache\\Frontend\\AbstractFrontend', array(), '', FALSE);
		$this->pageContextMock->sys_language_uid = 8;
		$this->pageContextMock->versioningWorkspaceId = 15;
		$this->pageContextMock->versioningPreview = TRUE;
		$this->fixture->__construct(42, '47-11,48-12', $this->pageContextMock);
		$this->assertTrue($cacheFrontendMock->isValidEntryIdentifier($this->fixture->getCacheIdentifier()));
	}

	/**
	 * @test
	 */
	public function getRecordArrayFetchesTranslationWhenLanguageIdIsSet() {
		$pageData = array(
			'uid' => 1,
			'title' => 'Original',
		);
		$pageDataTranslated = array(
			'uid' => 1,
			'title' => 'Translated',
			'_PAGES_OVERLAY_UID' => '2',
		);

		$this->fixture
			->expects($this->any())
			->method('enrichWithRelationFields')
			->with(2, $pageDataTranslated)
			->will($this->returnArgument(1));

		$databaseConnectionMock = $this->getMock('TYPO3\\CMS\\Core\\Database\\DatabaseConnection');
		$databaseConnectionMock
			->expects($this->once())
			->method('exec_SELECTgetSingleRow')
			->will(
				$this->returnValue($pageData)
			);
		$this->fixture->_set('databaseConnection',
			$databaseConnectionMock
		);

		$this->pageContextMock
				->expects($this->any())
				->method('getPageOverlay')
				->will($this->returnValue($pageDataTranslated));

		$this->fixture->_set('languageUid', 1);
		$this->assertSame($pageDataTranslated, $this->fixture->_call('getRecordArray', 1));
	}

	/**
	 * @test
	 */
	public function enrichWithRelationFieldsCreatesWhereClauseForDisabledField() {
		$mockDatabaseConnection = $this->getMock('\TYPO3\CMS\Core\Database\DatabaseConnection', array('exec_SELECTgetRows'), array(), '', FALSE);
		$subject = $this->getAccessibleMock('\TYPO3\CMS\Core\Utility\RootlineUtility', array('columnHasRelationToResolve'), array(1, '', $this->pageContextMock));
		$subject->_set('databaseConnection', $mockDatabaseConnection);
		$GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields'] = '';
		$foreign_table = $this->getUniqueId('foreign_table');
		$foreign_field = $this->getUniqueId('foreign_field');
		$GLOBALS['TCA'][$foreign_table]['ctrl']['enablecolumns']['disabled'] = $this->getUniqueId('disabled');
		$GLOBALS['TCA']['pages']['columns'] = array(
			'test' => array(
				'config' => array(
					'foreign_table' => $foreign_table,
					'foreign_field' => $foreign_field
				)
			)
		);
		$expected = array(
			$foreign_field . ' = 0',
			$foreign_table . '.' . $GLOBALS['TCA'][$foreign_table]['ctrl']['enablecolumns']['disabled'] . ' = 0'
		);
		$this->pageContextMock->expects($this->once())->method('deleteClause')->will($this->returnValue(''));
		$mockDatabaseConnection->expects(
			$this->once())->
			method('exec_SELECTgetRows')->
			with('uid', $foreign_table, implode(' AND ', $expected), '', '', '', '')->
			// the return value does not matter much, it is only here to prevent error messages from further code execution
			will($this->returnValue(array('uid' => 17))
		);
		$subject->expects($this->once())->method('columnHasRelationToResolve')->will($this->returnValue(TRUE));
		$subject->_call('enrichWithRelationFields', 17, array());
	}
}
