Coverage for tests/test_cli.py: 100.00%

79 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-17 17:18 +0200

1"""Tests for the `cli` module.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6 

7from duty import cli, debug 

8 

9 

10def test_no_duty(capsys: pytest.CaptureFixture) -> None: 

11 """Run no duties. 

12 

13 Parameters: 

14 capsys: Pytest fixture to capture output. 

15 """ 

16 assert cli.main([]) == 1 

17 captured = capsys.readouterr() 

18 assert "Available duties" in captured.out 

19 

20 

21def test_show_help(capsys: pytest.CaptureFixture) -> None: 

22 """Show help. 

23 

24 Parameters: 

25 capsys: Pytest fixture to capture output. 

26 """ 

27 assert cli.main(["-h"]) == 0 

28 captured = capsys.readouterr() 

29 assert "duty" in captured.out 

30 

31 

32def test_show_help_for_given_duties(capsys: pytest.CaptureFixture) -> None: 

33 """Show help for given duties. 

34 

35 Parameters: 

36 capsys: Pytest fixture to capture output. 

37 """ 

38 assert cli.main(["-d", "tests/fixtures/basic.py", "-h", "hello"]) == 0 

39 captured = capsys.readouterr() 

40 assert "hello" in captured.out 

41 

42 

43def test_show_help_unknown_duty(capsys: pytest.CaptureFixture) -> None: 

44 """Show help for an unknown duty. 

45 

46 Parameters: 

47 capsys: Pytest fixture to capture output. 

48 """ 

49 assert cli.main(["-d", "tests/fixtures/basic.py", "-h", "not-here"]) == 0 

50 captured = capsys.readouterr() 

51 assert "Unknown duty" in captured.out 

52 

53 

54def test_select_duties() -> None: 

55 """Run a duty.""" 

56 assert cli.main(["-d", "tests/fixtures/basic.py", "hello"]) == 0 

57 

58 

59def test_unknown_duty() -> None: 

60 """Don't run an unknown duty.""" 

61 assert cli.main(["-d", "tests/fixtures/basic.py", "byebye"]) == 1 

62 

63 

64def test_incorrect_arguments() -> None: 

65 """Use incorrect arguments.""" 

66 assert cli.main(["-d", "tests/fixtures/basic.py", "hello=1"]) == 1 

67 

68 

69# we use 300 because it's slightly above the valid maximum 255 

70@pytest.mark.parametrize("code", range(-100, 300, 7)) 

71def test_duty_failure(code: int) -> None: 

72 """Check exit code. 

73 

74 Parameters: 

75 code: Code to match. 

76 """ 

77 assert cli.main(["-d", "tests/fixtures/code.py", "exit_with", f"code={code}"]) == code 

78 

79 

80def test_multiple_duties(capfd: pytest.CaptureFixture) -> None: 

81 """Run multiple duties. 

82 

83 Parameters: 

84 capfd: Pytest fixture to capture output. 

85 """ 

86 assert cli.main(["-d", "tests/fixtures/multiple.py", "first_duty", "second_duty"]) == 0 

87 captured = capfd.readouterr() 

88 assert "first" in captured.out 

89 assert "second" in captured.out 

90 

91 

92def test_duty_arguments(capfd: pytest.CaptureFixture) -> None: 

93 """Run duty with arguments. 

94 

95 Parameters: 

96 capfd: Pytest fixture to capture output. 

97 """ 

98 assert cli.main(["-d", "tests/fixtures/arguments.py", "say_hello", "cat=fabric"]) == 0 

99 captured = capfd.readouterr() 

100 assert "cat fabric" in captured.out 

101 assert "dog dog" in captured.out 

102 

103 assert cli.main(["-d", "tests/fixtures/arguments.py", "say_hello", "dog=paramiko", "cat=invoke"]) == 0 

104 captured = capfd.readouterr() 

105 assert "cat invoke" in captured.out 

106 assert "dog paramiko" in captured.out 

107 

108 

109def test_list_duties(capsys: pytest.CaptureFixture) -> None: 

110 """List duties. 

111 

112 Parameters: 

113 capsys: Pytest fixture to capture output. 

114 """ 

115 assert cli.main(["-d", "tests/fixtures/list.py", "-l"]) == 0 

116 captured = capsys.readouterr() 

117 assert "Tong..." in captured.out 

118 assert "DEUM!" in captured.out 

119 

120 

121def test_global_options() -> None: 

122 """Test global options.""" 

123 assert cli.main(["-d", "tests/fixtures/code.py", "-z", "exit_with", "1"]) == 0 

124 

125 

126def test_global_and_local_options() -> None: 

