Issues (12)

tests/vcs/test_clone.py (3 issues)

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 View Code Duplication
def test_clone_should_abort_if_user_does_not_want_to_reclone(mocker, tmpdir):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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',
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
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 View Code Duplication
@pytest.mark.parametrize('error_message', [
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
142
    (
143
        "fatal: repository 'https://github.com/hackebro/cookiedozer' "
144
        "not found"
145
    ).encode('utf-8'),
146
    'hg: abort: HTTP Error 404: Not Found'.encode('utf-8'),
147
])
148
def test_clone_handles_repo_typo(mocker, clone_dir, error_message):
149
    """In `clone()`, repository not found errors should raise an
150
    appropriate exception.
151
    """
152
    # side_effect is set to an iterable here (and below),
153
    # because of a Python 3.4 unittest.mock regression
154
    # http://bugs.python.org/issue23661
155
    mocker.patch(
156
        'cookiecutter.vcs.subprocess.check_output',
157
        autospec=True,
158
        side_effect=[subprocess.CalledProcessError(
159
            -1, 'cmd', output=error_message
160
        )]
161
    )
162
163
    repository_url = 'https://github.com/hackebro/cookiedozer'
164
    with pytest.raises(exceptions.RepositoryNotFound) as err:
165
        vcs.clone(
166
            repository_url,
167
            clone_to_dir=clone_dir,
168
            no_input=True
169
        )
170
171
    assert str(err.value) == (
172
        'The repository {} could not be found, have you made a typo?'
173
    ).format(repository_url)
174
175
176 View Code Duplication
@pytest.mark.parametrize('error_message', [
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
177
    (
178
        "error: pathspec 'unknown_branch' did not match any file(s) known "
179
        "to git"
180
    ).encode('utf-8'),
181
    "hg: abort: unknown revision 'unknown_branch'!".encode('utf-8'),
182
])
183
def test_clone_handles_branch_typo(mocker, clone_dir, error_message):
184
    """In `clone()`, branch not found errors should raise an
185
    appropriate exception.
186
    """
187
    mocker.patch(
188
        'cookiecutter.vcs.subprocess.check_output',
189
        autospec=True,
190
        side_effect=[subprocess.CalledProcessError(
191
            -1, 'cmd', output=error_message
192
        )]
193
    )
194
195
    repository_url = 'https://github.com/pytest-dev/cookiecutter-pytest-plugin'
196
    with pytest.raises(exceptions.RepositoryCloneFailed) as err:
197
        vcs.clone(
198
            repository_url,
199
            clone_to_dir=clone_dir,
200
            checkout='unknown_branch',
201
            no_input=True
202
        )
203
204
    assert str(err.value) == (
205
        'The unknown_branch branch of repository '
206
        '{} could not found, have you made a typo?'
207
    ).format(repository_url)
208
209
210
def test_clone_unknown_subprocess_error(mocker, clone_dir):
211
    """In `clone()`, unknown subprocess errors should be raised."""
212
    mocker.patch(
213
        'cookiecutter.vcs.subprocess.check_output',
214
        autospec=True,
215
        side_effect=[subprocess.CalledProcessError(
216
            -1, 'cmd', output='Something went wrong'.encode('utf-8')
217
        )]
218
    )
219
220
    with pytest.raises(subprocess.CalledProcessError):
221
        vcs.clone(
222
            'https://github.com/pytest-dev/cookiecutter-pytest-plugin',
223
            clone_to_dir=clone_dir,
224
            no_input=True
225
        )
226