Coverage for tests/test_sleazy.py: 100%

210 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-12 17:08 +0200

1""" 

2Fixme: these tests were automatically generated and should still be manually confirmed! 

3""" 

4 

5import typing as t 

6 

7import pytest 

8 

9from src.sleazy import ( 

10 TypedDict, 

11 parse, 

12 parse_count_spec, 

13 stringify, 

14) 

15 

16 

17def test_parse_count_spec(): 

18 # Exact numbers 

19 assert parse_count_spec("0") == 0 

20 assert parse_count_spec("1") == 1 

21 assert parse_count_spec("") == 1 

22 assert parse_count_spec("5") == 5 

23 

24 # Direct argparse-style symbols 

25 assert parse_count_spec("+") == "+" 

26 assert parse_count_spec("*") == "*" 

27 assert parse_count_spec("?") == "?" 

28 

29 # Comparison operators are not supported anymore → default to "?" 

30 for spec in ("> 0", "<=5", ">= 1", "==3", "<1", ">=0", "invalid", "foo"): 

31 with pytest.raises(SyntaxError): 

32 parse_count_spec(spec) 

33 

34 

35def test_basic_type_parsing(): 

36 class BasicTypes(t.TypedDict): 

37 string_val: str 

38 int_val: int 

39 float_val: float 

40 bool_val: bool 

41 

42 args = [ 

43 "--string-val", 

44 "test", 

45 "--int-val", 

46 "42", 

47 "--float-val", 

48 "3.14", 

49 "--bool-val", 

50 ] 

51 result = parse(BasicTypes, args) 

52 

53 assert result["string_val"] == "test" 

54 assert result["int_val"] == 42 

55 assert result["float_val"] == 3.14 

56 assert result["bool_val"] is True 

57 

58 

59def test_positional_args(): 

60 class PositionalArgs(t.TypedDict): 

61 pos1: t.Annotated[str, "?"] 

62 pos2: t.Annotated[int, "?"] 

63 opt1: str 

64 

65 args = ["value1", "42", "--opt1", "option"] 

66 result = parse(PositionalArgs, args) 

67 

68 assert result["pos1"] == "value1" 

69 assert result["pos2"] == 42 

70 assert result["opt1"] == "option" 

71 

72 

73def test_literal_types(): 

74 class LiteralTypes(t.TypedDict): 

75 mode: t.Literal["auto", "manual", "hybrid"] 

76 level: t.Literal[1, 2, 3] 

77 

78 # Test valid literals 

79 args = ["--mode", "auto", "--level", "2"] 

80 result = parse(LiteralTypes, args) 

81 

82 assert result["mode"] == "auto" 

83 assert result["level"] == 2 

84 

85 # Test invalid literals - should raise error 

86 with pytest.raises(SystemExit): 

87 parse(LiteralTypes, ["--mode", "invalid", "--level", "2"]) 

88 

89 with pytest.raises(SystemExit): 

90 parse(LiteralTypes, ["--mode", "auto", "--level", "5"]) 

91 

92 

93def test_positional_literal(): 

94 class PosLiteral(t.TypedDict): 

95 mode: t.Annotated[t.Literal["auto", "manual"], "1"] 

96 

97 args = ["auto"] 

98 result = parse(PosLiteral, args) 

99 assert result["mode"] == "auto" 

100 

101 with pytest.raises(SystemExit): 

102 parse(PosLiteral, ["invalid"]) 

103 

104 

105def test_positional_count_zero_or_more(): 

106 class CountTest(t.TypedDict): 

107 files: t.Annotated[list[str], "*"] 

108 

109 # Test with multiple values 

110 args = ["file1.txt", "file2.txt", "file3.txt"] 

111 result = parse(CountTest, args) 

112 assert result["files"] == ["file1.txt", "file2.txt", "file3.txt"] 

113 

114 # Test with no values 

115 args = [] 

116 result = parse(CountTest, args) 

117 assert result["files"] == [] 

118 

119 

120def test_positional_count_one_or_more(): 

121 class CountTest(TypedDict): 

122 files: t.Annotated[list[str], "+"] 

123 

124 # Test with multiple values 

125 args = ["file1.txt", "file2.txt"] 

126 result = parse(CountTest, args) 

