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

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

    protected function setup()
    {
        $this->_object = new Core_QueryLanguage_Query;
    }
    /**
     * @covers Core_QueryLanguage_Query::isOperator
     */
    public function testCanTestForOperator()
    {
        $this->assertTrue($this->_object->isOperator('='));
        $this->assertTrue($this->_object->isOperator('<'));
        $this->assertTrue($this->_object->isOperator('!='));
        $this->assertTrue($this->_object->isOperator('>='));

        $this->assertFalse($this->_object->isOperator('ne'));
        $this->assertFalse($this->_object->isOperator('.'));
        $this->assertFalse($this->_object->isOperator('č'));
    }

    /**
     * @covers Core_QueryLanguage_Query::setSelect
     * @covers Core_QueryLanguage_Query::getSelect
     */
    public function testCanSetAndGetSelect()
    {
        $select = array(
            'field',
            'another_field',
            'Relation' => array(
                'field',
                'another_field'
            )
        );
        $this->_object->setSelect($select);

        $this->assertEquals($select, $this->_object->getSelect());
    }

    /**
     * @covers Core_QueryLanguage_Query::setSelect
     * @covers Core_QueryLanguage_Query::getSelect
     */
    public function testCanSetSelectWithStringNotation()
    {
        $this->markTestIncomplete('Don\'t know if to support this, parser needed');

        $select = array(
            'field',
            'another_field',
            'Relation.field',
            'Relation.another_field',
            'Relation.SubRelation.sub_field',
            'AnotherRelation.*' // all fields = *
        );
        $this->_object->setSelect($select);

        $this->assertEquals(array(
            'field',
            'another_field',
            'Relation' => array(
                'field',
                'another_field',
                'SubRelation' => array(
                    'sub_field'
                ),
            ),
            'AnotherRelation' => array(
                '*'
            )
        ), $this->_object->getSelect());
    }

    /**
     * @covers Core_QueryLanguage_Query::setSelect
     * @covers Core_QueryLanguage_Query::getSelect
     */
    public function testCanSetSelectByPassingString()
    {
        $this->markTestIncomplete('Don\'t know if to support this, parser needed');

        $select = 'field, another_field, Relation.field, Relation.another_field, Relation.SubRelation.sub_field, AnotherRelation.*';
        $this->_object->setSelect($select);

        $this->assertEquals(array(
            'field',
            'another_field',
            'Relation' => array(
                'field',
                'another_field',
                'SubRelation' => array(
                    'sub_field'
                ),
            ),
            'AnotherRelation' => array(
                '*'
            )
        ), $this->_object->getSelect());
    }

    /**
     * @covers Core_QueryLanguage_Query::setQuery
     * @covers Core_QueryLanguage_Query::getQuery
     */
    public function testCanSetAndGetQuery()
    {
        $query = array(
            'field'. Core_QueryLanguage_Query::OPERATOR_EQUAL .'value',
            'Relation.field'. Core_QueryLanguage_Query::OPERATOR_NOT_EQUAL .'other_value'
        );
        $this->_object->setQuery($query);

        $this->assertEquals($query, $this->_object->getQuery());
    }

    /**
     * @covers Core_QueryLanguage_Query::setQuery
     * @covers Core_QueryLanguage_Query::getQuery
     */
    public function testCanSetAndGetElaborateQuery()
    {
        $query = array(
            Core_QueryLanguage_Query::LOGICAL_AND => array(
                Core_QueryLanguage_Query::LOGICAL_OR => array(
                    'field'. Core_QueryLanguage_Query::OPERATOR_CONTAINS .'"some value"'
                ),
                Core_QueryLanguage_Query::LOGICAL_NOT => array(
                    'someOtherField'. Core_QueryLanguage_Query::OPERATOR_EQUAL .'"some other value"'
                )
            ),
        );
        $this->_object->setQuery($query);

        $this->assertEquals($query, $this->_object->getQuery());
    }

    /**
     * @covers Core_QueryLanguage_Query::setOrder
     * @covers Core_QueryLanguage_Query::getOrder
     */
    public function testCanSetAndGetOrderWhenPassingArray()
    {
        $order = array(
            'firstField'    => Core_QueryLanguage_Query::SORT_ASCENDING,
            'secondField'   => Core_QueryLanguage_Query::SORT_DESCENDING,
        );
        $this->_object->setOrder($order);

        $this->assertEquals($order, $this->_object->getOrder());
    }

    /**
     * @covers Core_QueryLanguage_Query::setOrder
     * @covers Core_QueryLanguage_Query::getOrder
     */
    public function testCanSetAndGetOrderWhenPassingString()
    {
        $this->_object->setOrder('firstField ASC');

        $order = array(
            'firstField'    => Core_QueryLanguage_Query::SORT_ASCENDING,
        );

        $this->assertEquals($order, $this->_object->getOrder());
    }

    /**
     * @covers Core_QueryLanguage_Query::setOrder
     * @covers Core_QueryLanguage_Query::getOrder
     */
    public function testCanSetAndGetOrderWhenPassingStringWithMultipleFields()
    {
        $this->_object->setOrder('firstField ASC, secondField DESC');

        $order = array(
            'firstField'    => Core_QueryLanguage_Query::SORT_ASCENDING,
            'secondField'   => Core_QueryLanguage_Query::SORT_DESCENDING,
        );

        $this->assertEquals($order, $this->_object->getOrder());
    }

    /**
     * @covers Core_QueryLanguage_Query::setOffset
     * @covers Core_QueryLanguage_Query::getOffset
     */
    public function testCanSetAndGetOffset()
    {
        $this->_object->setOffset(32);

        $this->assertEquals(32, $this->_object->getOffset());
    }

    /**
     * @covers Core_QueryLanguage_Query::setOffset
     * @expectedException LogicException
     */
    public function testThrowsExceptionIfOffsetIsLessThanZero()
    {
        $this->_object->setOffset(-43);
    }

    /**
     * @covers Core_QueryLanguage_Query::setLimit
     * @covers Core_QueryLanguage_Query::getLimit
     */
    public function testCanSetAndGetLimit()
    {
        $this->_object->setLimit(5);

        $this->assertEquals(5, $this->_object->getLimit());
    }

    /**
     * @covers Core_QueryLanguage_Query::setLimit
     * @expectedException LogicException
     */
    public function testThrowsExceptionIfLimitIsZero()
    {
        $this->_object->setLimit(0);
    }

    /**
     * @covers Core_QueryLanguage_Query::setLimit
     * @expectedException LogicException
     */
    public function testThrowsExceptionIfLimitIsLessThanZero()
    {
        $this->_object->setLimit(-30);
    }

    /**
     * @covers Core_QueryLanguage_Query::toArray
     */
    public function testCanGetArrayRepresentationOfQuery()
    {
        $this->_object->setSelect('test');
        $this->_object->setOffset(10);
        $this->_object->setLimit(100);

        $this->assertEquals(array(
            'select' => array(
                'test'
            ),
            'query'  => null,
            'order'  => null,
            'offset' => 10,
            'limit'  => 100
        ), $this->_object->toArray());
    }

    /**
     * @covers Core_QueryLanguage_Query::__toString
     */
    public function testCanGetStringRepresentationOfQuery()
    {
        $this->_object->setSelect('test');
        $this->_object->setOffset(10);
        $this->_object->setLimit(100);

        $string = json_encode(array(
            'select' => array(
                'test'
            ),
            'query'  => null,
            'order'  => null,
            'offset' => 10,
            'limit'  => 100
        ));

        $this->assertJsonStringEqualsJsonString($string, (string) $this->_object);
    }

    /**
     * @covers Core_QueryLanguage_Query::factory
     * @covers Core_QueryLanguage_Query::toArray
     */
    public function testCanCreateQueryWithFactoryMethodWhenPassingArray()
    {
        $query = Core_QueryLanguage_Query::factory(array(
            'select'    => array(
                'field',
                'another_field',
                'Relation' => array(
                    'field',
                    'another_field'
                )
            ),
            'id' => 32, // will be added to query
            'query'     => array(
                'field'. Core_QueryLanguage_Query::OPERATOR_EQUAL .'value',
                'Relation.field'. Core_QueryLanguage_Query::OPERATOR_NOT_EQUAL .'other_value'
            ),
            'order'     => 'firstField ASC, secondField DESC',
            'offset'    => 5,
            'limit'     => 10
        ));

        $this->assertEquals(array(
            'select'    => array(
                'field',
                'another_field',
                'Relation' => array(
                    'field',
                    'another_field'
                )
            ),
            'query'     => array(
                'field'. Core_QueryLanguage_Query::OPERATOR_EQUAL .'value',
                'Relation.field'. Core_QueryLanguage_Query::OPERATOR_NOT_EQUAL .'other_value',
                'id'. Core_QueryLanguage_Query::OPERATOR_EQUAL .'32',
            ),
            'order'     => array(
                'firstField'    => Core_QueryLanguage_Query::SORT_ASCENDING,
                'secondField'   => Core_QueryLanguage_Query::SORT_DESCENDING,
            ),
            'offset'    => 5,
            'limit'     => 10
        ), $query->toArray());
    }

    /**
     * @covers Core_QueryLanguage_Query::factory
     * @expectedException InvalidArgumentException
     */
    public function testThrowsExceptionWhenPassingWrongArgumentToFactoryMethod()
    {
        Core_QueryLanguage_Query::factory(false);
    }
}
