Coverage for tests\test_commands.py: 100%

191 statements  

« prev     ^ index     » next       coverage.py v7.3.0, created at 2023-08-27 21:50 -0700

1#! /usr/bin/python3 

2# -*- coding: utf-8 -*- 

3 

4############################################################################## 

5# Copyright (C) 2021-current alexpdev 

6# 

7# Licensed under the Apache License, Version 2.0 (the "License"); 

8# you may not use this file except in compliance with the License. 

9# You may obtain a copy of the License at 

10# 

11# http://www.apache.org/licenses/LICENSE-2.0 

12# 

13# Unless required by applicable law or agreed to in writing, software 

14# distributed under the License is distributed on an "AS IS" BASIS, 

15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

16# See the License for the specific language governing permissions and 

17# limitations under the License. 

18############################################################################## 

19""" 

20Testing functions for the sub-action commands from command line args. 

21""" 

22import io 

23import os 

24import sys 

25import shutil 

26from argparse import Namespace 

27from hashlib import sha1, sha256 # nosec 

28from pathlib import Path 

29from urllib.parse import quote_plus 

30 

31import pyben 

32import pytest 

33 

34from tests import ( 

35 dir1, dir2, file1, metafile1, metafile2, rmpath, tempfile, torrents) 

36from torrentfile.cli import execute 

37from torrentfile.commands import ( 

38 find_config_file, info, magnet, parse_config_file, rebuild, recheck) 

39from torrentfile.hasher import merkle_root 

40from torrentfile.utils import ArgumentError 

41 

42 

43def test_fix(): 

44 """ 

45 Test dir1 fixture is not None. 

46 """ 

47 assert dir1 and metafile1 and file1 and metafile2 and dir2 

48 

49 

50def test_magnet_uri(metafile1): 

51 """ 

52 Test create magnet function digest. 

53 """ 

54 magnet_link = magnet(metafile1) 

55 meta = pyben.load(metafile1) 

56 announce = meta["announce"] 

57 assert quote_plus(announce) in magnet_link 

58 

59 

60def test_magnet_hex(metafile1): 

61 """ 

62 Test create magnet function digest. 

63 """ 

64 magnet_link = magnet(metafile1) 

65 meta = pyben.load(metafile1) 

66 info = meta["info"] 

67 hashing_func = sha1 # nosec 

68 if "meta version" in info and "pieces" not in info: 

69 hashing_func = sha256 

70 binfo = hashing_func(pyben.dumps(info)).hexdigest() # nosec 

71 assert binfo in magnet_link 

72 

73 

74def test_magnet(metafile1): 

75 """ 

76 Test create magnet function scheme. 

77 """ 

78 magnet_link = magnet(metafile1) 

79 assert magnet_link.startswith("magnet") 

80 

81 

82def test_magnet_no_announce_list(metafile2): 

83 """ 

84 Test create magnet function scheme. 

85 """ 

86 meta = pyben.load(metafile2) 

87 del meta["announce-list"] 

88 pyben.dump(meta, metafile2) 

89 if "meta version" in meta["info"]: 

90 prefix = "btmh" 

91 else: 

92 prefix = "btih" 

93 magnet_link = magnet(metafile2) 

94 assert prefix in magnet_link 

95 

96 

97def test_magnet_no_announce(metafile2): 

98 """ 

99 Test create magnet function scheme. 

100 """ 

101 meta = pyben.load(metafile2) 

102 del meta["announce-list"] 

103 del meta["announce"] 

104 pyben.dump(meta, metafile2) 

105 magnet_link = magnet(metafile2) 

106 assert magnet_link.startswith("magnet") 

107 

108 

109def test_magnet_empty(): 

110 """ 

111 Test create magnet function scheme. 

112 """ 

113 try: 

114 magnet("file_that_does_not_exist") 

115 except FileNotFoundError: 

116 assert True 

117 

118 

119def test_info_stdout(file1): 

120 """ 

121 Test the info function properly sends output to stdout. 

122 """ 

123 outfile = str(file1) + ".torrent" 

124 args = [ 

125 "torrentfile", 

126 "create", 

127 "--announce", 

128 "url1", 

129 "--web-seed", 

130 "url5", 

131 "url3", 

132 "--http-seed", 

133 "url4", 

134 "--private", 

135 "-o", 

136 outfile, 

137 "--comment", 

138 "CommentExample", 

139 "--source", 

140 "SourceExample", 

141 str(file1), 

142 ] 

