# coding: utf-8
import unittest

import mock

import koji
import kojihub


UP = kojihub.UpdateProcessor
DP = kojihub.DeleteProcessor


class TestCancelBuildRootExports(unittest.TestCase):

    def setUp(self):
        self.get_build = mock.patch('kojihub.kojihub.get_build').start()
        self.cancel_build = mock.patch('kojihub.kojihub.cancel_build').start()
        self.exports = kojihub.RootExports()
        self.context = mock.patch('kojihub.kojihub.context').start()
        # It seems MagicMock will not automatically handle attributes that
        # start with "assert"
        self.context.session.assertPerm = mock.MagicMock()
        self.context.session.assertLogin = mock.MagicMock()

    def tearDown(self):
        mock.patch.stopall()

    def test_cancel_build_no_build(self):
        self.get_build.return_value = None
        rv = self.exports.cancelBuild(1)
        self.assertEqual(rv, False)
        self.get_build.assert_called_once_with(1, False)
        self.cancel_build.assert_not_called()

    def test_cancel_build_not_owner(self):
        self.context.session.user_id = 24
        self.context.session.hasPerm.return_value = False
        self.get_build.return_value = {'id': 1, 'owner_id': 23}
        with self.assertRaises(koji.ActionNotAllowed) as cm:
            self.exports.cancelBuild(1)
        self.assertEqual('Cannot cancel build, not owner', str(cm.exception))
        self.get_build.assert_called_once_with(1, False)
        self.cancel_build.assert_not_called()

    def test_cancel_build_valid(self):
        self.context.session.user_id = 23
        self.cancel_build.return_value = True
        self.get_build.return_value = {'id': 1, 'owner_id': 23}
        rv = self.exports.cancelBuild(1)
        self.assertEqual(rv, True)
        self.get_build.assert_called_once_with(1, False)
        self.cancel_build.assert_called_once_with(1)


class TestCancelBuildKojihub(unittest.TestCase):

    def setUp(self):
        self.get_build = mock.patch('kojihub.kojihub.get_build').start()
        self.build_notification = mock.patch('kojihub.kojihub.build_notification').start()
        self.run_callbacks = mock.patch('koji.plugin.run_callbacks').start()
        self.UpdateProcessor = mock.patch('kojihub.kojihub.UpdateProcessor',
                                          side_effect=self.getUpdate).start()
        self.updates = []
        self.DeleteProcessor = mock.patch('kojihub.kojihub.DeleteProcessor',
                                          side_effect=self.getDelete).start()
        self.deletes = []

    def tearDown(self):
        mock.patch.stopall()

    def getUpdate(self, *args, **kwargs):
        update = UP(*args, **kwargs)
        update.execute = mock.MagicMock()
        self.updates.append(update)
        return update

    def getDelete(self, *args, **kwargs):
        delete = DP(*args, **kwargs)
        delete.execute = mock.MagicMock()
        self.deletes.append(delete)
        return delete

    def test_cancel_build_not_st_building(self):
        self.get_build.return_value = {'id': 1, 'state': 2}
        rv = kojihub.cancel_build(1)
        self.assertEqual(rv, False)
        self.run_callbacks.assert_not_called()
        self.assertEqual(len(self.updates), 0)
        self.get_build.assert_called_once_with(1, strict=True)
        self.build_notification.assert_not_called()

    def test_cancel_build_not_st_canceled(self):
        self.get_build.side_effect = [{'id': 1, 'state': 0}, {'id': 1, 'state': 2}]
        rv = kojihub.cancel_build(1)
        self.assertEqual(rv, False)
        self.assertEqual(len(self.updates), 1)
        self.assertEqual(len(self.deletes), 0)
        update = self.updates[0]
        self.assertEqual(update.table, 'build')
        self.assertEqual(update.values, {'build_id': 1, 'st_building': 0})
        self.assertEqual(update.data, {'state': 4})
        self.assertEqual(update.rawdata, {'completion_time': 'NOW()'})
        self.assertEqual(update.clauses, ['id = %(build_id)i', 'state = %(st_building)i'])
        self.get_build.assert_has_calls([mock.call(1, strict=True), mock.call(1)])
        self.build_notification.assert_not_called()
        self.assertEqual(self.run_callbacks.call_count, 1)

    def test_cancel_build_valid(self):
        self.get_build.side_effect = [{'id': 1, 'state': 0}, {'id': 1, 'state': 4, 'task_id': 1},
                                      {'id': 1, 'state': 4, 'task_id': 1}]
        rv = kojihub.cancel_build(1, cancel_task=False)
        self.assertEqual(rv, True)

        self.assertEqual(len(self.updates), 1)
        self.assertEqual(len(self.deletes), 1)
        update = self.updates[0]
        self.assertEqual(update.table, 'build')
        self.assertEqual(update.values, {'build_id': 1, 'st_building': 0})
        self.assertEqual(update.data, {'state': 4})
        self.assertEqual(update.rawdata, {'completion_time': 'NOW()'})
        self.assertEqual(update.clauses, ['id = %(build_id)i', 'state = %(st_building)i'])

        delete = self.deletes[0]
        self.assertEqual(delete.table, 'build_reservations')
        self.assertEqual(delete.clauses, ['build_id = %(build_id)i'])
        self.assertEqual(delete.values, {'build_id': 1})

        self.get_build.assert_has_calls([mock.call(1, strict=True), mock.call(1),
                                         mock.call(1, strict=True)])
        self.build_notification.assert_called_once_with(1, 1)
        self.assertEqual(self.run_callbacks.call_count, 2)
