<?php

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

require_once dirname(__FILE__) . '/../../Registry.php';
require_once dirname(__FILE__) . '/../../Config.php';
require_once dirname(__FILE__) . '/../../cache/Cacher.php';
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__) . '/MyDbDriver.php';
require_once dirname(__FILE__) . '/../../model/Model.php';
require_once dirname(__FILE__) . '/../../model/SqlModel.php';

class SqlModelTest extends PHPUnit_Framework_TestCase
{

    private $model;

    public function setUp()
    {
        $conf = array('default' => array('driver' => 'MockDbDriver', 'hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234'));
        if (!class_exists('MockDbDriver')) {
            $this->getMockForAbstractClass('MyDbDriver', array($conf), 'MockDbDriver', false);
        }

        Config::set('Db', $conf);
        if (!class_exists('MockModel')) {
            $this->model = $this->getMockForAbstractClass('SqlModel', array(), 'MockModel');
        } else {
            $this->model = new MockModel();
        }
        set_new_overload(array($this, 'newCallback'));
    }

    public function testModel()
    {
        $this->assertInstanceOf('SqlModel', $this->model);
    }

    public function testGetInsertId()
    {
        $this->assertTrue($this->model->getInsertId());
    }

    public function testIdentify()
    {
        $param = 'param';
        $this->assertSame($param, $this->model->identify($param));
    }

    public function testQuote()
    {
        $param = 'param';
        $this->assertSame($param, $this->model->quote($param));
    }

    public function testGet()
    {
        $this->assertTrue($this->model->get(1));
    }

    public function testInsert()
    {
        $this->assertTrue($this->model->insert(array('data')));
    }

    public function testUpdate()
    {
        $this->assertSame('mock', $this->model->update(array('var' => 'val'), 1));
    }

    public function testDelete()
    {
        $this->assertSame('mock', $this->model->delete(1));
    }

    public function testOrder()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('order');
        $method->setAccessible(true);
        $this->assertSame(' ORDER BY id DESC', $method->invoke($this->model, array('sort' => 'id', 'order' => 'desc')));
        $this->assertSame(' ORDER BY name ASC', $method->invoke($this->model, array('sort' => 'name'), array('id', 'name')));
        $this->assertEmpty($method->invoke($this->model, array()));

        $this->assertSame(' ORDER BY name ASC', $method->invoke($this->model, array('sort' => 'name', 'order' => 'DESC'), array('id', 'name')));
    }

    public function testSearch()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('search');
        $method->setAccessible(true);
        $this->assertEmpty($method->invoke($this->model, array()));
        $this->assertEmpty($method->invoke($this->model, array('q' => 'in', 'qt' => 'name')));
        $this->assertSame('test.name LIKE %on%', $method->invoke($this->model, array('qt' => 'name', 'q' => 'on'), array('name'), 'test'));
    }

    public function testFetch()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('fetch');
        $method->setAccessible(true);
        $key = $this->getCacheKeyMockGetSet();
        $this->assertTrue(true, $method->invoke($this->model, 'SELECT', array(), $key));
    }

    public function testFetchField()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('fetchField');
        $method->setAccessible(true);

        $key = $this->getCacheKeyMockGetSet();
        $this->assertSame('field', $method->invoke($this->model, 'SELECT', array(), 'field', $key));
    }

    public function testFetchAll()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('fetchAll');
        $method->setAccessible(true);

        $key = $this->getCacheKeyMockGetSet();
        $this->assertTrue(true, $method->invoke($this->model, 'SELECT', array(), $key));
    }

    public function testGetCache()
    {
        $model = new ReflectionClass('MockModel');
        $method = $model->getMethod('getCache');
        $method->setAccessible(true);

        Config::set('Model', 'MockCache');
        if (!class_exists('MockCache')) {
            $this->getMock('Cache', array(), array(), 'MockCache');
        }
        $this->assertInstanceOf('MockCache', $method->invoke($this->model));
    }

    public function testCacheKey()
    {
        $model = new ReflectionClass('MockModel');
        $method = $model->getMethod('cacheKey');
        $method->setAccessible(true);

        Config::set('Model', 'MockCache');
        if (!class_exists('MockCache')) {
            $this->getMock('Cache', array(), array(), 'MockCache', false);
        }
        if (!class_exists('MockCacheKey')) {
            $this->getMock('CacheKey', array(), array(), 'MockCacheKey', false);
        }
        $this->assertInstanceOf('MockCacheKey', $method->invoke($this->model, 'name'));
    }

    public function testAddCleanCache()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('addCleanCache');
        $method->setAccessible(true);

        $key = $this->getMock('Key', array('set', 'get'));
        $method->invoke($this->model, $key);
        $method->invoke($this->model, $key);
        $method->invoke($this->model, $key);
        $this->assertAttributeEquals(array($key, $key, $key), 'caches_clean', $this->model);
    }

    public function testCleanCaches()
    {
        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('addCleanCache');
        $method->setAccessible(true);

        $key = $this->getCacheKeyMockDel(3);
        $method->invoke($this->model, $key);
        $method->invoke($this->model, $key);
        $method->invoke($this->model, $key);
        $this->assertAttributeEquals(array($key, $key, $key), 'caches_clean', $this->model);

        $model = new ReflectionClass('SqlModel');
        $method = $model->getMethod('cleanCaches');
        $method->setAccessible(true);

        $method->invoke($this->model, $key);
        $this->assertAttributeEquals(array(), 'caches_clean', $this->model);
    }

    protected function getCacheKeyMockGetSet()
    {
        $key = $this->getMock('Key', array('set', 'get'));
        $key
                ->expects($this->any())
                ->method('set')
                ->with($this->anything())
                ->will($this->returnValue(true));
        $key
                ->expects($this->any())
                ->method('get')
                ->will($this->returnValue(false));
        return $key;
    }

    protected function getCacheKeyMockDel($count)
    {
        $key = $this->getMock('Key', array('del'));
        $key
                ->expects($this->exactly($count))
                ->method('del')
                ->will($this->returnValue(true));
        return $key;
    }

    public function tearDown()
    {
        Config::getInstance()->offsetUnset('Db');
        $config = new ReflectionClass('Db');
        $registry = $config->getProperty('connections');
        $registry->setAccessible(true);
        $registry->setValue('Db', array());
        unset_new_overload();
    }


    protected function newCallback($className)
    {
        switch ($className) {
            case 'MockDbDriver':
                return 'MockDbDriver';
            case 'CacheKey':
                return 'MockCacheKey';
            default:
                return $className;
        }
    }
}