<?php

/*
 * @copyright NetMonsters <team@netmonsters.ru>
 * @link http://netmonsters.ru
 * @package Majestic
 * @subpackage UnitTests
 * @since 2011-11-3
 * 
 * Unit tests for DbStatement class
 */

require_once dirname(__FILE__) . '/../../model/Db.php';
require_once dirname(__FILE__) . '/../../model/DbDriver.php';
require_once dirname(__FILE__) . '/../../model/DbStatement.php';
require_once dirname(__FILE__) . '/../../exception/GeneralException.php';

class DbStatementTest extends PHPUnit_Framework_TestCase
{
    private $driver;

    private $sql;

    private $stmt;

    private $testData = array(
        array('one' => 11, 'two' => 12),
        array('one' => 21, 'two' => 22),
        array('one' => 31, 'two' => 32),
    );

    public function setUp()
    {
        if (!isset($this->stmt)) {
            $this->driver = $this->getMockBuilder('DbDriverMock')
                    ->disableOriginalConstructor()
                    ->setMethods(array('quote'))
                    ->getMock();
            $this->sql = 'SELECT * :place FROM :place AND :new';
            $this->stmt = $this->getMockForAbstractClass('DbStatement', array($this->driver, $this->sql));
        }
    }

    public function testConstruct()
    {
        $this->assertAttributeEquals($this->driver, 'driver', $this->stmt);
        $this->assertAttributeEquals($this->sql, 'sql', $this->stmt);
    }

    public function testBindParam()
    {
        $val = $this->getMockBuilder('DbExpr')
                ->disableOriginalConstructor()
                ->getMock();
        $this->assertFalse($this->stmt->bindParam('var', $val));
        $this->assertTrue($this->stmt->bindParam('place', $val));
    }

    /**
     * @TODO: change Exception Message 'Placeholder must be an array or string' - no arrays available
     */
    public function testBindParamExceptionParam()
    {
        $val = 1;
        $this->setExpectedException('GeneralException', 'Placeholder must be an array or string');
        $this->stmt->bindParam(array(), $val);
    }


    public function testBindParamExceptionWrongObject()
    {
        $this->setExpectedException('GeneralException', 'Objects excepts DbExpr not allowed.');
        $val = $this->getMock('NotDbExpr');
        $this->stmt->bindParam('paa', $val);
    }

    public function testExecute()
    {
        $this->stmt
                ->expects($this->once())
                ->method('driverExecute')
                ->with($this->anything())
                ->will($this->returnCallback(array($this, 'dbStatementAssemble')));

        $this->driver
                ->expects($this->any())
                ->method('quote')
                ->with($this->anything())
                ->will($this->returnCallback(array($this, 'driverQuote')));

        $result = $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val'));
        $this->assertEquals('SELECT * place_val FROM place_val AND new_val', $result);
    }

    public function testExecuteNoPlaceholders()
    {
        $this->sql = 'PLAIN SQL';
        $this->stmt = $this->getMockForAbstractClass('DbStatement', array($this->driver, $this->sql));
        $this->stmt
                ->expects($this->once())
                ->method('driverExecute')
                ->with($this->anything())
                ->will($this->returnCallback(array($this, 'dbStatementAssemble')));
        $result = $this->stmt->execute(array());
        $this->assertEquals('PLAIN SQL', $result);
    }

    public function testFetch()
    {
        $this->stmt
                ->expects($this->any())
                ->method('fetch')
                ->with($this->anything())
                ->will($this->returnCallback(array($this, 'dbStatementFetch')));

        $this->assertEquals(11, $this->stmt->fetchField('one'));
        $this->assertFalse($this->stmt->fetchField('zero'));

        reset($this->testData);
        $this->assertEquals(array(11, 21, 31), $this->stmt->fetchColumn('one'));

        reset($this->testData);
        $result = $this->stmt->fetchAll();
        $this->assertEquals(11, $result[0]->one);
        $this->assertEquals(32, $result[2]->two);

        reset($this->testData);
        $result = $this->stmt->fetchPairs();
        $this->assertEquals(31, $result['one']);
    }

    public function dbStatementAssemble($val)
    {
        return $val;
    }

    public function driverQuote($val)
    {
        return $val;
    }

    public function dbStatementFetch($style)
    {
        $result = null;
        switch ($style) {
            case Db::FETCH_OBJ:
                $data = each($this->testData);
                if ($data !== false) {
                    $result = new ArrayObject($data['value'], ArrayObject::ARRAY_AS_PROPS);
                } else {
                    $result = false;
                }
                break;
            case Db::FETCH_ASSOC:
                $data = each($this->testData);
                $result = $data['value'];
                break;
            case Db::FETCH_NUM:
                $data = each($this->testData);
                if ($data !== false) {
                    $data = $data['value'];
                    $result[0] = key($data);
                    $result[1] = current($data);
                } else {
                    $result = false;
                }
        }
        return $result;
    }
}