1# -*- coding: utf-8 -*- 2# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs 3# Copyright (C) 2012-2014 Bastian Kleineidam 4# Copyright (C) 2015-2020 Tobias Gruetzmacher 5 6from __future__ import absolute_import, division, print_function 7 8import json 9import os 10import re 11 12import pytest 13import responses 14 15import dosagelib.cmd 16import httpmocks 17 18 19def cmd(*options): 20 """'Fake' run dosage with given options.""" 21 return dosagelib.cmd.main(('--allow-multiple',) + options) 22 23 24def cmd_ok(*options): 25 assert cmd(*options) == 0 26 27 28def cmd_err(*options): 29 assert cmd(*options) == 1 30 31 32@pytest.mark.usefixtures("nosleep") 33class TestDosage(object): 34 """Test the dosage commandline client.""" 35 36 # This shouldn't hit the network at all, so add responses without mocks to 37 # make sure it doesn't do that 38 @responses.activate 39 def test_list_comics(self): 40 for option in ("-l", "--list", "--singlelist"): 41 cmd_ok(option) 42 43 @responses.activate 44 def test_display_version(self): 45 cmd_ok("--version") 46 47 @responses.activate 48 def test_update_available(self, capsys): 49 responses.add(responses.GET, re.compile(r'https://api\.github\.com/'), 50 json={'tag_name': '9999.0', 'assets': [ 51 {'browser_download_url': 'TEST.whl'}, 52 {'browser_download_url': 'TEST.exe'}, 53 ]}) 54 cmd_ok('--version', '-v') 55 captured = capsys.readouterr() 56 best = 'TEST.exe' if os.name == 'nt' else 'TEST.whl' 57 assert best in captured.out 58 assert 'A new version' in captured.out 59 60 @responses.activate 61 def test_no_update_available(self, capsys): 62 responses.add(responses.GET, re.compile(r'https://api\.github\.com/'), 63 json={'tag_name': '1.0'}) 64 cmd_ok('--version', '-v') 65 captured = capsys.readouterr() 66 assert 'Detected local or development' in captured.out 67 68 @responses.activate 69 def test_current(self, capsys): 70 responses.add(responses.GET, re.compile(r'https://api\.github\.com/'), 71 json={'tag_name': dosagelib.__version__}) 72 cmd_ok('--version', '-v') 73 captured = capsys.readouterr() 74 assert captured.out.endswith('issues\n') 75 76 @responses.activate 77 def test_update_broken(self, capsys): 78 responses.add(responses.GET, re.compile(r'https://api\.github\.com/'), 79 json={}) 80 cmd_ok('--version', '-v') 81 captured = capsys.readouterr() 82 assert 'invalid update file' in captured.out 83 84 def test_display_help(self): 85 for option in ("-h", "--help"): 86 with pytest.raises(SystemExit): 87 cmd(option) 88 89 def test_module_help(self): 90 cmd_ok("-m", "xkcd") 91 92 def test_no_comics_specified(self): 93 cmd_err() 94 95 def test_unknown_option(self): 96 with pytest.raises(SystemExit): 97 cmd('--imadoofus') 98 99 def test_multiple_comics_match(self): 100 cmd_err('Garfield') 101 102 @responses.activate 103 def test_fetch_html_and_rss_json(self, tmpdir): 104 httpmocks.xkcd() 105 cmd_ok("-n", "2", "-v", "-b", str(tmpdir), "-o", "html", "-o", "rss", 106 "-o", "json", "--no-downscale", "xkcd") 107 108 @responses.activate 109 def test_fetch_html_and_rss_2(self, tmp_path): 110 httpmocks.page('http://www.bloomingfaeries.com/', 'bf-home') 111 httpmocks.page(re.compile('http://www.*faeries-405/'), 'bf-405') 112 httpmocks.png(re.compile(r'http://www\.blooming.*405.*jpg')) 113 httpmocks.png(re.compile(r'http://www\.blooming.*406.*jpg'), 'tall') 114 115 cmd_ok("--numstrips", "2", "--baseurl", "bla", "--basepath", 116 str(tmp_path), "--output", "rss", "--output", "html", "--adult", 117 "BloomingFaeries") 118 119 html = next((tmp_path / 'html').glob('*.html')).read_text() 120 assert "width=" in html 121 122 @responses.activate 123 def test_fetch_html_broken_img(self, tmp_path): 124 httpmocks.page('http://www.bloomingfaeries.com/', 'bf-home') 125 httpmocks.page(re.compile('http://www.*faeries-405/'), 'bf-405') 126 responses.add(responses.GET, re.compile(r'.*\.jpg'), body=b'\377\330', 127 content_type='image/jpeg') 128 129 cmd_ok("--numstrips", "2", "--baseurl", "bla", "--basepath", 130 str(tmp_path), "--output", "html", "--adult", "BloomingFaeries") 131 132 html = next((tmp_path / 'html').glob('*.html')).read_text() 133 assert "width=" not in html 134 135 @responses.activate 136 def test_fetch_indexed(self, tmpdir): 137 httpmocks.xkcd() 138 cmd_ok("-n", "2", "-v", "-b", str(tmpdir), "xkcd:303") 139 140 @responses.activate 141 def test_json_page_key_bounce_and_multi_image(self, tmpdir): 142 httpmocks.page('https://zenpencils.com/', 'zp-home') 143 httpmocks.page('https://zenpencils.com/comic/missing/', 'zp-223') 144 httpmocks.page('https://zenpencils.com/comic/lifejacket/', 'zp-222') 145 httpmocks.jpeg(re.compile(r'https://cdn-.*\.jpg')) 146 147 cmd_ok("-v", "-b", str(tmpdir), "-o", "json", "ZenPencils") 148 149 directory = tmpdir.join('ZenPencils') 150 f = directory.join('dosage.json').open(encoding='utf-8') 151 data = json.load(f) 152 f.close() 153 154 pages = data['pages'] 155 assert len(pages) == 1 156 157 page = list(pages.keys())[0] 158 assert page == 'https://zenpencils.com/comic/missing/' 159 160 images = data['pages'][page]['images'] 161 assert len(images) == 2 162 163 for imgurl, imgfile in images.items(): 164 assert directory.join(imgfile).check(file=1) 165