Completed
Pull Request — master (#603)
by
unknown
58s
created

tests.test_clone_should_invoke_git()   B

Complexity

Conditions 2

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 41
rs 8.8571
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.which',
121
        autospec=True,
122
        return_value=''
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('repo_type, repo_url, repo_name', [
132
    ('git', 'https://github.com/hello/world.git', 'world'),
133
    ('hg', 'https://bitbucket.org/foo/bar', 'bar'),
134
])
135
def test_clone_should_invoke_git(
136
        mocker, clone_dir, repo_type, repo_url, repo_name):
137
    """When `clone()` is called with a git/hg repo, the corresponding VCS
138
    command should be run via `subprocess.check_call()`.
139
140
    This should take place:
141
    * In the correct dir
142
    * With the correct args.
143
    """
144
    mocker.patch(
145
        'cookiecutter.vcs.is_vcs_installed',
146
        autospec=True,
147
        return_value=True
148
    )
149
150
    mock_subprocess = mocker.patch(
151
        'cookiecutter.vcs.subprocess.check_call',
152
        autospec=True,
153
    )
154
    expected_repo_dir = os.path.normpath(os.path.join(clone_dir, repo_name))
155
156
    branch = 'foobar'
157
158
    repo_dir = vcs.clone(
159
        repo_url,
160
        checkout=branch,
161
        clone_to_dir=clone_dir,
162
        no_input=True
163
    )
164
165
    assert repo_dir == expected_repo_dir
166
167
    mock_subprocess.assert_any_call(
168
        [repo_type, 'clone', repo_url], cwd=clone_dir
169
    )
170
    mock_subprocess.assert_any_call(
171
        [repo_type, 'checkout', branch], cwd=expected_repo_dir
172
    )
173
174
175
@pytest.mark.raphael
176
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
177
    """In `clone()`, if user doesn't want to reclone, Cookiecutter should exit
178
    without cloning anything.
179
    """
180
    mocker.patch(
181
        'cookiecutter.vcs.is_vcs_installed',
182
        autospec=True,
183
        return_value=True
184
    )
185
    mocker.patch(
186
        'cookiecutter.vcs.prompt_and_delete_repo',
187
        side_effect=SystemExit,
188
        autospec=True
189
    )
190
    mock_subprocess = mocker.patch(
191
        'cookiecutter.vcs.subprocess.check_call',
192
        autospec=True,
193
    )
194
195
    clone_to_dir = tmpdir.mkdir('clone')
196
197
    # Create repo_dir to trigger prompt_and_delete_repo
198
    clone_to_dir.mkdir('cookiecutter-pytest-plugin')
199
200
    repo_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin.git'
201
202
    with pytest.raises(SystemExit):
203
        vcs.clone(repo_url, clone_to_dir=str(clone_to_dir))
204
    assert not mock_subprocess.called
205
206
207
def test_clone_should_rstrip_trailing_slash_in_repo_url(mocker, clone_dir):
208
    """In `clone()`, repo URL's trailing slash should be stripped if one is
209
    present.
210
    """
211
    mocker.patch(
212
        'cookiecutter.vcs.is_vcs_installed',
213
        autospec=True,
214
        return_value=True
215
    )
216
217
    mock_subprocess = mocker.patch(
218
        'cookiecutter.vcs.subprocess.check_call',
219
        autospec=True,
220
    )
221
222
    vcs.clone(
223
        'https://github.com/foo/bar/',
224
        clone_to_dir=clone_dir,
225
        no_input=True
226
    )
227
228
    mock_subprocess.assert_called_once_with(
229
        ['git', 'clone', 'https://github.com/foo/bar'], cwd=clone_dir
230
    )
231