Completed
Push — extra-context-cli ( af51e0...e7614b )
by Michael
01:02
created

tests.clone_dir()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 6
rs 9.4285
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
test_vcs
6
------------
7
8
Tests for `cookiecutter.vcs` module.
9
"""
10
11
import os
12
import pytest
13
14
from cookiecutter import exceptions, vcs
15
16
17
@pytest.mark.parametrize('repo_url, exp_repo_type, exp_repo_url', [
18
    (
19
        "git+https://github.com/pytest-dev/cookiecutter-pytest-plugin.git",
20
        "git",
21
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git"
22
    ), (
23
        "hg+https://bitbucket.org/foo/bar.hg",
24
        "hg",
25
        "https://bitbucket.org/foo/bar.hg"
26
    ), (
27
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git",
28
        "git",
29
        "https://github.com/pytest-dev/cookiecutter-pytest-plugin.git"
30
    ), (
31
        "https://bitbucket.org/foo/bar.hg",
32
        "hg",
33
        "https://bitbucket.org/foo/bar.hg"
34
    )
35
])
36
def test_identify_known_repo(repo_url, exp_repo_type, exp_repo_url):
37
    assert vcs.identify_repo(repo_url) == (exp_repo_type, exp_repo_url)
38
39
40
@pytest.fixture(params=[
41
    "foo+git",  # uses explicit identifier with 'git' in the wrong place
42
    "foo+hg",  # uses explicit identifier with 'hg' in the wrong place
43
    "foo+bar",  # uses explicit identifier with neither 'git' nor 'hg'
44
    "foobar"  # no identifier but neither 'git' nor 'bitbucket' in url
45
])
46
def unknown_repo_type_url(request):
47
    return request.param
48
49
50
def test_identify_raise_on_unknown_repo(unknown_repo_type_url):
51
    with pytest.raises(exceptions.UnknownRepoType):
52
        vcs.identify_repo(unknown_repo_type_url)
53
54
55
def test_prompt_should_ask_and_rm_repo_dir(mocker, tmpdir):
56
    """In `prompt_and_delete_repo()`, if the user agrees to delete/reclone the
57
    repo, the repo should be deleted.
58
    """
59
    mock_read_user = mocker.patch(
60
        'cookiecutter.vcs.read_user_yes_no',
61
        return_value=True,
62
        autospec=True
63
    )
64
    repo_dir = tmpdir.mkdir('repo')
65
66
    vcs.prompt_and_delete_repo(str(repo_dir))
67
68
    assert mock_read_user.called
69
    assert not repo_dir.exists()
70
71
72
def test_prompt_should_ask_and_keep_repo_dir(mocker, tmpdir):
73
    """In `prompt_and_delete_repo()`, if the user wants to keep their old
74
    cloned template repo, it should not be deleted.
75
    """
76
    mock_read_user = mocker.patch(
77
        'cookiecutter.vcs.read_user_yes_no',
78
        return_value=False,
79
        autospec=True
80
    )
81
    repo_dir = tmpdir.mkdir('repo')
82
83
    with pytest.raises(SystemExit):
84
        vcs.prompt_and_delete_repo(str(repo_dir))
85
86
    assert mock_read_user.called
87
    assert repo_dir.exists()
88
89
90
def test_prompt_should_not_ask_if_no_input_and_rm_repo_dir(mocker, tmpdir):
91
    """In `prompt_and_delete_repo()`, if `no_input` is True, the call to
92
    `vcs.read_user_yes_no()` should be suppressed.
93
    """
94
    mock_read_user = mocker.patch(
95
        'cookiecutter.vcs.read_user_yes_no',
96
        return_value=True,
97
        autospec=True
98
    )
99
    repo_dir = tmpdir.mkdir('repo')
100
101
    vcs.prompt_and_delete_repo(str(repo_dir), no_input=True)
102
103
    assert not mock_read_user.called
104
    assert not repo_dir.exists()
105
106
107
@pytest.fixture
108
def clone_dir(tmpdir):
109
    """Simulates creation of a directory called `clone_dir` inside of `tmpdir`.
110
    Returns a str to said directory.
