<?php

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

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

class SqlDbDriverTest extends PHPUnit_Framework_TestCase
{
    private $driver;

    public function setUp()
    {
        $conf = array('hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234');
        $this->driver = $this->getMockForAbstractClass('SqlDbDriver', array($conf));
    }

    public function testConstruct()
    {
        $conf = array('hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234');
        try {
            $this->driver = $this->getMockForAbstractClass('SqlDbDriver', array($conf));
        }
        catch (GeneralException $expected) {
            $this->fail($expected->getMessage());
        }
        $this->assertInstanceOf('DbDriver', $this->driver);
    }

    public function testConstructWrongConfig()
    {
        $conf = array('hostname' => 'localhost', 'database' => 'db');

        $this->setExpectedException('GeneralException', 'Configuration must have a "username"');
        
        $this->getMockForAbstractClass('SqlDbDriver', array($conf));
    }

    public function testGetConnection()
    {
        $this->assertNull($this->driver->getConnection());
    }

    public function testBeginTransaction()
    {
        $this->assertSame($this->driver, $this->driver->beginTransaction());
    }

    public function testCommit()
    {
        $this->assertSame($this->driver, $this->driver->commit());
    }

    public function testRollback()
    {
        $this->assertSame($this->driver, $this->driver->rollback());
    }

    public function testQuery()
    {
        $this->setDriverPrepareFunction();

        $stmt = $this->driver->query('SELECT * FROM table');
        $this->assertInstanceOf('DbStmt', $stmt);
        $this->assertSame('SELECT * FROM table', $stmt->string());

        $stmt = $this->driver->query('SELECT * FROM table', 'simple');
        $this->assertInstanceOf('DbStmt', $stmt);
        $this->assertSame('SELECT * FROM table', $stmt->string());
    }

    public function testInsert()
    {
        $this->setDriverPrepareFunction()
                ->setDriverQuoteFunction();
        $bind = array('table.name' => 'tony', 'chair.age' => 21, 'height' => 185);
        $sql = $this->driver->insert('users', $bind);
        $this->assertSame('INSERT INTO `users` (`table`.`name`, `chair`.`age`, `height`) VALUES (tony, 21, 185)', $sql);
    }

    public function testUpdate()
    {
        $this->setDriverPrepareFunction()
                ->setDriverQuoteFunction();

        $bind = array('table.name' => 'tony', 'chair.age' => 21, 'height' => 185);
        $sql = $this->driver->update('users', $bind);
        $this->assertSame('UPDATE `users` SET `table`.`name`=tony, `chair`.`age`=21, `height`=185', $sql);
    }

    public function testDeleteNoWHERE()
    {
        $this->setDriverPrepareFunction();

        $sql = $this->driver->delete('users');
        $this->assertSame('DELETE FROM `users`', $sql);
    }

    public function testDeleteWithWHEREArray()
    {
        $this->setDriverPrepareFunction()
                ->setDriverQuoteFunction();

        $sql = $this->driver->delete('users', array('name?tony' => new DbExpr('='), 'height?185' => '>'));
        $this->assertSame('DELETE FROM `users` WHERE name=tony AND height>185', $sql);
    }

    public function testDeleteWithWHERESimpleCond()
    {
        $this->setDriverPrepareFunction();

        $sql = $this->driver->delete('users', 'name=tony');
        $this->assertSame('DELETE FROM `users` WHERE name=tony', $sql);
    }

    public function testDeleteWithWHEREKeyArray()
    {
        $this->setDriverPrepareFunction();

        $sql = $this->driver->delete('users', array('name=tony' => 0));
        $this->assertSame('DELETE FROM `users` WHERE name=tony', $sql);
    }

    public function testDeleteWithWHEREDbExpr()
    {
        $this->setDriverPrepareFunction();

        $sql = $this->driver->delete('users', new DbExpr('name=tony'));
        $this->assertSame('DELETE FROM `users` WHERE name=tony', $sql);
    }

    protected function setDriverPrepareFunction()
    {
        $this->driver
                ->expects($this->any())
                ->method('prepare')
                ->with($this->anything())
                ->will($this->returnCallback(array($this, 'dbDriverPrepare')));
        return $this;
    }

    protected function setDriverQuoteFunction()
    {
        $this->driver
                ->expects($this->any())
                ->method('driverQuote')
                ->with($this->anything())
                ->will($this->returnArgument(0));
        return $this;
    }

    public function dbDriverPrepare($sql)
    {
        $stmt = $this->getMock('DbStmt', array('execute', 'string', 'affectedRows'));
        $stmt->expects($this->any())
                ->method('execute')
                ->with($this->anything());
        $stmt->expects($this->any())
                ->method('string')
                ->will($this->returnValue($sql));
        $stmt->expects($this->any())
                ->method('affectedRows')
                ->will($this->returnValue($sql));
        return $stmt;
    }
}