<?php
// Call JobTitleHistoryTest::main() if this source file is executed directly.
if (!defined("PHPUnit_MAIN_METHOD")) {
    define("PHPUnit_MAIN_METHOD", "JobTitleHistoryTest::main");
}

require_once "PHPUnit/Framework/TestCase.php";
require_once "PHPUnit/Framework/TestSuite.php";

require_once "testConf.php";
require_once ROOT_PATH."/lib/confs/Conf.php";
require_once ROOT_PATH."/lib/confs/sysConf.php";
require_once ROOT_PATH."/lib/common/LocaleUtil.php";
require_once ROOT_PATH."/lib/common/UniqueIDGenerator.php";

require_once 'JobTitleHistory.php';

/**
 * Test class for JobTitleHistory.
 * Generated by PHPUnit_Util_Skeleton on 2008-03-11 at 20:06:47.
 */
class JobTitleHistoryTest extends PHPUnit_Framework_TestCase {

    private $jobtitleHistory;

    /**
     * Runs the test methods of this class.
     *
     * @access public
     * @static
     */
    public static function main() {
        require_once "PHPUnit/TextUI/TestRunner.php";

        $suite  = new PHPUnit_Framework_TestSuite("JobTitleHistoryTest");
        $result = PHPUnit_TextUI_TestRunner::run($suite);
    }

    /**
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     *
     * @access protected
     */
    protected function setUp() {
        $conf = new Conf();
        $this->connection = mysql_connect($conf->dbhost.":".$conf->dbport, $conf->dbuser, $conf->dbpass);
        mysql_select_db($conf->dbname);
        $this->_deleteTables();

        // Insert job titles
        $this->_runQuery("INSERT INTO hs_hr_job_title(jobtit_code, jobtit_name, jobtit_desc, jobtit_comm, sal_grd_code) " .
                "VALUES('JOB001', 'Manager', 'Manager job title', 'no comments', null)");
        $this->_runQuery("INSERT INTO hs_hr_job_title(jobtit_code, jobtit_name, jobtit_desc, jobtit_comm, sal_grd_code) " .
                "VALUES('JOB002', 'Driver', 'Driver job title', 'no comments', null)");
        $this->_runQuery("INSERT INTO hs_hr_job_title(jobtit_code, jobtit_name, jobtit_desc, jobtit_comm, sal_grd_code) " .
                "VALUES('JOB003', 'Typist', 'Typist job title', 'no comments', null)");
        $this->_runQuery("INSERT INTO hs_hr_job_title(jobtit_code, jobtit_name, jobtit_desc, jobtit_comm, sal_grd_code) " .
                "VALUES('JOB004', 'Programmer', 'Software Programmer', 'no comments', null)");

        // Insert employees (managers)
        $this->_runQuery("INSERT INTO hs_hr_employee(emp_number, employee_id, emp_lastname, emp_firstname, emp_middle_name, job_title_code) " .
                    "VALUES(11, '0011', 'Rajasinghe', 'Saman', 'Marlon', 'JOB001')");
        $this->_runQuery("INSERT INTO hs_hr_employee(emp_number, employee_id, emp_lastname, emp_firstname, emp_middle_name, job_title_code) " .
                    "VALUES(12, '0022', 'Jayasinghe', 'Aruna', 'Shantha', 'JOB001')");

        // Job title history for employee 11
        $this->jobtitleHistory[1] = $this->_getJobTitleHistory(11, 'JOB003', 'Typist', '-4 years', '-2 years');
        $this->jobtitleHistory[2] = $this->_getJobTitleHistory(11, 'JOB004', 'Programmer', '-2 years', '-1 years');
        $this->jobtitleHistory[3] = $this->_getJobTitleHistory(11, 'JOB001', 'Manager', '-1 years', '-1 month');
        $this->jobtitleHistory[4] = $this->_getJobTitleHistory(11, 'JOB002', 'Driver', '-1 month', null);

        // Job title history for employee 12
        $this->jobtitleHistory[5] = $this->_getJobTitleHistory(12, 'JOB002', 'Driver', '-5 years', '-2 year');
        $this->jobtitleHistory[6] = $this->_getJobTitleHistory(12, 'JOB003', 'Programmer', '-2 years', null);

        foreach($this->jobtitleHistory as $his) {
            $this->_insertJobTitleHistory($his);
        }

        UniqueIDGenerator::getInstance()->resetIDs();
    }

    /**
     * Tears down the fixture, removed database entries created during test.
     *
     * @access protected
     */
    protected function tearDown() {
        $this->_deleteTables();
        UniqueIDGenerator::getInstance()->resetIDs();
    }

