import mock

import pytest

from jenkinsapi.jenkinsbase import JenkinsBase
from jenkinsapi.view import View
from jenkinsapi.job import Job
from jenkinsapi.custom_exceptions import NotFound

DATA = {'description': 'Important Shizz',
        'jobs': [
            {'color': 'blue',
             'name': 'foo',
             'url': 'http://halob:8080/job/foo/'},
            {'color': 'red',
             'name': 'test_jenkinsapi',
             'url': 'http://halob:8080/job/test_jenkinsapi/'}
        ],
        'name': 'FodFanFo',
        'property': [],
        'url': 'http://halob:8080/view/FodFanFo/'}

JOB_DATA = {
    "actions": [],
    "description": "test job",
    "displayName": "foo",
    "displayNameOrNull": None,
    "name": "foo",
    "url": "http://halob:8080/job/foo/",
    "buildable": True,
    "builds": [
        {"number": 3, "url": "http://halob:8080/job/foo/3/"},
        {"number": 2, "url": "http://halob:8080/job/foo/2/"},
        {"number": 1, "url": "http://halob:8080/job/foo/1/"}
    ],
    "color": "blue",
    "firstBuild": {"number": 1, "url": "http://halob:8080/job/foo/1/"},
    "healthReport": [
        {"description": "Build stability: No recent builds failed.",
         "iconUrl": "health-80plus.png", "score": 100}
    ],
    "inQueue": False,
    "keepDependencies": False,
    "lastBuild": {"number": 3, "url": "http://halob:8080/job/foo/3/"},
    "lastCompletedBuild": {"number": 3, "url": "http://halob:8080/job/foo/3/"},
    "lastFailedBuild": None,
    "lastStableBuild": {"number": 3, "url": "http://halob:8080/job/foo/3/"},
    "lastSuccessfulBuild": {"number": 3, "url": "http://halob:8080/job/foo/3/"},
    "lastUnstableBuild": None,
    "lastUnsuccessfulBuild": None,
    "nextBuildNumber": 4,
    "property": [],
    "queueItem": None,
    "concurrentBuild": False,
    "downstreamProjects": [],
    "scm": {},
    "upstreamProjects": []
}


@pytest.fixture
def jenkins():
    jenkins = mock.MagicMock(auto_spec=True)
    jenkins.has_job.return_value = False
    return jenkins


@mock.patch.object(Job, '_poll', auto_spec=True)
@mock.patch.object(View, '_poll', auto_spec=True)
@pytest.fixture
def view(_view_poll, _job_poll, jenkins):
    _view_poll.return_value = DATA
    _job_poll.return_value = JOB_DATA
    return View('http://localhost:800/view/FodFanFo',
                'FodFanFo',
                jenkins)


@pytest.fixture
def jenkins_patch():

    class Jenkins:
        def has_job(self, job_name):
            return False

        def get_jenkins_obj_from_url(self, url):
            return self

    return Jenkins


@pytest.fixture
def busy_patch():

    class Jenkins:
        def has_job(self, job_name):
            return True

        def get_jenkins_obj_from_url(self, url):
            return self

    return Jenkins


class TestView:

    def test_returns_name_when_repr_is_called(self, view):
        assert repr(view) == 'FodFanFo'

    def test_returns_name_when_str_method_called(self, view):
        assert str(view) == 'FodFanFo'

    def test_raises_error_when_is_called(self, view):
        with pytest.raises(AttributeError):
            view.id()

    def test_returns_name_when_name_property_is_called(self, view):
        assert view.name == 'FodFanFo'

    @mock.patch.object(JenkinsBase, '_poll')
    def test_iteritems(self, _poll, view):
        _poll.return_value = JOB_DATA
        for job_name, job_obj in view.iteritems():
            assert isinstance(job_obj, Job)
            assert job_name in ['foo', 'test_jenkinsapi']

    def test_returns_dict_of_job_info_when_job_dict_method_called(self, view):
        jobs = view.get_job_dict()

        assert jobs == {
            'foo': 'http://halob:8080/job/foo/',
            'test_jenkinsapi': 'http://halob:8080/job/test_jenkinsapi/'
        }

    def test_returns_len_when_len_is_called(self, view):
        assert len(view) == 2

    @mock.patch.object(JenkinsBase, '_poll')
    def test_getitem(self, _poll, view):
        _poll.return_value = JOB_DATA

        assert isinstance(view['foo'], Job)

    def test_sets_delete_to_true_when_deleted(self, view):
        view.delete()

        assert view.deleted

    def test_returns_url_when_get_job_url_is_called(self, view):
        url = view.get_job_url('foo')

        assert url == 'http://halob:8080/job/foo/'

    def test_raises_not_found_when_get_job_url_is_invalid(self, view):
        with pytest.raises(NotFound):
            view.get_job_url('bar')

    @mock.patch.object(View, 'get_jenkins_obj')
    def test_returns_false_when_adding_wrong_job(
            self, _get_jenkins, view, jenkins_patch):
        _get_jenkins.return_value = jenkins_patch()
        result = view.add_job('bar')

        assert result is False

    def test_returns_false_when_add_existing_job(self, view):
        result = view.add_job('foo')

        assert result is False

    def test_get_nested_view_dict(self, view):
        result = view.get_nested_view_dict()

        assert isinstance(result, dict)

    def test_returns_jenkins_obj_when_get_jenkins_obj_is_called(self, view):
        obj = view.get_jenkins_obj()

        assert obj == view.jenkins_obj


class TestKeys:

    def test_returns_key_when_called(self, view):
        keys = view.keys()

        assert 'foo' in list(keys)
        assert 'test_jenkinsapi' in list(keys)


class TestAddJob:

    @mock.patch.object(JenkinsBase, '_poll')
    def test_returns_true_when_no_job_provided(self, _poll, view):
        _poll.return_value = DATA

        result = view.add_job('bar')

        assert result is True

    @mock.patch.object(JenkinsBase, '_poll')
    def test_returns_false_when_already_registered(self, _poll, view):
        _poll.return_value = DATA

        result = view.add_job('foo')

        assert result is False

    @mock.patch.object(View, 'get_jenkins_obj')
    def test_returns_false_when_jenkins_has_job(self, _get_jenkins, view, jenkins_patch):
        _get_jenkins.return_value = jenkins_patch()

        result = view.add_job('Foo')

        _get_jenkins.assert_called()
        assert result is False

    @mock.patch.object(View, 'get_jenkins_obj')
    @mock.patch.object(JenkinsBase, '_poll')
    def test_returns_true_when_jenkins_has_job(self, _poll, _get_jenkins, view, jenkins):
        _get_jenkins.return_value = jenkins()
        _poll.return_value = DATA
        result = view.add_job('Foo')

        _get_jenkins.assert_called()
        assert result is True


class TestRemove:
    @mock.patch.object(JenkinsBase, '_poll')
    def test_returns_true_when_job_has_been_removed(self, _poll, view):
        _poll.return_value = DATA

        result = view.remove_job('foo')

        assert result is True

    @mock.patch.object(JenkinsBase, '_poll')
    def test_returns_false_when_job_does_not_exist(self, _poll, view):
        _poll.return_value = DATA

        result = view.remove_job('Non-existant Foo')

        assert result is False
