19
19
import asyncio
20
20
from contextlib import asynccontextmanager
21
21
from pathlib import Path
22
+ from secrets import token_hex
22
23
from types import SimpleNamespace
23
- from uuid import uuid1
24
24
25
25
import pytest
26
26
27
- from cylc .flow .exceptions import (
28
- WorkflowFilesError ,
29
- )
30
- from cylc .flow .install import (
31
- reinstall_workflow ,
32
- )
27
+ from cylc .flow .exceptions import WorkflowFilesError
28
+ from cylc .flow .install import reinstall_workflow
33
29
from cylc .flow .option_parsers import Options
34
30
from cylc .flow .scripts .reinstall import (
35
31
get_option_parser as reinstall_gop ,
36
32
reinstall_cli ,
37
33
)
38
- from cylc .flow .workflow_files import (
39
- WorkflowFiles ,
40
- )
34
+ from cylc .flow .workflow_files import WorkflowFiles
41
35
42
36
from .utils .entry_points import EntryPointWrapper
43
37
38
+
44
39
ReInstallOptions = Options (reinstall_gop ())
45
40
46
41
# cli opts
@@ -66,6 +61,18 @@ def non_interactive(monkeypatch):
66
61
)
67
62
68
63
64
+ @pytest .fixture
65
+ def answer_prompt (monkeypatch : pytest .MonkeyPatch ):
66
+ """Answer reinstall prompt."""
67
+
68
+ def inner (answer : str ):
69
+ monkeypatch .setattr (
70
+ 'cylc.flow.scripts.reinstall._input' , lambda x : answer
71
+ )
72
+
73
+ return inner
74
+
75
+
69
76
@pytest .fixture
70
77
def one_src (tmp_path ):
71
78
src_dir = tmp_path / 'src'
@@ -77,7 +84,7 @@ def one_src(tmp_path):
77
84
78
85
@pytest .fixture
79
86
def one_run (one_src , test_dir , run_dir ):
80
- w_run_dir = test_dir / str ( uuid1 () )
87
+ w_run_dir = test_dir / token_hex ( 4 )
81
88
w_run_dir .mkdir ()
82
89
(w_run_dir / 'flow.cylc' ).touch ()
83
90
(w_run_dir / 'rose-suite.conf' ).touch ()
@@ -148,7 +155,7 @@ async def test_interactive(
148
155
capsys ,
149
156
capcall ,
150
157
interactive ,
151
- monkeypatch
158
+ answer_prompt
152
159
):
153
160
"""It should perform a dry-run and prompt in interactive mode."""
154
161
# capture reinstall calls
@@ -159,23 +166,18 @@ async def test_interactive(
159
166
# give it something to reinstall
160
167
(one_src .path / 'a' ).touch ()
161
168
162
- # reinstall answering "no" to any prompt
163
- monkeypatch . setattr (
164
- 'cylc.flow.scripts.reinstall._input' ,
165
- lambda x : 'n'
169
+ answer_prompt ( 'n' )
170
+ assert (
171
+ await reinstall_cli ( opts = ReInstallOptions (), workflow_id = one_run . id )
172
+ is False
166
173
)
167
- assert await reinstall_cli (opts = ReInstallOptions (), workflow_id = one_run .id ) is False
168
174
169
175
# only one rsync call should have been made (i.e. the --dry-run)
170
176
assert [call [1 ].get ('dry_run' ) for call in reinstall_calls ] == [True ]
171
177
assert 'Reinstall canceled, no changes made.' in capsys .readouterr ().out
172
178
reinstall_calls .clear ()
173
179
174
- # reinstall answering "yes" to any prompt
175
- monkeypatch .setattr (
176
- 'cylc.flow.scripts.reinstall._input' ,
177
- lambda x : 'y'
178
- )
180
+ answer_prompt ('y' )
179
181
assert await reinstall_cli (opts = ReInstallOptions (), workflow_id = one_run .id )
180
182
181
183
# two rsync calls should have been made (i.e. the --dry-run and the real)
@@ -234,7 +236,9 @@ async def test_rsync_stuff(one_src, one_run, capsys, non_interactive):
234
236
assert not (one_run .path / 'c' ).exists ()
235
237
236
238
237
- async def test_rose_warning (one_src , one_run , capsys , interactive , monkeypatch ):
239
+ async def test_rose_warning (
240
+ one_src , one_run , capsys , interactive , answer_prompt
241
+ ):
238
242
"""It should warn that Rose installed files will be deleted.
239
243
240
244
See https://github.com/cylc/cylc-rose/issues/149
@@ -244,11 +248,7 @@ async def test_rose_warning(one_src, one_run, capsys, interactive, monkeypatch):
244
248
'Files created by Rose file installation will show as deleted'
245
249
)
246
250
247
- # reinstall answering "no" to any prompt
248
- monkeypatch .setattr (
249
- 'cylc.flow.scripts.reinstall._input' ,
250
- lambda x : 'n'
251
- )
251
+ answer_prompt ('n' )
252
252
(one_src .path / 'a' ).touch () # give it something to install
253
253
254
254
# reinstall (with rose-suite.conf file)
@@ -303,6 +303,35 @@ async def test_rsync_fail(one_src, one_run, mock_glbl_cfg, non_interactive):
303
303
assert 'An error occurred reinstalling' in str (exc_ctx .value )
304
304
305
305
306
+ async def test_permissions_change (
307
+ one_src ,
308
+ one_run ,
309
+ interactive ,
310
+ answer_prompt ,
311
+ monkeypatch : pytest .MonkeyPatch ,
312
+ capsys : pytest .CaptureFixture ,
313
+ ):
314
+ """It detects permissions changes."""
315
+ # Add script file:
316
+ script_path : Path = one_src .path / 'myscript'
317
+ script_path .touch ()
318
+ await reinstall_cli (
319
+ opts = ReInstallOptions (skip_interactive = True ), workflow_id = one_run .id
320
+ )
321
+ assert (one_run .path / 'myscript' ).is_file ()
322
+ capsys .readouterr () # clears capsys
323
+
324
+ # Change permissions (e.g. user forgot to make it executable before)
325
+ script_path .chmod (0o777 )
326
+ # Answer "no" to reinstall prompt (we just want to test dry run)
327
+ answer_prompt ('n' )
328
+ await reinstall_cli (
329
+ opts = ReInstallOptions (), workflow_id = one_run .id
330
+ )
331
+ out , _ = capsys .readouterr ()
332
+ assert "send myscript" in out
333
+
334
+
306
335
@pytest .fixture
307
336
def my_install_plugin (monkeypatch ):
308
337
"""This configures a single post_install plugin.
0 commit comments