143 sys.argv = args 

144 execute() 

145 

146 class NameSpace: 

147 """ 

148 Stand in substitution for argparse.Namespace object. 

149 """ 

150 

151 metafile = str(file1) + ".torrent" 

152 

153 stdout = io.StringIO() 

154 sys.stdout = stdout 

155 

156 output = info(NameSpace) 

157 stdout.seek(0) 

158 written = stdout.read() 

159 assert output in written 

160 

161 

162@pytest.mark.parametrize( 

163 "field", 

164 ["name", "announce", "source", "comment", "private", "announce-list"], 

165) 

166def test_info(field, file1): 

167 """ 

168 Test the info_command action from the Command Line Interface. 

169 """ 

170 outfile = str(file1) + ".torrent" 

171 args = [ 

172 "torrentfile", 

173 "create", 

174 "-a", 

175 "url1", 

176 "url2", 

177 "url3", 

178 "--web-seed", 

179 "url4", 

180 "url5", 

181 "--http-seed", 

182 "url6", 

183 "url7", 

184 "--private", 

185 "-o", 

186 outfile, 

187 "--comment", 

188 "ExampleComment", 

189 "--source", 

190 "examplesource", 

191 str(file1), 

192 ] 

193 sys.argv = args 

194 execute() 

195 

196 class Space: 

197 """ 

198 Stand in substitution for argparse.Namespace object. 

199 """ 

200 

201 metafile = str(file1) + ".torrent" 

202 

203 output = info(Space) 

204 assert field in output 

205 

206 

207def test_magnet_cli(metafile1): 

208 """ 

209 Test magnet creation through CLI interface. 

210 """ 

211 sys.argv[1:] = ["m", str(metafile1)] 

212 uri = execute() 

213 assert "magnet" in uri 

214 

215 

216def test_create_unicode_name(file1): 

217 """ 

218 Test Unicode information in CLI args. 

219 """ 

220 parent = os.path.dirname(file1) 

221 filename = os.path.join(parent, "丂七万丈三与丏丑丒专且丕世丗両丢丣两严丩个丫丬中丮丯.torrent") 

222 args = [ 

223 "torrentfile", 

224 "-v", 

225 "create", 

226 "-a", 

227 "tracker_url.com/announce_3456", 

228 "tracker_url.net/announce_3456", 

229 "--source", 

230 "sourcetext", 

231 "--comment", 

232 "filename is 丂七万丈三与丏丑丒专且丕世丗両丢丣两严丩个丫丬中丮丯.torrent", 

233 "-o", 

234 str(filename), 

235 str(file1), 

236 ] 

237 sys.argv = args 

238 execute() 

239 assert os.path.exists(filename) 

240 

241 

242@pytest.mark.parametrize("blocks", [[], [sha1(b"1010").digest()]]) # nosec 

243def test_merkle_root_no_blocks(blocks): 

244 """ 

245 Test running merkle root function with 1 and 0 len lists. 

246 """ 

247 if blocks: 

248 assert merkle_root(blocks) 

249 else: 

250 assert not merkle_root(blocks) 

251 

252 

253@pytest.mark.parametrize("torrent", torrents()) 

254def test_mixins_progbar(torrent): 

255 """ 

256 Test progbar mixins with small file. 

257 """ 

258 tfile = tempfile(exp=14) 

259 outfile = str(tfile) + ".torrent" 

260 msg = "1234abcd" * 80 

261 with open(tfile, "wb") as temp: 

262 temp.write(msg.encode("utf-8")) 

263 args = { 

264 "path": str(tfile), 

265 "--prog": "1", 

266 } 

267 metafile = torrent(**args) 

268 output, _ = metafile.write(outfile=outfile) 

269 assert output == outfile 

270 rmpath(tfile, outfile) 

271 

272 

273@pytest.mark.parametrize("torrent", torrents()) 

274def test_mixins_progbar_deep_nesting(torrent): 

275 """ 

276 Test progbar mixins with small file. 

277 """ 

278 tfile = tempfile(exp=14) 

279 dirname = os.path.dirname(tfile) 

280 nested = "some_super_long_name_to_test_path_length_limits" 

281 nesting = os.path.join(dirname, nested) 

