Completed
Push — vcs-error-messages ( 931585...7a9569 )
by Michael
01:11
created

test_clone_handles_repo_typo()   A

Complexity

Conditions 2

Size

Total Lines 21

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 21
rs 9.3142
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
@pytest.mark.parametrize('which_return, result', [
35
    ('', False),
36
    (None, False),
37
    (False, False),
38
    ('/usr/local/bin/git', True),
39
])
40
def test_is_vcs_installed(mocker, which_return, result):
41
    mocker.patch(
42
        'cookiecutter.vcs.which',
43
        autospec=True,
44
        return_value=which_return
45
    )
46
    assert vcs.is_vcs_installed('git') == result
47
48
49
@pytest.mark.parametrize('repo_type, repo_url, repo_name', [
50
    ('git', 'https://github.com/hello/world.git', 'world'),
51
    ('hg', 'https://bitbucket.org/foo/bar', 'bar'),
52
])
53
def test_clone_should_invoke_git(
54
        mocker, clone_dir, repo_type, repo_url, repo_name):
55
    """When `clone()` is called with a git/hg repo, the corresponding VCS
56
    command should be run via `subprocess.check_output()`.
57
58
    This should take place:
59
    * In the correct dir
60
    * With the correct args.
61
    """
62
    mocker.patch(
63
        'cookiecutter.vcs.is_vcs_installed',
64
        autospec=True,
65
        return_value=True
66
    )
67
68
    mock_subprocess = mocker.patch(
69
        'cookiecutter.vcs.subprocess.check_output',
70
        autospec=True,
71
    )
72
    expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name))
73
74
    branch = 'foobar'
75
76
    repo_dir = vcs.clone(
77
        repo_url,
78
        checkout=branch,
79
        clone_to_dir=clone_dir,
80
        no_input=True
81
    )
82
83
    assert repo_dir == expected_repo_dir
84
85
    mock_subprocess.assert_any_call(
86
        [repo_type, 'clone', repo_url],
87
        cwd=clone_dir,
88
        stderr=subprocess.STDOUT
89
    )
90
    mock_subprocess.assert_any_call(
91
        [repo_type, 'checkout', branch],
92
        cwd=expected_repo_dir,
93
        stderr=subprocess.STDOUT
94
    )
95
96
97
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
98
    """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit
99
    without cloning anything.
100
    """
101
    mocker.patch(
102
        'cookiecutter.vcs.is_vcs_installed',
103
        autospec=True,
104
        return_value=True
105
    )
106
    mocker.patch(
107
        'cookiecutter.vcs.prompt_and_delete_repo',
108
        side_effect=SystemExit,
109
        autospec=True
110
    )
111
    mock_subprocess = mocker.patch(
112
        'cookiecutter.vcs.subprocess.check_output',
113
        autospec=True,
114
    )
115
116
    clone_to_dir = tmpdir.mkdir('clone')
117
118
    # Create repo_dir to trigger prompt_and_delete_repo
119
    clone_to_dir.mkdir('cookiecutter-pytest-plugin')
120
121
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
122
123
    with pytest.raises(SystemExit):
124
        vcs.clone(repo_url, clone_to_dir=str(clone_to_dir))
125
    assert not mock_subprocess.called
126
127
128
def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir):
129
    """In `clone()`, repo URL's trailing slash should be stripped if one is
130
    present.
131
    """
132
    mocker.patch(
133
        'cookiecutter.vcs.is_vcs_installed',
134
        autospec=True,
135
        return_value=True
136
    )
137
138
    mock_subprocess = mocker.patch(
139
        'cookiecutter.vcs.subprocess.check_output',
140
        autospec=True,
141
    )
142
143
    vcs.clone(
144
        'https://github.com/foo/bar/',
145
        clone_to_dir=clone_dir,
146
        no_input=True
147
    )
148
149
    mock_subprocess.assert_called_once_with(
150
        ['git', 'clone', 'https://github.com/foo/bar'],
151
        cwd=clone_dir,
152
        stderr=subprocess.STDOUT
153
    )
154
155
156
@pytest.mark.parametrize('error_message', [
157
    "fatal: repository 'https://github.com/hackebro/cookiedozer' not found",
158
    'hg: abort: HTTP Error 404: Not Found',
159
])
160
def test_clone_handles_repo_typo(mocker, clone_dir, error_message):
161
    """In `clone()`, repository not found errors should raise an
162
    appropriate exception.
163
    """
164
    mocker.patch(
165
        'cookiecutter.vcs.subprocess.check_output',
166
        autospec=True,
167
        side_effect=subprocess.CalledProcessError(
168
            -1, 'cmd', output=error_message
169
        )
170
    )
171
172
    with pytest.raises(exceptions.RepositoryCloneFailed):
173
        vcs.clone(
174
            'https://github.com/hackebro/cookiedozer',
175
            clone_to_dir=clone_dir,
176
            no_input=True
177
        )
178
179
180
@pytest.mark.parametrize('error_message', [
181
    "error: pathspec 'unknown_branch' did not match any file(s) known to git.",
182
    "hg: abort: unknown revision 'unknown_branch'!",
183
])
184
def test_clone_handles_branch_typo(mocker, clone_dir, error_message):
185
    """In `clone()`, branch not found errors should raise an
186
    appropriate exception.
187
    """
188
    mocker.patch(
189
        'cookiecutter.vcs.subprocess.check_output',
190
        autospec=True,
191
        side_effect=subprocess.CalledProcessError(
192
            -1, 'cmd', output=error_message
193
        )
194
    )
195
196
    with pytest.raises(exceptions.RepositoryCloneFailed):
197
        vcs.clone(
198
            'https://github.com/pytest-dev/cookiecutter-pytest-plugin',
199
            clone_to_dir=clone_dir,
200
            checkout='unknown_branch',
201
            no_input=True
202
        )
203
204
205
def test_clone_unknown_subprocess_error(mocker, clone_dir):
206
    """In `clone()`, unknown subprocess errors should be raised."""
207
    mocker.patch(
208
        'cookiecutter.vcs.subprocess.check_output',
209
        autospec=True,
210
        side_effect=subprocess.CalledProcessError(
211
            -1, 'cmd', output='Something went wrong'
212
        )
213
    )
214
215
    with pytest.raises(subprocess.CalledProcessError):
216
        vcs.clone(
217
            'https://github.com/pytest-dev/cookiecutter-pytest-plugin',
218
            clone_to_dir=clone_dir,
219
            no_input=True
220
        )
221