    private function _deleteTables() {
        $this->_runQuery("TRUNCATE TABLE `hs_hr_emp_jobtitle_history`");
        $this->_runQuery("TRUNCATE TABLE `hs_hr_job_title`");
        $this->_runQuery("TRUNCATE TABLE `hs_hr_employee`");
    }

    /**
     * Test case for updateHistory().
     */
    public function testUpdateHistory() {
        $history = new JobTitleHistory();

        // invalid emp number
        try {
            $history->updateHistory('ab1', 'JOB003');
            $this->fail("Exception expected");
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        // invalid job title code
        try {
            $history->updateHistory(11, 'JOBA003');
            $this->fail("Exception expected");
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        // No change
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND code = 'JOB003' AND end_date IS NULL"));
        $before = $this->_getNumRows();
        $result = $history->updateHistory(12, 'JOB003');
        $this->assertFalse($result);
        $this->assertEquals($before, $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND code = 'JOB003' AND end_date IS NULL"));

        // Employee with 2 current items, verify exception thrown
        $this->_runQuery('UPDATE hs_hr_emp_jobtitle_history SET end_date = null WHERE id=' . $this->jobtitleHistory[3]->getId());

        try {
            $result = $history->updateHistory(11, 'JOB001');
            $this->fail('Exception expected');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::MULTIPLE_CURRENT_ITEMS_NOT_ALLOWED, $e->getCode());
        }

        // Change job title
        $result = $history->updateHistory(12, 'JOB001');
        $this->assertTrue($result);
        $this->assertEquals($before + 1, $this->_getNumRows());
        $this->assertEquals(0, $this->_getNumRows("emp_number = 12 AND code = 'JOB002' AND end_date IS NULL"));
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND code = 'JOB001' AND end_date IS NULL"));

        // validate end date of old job correctly set
        $result = $this->_getMatchingRows('id = ' . $this->jobtitleHistory[6]->getId());
        $this->assertTrue(is_array($result));
        $this->assertEquals(1, count($result));
        $this->assertNotNull($result[0]['end_date']);

        // Verify the end time is correct
        $endDate = $result[0]['end_date'];
        $this->assertTrue((time() - strtotime($endDate)) < 30);

        // Verify name is current
        $this->assertEquals('Programmer', $result[0]['name']);

        // validate start date of new job correctly set
        $result = $this->_getMatchingRows("emp_number = 12 AND code = 'JOB001' AND end_date IS NULL");
        $this->assertTrue(is_array($result));
        $this->assertEquals(1, count($result));
        $this->assertNotNull($result[0]['start_date']);

        // Verify the start time is correct
        $startDate = $result[0]['start_date'];
        $this->assertTrue((time() - strtotime($startDate)) < 30);

        // Verify name is current
        $this->assertEquals('Manager', $result[0]['name']);

        // Update history for employee with no current history items.
        $this->_runQuery('DELETE from hs_hr_emp_jobtitle_history');

        $this->assertEquals(0, $this->_getNumRows());
        $result = $history->updateHistory(12, 'JOB003');
        $this->assertTrue($result);
        $this->assertEquals(1, $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND code = 'JOB003' AND end_date IS NULL"));
    }

    /**
     * Test case for getHistory().
     */
    public function testGetHistory() {

        $history = new JobTitleHistory();

        // invalid emp number
        try {
            $list = $history->getHistory('A22');
            $this->fail("Exception expected");
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        // non existent emp number
        $list = $history->getHistory(14);
        $this->assertTrue(is_array($list));
        $this->assertEquals(0, count($list));

        // emp with 1 history item and one current items
        $list = $history->getHistory(12);
        $this->assertTrue(is_array($list));
        $this->assertEquals(1, count($list));
        $this->_compareHistory(array($this->jobtitleHistory[5]), $list);

        // emp with 3 history items and one current items
        $list = $history->getHistory(11);
        $this->assertTrue(is_array($list));
        $this->assertEquals(3, count($list));
        $this->_compareHistory(array($this->jobtitleHistory[1], $this->jobtitleHistory[2], $this->jobtitleHistory[3]), $list);

        // emp with 2 history items and 2 current items
        /*$this->_runQuery('UPDATE hs_hr_emp_jobtitle_history SET end_date = null WHERE id=' . $this->jobtitleHistory[3]->getId());
        $list = $history->getHistory(11);
        $this->assertTrue(is_array($list));
        $this->assertEquals(2, count($list));
        $this->_compareHistory(array($this->jobtitleHistory[1], $this->jobtitleHistory[2]), $list);*/


        // emp with 1 history item only
        $this->_runQuery('DELETE from hs_hr_emp_jobtitle_history WHERE emp_number = 12 AND end_date is null');
        $list = $history->getHistory(12);
        $this->assertTrue(is_array($list));
        $this->assertEquals(1, count($list));
        $this->_compareHistory(array($this->jobtitleHistory[5]), $list);

        // emp number with no history
        $this->_runQuery('DELETE from hs_hr_emp_jobtitle_history WHERE emp_number = 12');
        $list = $history->getHistory(14);
        $this->assertTrue(is_array($list));
        $this->assertEquals(0, count($list));
    }

    /**
     * Test delete() method
     */
    public function testDelete() {

        // find array of id's that are not available in database
        foreach ($this->jobtitleHistory as $hist) {
            $ids[] = $hist->getId();
        }
        $notIds = array_values(array_diff(range(1, 14), $ids));

        $before = $this->_getNumRows();
        $history = new JobTitleHistory();

        // invalid params
        try {
            $history->delete(34);
            $this->fail("Exception not thrown");
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        // invalid params
        try {
            $history->delete(array(1, 'w', 12));
            $this->fail("Exception not thrown");
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        // empty array
        $res = $history->delete(array());
        $this->assertEquals(0, $res);
        $this->assertEquals($before, $this->_getNumRows());

        // no matches
        $res = $history->delete(array($notIds[1], $notIds[4]));
        $this->assertEquals(0, $res);
        $this->assertEquals($before, $this->_getNumRows());

        // one match
        $res = $history->delete(array($ids[0], $notIds[3]));
        $this->assertEquals(1, $res);
        $this->assertEquals(1, $before - $this->_getNumRows());

        $before = $this->_getNumRows();

        // one more the rest
        $res = $history->delete(array($ids[2]));
        $this->assertEquals(1, $res);
        $this->assertEquals(1, $before - $this->_getNumRows());

        $before = $this->_getNumRows();

        // rest
        $res = $history->delete(array($ids[1], $ids[3], $ids[4], $ids[5]));
        $this->assertEquals(4, $res);
        $this->assertEquals(4, $before - $this->_getNumRows());

        $this->assertEquals(0, $this->_getNumRows());
    }

    /**
     * Test save() method
     */
    public function testSave() {

        // empNum missing
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(null, 'JOB003', 'Typist', '-4 years', '-2 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        $this->assertEquals($before, $this->_getNumRows());

        // code missing
        $history = $this->_getJobTitleHistory(11, null, 'Typist', '-4 years', '-2 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        $this->assertEquals($before, $this->_getNumRows());

        // start time missing
        $history = $this->_getJobTitleHistory(11, 'JOB001', 'Typist', null, '-2 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        $this->assertEquals($before, $this->_getNumRows());

        // Invalid emp number
        $history = $this->_getJobTitleHistory('X1', 'JOB001', 'Typist', '-3 years', '-2 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }

        $this->assertEquals($before, $this->_getNumRows());

        // Invalid job title code
        $history = $this->_getJobTitleHistory(11, '001', 'Typist', '-3 years', '-2 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::INVALID_PARAMETER, $e->getCode());
        }
        $this->assertEquals($before, $this->_getNumRows());

        // Start date greater than end date
        $history = $this->_getJobTitleHistory(11, 'JOB001', 'Typist', '-3 years', '-4 years');

        try {
            $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
            $this->assertEquals(EmpHistoryException::END_BEFORE_START, $e->getCode(), $e->getMessage());
        }
        $this->assertEquals($before, $this->_getNumRows());

        // new
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(11, 'JOB001', 'Typist', '-4 years', '-3 years');
        $id = $history->save();
        $this->assertEquals(($before + 1), $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 11 AND id = $id AND code = 'JOB001'"));

        // update
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(11, 'JOB004', 'Typist', '-7 years', '-5 years');
        $history->setId($id);
        $newId = $history->save();
        $this->assertEquals($id, $newId);
        $this->assertEquals($before, $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 11 AND id = $id AND code = 'JOB004'"));

        // update without job title code
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(11, null, 'Typist', '-7 years', '-5 years');
        $history->setId($id);

        try {
            $id = $history->save();
            $this->fail('Should throw exception');
        } catch (EmpHistoryException $e) {
        }
        $this->assertEquals($before, $this->_getNumRows());

        // Add second item of same type for same employee
        $this->assertEquals(0, $this->_getNumRows("emp_number = 12 AND code = 'JOB004'"));

        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(12, 'JOB004', 'Typist', '-6 years', '-2 years');
        $id = $history->save();

        $history = $this->_getJobTitleHistory(12, 'JOB004', 'Typist', '-4 years', '-2 years');
        $id = $history->save();

        $this->assertEquals($before + 2, $this->_getNumRows());
        $this->assertEquals(2, $this->_getNumRows("emp_number = 12 AND code = 'JOB004'"));

        // New item without end date allowed.
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(12, 'JOB003', 'Typist', '-4 years', null);
        $id = $history->save();
        $this->assertEquals(($before + 1), $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND id = $id AND code = 'JOB003' AND name='Typist'"));

        // New item with name not set allowed. Verify correct name is taken from job title table
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(12, 'JOB002', null, '-4 years', '-1 years');
        $id = $history->save();
        $this->assertEquals(($before + 1), $this->_getNumRows());
        $this->assertEquals(1, $this->_getNumRows("emp_number = 12 AND id = $id AND code = 'JOB002' AND name='Driver'"));

        // Update with name null, verify name updated.
        $before = $this->_getNumRows();
        $history = $this->_getJobTitleHistory(12, 'JOB002', null, '-4 years', '-1 years');
        $history->setId($id);
        $newId = $history->save();
        $this->assertEquals($id, $newId);
        $this->assertEquals($before, $this->_getNumRows());

        $result = $this->_getMatchingRows('id = ' . $newId);
        $this->assertTrue(is_array($result));
        $this->assertEquals(1, count($result));
        $this->assertEquals('Driver', $result[0]['name']);
    }

    /**
     * Compares two arrays of history objects
     *
     * @param array $expected Expected
     * @param array $result Result
     */
    private function _compareHistory($expected, $result) {
        $this->assertEquals(count($expected), count($result));

        $i = 0;
        foreach ($result as $empLocation) {
            $this->assertTrue($empLocation instanceof JobTitleHistory, "Should return JobTitleHistory objects");
            $this->assertEquals($expected[$i], $empLocation);
            $i++;
        }
    }

    /**
     * Gets a job title history object with the given parameters
     */
    private function _getJobTitleHistory($empNum, $jobtitleCode, $jobTitleName, $startDate, $endDate) {

        if (!empty($startDate)) {
            $startDate = date(LocaleUtil::STANDARD_TIMESTAMP_FORMAT, strtotime($startDate));
        }

        if (!empty($endDate)) {
            $endDate = date(LocaleUtil::STANDARD_TIMESTAMP_FORMAT, strtotime($endDate));
        }

        $jobHis = new JobTitleHistory();
        $jobHis->setEmpNumber($empNum);
        $jobHis->setCode($jobtitleCode);
        $jobHis->setName($jobTitleName);
        $jobHis->setStartDate($startDate);
        $jobHis->setEndDate($endDate);

        return $jobHis;
    }

    /**
     * Insert given job title history item to the database
     */
    private function _insertJobTitleHistory(&$jobTitleHistory) {

        $startDate = $jobTitleHistory->getStartDate();
        $endDate = $jobTitleHistory->getEndDate();
        $startDate = is_null($startDate) ? 'null' : "'{$startDate}'";
        $endDate = is_null($endDate) ? 'null' : "'{$endDate}'";

        $sql = sprintf("INSERT INTO hs_hr_emp_jobtitle_history(emp_number,code, name," .
                        "start_date, end_date) VALUES (%d, '%s', '%s', %s, %s)",
                        $jobTitleHistory->getEmpNumber(), $jobTitleHistory->getCode(),
                        $jobTitleHistory->getName(), $startDate,
                        $endDate);

        $this->_runQuery($sql);
        $id = mysql_insert_id();
        $jobTitleHistory->setId($id);
    }

    /**
     * Returns the number of rows in the hs_hr_emp_jobtitle_history table
     *
     * @param  string $where where clause
     * @return int number of rows
     */
    private function _getNumRows($where = null) {

        $sql = "SELECT COUNT(*) FROM hs_hr_emp_jobtitle_history";
        if (!empty($where)) {
            $sql .= " WHERE " . $where;
        }
        $result = mysql_query($sql);
        $row = mysql_fetch_array($result, MYSQL_NUM);
        $count = $row[0];
        return $count;
    }

    /**
     * Returns rows that match the given query from the database.
     *
     * @param  string $where where clause
     * @return Array 2D associative array with results. Null if no matching results found
     */
    private function _getMatchingRows($where = null) {

        $sql = "SELECT * FROM hs_hr_emp_jobtitle_history";
        if (!empty($where)) {
            $sql .= " WHERE " . $where;
        }

        $list = null;

        $result = mysql_query($sql);
        while ($result && ($row = mysql_fetch_assoc($result))) {
            $list[] = $row;
        }

        return $list;
    }

    private function _runQuery($sql) {
        $this->assertTrue(mysql_query($sql), mysql_error());
    }
}

// Call JobTitleHistoryTest::main() if this source file is executed directly.
if (PHPUnit_MAIN_METHOD == "JobTitleHistoryTest::main") {
    JobTitleHistoryTest::main();
}
?>
