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
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-27 21:50 -0700
1#! /usr/bin/python3
2# -*- coding: utf-8 -*-
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
31import pyben
32import pytest
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
43def test_fix():
44 """
45 Test dir1 fixture is not None.
46 """
47 assert dir1 and metafile1 and file1 and metafile2 and dir2
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
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
74def test_magnet(metafile1):
75 """
76 Test create magnet function scheme.
77 """
78 magnet_link = magnet(metafile1)
79 assert magnet_link.startswith("magnet")
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
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")
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
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()
146 class NameSpace:
147 """
148 Stand in substitution for argparse.Namespace object.
149 """
151 metafile = str(file1) + ".torrent"
153 stdout = io.StringIO()
154 sys.stdout = stdout
156 output = info(NameSpace)
157 stdout.seek(0)
158 written = stdout.read()
159 assert output in written
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()
196 class Space:
197 """
198 Stand in substitution for argparse.Namespace object.
199 """
201 metafile = str(file1) + ".torrent"
203 output = info(Space)
204 assert field in output
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
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)
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)
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)
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)
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)
310 class Namespace:
311 """Command line args for rebuild command."""
313 metafiles = [os.path.dirname(metafile2)]
314 contents = [basedir]
315 destination = dest
317 yield Namespace
318 rmpath(dest)
321def test_rebuild(build):
322 """Test the rebuild function in the commands module."""
323 counter = rebuild(build)
324 assert counter > 0
327def test_recheck_with_dir():
328 """Test running the recheck command with a directory as the metafile."""
329 path = os.path.dirname(__file__)
331 class Namespace:
332 """Emulates the namespace class from argparse module."""
334 metafile = path
335 content = path
337 try:
338 recheck(Namespace)
339 except ArgumentError:
340 assert True
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
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
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)
387def test_find_config_file(configfile):
388 """Test find config file function in commands module."""
389 assert find_config_file(configfile) is not None
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
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