127 """Test global and local options.""" 

128 assert cli.main(["-d", "tests/fixtures/code.py", "-z", "exit_with", "-Z", "1"]) == 1 

129 

130 

131def test_options_precedence() -> None: 

132 """Test options precedence.""" 

133 # @duty(nofail=True) is overridden by ctx.run(nofail=False) 

134 assert cli.main(["-d", "tests/fixtures/precedence.py", "precedence"]) == 1 

135 

136 # ctx.run(nofail=False) is overridden by local option -z 

137 assert cli.main(["-d", "tests/fixtures/precedence.py", "precedence", "-z"]) == 0 

138 

139 # ctx.run(nofail=False) is overridden by global option -z 

140 assert cli.main(["-d", "tests/fixtures/precedence.py", "-z", "precedence"]) == 0 

141 

142 # global option -z is overridden by local option -z 

143 assert cli.main(["-d", "tests/fixtures/precedence.py", "-z", "precedence", "-Z"]) == 1 

144 

145 

146# test options precedence (CLI option, env var, ctx.run, @duty 

147# test positional arguments 

148# test extra keyword arguments 

149# test complete (global options + local options + multi duties + positional args + keyword args + extra keyword args) 

150 

151 

152@pytest.mark.parametrize( 

153 ("param", "expected"), 

154 [ 

155 ("", 1), 

156 ("n", 1), 

157 ("N", 1), 

158 ("no", 1), 

159 ("NO", 1), 

160 ("false", 1), 

161 ("FALSE", 1), 

162 ("off", 1), 

163 ("OFF", 1), 

164 ("zero=", 1), 

165 ("zero=0", 1), 

166 ("zero=n", 1), 

167 ("zero=N", 1), 

168 ("zero=no", 1), 

169 ("zero=NO", 1), 

170 ("zero=false", 1), 

171 ("zero=FALSE", 1), 

172 ("zero=off", 1), 

173 ("zero=OFF", 1), 

174 ("y", 0), 

175 ("Y", 0), 

176 ("yes", 0), 

177 ("YES", 0), 

178 ("on", 0), 

179 ("ON", 0), 

180 ("true", 0), 

181 ("TRUE", 0), 

182 ("anything else", 0), 

183 ("-1", 0), 

184 ("1", 0), 

185 ("zero=y", 0), 

186 ("zero=Y", 0), 

187 ("zero=yes", 0), 

188 ("zero=YES", 0), 

189 ("zero=on", 0), 

190 ("zero=ON", 0), 

191 ("zero=true", 0), 

192 ("zero=TRUE", 0), 

193 ("zero=anything else", 0), 

194 ("zero=-1", 0), 

195 ("zero=1", 0), 

196 ], 

197) 

198def test_cast_bool_parameter(param: str, expected: int) -> None: 

199 """Test parameters casting as boolean. 

200 

201 Parameters: 

202 param: Pytest parametrization fixture. 

203 expected: Pytest parametrization fixture. 

204 """ 

205 assert cli.main(["-d", "tests/fixtures/booleans.py", "boolean", param]) == expected 

206 

207 

208def test_invalid_params(capsys: pytest.CaptureFixture) -> None: 

209 """Check that invalid parameters are early and correctly detected. 

210 

211 Parameters: 

212 capsys: Pytest fixture to capture output. 

213 """ 

214 assert cli.main(["-d", "tests/fixtures/booleans.py", "boolean", "zore=off"]) == 1 

215 captured = capsys.readouterr() 

216 assert "unexpected keyword argument 'zore'" in captured.err 

217 

218 assert cli.main(["-d", "tests/fixtures/code.py", "exit_with"]) == 1 

219 captured = capsys.readouterr() 

220 assert "missing 1 required positional argument: 'code'" in captured.err 

221 

222 

223def test_show_version(capsys: pytest.CaptureFixture) -> None: 

224 """Show version. 

225 

226 Parameters: 

227 capsys: Pytest fixture to capture output. 

228 """ 

229 with pytest.raises(SystemExit): 

230 cli.main(["-V"]) 

231 captured = capsys.readouterr() 

232 assert debug.get_version() in captured.out 

233 

234 

235def test_show_debug_info(capsys: pytest.CaptureFixture) -> None: 

236 """Show debug information. 

237 

238 Parameters: 

239 capsys: Pytest fixture to capture output. 

240 """ 

241 with pytest.raises(SystemExit): 

242 cli.main(["--debug-info"]) 

243 captured = capsys.readouterr().out.lower() 

244 assert "python" in captured 

245 assert "system" in captured 

246 assert "environment" in captured 

247 assert "packages" in captured