Passed
Push — master ( b1f642...d6037b )
by
unknown
01:06
created

test_unzip.test_unzip_url_with_empty_chunks()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 21
rs 9.75
c 0
b 0
f 0
cc 1
nop 2
1
"""Tests for function unzip() from zipfile module."""
2
import tempfile
3
4
import pytest
5
import shutil
6
7
from cookiecutter import zipfile
8
from cookiecutter.exceptions import InvalidZipRepository
9
10
11
def mock_download():
12
    """Fake download function."""
13
    with open('tests/files/fake-repo-tmpl.zip', 'rb') as zf:
14
        chunk = zf.read(1024)
15
        while chunk:
16
            yield chunk
17
            chunk = zf.read(1024)
18
19
20
def mock_download_with_empty_chunks():
21
    """Fake download function."""
22
    yield
23
    with open('tests/files/fake-repo-tmpl.zip', 'rb') as zf:
24
        chunk = zf.read(1024)
25
        while chunk:
26
            yield chunk
27
            chunk = zf.read(1024)
28
29
30
def test_unzip_local_file(mocker, clone_dir):
31
    """Local file reference can be unzipped."""
32
    mock_prompt_and_delete = mocker.patch(
33
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
34
    )
35
36
    output_dir = zipfile.unzip(
37
        'tests/files/fake-repo-tmpl.zip', is_url=False, clone_to_dir=str(clone_dir)
38
    )
39
40
    assert output_dir.startswith(tempfile.gettempdir())
41
    assert not mock_prompt_and_delete.called
42
43
44
def test_unzip_protected_local_file_environment_password(mocker, clone_dir):
45
    """In `unzip()`, the environment can be used to provide a repo password."""
46
    mock_prompt_and_delete = mocker.patch(
47
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
48
    )
49
50
    output_dir = zipfile.unzip(
51
        'tests/files/protected-fake-repo-tmpl.zip',
52
        is_url=False,
53
        clone_to_dir=str(clone_dir),
54
        password='sekrit',
55
    )
56
57
    assert output_dir.startswith(tempfile.gettempdir())
58
    assert not mock_prompt_and_delete.called
59
60
61
def test_unzip_protected_local_file_bad_environment_password(mocker, clone_dir):
62
    """In `unzip()`, an error occurs if the environment has a bad password."""
63
    mocker.patch(
64
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
65
    )
66
67
    with pytest.raises(InvalidZipRepository):
68
        zipfile.unzip(
69
            'tests/files/protected-fake-repo-tmpl.zip',
70
            is_url=False,
71
            clone_to_dir=str(clone_dir),
72
            password='not-the-right-password',
73
        )
74
75
76
def test_unzip_protected_local_file_user_password_with_noinput(mocker, clone_dir):
77
    """Can't unpack a password-protected repo in no_input mode."""
78
    mocker.patch(
79
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
80
    )
81
82
    with pytest.raises(InvalidZipRepository):
83
        zipfile.unzip(
84
            'tests/files/protected-fake-repo-tmpl.zip',
85
            is_url=False,
86
            clone_to_dir=str(clone_dir),
87
            no_input=True,
88
        )
89
90
91
def test_unzip_protected_local_file_user_password(mocker, clone_dir):
92
    """A password-protected local file reference can be unzipped."""
93
    mock_prompt_and_delete = mocker.patch(
94
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
95
    )
96
    mocker.patch('cookiecutter.zipfile.read_repo_password', return_value='sekrit')
97
98
    output_dir = zipfile.unzip(
99
        'tests/files/protected-fake-repo-tmpl.zip',
100
        is_url=False,
101
        clone_to_dir=str(clone_dir),
102
    )
103
104
    assert output_dir.startswith(tempfile.gettempdir())
105
    assert not mock_prompt_and_delete.called
106
107
108
def test_unzip_protected_local_file_user_bad_password(mocker, clone_dir):
109
    """Error in `unzip()`, if user can't provide a valid password."""
110
    mocker.patch(
111
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
112
    )
113
    mocker.patch(
114
        'cookiecutter.zipfile.read_repo_password', return_value='not-the-right-password'
115
    )
116
117
    with pytest.raises(InvalidZipRepository):
118
        zipfile.unzip(
119
            'tests/files/protected-fake-repo-tmpl.zip',
120
            is_url=False,
121
            clone_to_dir=str(clone_dir),
122
        )
123
124
125
def test_empty_zip_file(mocker, clone_dir):
126
    """In `unzip()`, an empty file raises an error."""
127
    mocker.patch(
128
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
129
    )
130
131
    with pytest.raises(InvalidZipRepository):
132
        zipfile.unzip(
133
            'tests/files/empty.zip', is_url=False, clone_to_dir=str(clone_dir)
134
        )
135
136
137
def test_non_repo_zip_file(mocker, clone_dir):
138
    """In `unzip()`, a repository must have a top level directory."""
139
    mocker.patch(
140
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
141
    )
142
143
    with pytest.raises(InvalidZipRepository):
144
        zipfile.unzip(
145
            'tests/files/not-a-repo.zip', is_url=False, clone_to_dir=str(clone_dir)
146
        )
147
148
149
def test_bad_zip_file(mocker, clone_dir):
150
    """In `unzip()`, a corrupted zip file raises an error."""
151
    mocker.patch(
152
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
153
    )
154
155
    with pytest.raises(InvalidZipRepository):
156
        zipfile.unzip(
157
            'tests/files/bad-zip-file.zip', is_url=False, clone_to_dir=str(clone_dir)
158
        )