127 assert result["files"] == ["file1.txt", "file2.txt"] 

128 

129 # Test with no values - should fail 

130 with pytest.raises(SystemExit): 

131 parse(CountTest, []) 

132 

133 

134def test_positional_count_greater_than(): 

135 class CountTest(t.TypedDict): 

136 files: t.Annotated[list[str], "+"] 

137 

138 # Test with values 

139 args = ["file1.txt", "file2.txt"] 

140 result = parse(CountTest, args) 

141 assert result["files"] == ["file1.txt", "file2.txt"] 

142 

143 # Test with no values - should fail 

144 with pytest.raises(SystemExit): 

145 parse(CountTest, []) 

146 

147 

148def test_positional_count_at_most_one(): 

149 class CountTest(t.TypedDict): 

150 file: t.Annotated[str, "?"] 

151 

152 # Test with one value 

153 args = ["file1.txt"] 

154 result = parse(CountTest, args) 

155 assert result["file"] == "file1.txt" 

156 

157 # Test with no values 

158 args = [] 

159 result = parse(CountTest, args) 

160 assert result["file"] is None 

161 

162 # Test with multiple values - should fail 

163 with pytest.raises(SystemExit): 

164 parse(CountTest, ["file1.txt", "file2.txt"]) 

165 

166 

167def test_positional_count_less_than(): 

168 class CountTest(t.TypedDict): 

169 file: t.Annotated[str, "?"] 

170 

171 # Test with one value 

172 args = ["file1.txt"] 

173 result = parse(CountTest, args) 

174 assert result["file"] == "file1.txt" 

175 

176 # Test with no values 

177 args = [] 

178 result = parse(CountTest, args) 

179 assert result["file"] is None 

180 

181 # Test with multiple values - should fail 

182 with pytest.raises(SystemExit): 

183 parse(CountTest, ["file1.txt", "file2.txt"]) 

184 

185 

186def test_positional_count_exactly(): 

187 class CountTest(t.TypedDict): 

188 files: t.Annotated[list[str], "3"] 

189 

190 # Test with exact number of values 

191 args = ["file1.txt", "file2.txt", "file3.txt"] 

192 result = parse(CountTest, args) 

193 assert result["files"] == ["file1.txt", "file2.txt", "file3.txt"] 

194 

195 # Test with too few values - should fail 

196 with pytest.raises(SystemExit): 

197 parse(CountTest, ["file1.txt", "file2.txt"]) 

198 

199 # Test with too many values - should fail 

200 with pytest.raises(SystemExit): 

201 parse(CountTest, ["file1.txt", "file2.txt", "file3.txt", "file4.txt"]) 

202 

203 

204def test_positional_count_exactly_one(): 

205 class CountTest(t.TypedDict): 

206 command: t.Annotated[str, 1] 

207 

208 # Test with single value 

209 args = ["build"] 

210 result = parse(CountTest, args) 

211 assert result["command"] == "build" # Should be a string, not a list 

212 assert not isinstance(result["command"], list) 

213 

214 # Test with multiple values - should fail 

215 with pytest.raises(SystemExit): 

216 parse(CountTest, ["build", "extra"]) 

217 

218 

219def test_multiple_positional_args_with_fixed_counts(): 

220 class FixedCounts(t.TypedDict): 

221 command: t.Annotated[str, "1"] 

222 subcommand: t.Annotated[str, 1] 

223 target: t.Annotated[str, "1"] 

224 option: t.Annotated[str, "?"] 

225 

226 # Test with all arguments 

227 args = ["build", "web", "app.py", "debug"] 

228 result = parse(FixedCounts, args) 

229 assert result["command"] == "build" 

230 assert result["subcommand"] == "web" 

231 assert result["target"] == "app.py" 

232 assert result["option"] == "debug" 

233 

234 # Test with minimum required 

235 args = ["build", "web", "app.py"] 

236 result = parse(FixedCounts, args) 

237 assert result["command"] == "build" 

238 assert result["subcommand"] == "web" 

239 assert result["target"] == "app.py" 

240 assert result["option"] is None 

241 

242 

243def test_positional_with_count_constraints(): 

244 class PositionalWithConstraints(t.TypedDict): 

245 command: t.Annotated[str, "1"] 