111
    """
112
    return str(tmpdir.mkdir('clone_dir'))
113
114
115
def test_clone_should_raise_if_vcs_not_installed(mocker, clone_dir):
116
    """In `clone()`, a `VCSNotInstalled` exception should be raised if no VCS
117
    is installed.
118
    """
119
    mocker.patch(
120
        'cookiecutter.vcs.is_vcs_installed',
121
        autospec=True,
122
        return_value=False
123
    )
124
125
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
126
127
    with pytest.raises(exceptions.VCSNotInstalled):
128
        vcs.clone(repo_url, clone_to_dir=clone_dir)
129
130
131
@pytest.mark.parametrize('which_return, result', [
132
    ('', False),
133
    (None, False),
134
    (False, False),
135
    ('/usr/local/bin/git', True),
136
])
137
def test_is_vcs_installed(mocker, which_return, result):
138
    mocker.patch(
139
        'cookiecutter.vcs.which',
140
        autospec=True,
141
        return_value=which_return
142
    )
143
    assert vcs.is_vcs_installed('git') == result
144
145
146
@pytest.mark.parametrize('repo_type, repo_url, repo_name', [
147
    ('git', 'https://github.com/hello/world.git', 'world'),
148
    ('hg', 'https://bitbucket.org/foo/bar', 'bar'),
149
])
150
def test_clone_should_invoke_git(
151
        mocker, clone_dir, repo_type, repo_url, repo_name):
152
    """When `clone()` is called with a git/hg repo, the corresponding VCS
153
    command should be run via `subprocess.check_call()`.
154
155
    This should take place:
156
    * In the correct dir
157
    * With the correct args.
158
    """
159
    mocker.patch(
160
        'cookiecutter.vcs.is_vcs_installed',
161
        autospec=True,
162
        return_value=True
163
    )
164
165
    mock_subprocess = mocker.patch(
166
        'cookiecutter.vcs.subprocess.check_call',
167
        autospec=True,
168
    )
169
    expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name))
170
171
    branch = 'foobar'
172
173
    repo_dir = vcs.clone(
174
        repo_url,
175
        checkout=branch,
176
        clone_to_dir=clone_dir,
177
        no_input=True
178
    )
179
180
    assert repo_dir == expected_repo_dir
181
182
    mock_subprocess.assert_any_call(
183
        [repo_type, 'clone', repo_url], cwd=clone_dir
184
    )
185
    mock_subprocess.assert_any_call(
186
        [repo_type, 'checkout', branch], cwd=expected_repo_dir
187
    )
188
189
190
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
191
    """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit
192
    without cloning anything.
193
    """
194
    mocker.patch(
195
        'cookiecutter.vcs.is_vcs_installed',
196
        autospec=True,
197
        return_value=True
198
    )
199
    mocker.patch(
200
        'cookiecutter.vcs.prompt_and_delete_repo',
201
        side_effect=SystemExit,
202
        autospec=True
203
    )
204
    mock_subprocess = mocker.patch(
205
        'cookiecutter.vcs.subprocess.check_call',
206
        autospec=True,
207
    )
208
209
    clone_to_dir = tmpdir.mkdir('clone')
210
211
    # Create repo_dir to trigger prompt_and_delete_repo
212
    clone_to_dir.mkdir('cookiecutter-pytest-plugin')
213
214
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
215
216
    with pytest.raises(SystemExit):
217
        vcs.clone(repo_url, clone_to_dir=str(clone_to_dir))
218
    assert not mock_subprocess.called
219
220
221
def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir):
222
    """In `clone()`, repo URL's trailing slash should be stripped if one is
223
    present.
224
    """
225
    mocker.patch(
226
        'cookiecutter.vcs.is_vcs_installed',
227
        autospec=True,
228
        return_value=True
229
    )
230
231
    mock_subprocess = mocker.patch(
232
        'cookiecutter.vcs.subprocess.check_call',
233
        autospec=True,
234
    )
235
236
    vcs.clone(
237
        'https://github.com/foo/bar/',
238
        clone_to_dir=clone_dir,
239
        no_input=True
240
    )
241
242
    mock_subprocess.assert_called_once_with(
243
        ['git', 'clone', 'https://github.com/foo/bar'], cwd=clone_dir
244
    )
245