159
160
161
def test_unzip_url(mocker, clone_dir):
162
    """In `unzip()`, a url will be downloaded and unzipped."""
163
    mock_prompt_and_delete = mocker.patch(
164
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
165
    )
166
167
    request = mocker.MagicMock()
168
    request.iter_content.return_value = mock_download()
169
170
    mocker.patch(
171
        'cookiecutter.zipfile.requests.get', return_value=request, autospec=True,
172
    )
173
174
    output_dir = zipfile.unzip(
175
        'https://example.com/path/to/fake-repo-tmpl.zip',
176
        is_url=True,
177
        clone_to_dir=str(clone_dir),
178
    )
179
180
    assert output_dir.startswith(tempfile.gettempdir())
181
    assert not mock_prompt_and_delete.called
182
183
184
def test_unzip_url_with_empty_chunks(mocker, clone_dir):
185
    """In `unzip()` empty chunk must be ignored."""
186
    mock_prompt_and_delete = mocker.patch(
187
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
188
    )
189
190
    request = mocker.MagicMock()
191
    request.iter_content.return_value = mock_download_with_empty_chunks()
192
193
    mocker.patch(
194
        'cookiecutter.zipfile.requests.get', return_value=request, autospec=True,
195
    )
196
197
    output_dir = zipfile.unzip(
198
        'https://example.com/path/to/fake-repo-tmpl.zip',
199
        is_url=True,
200
        clone_to_dir=str(clone_dir),
201
    )
202
203
    assert output_dir.startswith(tempfile.gettempdir())
204
    assert not mock_prompt_and_delete.called
205
206
207
def test_unzip_url_existing_cache(mocker, clone_dir):
208
    """Url should be downloaded and unzipped, old zip file will be removed."""
209
    mock_prompt_and_delete = mocker.patch(
210
        'cookiecutter.zipfile.prompt_and_delete', return_value=True, autospec=True
211
    )
212
213
    request = mocker.MagicMock()
214
    request.iter_content.return_value = mock_download()
215
216
    mocker.patch(
217
        'cookiecutter.zipfile.requests.get', return_value=request, autospec=True,
218
    )
219
220
    # Create an existing cache of the zipfile
221
    existing_zip = clone_dir.joinpath('fake-repo-tmpl.zip')
222
    existing_zip.write_text('This is an existing zipfile')
223
224
    output_dir = zipfile.unzip(
225
        'https://example.com/path/to/fake-repo-tmpl.zip',
226
        is_url=True,
227
        clone_to_dir=str(clone_dir),
228
    )
229
230
    assert output_dir.startswith(tempfile.gettempdir())
231
    assert mock_prompt_and_delete.call_count == 1
232
233
234
def test_unzip_url_existing_cache_no_input(mocker, clone_dir):
235
    """If no_input is provided, the existing file should be removed."""
236
    request = mocker.MagicMock()
237
    request.iter_content.return_value = mock_download()
238
239
    mocker.patch(
240
        'cookiecutter.zipfile.requests.get', return_value=request, autospec=True,
241
    )
242
243
    # Create an existing cache of the zipfile
244
    existing_zip = clone_dir.joinpath('fake-repo-tmpl.zip')
245
    existing_zip.write_text('This is an existing zipfile')
246
247
    output_dir = zipfile.unzip(
248
        'https://example.com/path/to/fake-repo-tmpl.zip',
249
        is_url=True,
250
        clone_to_dir=str(clone_dir),
251
        no_input=True,
252
    )
253
254
    assert output_dir.startswith(tempfile.gettempdir())
255
256
257
def test_unzip_should_abort_if_no_redownload(mocker, clone_dir):
258
    """Should exit without cloning anything If no redownload."""
259
    mocker.patch(
260
        'cookiecutter.zipfile.prompt_and_delete', side_effect=SystemExit, autospec=True
261
    )
262
263
    mock_requests_get = mocker.patch(
264
        'cookiecutter.zipfile.requests.get', autospec=True,
265
    )
266
267
    # Create an existing cache of the zipfile
268
    existing_zip = clone_dir.joinpath('fake-repo-tmpl.zip')
269
    existing_zip.write_text('This is an existing zipfile')
270
271
    zipfile_url = 'https://example.com/path/to/fake-repo-tmpl.zip'
272
273
    with pytest.raises(SystemExit):
274
        zipfile.unzip(zipfile_url, is_url=True, clone_to_dir=str(clone_dir))
275
276
    assert not mock_requests_get.called
277
278
279
def test_unzip_is_ok_to_reuse(mocker, clone_dir):
280
    """Already downloaded zip should not be downloaded again."""
281
    mock_prompt_and_delete = mocker.patch(
282
        'cookiecutter.zipfile.prompt_and_delete', return_value=False, autospec=True
283
    )
284
285
    request = mocker.MagicMock()
286
287
    existing_zip = clone_dir.joinpath('fake-repo-tmpl.zip')
288
    shutil.copy('tests/files/fake-repo-tmpl.zip', existing_zip)
289
290
    output_dir = zipfile.unzip(
291
        'https://example.com/path/to/fake-repo-tmpl.zip',
292
        is_url=True,
293
        clone_to_dir=str(clone_dir),
294
    )
295
296
    assert output_dir.startswith(tempfile.gettempdir())
297
    assert mock_prompt_and_delete.call_count == 1
298
    assert request.iter_content.call_count == 0
299