282 if not os.path.exists(nesting): 

283 os.mkdir(nesting) 

284 tmpfile = os.path.join(nesting, os.path.basename(tfile)) 

285 shutil.move(tfile, tmpfile) 

286 outfile = str(tmpfile) + ".torrent" 

287 msg = "1234abcd" * 80 

288 with open(tmpfile, "wb") as temp: 

289 temp.write(msg.encode("utf-8")) 

290 args = { 

291 "path": str(tmpfile), 

292 "--prog": "1", 

293 } 

294 metafile = torrent(**args) 

295 output, _ = metafile.write(outfile=outfile) 

296 assert output == outfile 

297 rmpath(tmpfile, outfile) 

298 

299 

300@pytest.fixture 

301def build(dir2, metafile2): 

302 """Create fixture for testing rebuild command.""" 

303 basedir = os.path.dirname(dir2) 

304 parent = os.path.dirname(basedir) 

305 dest = os.path.join(parent, "dest") 

306 if os.path.exists(dest): 

307 rmpath(dest) # pragma: nocover 

308 os.mkdir(dest) 

309 

310 class Namespace: 

311 """Command line args for rebuild command.""" 

312 

313 metafiles = [os.path.dirname(metafile2)] 

314 contents = [basedir] 

315 destination = dest 

316 

317 yield Namespace 

318 rmpath(dest) 

319 

320 

321def test_rebuild(build): 

322 """Test the rebuild function in the commands module.""" 

323 counter = rebuild(build) 

324 assert counter > 0 

325 

326 

327def test_recheck_with_dir(): 

328 """Test running the recheck command with a directory as the metafile.""" 

329 path = os.path.dirname(__file__) 

330 

331 class Namespace: 

332 """Emulates the namespace class from argparse module.""" 

333 

334 metafile = path 

335 content = path 

336 

337 try: 

338 recheck(Namespace) 

339 except ArgumentError: 

340 assert True 

341 

342 

343@pytest.fixture(params=[("http-seed", False), ("web-seed", True)]) 

344def config(request): 

345 """Test config file contents.""" 

346 field, private = request.param 

347 contents = f""" 

348[config] 

349announce = 

350 url3 

351 url4 

352{field} = 

353 url4 

354 url5 

355private = {str(private).lower()} 

356piece-length = 16 

357comment = some comment 

358source = tracker 

359""" 

360 return contents 

361 

362 

363@pytest.fixture(params=[True, False]) 

364def namespace(request, tmp_path): 

365 """Test fixture for pytest.""" 

366 if request.param: 

367 ns = Namespace(config=True, config_path=None) 

368 else: 

369 ns = Namespace(config=True, config_path=tmp_path / "torrentfile.ini") 

370 return ns 

371 

372 

373@pytest.fixture 

374def configfile(namespace, config): 

375 """Test fixture for configfile parsing.""" 

376 if namespace.config_path: 

377 path = namespace.config_path 

378 else: 

379 base = Path.home() / ".torrentfile" 

380 None if os.path.exists(base) else os.mkdir(base) 

381 path = base / "torrentfile.ini" 

382 path.write_text(config, encoding="utf8") 

383 yield namespace 

384 os.remove(path) 

385 

386 

387def test_find_config_file(configfile): 

388 """Test find config file function in commands module.""" 

389 assert find_config_file(configfile) is not None 

390 

391 

392def test_parse_config_file(configfile): 

393 """Test parse config file function in commands module.""" 

394 kwargs = {"out": "./somepath"} 

395 config = find_config_file(configfile) 

396 parse_config_file(config, kwargs) 

397 assert "announce" in kwargs 

398 

399 

400@pytest.mark.parametrize("path", [None, Path.home() / "torrentfile.ini"]) 

401def test_find_config_file_missing(path): 

402 """Test find config file function with missing config file.""" 

403 ns = Namespace(config=True, config_path=path) 

404 filename = "torrentfile.ini" 

405 home = Path.home() 

406 paths = [ 

407 os.path.join(os.getcwd(), filename), 

408 home / ".torrentfile" / filename, 

409 home / ".config" / ".torrentfile" / filename, 

410 ] 

411 existing = [i for i in paths if os.path.exists(i)] 

412 list(map(os.remove, existing)) 

413 try: 

414 find_config_file(ns) 

415 except FileNotFoundError: 

416 assert True