Completed
Push — master ( a65ea9...4ad11c )
by Michael
01:20
created

test_clone_should_invoke_vcs_command()   B

Complexity

Conditions 2

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 45
rs 8.8571
1
# -*- coding: utf-8 -*-
2
import os
3
import subprocess
4
5
import pytest
6
7
from cookiecutter import exceptions, vcs
8
9
10
@pytest.fixture
11
def clone_dir(tmpdir):
12
    """Simulates creation of a directory called `clone_dir` inside of `tmpdir`.
13
    Returns a str to said directory.
14
    """
15
    return str(tmpdir.mkdir('clone_dir'))
16
17
18
def test_clone_should_raise_if_vcs_not_installed(mocker, clone_dir):
19
    """In `clone()`, a `VCSNotInstalled` exception should be raised if no VCS
20
    is installed.
21
    """
22
    mocker.patch(
23
        'cookiecutter.vcs.is_vcs_installed',
24
        autospec=True,
25
        return_value=False
26
    )
27
28
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
29
30
    with pytest.raises(exceptions.VCSNotInstalled):
31
        vcs.clone(repo_url, clone_to_dir=clone_dir)
32
33
34
def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir):
35
    """In `clone()`, repo URL's trailing slash should be stripped if one is
36
    present.
37
    """
38
    mocker.patch(
39
        'cookiecutter.vcs.is_vcs_installed',
40
        autospec=True,
41
        return_value=True
42
    )
43
44
    mock_subprocess = mocker.patch(
45
        'cookiecutter.vcs.subprocess.check_output',
46
        autospec=True,
47
    )
48
49
    vcs.clone(
50
        'https://github.com/foo/bar/',
51
        clone_to_dir=clone_dir,
52
        no_input=True
53
    )
54
55
    mock_subprocess.assert_called_once_with(
56
        ['git', 'clone', 'https://github.com/foo/bar'],
57
        cwd=clone_dir,
58
        stderr=subprocess.STDOUT
59
    )
60
61
62
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
63
    """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit
64
    without cloning anything.
65
    """
66
    mocker.patch(
67
        'cookiecutter.vcs.is_vcs_installed',
68
        autospec=True,
69
        return_value=True
70
    )
71
    mocker.patch(
72
        'cookiecutter.vcs.prompt_and_delete_repo',
73
        side_effect=SystemExit,
74
        autospec=True
75
    )
76
    mock_subprocess = mocker.patch(
77
        'cookiecutter.vcs.subprocess.check_output',
78
        autospec=True,
79
    )
80
81
    clone_to_dir = tmpdir.mkdir('clone')
82
83
    # Create repo_dir to trigger prompt_and_delete_repo
84
    clone_to_dir.mkdir('cookiecutter-pytest-plugin')
85
86
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
87
88
    with pytest.raises(SystemExit):
89
        vcs.clone(repo_url, clone_to_dir=str(clone_to_dir))
90
    assert not mock_subprocess.called
91
92
93
@pytest.mark.parametrize('repo_type, repo_url, repo_name', [
94
    ('git', 'https://github.com/hello/world.git', 'world'),
95
    ('hg', 'https://bitbucket.org/foo/bar', 'bar'),
96
])
97
def test_clone_should_invoke_vcs_command(
98
        mocker, clone_dir, repo_type, repo_url, repo_name):
99
    """When `clone()` is called with a git/hg repo, the corresponding VCS
100
    command should be run via `subprocess.check_output()`.
101
102
    This should take place:
103
    * In the correct dir
104
    * With the correct args.
105
    """
106
    mocker.patch(
107
        'cookiecutter.vcs.is_vcs_installed',
108
        autospec=True,
109
        return_value=True
110
    )
111
112
    mock_subprocess = mocker.patch(
113
        'cookiecutter.vcs.subprocess.check_output',
114
        autospec=True,
115
    )
116
    expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name))
117
118
    branch = 'foobar'
119
120
    repo_dir = vcs.clone(
121
        repo_url,
122
        checkout=branch,
123
        clone_to_dir=clone_dir,
124
        no_input=True
125
    )
126
127
    assert repo_dir == expected_repo_dir
128
129
    mock_subprocess.assert_any_call(
130
        [repo_type, 'clone', repo_url],
131
        cwd=clone_dir,
132
        stderr=subprocess.STDOUT
133
    )
134
    mock_subprocess.assert_any_call(
135
        [repo_type, 'checkout', branch],
136
        cwd=expected_repo_dir,
137
        stderr=subprocess.STDOUT
138
    )
139
140
141
@pytest.mark.parametrize('error_message', [
142
    "fatal: repository 'https://github.com/hackebro/cookiedozer' not found",
143
    'hg: abort: HTTP Error 404: Not Found',
144
])
145
def test_clone_handles_repo_typo(mocker, clone_dir, error_message):
146
    """In `clone()`, repository not found errors should raise an
147
    appropriate exception.
148
    """
149
    # side_effect is set to an iterable here (and below),
150
    # because of a Python 3.4 unittest.mock regression
151
    # http://bugs.python.org/issue23661
152
    mocker.patch(
153
        'cookiecutter.vcs.subprocess.check_output',
154
        autospec=True,
155
        side_effect=[subprocess.CalledProcessError(
156
            -1, 'cmd', output=error_message
157
        )]
158
    )
159
160
    repository_url = 'https://github.com/hackebro/cookiedozer'
161
    with pytest.raises(exceptions.RepositoryNotFound) as err:
162
        vcs.clone(
163
            repository_url,
164
            clone_to_dir=clone_dir,
165
            no_input=True
166
        )
167
168
    assert str(err.value) == (
169
        'The repository {} could not be found, have you made a typo?'
170
    ).format(repository_url)
171
172
173
@pytest.mark.parametrize('error_message', [
174
    "error: pathspec 'unknown_branch' did not match any file(s) known to git.",
175
    "hg: abort: unknown revision 'unknown_branch'!",
176
])
177
def test_clone_handles_branch_typo(mocker, clone_dir, error_message):
178
    """In `clone()`, branch not found errors should raise an
179
    appropriate exception.
180
    """
181
    mocker.patch(
182
        'cookiecutter.vcs.subprocess.check_output',
183
        autospec=True,
184
        side_effect=[subprocess.CalledProcessError(
185
            -1, 'cmd', output=error_message
186
        )]
187
    )
188
189
    repository_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin'
190
    with pytest.raises(exceptions.RepositoryCloneFailed) as err:
191
        vcs.clone(
192
            repository_url,
193
            clone_to_dir=clone_dir,
194
            checkout='unknown_branch',
195
            no_input=True
196
        )
197
198
    assert str(err.value) == (
199
        'The unknown_branch branch of repository '
200
        '{} could not found, have you made a typo?'
201
    ).format(repository_url)
202
203
204
def test_clone_unknown_subprocess_error(mocker, clone_dir):
205
    """In `clone()`, unknown subprocess errors should be raised."""
206
    mocker.patch(
207
        'cookiecutter.vcs.subprocess.check_output',
208
        autospec=True,
209
        side_effect=[subprocess.CalledProcessError(
210
            -1, 'cmd', output='Something went wrong'
211
        )]
212
    )
213
214
    with pytest.raises(subprocess.CalledProcessError):
215
        vcs.clone(
216
            'https://github.com/pytest-dev/cookiecutter-pytest-plugin',
217
            clone_to_dir=clone_dir,
218
            no_input=True
219
        )
220