# -*- coding: utf-8 -*-

import os, tempfile, shutil, time
import unittest
from trac.test import EnvironmentStub, Mock
from trac.ticket import Ticket, TicketSystem

from tracsteinschart.worktime.web_ui import *

class _BaseTestCase(unittest.TestCase):
    def setUp(self):
        self.basedir = os.path.realpath(tempfile.mkdtemp())
        self.env = EnvironmentStub()
        self.env.path = os.path.join(self.basedir, 'trac-tempenv')
        os.mkdir(self.env.path)
        ts = TicketSystem(self.env)
        ts.fields.append({"type": "text", "name": "totalhours", 
                          "label": u"total hours", "custom": "True"})
        ts.fields.append({"type": "text", "name": "estimatedhours", 
                          "label": u"estimated hours", "custom": "True"})

    def tearDown(self):
        shutil.rmtree(self.basedir)

class WorktimeRecorderTestCase(_BaseTestCase):

    def setUp(self):
        _BaseTestCase.setUp(self)
        self.recorder = WorktimeRecorder(self.env)
        self.req = Mock(authname="user") # just workaround

    def create_ticket(self, totalhours = None, estimatedhours = None):
        t = Ticket(self.env)
        t["summary"] = "test"
        t["status"] = "new"
        if totalhours is not None:
            t["totalhours"] = str(totalhours)
        if estimatedhours is not None:
            t["estimatedhours"] = str(estimatedhours)
        t.insert()
        return t

    def get_ticket_change(self, db, id, field):
        cursor = db.cursor()
        sql = \
            """select oldvalue, newvalue from ticket_change
            where ticket=%s and field=%s order by time asc"""
        cursor.execute(sql, (id, field))
        return cursor.fetchall()

    def test_add_hours(self):
        t = self.create_ticket(totalhours = 0.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)        
        # 作業時間を追加／見積は変更なし
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "2.0", "remainedhours": "8.0"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "2.0")
        self.assertEqual(t["estimatedhours"], "10.0")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("0.0", "2.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "2.0")])

    def test_add_more_hours(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)        
        # 作業時間を追加／見積は変更なし
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "2.0", "remainedhours": "6.0"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "4.0")
        self.assertEqual(t["estimatedhours"], "10.0")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("2.0", "4.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "2.0")])

    def test_increase_estimate(self):
        t = self.create_ticket(totalhours = 0.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 見積を増加
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "", "remainedhours": "10.5"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "0.0")
        self.assertEqual(t["estimatedhours"], "10.5")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [("10.0", "10.5")])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [])

    def test_decrease_estimate(self):
        t = self.create_ticket(totalhours = 0.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 見積を減少
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "", "remainedhours": "9.5"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "0.0")
        self.assertEqual(t["estimatedhours"], "9.5")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [("10.0", "9.5")])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [])

    def test_increase_estimate_more(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 見積を増加(作業時間入力済み)
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "", "remainedhours": "8.5"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "2.0")
        self.assertEqual(t["estimatedhours"], "10.5")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [("10.0", "10.5")])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [])

    def test_add_hour_and_increase_estimate(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 作業時間入力と見積の増加を同時に
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "2.0", "remainedhours": "6.5"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "4.0")
        self.assertEqual(t["estimatedhours"], "10.5")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("2.0", "4.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [("10.0", "10.5")])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "2.0")])

    def test_add_hour_and_zero_remained(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 見積もり通りに作業を完了
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "8.0", "remainedhours": "0.0"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "10.0")
        self.assertEqual(t["estimatedhours"], "10.0")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("2.0", "10.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "8.0")])

    def test_add_hour_and_zero_remained_2(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        # 当初の見積もりより早く作業を完了
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "7.0", "remainedhours": "0.0"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "9.0")
        self.assertEqual(t["estimatedhours"], "9.0")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("2.0", "9.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [("10.0", "9.0")])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "7.0")])

    def test_close(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        db = self.env.get_db_cnx()
        params = {str(t.id): {
            "hours": "8.0", "remainedhours": "0.0", "close": "on"
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "10.0")
        self.assertEqual(t["estimatedhours"], "10.0")
        self.assertEqual(t["status"], "closed")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [("2.0", "10.0")])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [("0", "8.0")])
        
        data = self.get_ticket_change(db, t.id, "status")
        self.assertEqual(data, [("new", "closed")])

    def test_nochange(self):
        t = self.create_ticket(totalhours = 2.0, estimatedhours = 10.0)
        self.assertTrue(t.id > 0)
        db = self.env.get_db_cnx()
        params = {str(t.id): {
                "hours": "", "remainedhours": ""
            }}
        self.recorder.update_tickets(self.req, db.cursor(), db, params)
        t = Ticket(self.env, t.id)
        self.assertEqual(t["totalhours"], "2.0")
        self.assertEqual(t["estimatedhours"], "10.0")

        data = self.get_ticket_change(db, t.id, "totalhours")
        self.assertEqual(data, [])

        data = self.get_ticket_change(db, t.id, "estimatedhours")
        self.assertEqual(data, [])
        
        data = self.get_ticket_change(db, t.id, "hours")
        self.assertEqual(data, [])

def suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(WorktimeRecorderTestCase, 'test'))
    return suite

if __name__ == '__main__':
    unittest.main(defaultTest='suite')