246 files: t.Annotated[list[str], "2"] 

247 

248 # Test with exact file count 

249 args = ["compress", "input.txt", "output.gz"] 

250 result = parse(PositionalWithConstraints, args) 

251 assert result["command"] == "compress" 

252 assert result["files"] == ["input.txt", "output.gz"] 

253 

254 # Test with wrong file count - should fail 

255 with pytest.raises(SystemExit): 

256 print(parse(PositionalWithConstraints, ["compress", "input.txt"])) 

257 

258 

259def test_exact_numeric_count(): 

260 class CountTest(t.TypedDict): 

261 files: t.Annotated[list[str], "2"] 

262 

263 # Test with exact number 

264 args = ["file1.txt", "file2.txt"] 

265 result = parse(CountTest, args) 

266 assert result["files"] == ["file1.txt", "file2.txt"] 

267 

268 # Test with wrong number - should fail 

269 with pytest.raises(SystemExit): 

270 parse(CountTest, ["file1.txt"]) 

271 

272 

273def test_larger_exact_count(): 

274 class CountTest(t.TypedDict): 

275 files: t.Annotated[list[str], "5"] 

276 

277 # Test with exact number 

278 args = ["file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt"] 

279 result = parse(CountTest, args) 

280 assert result["files"] == [ 

281 "file1.txt", 

282 "file2.txt", 

283 "file3.txt", 

284 "file4.txt", 

285 "file5.txt", 

286 ] 

287 

288 

289def test_typeddict_to_cli_args_basic(): 

290 class TestDict(t.TypedDict): 

291 name: str 

292 count: int 

293 verbose: bool 

294 

295 # Create a dictionary that would be an instance of TestDict 

296 data: TestDict = {"name": "test", "count": 42, "verbose": True} 

297 

298 args = stringify(data, TestDict) 

299 # The order might vary, so we'll check for inclusion 

300 assert "--name" in args 

301 assert "test" in args 

302 assert "--count" in args 

303 assert "42" in args 

304 assert "--verbose" in args 

305 

306 

307def test_typeddict_to_cli_args_with_positionals(): 

308 class TestDict(t.TypedDict): 

309 pos1: t.Annotated[str, "1"] 

310 pos_multi: t.Annotated[list[str], "+"] 

311 flag: bool 

312 option: str 

313 

314 # Create a dictionary that would be an instance of TestDict 

315 data: TestDict = { 

316 "pos1": "value1", 

317 "pos_multi": ["a", "b", "c"], 

318 "flag": True, 

319 "option": "opt_val", 

320 } 

321 

322 args = stringify(data, TestDict) 

323 

324 # The positionals should come first in order 

325 assert args[0] == "value1" 

326 assert args[1:4] == ["a", "b", "c"] 

327 

328 # Check for inclusion of optional arguments 

329 assert "--flag" in args 

330 assert "--option" in args 

331 assert "opt_val" in args 

332 

333 

334def test_typeddict_to_cli_args_with_literal(): 

335 class TestDict(t.TypedDict): 

336 mode: t.Literal["fast", "slow"] 

337 level: t.Annotated[t.Literal[1, 2, 3], "1"] 

338 

339 data = {"mode": "fast", "level": 2} 

340 

341 args = stringify(data, TestDict) 

342 

343 assert args[0] == "2" # Positional comes first 

344 assert "--mode" in args 

345 assert "fast" in args 

346 

347 

348## hooman: 

349 

350 

351def test_list_repeat(): 

352 class MyConfigDict(t.TypedDict): 

353 repeat_me: list[str] 

354 

355 a = parse(MyConfigDict, ["--repeat-me", "once"]) 

356 b = parse(MyConfigDict, ["--repeat-me", "once", "--repeat-me", "twice"]) 

357 c = parse(MyConfigDict, []) 

358 

359 assert a["repeat_me"] == ["once"] 

360 assert b["repeat_me"] == ["once", "twice"] 

361 

362 assert stringify(a) == ["--repeat-me", "once"] 

363 assert stringify(a, MyConfigDict) == ["--repeat-me", "once"] 

364 assert stringify(b, MyConfigDict) == ["--repeat-me", "once", "--repeat-me", "twice"] 

365 assert stringify(c, MyConfigDict) == []