<?php

/*
 * @copyright NetMonsters <team@netmonsters.ru>
 * @link http://netmonsters.ru
 * @package Majestic
 * @subpackage UnitTests
 * @since 2011-11-7
 *
 * Unit tests for MongoModel 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/MongoDbCommand.php';
require_once dirname(__FILE__) . '/../../model/DbStatement.php';
require_once dirname(__FILE__) . '/../../model/MongoStatement.php';
require_once dirname(__FILE__) . '/../../model/DbDriver.php';
require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php';
require_once dirname(__FILE__) . '/../../model/MongoDriver.php';
require_once dirname(__FILE__) . '/../../model/Model.php';
require_once dirname(__FILE__) . '/../../model/MongoModel.php';

class MongoModelTest extends PHPUnit_Framework_TestCase
{

    private $model;

    public function run(PHPUnit_Framework_TestResult $result = NULL)
    {
        $this->setPreserveGlobalState(false);
        return parent::run($result);
    }

    public function setUp()
    {
        $conf = array('default' => array('driver' => 'MongoDriver', 'hostname' => 'localhost', 'database' => 'test', 'username' => 'test', 'password' => '1234', 'port' => 27017));

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

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

    /**
     * @runInSeparateProcess
     */
    public function testFind()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }
        $result = $this->model->find();
        $this->assertInstanceOf('MongoStatement', $result);
        $this->assertEquals('milk', $result->limit(2)->order(array('name' => -1))->fetch()->name);
        $this->assertEquals('fish', $result->fetch()->name);
    }

    /**
     * @runInSeparateProcess
     */
    public function testGet()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }
        $result = $this->model->find()->limit(1)->order(array('name' => 1));
        $result = $result->fetch();
        $this->assertEquals('bread', $result->name);
        $id = $result->_id;
        $this->assertEquals(10, $this->model->get($id)->quantity);
    }

    /**
     * @runInSeparateProcess
     */
    public function testDelete()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }
        $result = $this->model->find()->limit(1)->order(array('name' => 1));
        $id = $result->fetch()->_id;
        $this->assertEquals(1, $this->model->delete($id));
        $this->assertFalse($this->model->find(array('name' => 'bread'))->fetch());
    }

    /**
     * @runInSeparateProcess
     */
    public function testDeleteAll()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }

        $this->assertEquals(2, $this->model->count(array('name' => 'eggs')));
        $this->assertEquals(0, $this->model->deleteAll(array('name' => 'eggs')));
        $this->assertFalse($this->model->find(array('name' => 'eggs'))->fetch());
    }

    /**
     * @runInSeparateProcess
     */
    public function testCount()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }
        $this->assertEquals(5, $this->model->count());
        $this->assertEquals(2, $this->model->count(array('name' => 'eggs')));
    }

    /**
     * @runInSeparateProcess
     */
    public function testFetch()
    {
        if (!defined('DEBUG')) {
            define('DEBUG', false);
        }

        $mock = $this->getMock('CacheKey', array('set', 'get'));
        $mock->expects($this->exactly(3))
            ->method('set')
            ->will($this->returnValue(true));
        $mock->expects($this->exactly(3))
            ->method('get')
            ->will($this->returnValue(false));

        $model = new ReflectionClass('MongoModel');
        $method = $model->getMethod('fetchField');
        $method->setAccessible(true);

        $result = $method->invoke($this->model, array('name' => 'milk'), array(), 'quantity', $mock);
        $this->assertEquals(1, $result);

        $model = new ReflectionClass('MongoModel');
        $method = $model->getMethod('fetch');
        $method->setAccessible(true);

        $result = $method->invoke($this->model, array('name' => 'bread'), array(), $mock);
        $this->assertEquals('bread', $result->name);

        $model = new ReflectionClass('MongoModel');
        $method = $model->getMethod('fetchAll');
        $method->setAccessible(true);

        $result = $method->invoke($this->model, array('name' => 'eggs'), array(), $mock);
        $this->assertEquals(2, count($result));
        $this->assertEquals('eggs', $result[0]->name);
    }

    public function tearDown()
    {
        $conf = array('driver' => 'MongoDriver', 'hostname' => 'localhost', 'database' => 'test', 'username' => 'test', 'password' => '1234', 'port' => 27017);


        $connection = new Mongo('mongodb://' . $conf['hostname'] . ':' . $conf['port']);
        $db = $connection->selectDB($conf['database']);
        $db->authenticate($conf['username'], $conf['password']);
        $collection = 'mongomock';
        $db->dropCollection($collection);
    }

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

    public function dbSetUp($conf)
    {
        $data = array(
            array(
                'name' => 'bread',
                'price' => 3.2,
                'quantity' => 10
            ),
            array(
                'name' => 'eggs',
                'price' => 2.1,
                'quantity' => 20
            ),
            array(
                'name' => 'fish',
                'price' => 13.2,
                'quantity' => 2
            ),
            array(
                'name' => 'milk',
                'price' => 3.8,
                'quantity' => 1
            ),
            array(
                'name' => 'eggs',
                'price' => 2.3,
                'quantity' => 5
            )
        );
        $connection = new Mongo('mongodb://' . $conf['default']['hostname'] . ':' . $conf['default']['port']);
        $db = $connection->selectDB($conf['default']['database']);
        $db->authenticate($conf['default']['username'], $conf['default']['password']);
        $collection = 'mongomock';
        $db->dropCollection($collection);
        $collection = $db->selectCollection($collection);
        foreach($data as $document) {
            $collection->insert($document);
        }
    }

}