83 changed files with 3484 additions and 4053 deletions
Binary file not shown.
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,71 +0,0 @@ |
|||
#!/usr/bin/python -OO |
|||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org> |
|||
# |
|||
# This program is free software; you can redistribute it and/or |
|||
# modify it under the terms of the GNU General Public License |
|||
# as published by the Free Software Foundation; either version 2 |
|||
# of the License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|||
|
|||
""" |
|||
tests.conftest - Wrappers to start SABnzbd for testing |
|||
""" |
|||
|
|||
import os |
|||
import itertools |
|||
import urllib.request, urllib.error, urllib.parse |
|||
import pytest |
|||
import shutil |
|||
import time |
|||
import testhelper |
|||
|
|||
from xprocess import ProcessStarter |
|||
|
|||
@pytest.fixture(scope='session') |
|||
def sabnzbd_connect(request, xprocess): |
|||
# Get cache directory |
|||
base_path = os.path.dirname(os.path.abspath(__file__)) |
|||
cache_dir = os.path.join(base_path, 'cache') |
|||
|
|||
# Copy basic config file |
|||
try: |
|||
os.mkdir(cache_dir) |
|||
shutil.copyfile(os.path.join(base_path, 'sabnzbd.basic.ini'), os.path.join(cache_dir, 'sabnzbd.ini')) |
|||
except: |
|||
pass |
|||
|
|||
class Starter(ProcessStarter): |
|||
# Wait for SABnzbd to start |
|||
pattern = "ENGINE Bus STARTED" |
|||
|
|||
# Start without browser and with basic logging |
|||
args = 'python ../../SABnzbd.py -l1 -s %s:%s -b0 -f %s' % (testhelper.SAB_HOST, testhelper.SAB_PORT, cache_dir) |
|||
args = args.split() |
|||
|
|||
# We have to wait a bit longer than default |
|||
def filter_lines(self, lines): |
|||
return itertools.islice(lines, 500) |
|||
|
|||
# Shut it down at the end |
|||
def shutdown_sabnzbd(): |
|||
# Gracefull shutdown request |
|||
testhelper.get_url_result('shutdown') |
|||
# Takes a second to shutdown |
|||
for x in range(5): |
|||
try: |
|||
shutil.rmtree(cache_dir) |
|||
break |
|||
except: |
|||
time.sleep(1) |
|||
request.addfinalizer(shutdown_sabnzbd) |
|||
|
|||
return xprocess.ensure("sabnzbd", Starter) |
|||
|
@ -1,8 +1,8 @@ |
|||
# SAB-Specific |
|||
cheetah |
|||
cheetah3 |
|||
cryptography |
|||
sabyenc |
|||
|
|||
# Testing |
|||
pytest-xprocess |
|||
selenium |
|||
requests |
@ -1,11 +1,4 @@ |
|||
__version__ = 19 |
|||
__encoding__ = utf-8 |
|||
[misc] |
|||
api_key = apikey |
|||
|
|||
[servers] |
|||
[[sabnzbd.test]] |
|||
enable = 1 |
|||
host = sabnzd.test |
|||
username = sabnzbd |
|||
password = sabnzbd |
|||
api_key = apikey |
@ -1,61 +0,0 @@ |
|||
#!/usr/bin/python -OO |
|||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org> |
|||
# |
|||
# This program is free software; you can redistribute it and/or |
|||
# modify it under the terms of the GNU General Public License |
|||
# as published by the Free Software Foundation; either version 2 |
|||
# of the License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|||
|
|||
""" |
|||
tests.test_api_pages - The most basic testing if things work |
|||
""" |
|||
|
|||
import pytest |
|||
import testhelper |
|||
|
|||
|
|||
def test_basic_api(sabnzbd_connect): |
|||
# Basic API test |
|||
assert 'queue' in testhelper.get_api_result('queue') |
|||
assert 'history' in testhelper.get_api_result('history') |
|||
assert 'status' in testhelper.get_api_result('fullstatus') |
|||
assert 'config' in testhelper.get_api_result('get_config') |
|||
|
|||
|
|||
def test_main_pages(sabnzbd_connect): |
|||
# See if the basic pages work |
|||
assert 'Traceback' not in testhelper.get_url_result() |
|||
assert 'Traceback' not in testhelper.get_url_result('history') |
|||
assert 'Traceback' not in testhelper.get_url_result('queue') |
|||
assert 'Traceback' not in testhelper.get_url_result('status') |
|||
|
|||
|
|||
def test_wizard_pages(sabnzbd_connect): |
|||
# Test if wizard pages work |
|||
assert 'Traceback' not in testhelper.get_url_result('wizard') |
|||
assert 'Traceback' not in testhelper.get_url_result('wizard/one') |
|||
assert 'Traceback' not in testhelper.get_url_result('wizard/two') |
|||
|
|||
|
|||
def test_config_pages(sabnzbd_connect): |
|||
# Test if config pages work |
|||
assert 'Traceback' not in testhelper.get_url_result('config') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/general') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/server') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/categories') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/switches') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/sorting') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/notify') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/scheduling') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/rss') |
|||
assert 'Traceback' not in testhelper.get_url_result('config/special') |
|||
|
@ -0,0 +1,295 @@ |
|||
#!/usr/bin/python -OO |
|||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org> |
|||
# |
|||
# This program is free software; you can redistribute it and/or |
|||
# modify it under the terms of the GNU General Public License |
|||
# as published by the Free Software Foundation; either version 2 |
|||
# of the License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|||
|
|||
""" |
|||
tests.test_functional - The most basic testing if things work |
|||
""" |
|||
|
|||
import unittest |
|||
import random |
|||
|
|||
from selenium import webdriver |
|||
from selenium.common.exceptions import WebDriverException, NoSuchElementException |
|||
from selenium.webdriver.chrome.options import Options as ChromeOptions |
|||
from selenium.webdriver.firefox.options import Options as FirefoxOptions |
|||
from selenium.webdriver.common.keys import Keys |
|||
from selenium.webdriver.support.ui import WebDriverWait |
|||
|
|||
from testhelper import * |
|||
|
|||
|
|||
class SABnzbdBaseTest(unittest.TestCase): |
|||
|
|||
@classmethod |
|||
def setUpClass(cls): |
|||
# We try Chrome, fallback to Firefox |
|||
|
|||
try: |
|||
driver_options = ChromeOptions() |
|||
# Headless on Appveyor/Travis |
|||
if "CI" in os.environ: |
|||
driver_options.add_argument("--headless") |
|||
driver_options.add_argument("--no-sandbox") |
|||
cls.driver = webdriver.Chrome(chrome_options=driver_options) |
|||
except WebDriverException: |
|||
driver_options = FirefoxOptions() |
|||
# Headless on Appveyor/Travis |
|||
if "CI" in os.environ: |
|||
driver_options.headless = True |
|||
cls.driver = webdriver.Firefox(firefox_options=driver_options) |
|||
|
|||
# Get the newsserver-info, if available |
|||
if "SAB_NEWSSERVER_HOST" in os.environ: |
|||
cls.newsserver_host = os.environ['SAB_NEWSSERVER_HOST'] |
|||
cls.newsserver_user = os.environ['SAB_NEWSSERVER_USER'] |
|||
cls.newsserver_password = os.environ['SAB_NEWSSERVER_PASSWORD'] |
|||
|
|||
@classmethod |
|||
def tearDownClass(cls): |
|||
cls.driver.close() |
|||
cls.driver.quit() |
|||
|
|||
def no_page_crash(self): |
|||
# Do a base test if CherryPy did not report test |
|||
self.assertNotIn('500 Internal Server Error', self.driver.title) |
|||
|
|||
def open_page(self, url): |
|||
# Open a page and test for crash |
|||
self.driver.get(url) |
|||
self.no_page_crash() |
|||
|
|||
def scroll_to_top(self): |
|||
self.driver.find_element_by_tag_name('body').send_keys(Keys.CONTROL + Keys.HOME) |
|||
time.sleep(2) |
|||
|
|||
def wait_for_ajax(self): |
|||
wait = WebDriverWait(self.driver, 15) |
|||
wait.until(lambda driver_wait: self.driver.execute_script('return jQuery.active') == 0) |
|||
wait.until(lambda driver_wait: self.driver.execute_script('return document.readyState') == 'complete') |
|||
|
|||
|
|||
@unittest.skipIf("SAB_NEWSSERVER_HOST" not in os.environ, "Test-server not specified") |
|||
class SABnzbdDownloadFlow(SABnzbdBaseTest): |
|||
|
|||
def test_full(self): |
|||
# Wrapper for all the tests in order |
|||
self.start_wizard() |
|||
|
|||
# Basic test |
|||
self.add_nzb_from_url("http://sabnzbd.org/tests/basic_rar5.nzb", "testfile.bin") |
|||
|
|||
# Unicode test |
|||
self.add_nzb_from_url("http://sabnzbd.org/tests/unicode_rar.nzb", u"\u4f60\u597d\u4e16\u754c.bin") |
|||
|
|||
# Unicode test with a missing article |
|||
#self.add_nzb_from_url("http://sabnzbd.org/tests/unicode_rar_broken.nzb", u"\u4f60\u597d\u4e16\u754c.bin") |
|||
|
|||
def start_wizard(self): |
|||
# Language-selection |
|||
self.open_page("http://%s:%s/sabnzbd/wizard/" % (SAB_HOST, SAB_PORT)) |
|||
self.driver.find_element_by_id("en").click() |
|||
self.driver.find_element_by_css_selector('.btn.btn-default').click() |
|||
|
|||
# Fill server-info |
|||
self.no_page_crash() |
|||
host_inp = self.driver.find_element_by_name("host") |
|||
host_inp.clear() |
|||
host_inp.send_keys(self.newsserver_host) |
|||
username_imp = self.driver.find_element_by_name("username") |
|||
username_imp.clear() |
|||
username_imp.send_keys(self.newsserver_user) |
|||
pass_inp = self.driver.find_element_by_name("password") |
|||
pass_inp.clear() |
|||
pass_inp.send_keys(self.newsserver_password) |
|||
|
|||
# With SSL |
|||
ssl_imp = self.driver.find_element_by_name("ssl") |
|||
if not ssl_imp.get_attribute('checked'): |
|||
ssl_imp.click() |
|||
|
|||
# Test server-check |
|||
self.driver.find_element_by_id("serverTest").click() |
|||
self.wait_for_ajax() |
|||
self.assertIn("Connection Successful", self.driver.find_element_by_id("serverResponse").text) |
|||
|
|||
# Final page done |
|||
self.driver.find_element_by_id("next-button").click() |
|||
self.no_page_crash() |
|||
self.assertIn("http://%s:%s/sabnzbd" % (SAB_HOST, SAB_PORT), self.driver.find_element_by_class_name("quoteBlock").text) |
|||
|
|||
# Go to SAB! |
|||
self.driver.find_element_by_css_selector('.btn.btn-success').click() |
|||
self.no_page_crash() |
|||
|
|||
def add_nzb_from_url(self, file_url, file_output): |
|||
test_job_name = 'testfile_%s' % random.randint(500, 1000) |
|||
|
|||
self.open_page("http://%s:%s/sabnzbd/" % (SAB_HOST, SAB_PORT)) |
|||
|
|||
# Wait for modal to open, add URL |
|||
self.driver.find_element_by_css_selector('a[href="#modal-add-nzb"]').click() |
|||
time.sleep(1) |
|||
self.driver.find_element_by_name("nzbURL").send_keys(file_url) |
|||
self.driver.find_element_by_name("nzbname").send_keys(test_job_name) |
|||
self.driver.find_element_by_css_selector('form[data-bind="submit: addNZBFromURL"] input[type="submit"]').click() |
|||
|
|||
# We wait for 30 seconds to let it complete |
|||
for _ in range(120): |
|||
try: |
|||
# Locate resulting row |
|||
result_row = self.driver.find_element_by_xpath('//*[@id="history-tab"]//tr[td//text()[contains(., "%s")]]' % test_job_name) |
|||
# Did it complete? |
|||
if result_row.find_element_by_css_selector('td.status').text == 'Completed': |
|||
break |
|||
else: |
|||
time.sleep(1) |
|||
except NoSuchElementException: |
|||
time.sleep(1) |
|||
else: |
|||
self.fail("Download did not complete") |
|||
|
|||
# Check if the file exists on disk |
|||
file_to_find = os.path.join(SAB_COMPLETE_DIR, test_job_name, file_output) |
|||
self.assertTrue(os.path.exists(file_to_find), "File not found") |
|||
|
|||
# Shutil can't handle unicode, need to remove the file here |
|||
os.remove(file_to_find) |
|||
|
|||
|
|||
class SABnzbdBasicPagesTest(SABnzbdBaseTest): |
|||
|
|||
def test_base_pages(self): |
|||
# Quick-check of all Config pages |
|||
test_urls = ['config', |
|||
'config/general', |
|||
'config/folders', |
|||
'config/server', |
|||
'config/categories', |
|||
'config/switches', |
|||
'config/sorting', |
|||
'config/notify', |
|||
'config/scheduling', |
|||
'config/rss', |
|||
'config/special'] |
|||
|
|||
for test_url in test_urls: |
|||
self.open_page("http://%s:%s/%s" % (SAB_HOST, SAB_PORT, test_url)) |
|||
|
|||
|
|||
@unittest.skipIf("SAB_NEWSSERVER_HOST" not in os.environ, "Test-server not specified") |
|||
class SABnzbdConfigServers(SABnzbdBaseTest): |
|||
|
|||
server_name = "_SeleniumServer" |
|||
|
|||
def open_config_servers(self): |
|||
# Test if base page works |
|||
self.open_page("http://%s:%s/sabnzbd/config/server" % (SAB_HOST, SAB_PORT)) |
|||
self.scroll_to_top() |
|||
|
|||
# Show advanced options |
|||
advanced_btn = self.driver.find_element_by_name("advanced-settings-button") |
|||
if not advanced_btn.get_attribute('checked'): |
|||
advanced_btn.click() |
|||
|
|||
def add_test_server(self): |
|||
# Add server |
|||
self.driver.find_element_by_id("addServerButton").click() |
|||
host_inp = self.driver.find_element_by_name("host") |
|||
host_inp.clear() |
|||
host_inp.send_keys(self.newsserver_host) |
|||
username_imp = self.driver.find_element_by_css_selector("#addServerContent input[data-hide='username']") |
|||
username_imp.clear() |
|||
username_imp.send_keys(self.newsserver_user) |
|||
pass_inp = self.driver.find_element_by_css_selector("#addServerContent input[data-hide='password']") |
|||
pass_inp.clear() |
|||
pass_inp.send_keys(self.newsserver_password) |
|||
|
|||
# With SSL |
|||
ssl_imp = self.driver.find_element_by_name("ssl") |
|||
if not ssl_imp.get_attribute('checked'): |
|||
ssl_imp.click() |
|||
|
|||
# Check that we filled the right port automatically |
|||
self.assertEqual(self.driver.find_element_by_id("port").get_attribute('value'), '563') |
|||
|
|||
# Test server-check |
|||
self.driver.find_element_by_css_selector("#addServerContent .testServer").click() |
|||
self.wait_for_ajax() |
|||
self.assertIn("Connection Successful", self.driver.find_element_by_css_selector('#addServerContent .result-box').text) |
|||
|
|||
# Set test-servername |
|||
self.driver.find_element_by_id("displayname").send_keys(self.server_name) |
|||
|
|||
# Add and show details |
|||
pass_inp.send_keys(Keys.RETURN) |
|||
time.sleep(1) |
|||
if not self.driver.find_element_by_id("host0").is_displayed(): |
|||
self.driver.find_element_by_class_name("showserver").click() |
|||
|
|||
def remove_server(self): |
|||
# Remove the first server and accept the confirmation |
|||
self.driver.find_element_by_class_name("delServer").click() |
|||
self.driver.switch_to.alert.accept() |
|||
|
|||
# Check that it's gone |
|||
time.sleep(2) |
|||
self.assertNotIn(self.server_name, self.driver.page_source) |
|||
|
|||
def test_add_and_remove_server(self): |
|||
self.open_config_servers() |
|||
self.add_test_server() |
|||
self.remove_server() |
|||
|
|||
def test_empty_bad_password(self): |
|||
self.open_config_servers() |
|||
self.add_test_server() |
|||
|
|||
# Test server-check with empty password |
|||
pass_inp = self.driver.find_elements_by_css_selector("input[data-hide='password']")[1] |
|||
pass_inp.clear() |
|||
self.driver.find_elements_by_css_selector(".testServer")[1].click() |
|||
self.wait_for_ajax() |
|||
check_result = self.driver.find_elements_by_css_selector('.result-box')[1].text.lower() |
|||
self.assertTrue("authentication failed" in check_result or "invalid username or password" in check_result) |
|||
|
|||
# Test server-check with bad password |
|||
pass_inp.send_keys("bad") |
|||
self.driver.find_elements_by_css_selector(".testServer")[1].click() |
|||
self.wait_for_ajax() |
|||
self.assertTrue("authentication failed" in check_result or "invalid username or password" in check_result) |
|||
|
|||
# Finish |
|||
self.remove_server() |
|||
|
|||
|
|||
class SABnzbdConfigCategories(SABnzbdBaseTest): |
|||
|
|||
category_name = "testCat" |
|||
|
|||
def test_page(self): |
|||
# Test if base page works |
|||
self.open_page("http://%s:%s/sabnzbd/config/categories" % (SAB_HOST, SAB_PORT)) |
|||
|
|||
# Add new category |
|||
self.driver.find_elements_by_name("newname")[1].send_keys("testCat") |
|||
self.driver.find_element_by_xpath("//button/text()[normalize-space(.)='Add']/parent::*").click() |
|||
self.no_page_crash() |
|||
self.assertNotIn(self.category_name, self.driver.page_source) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
unittest.main(failfast=True) |
@ -1,58 +0,0 @@ |
|||
#!/usr/bin/python -OO |
|||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org> |
|||
# |
|||
# This program is free software; you can redistribute it and/or |
|||
# modify it under the terms of the GNU General Public License |
|||
# as published by the Free Software Foundation; either version 2 |
|||
# of the License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|||
|
|||
""" |
|||
tests.test_nzb - Basic NZB adding support |
|||
""" |
|||
|
|||
import os |
|||
import pytest |
|||
import testhelper |
|||
|
|||
|
|||
# Where are we now? |
|||
base_path = os.path.dirname(os.path.abspath(__file__)) |
|||
|
|||
|
|||
def nzo_in_queue(nzo_response): |
|||
""" Helper function for checking if file is in queue and then remove it """ |
|||
queue_res = testhelper.get_api_result('queue') |
|||
nzo_id = nzo_response['nzo_ids'][0] |
|||
|
|||
# Was it added? |
|||
assert nzo_response['status'] == True |
|||
assert queue_res['queue']['slots'][0]['nzo_id'] == nzo_response['nzo_ids'][0] |
|||
|
|||
# Let's remove it |
|||
remove_response = testhelper.get_api_result('queue', {'name': 'delete', 'value': nzo_id}) |
|||
assert nzo_response['status'] == True |
|||
|
|||
# Really gone? |
|||
queue_res = testhelper.get_api_result('queue') |
|||
assert not queue_res['queue']['slots'] |
|||
|
|||
|
|||
def test_addfile(sabnzbd_connect): |
|||
# See if basic upload works |
|||
nzo_response = testhelper.upload_nzb(os.path.join(base_path, 'data', 'reftestnzb.nzb')) |
|||
nzo_in_queue(nzo_response) |
|||
|
|||
|
|||
def test_addlocalfile(sabnzbd_connect): |
|||
# See if basic adding from disk-file works |
|||
nzo_response = testhelper.get_api_result('addlocalfile', {'name': os.path.join(base_path, 'data', 'reftestnzb.nzb')}) |
|||
nzo_in_queue(nzo_response) |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue