# -*- coding: utf-8 -*- ########################################################################## # OpenLP - Open Source Lyrics Projection # # ---------------------------------------------------------------------- # # Copyright (c) 2008-2020 OpenLP Developers # # ---------------------------------------------------------------------- # # 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 3 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, see . # ########################################################################## """ Functional tests to test the AppLocation class and related methods. """ import os import pytest import tempfile from pathlib import Path from unittest.mock import MagicMock, patch from openlp.core.common.httputils import ProxyMode, download_file, get_proxy_settings, get_url_file_size, \ get_random_user_agent, get_web_page @pytest.yield_fixture def temp_file(settings): tmp_file = os.path.join(tempfile.gettempdir(), 'testfile') yield tmp_file if os.path.isfile(tmp_file): os.remove(tmp_file) def test_get_random_user_agent_linux(): """ Test that getting a user agent on Linux returns a user agent suitable for Linux """ with patch('openlp.core.common.httputils.sys') as mocked_sys: # GIVEN: The system is Linux mocked_sys.platform = 'linux2' # WHEN: We call get_random_user_agent() user_agent = get_random_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent result = 'Linux' in user_agent or 'CrOS' in user_agent assert result is True, 'The user agent should be a valid Linux user agent' def test_get_random_user_agent_windows(): """ Test that getting a user agent on Windows returns a user agent suitable for Windows """ with patch('openlp.core.common.httputils.sys') as mocked_sys: # GIVEN: The system is Windows mocked_sys.platform = 'win32' # WHEN: We call get_random_user_agent() user_agent = get_random_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent assert 'Windows' in user_agent, 'The user agent should be a valid Windows user agent' def test_get_random_user_agent_macos(): """ Test that getting a user agent on OS X returns a user agent suitable for OS X """ with patch('openlp.core.common.httputils.sys') as mocked_sys: # GIVEN: The system is macOS mocked_sys.platform = 'darwin' # WHEN: We call get_random_user_agent() user_agent = get_random_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent assert 'Mac OS X' in user_agent, 'The user agent should be a valid OS X user agent' def test_get_random_user_agent_default(): """ Test that getting a user agent on a non-Linux/Windows/OS X platform returns the default user agent """ with patch('openlp.core.common.httputils.sys') as mocked_sys: # GIVEN: The system is something else mocked_sys.platform = 'freebsd' # WHEN: We call get_random_user_agent() user_agent = get_random_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent assert 'NetBSD' in user_agent, 'The user agent should be the default user agent' def test_get_web_page_no_url(): """ Test that sending a URL of None to the get_web_page method returns None """ # GIVEN: A None url test_url = None # WHEN: We try to get the test URL result = get_web_page(test_url) # THEN: None should be returned assert result is None, 'The return value of get_web_page should be None' @patch('openlp.core.common.httputils.requests') @patch('openlp.core.common.httputils.get_random_user_agent') @patch('openlp.core.common.httputils.Registry') def test_get_web_page(MockRegistry, mocked_get_random_user_agent, mocked_requests): """ Test that the get_web_page method works correctly """ # GIVEN: Mocked out objects and a fake URL mocked_requests.get.return_value = MagicMock(text='text') mocked_get_random_user_agent.return_value = 'user_agent' fake_url = 'this://is.a.fake/url' # WHEN: The get_web_page() method is called returned_page = get_web_page(fake_url) # THEN: The correct methods are called with the correct arguments and a web page is returned mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'}, proxies=None, timeout=30.0) mocked_get_random_user_agent.assert_called_once_with() assert MockRegistry.call_count == 1, 'The Registry() object should have been called once' assert returned_page == 'text', 'The returned page should be the mock object' @patch('openlp.core.common.httputils.requests') @patch('openlp.core.common.httputils.get_random_user_agent') def test_get_web_page_with_header(mocked_get_random_user_agent, mocked_requests, settings): """ Test that adding a header to the call to get_web_page() adds the header to the request """ # GIVEN: Mocked out objects, a fake URL and a fake header mocked_requests.get.return_value = MagicMock(text='text') mocked_get_random_user_agent.return_value = 'user_agent' fake_url = 'this://is.a.fake/url' fake_headers = {'Fake-Header': 'fake value'} # WHEN: The get_web_page() method is called returned_page = get_web_page(fake_url, headers=fake_headers) # THEN: The correct methods are called with the correct arguments and a web page is returned expected_headers = dict(fake_headers) expected_headers.update({'User-Agent': 'user_agent'}) mocked_requests.get.assert_called_once_with(fake_url, headers=expected_headers, proxies=None, timeout=30.0) mocked_get_random_user_agent.assert_called_with() assert returned_page == 'text', 'The returned page should be the mock object' @patch('openlp.core.common.httputils.requests') @patch('openlp.core.common.httputils.get_random_user_agent') def test_get_web_page_with_user_agent_in_headers(mocked_get_random_user_agent, mocked_requests, settings): """ Test that adding a user agent in the header when calling get_web_page() adds that user agent to the request """ # GIVEN: Mocked out objects, a fake URL and a fake header mocked_requests.get.return_value = MagicMock(text='text') fake_url = 'this://is.a.fake/url' user_agent_headers = {'User-Agent': 'OpenLP/2.2.0'} # WHEN: The get_web_page() method is called returned_page = get_web_page(fake_url, headers=user_agent_headers) # THEN: The correct methods are called with the correct arguments and a web page is returned mocked_requests.get.assert_called_once_with(fake_url, headers=user_agent_headers, proxies=None, timeout=30.0) assert mocked_get_random_user_agent.call_count == 0, 'get_random_user_agent() should not have been called' assert returned_page == 'text', 'The returned page should be "test"' @patch('openlp.core.common.httputils.requests') @patch('openlp.core.common.httputils.get_random_user_agent') @patch('openlp.core.common.httputils.Registry') def test_get_web_page_update_openlp(MockRegistry, mocked_get_random_user_agent, mocked_requests): """ Test that passing "update_openlp" as true to get_web_page calls Registry().get('app').process_events() """ # GIVEN: Mocked out objects, a fake URL mocked_requests.get.return_value = MagicMock(text='text') mocked_get_random_user_agent.return_value = 'user_agent' mocked_registry_object = MagicMock() mocked_application_object = MagicMock() mocked_registry_object.get.return_value = mocked_application_object MockRegistry.return_value = mocked_registry_object fake_url = 'this://is.a.fake/url' # WHEN: The get_web_page() method is called returned_page = get_web_page(fake_url, update_openlp=True) # THEN: The correct methods are called with the correct arguments and a web page is returned mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'}, proxies=None, timeout=30.0) mocked_get_random_user_agent.assert_called_once_with() mocked_registry_object.get.assert_called_with('application') mocked_application_object.process_events.assert_called_with() assert returned_page == 'text', 'The returned page should be the mock object' @patch('openlp.core.common.httputils.requests') def test_get_url_file_size(mocked_requests, settings): """ Test that calling "get_url_file_size" works correctly """ # GIVEN: Mocked out objects, a fake URL mocked_requests.head.return_value = MagicMock(headers={'Content-Length': 100}) fake_url = 'this://is.a.fake/url' # WHEN: The get_url_file_size() method is called file_size = get_url_file_size(fake_url) # THEN: The correct methods are called with the correct arguments and a web page is returned mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0) assert file_size == 100 @patch('openlp.core.common.httputils.requests') def test_socket_timeout(mocked_requests, temp_file): """ Test socket timeout gets caught """ # GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download mocked_requests.get.side_effect = OSError # WHEN: Attempt to retrieve a file download_file(MagicMock(), url='http://localhost/test', file_path=Path(temp_file)) # THEN: socket.timeout should have been caught # NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files assert os.path.exists(temp_file) is False, 'temp_file should have been deleted' def test_no_proxy_mode(settings): """ Test that a dictionary with http and https values are set to None is returned, when `NO_PROXY` mode is specified """ # GIVEN: A `proxy mode` setting of NO_PROXY settings.setValue('advanced/proxy mode', ProxyMode.NO_PROXY) # WHEN: Calling `get_proxy_settings` result = get_proxy_settings() # THEN: The returned value should be a dictionary with http and https values set to None assert result == {'http': None, 'https': None} def test_system_proxy_mode(settings): """ Test that None is returned, when `SYSTEM_PROXY` mode is specified """ # GIVEN: A `proxy mode` setting of SYSTEM_PROXY settings.setValue('advanced/proxy mode', ProxyMode.SYSTEM_PROXY) # WHEN: Calling `get_proxy_settings` result = get_proxy_settings() # THEN: The returned value should be None assert result is None def test_manual_proxy_mode_no_auth(settings): """ Test that the correct proxy addresses are returned when basic authentication is not used """ # GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers, but no auth credentials are supplied settings.setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY) settings.setValue('advanced/proxy http', 'testhttp.server:port') settings.setValue('advanced/proxy https', 'testhttps.server:port') settings.setValue('advanced/proxy username', '') settings.setValue('advanced/proxy password', '') # WHEN: Calling `get_proxy_settings` result = get_proxy_settings() # THEN: The returned value should be the proxy servers without authentication assert result == {'http': 'http://testhttp.server:port', 'https': 'https://testhttps.server:port'} def test_manual_proxy_mode_auth(settings): """ Test that the correct proxy addresses are returned when basic authentication is used """ # GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers and auth credentials supplied settings.setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY) settings.setValue('advanced/proxy http', 'testhttp.server:port') settings.setValue('advanced/proxy https', 'testhttps.server:port') settings.setValue('advanced/proxy username', 'user') settings.setValue('advanced/proxy password', 'pass') # WHEN: Calling `get_proxy_settings` result = get_proxy_settings() # THEN: The returned value should be the proxy servers with the authentication credentials assert result == {'http': 'http://user:pass@testhttp.server:port', 'https': 'https://user:pass@testhttps.server:port'} def test_manual_proxy_mode_no_servers(settings): """ Test that the system proxies are overidden when the MANUAL_PROXY mode is specified, but no server addresses are supplied """ # GIVEN: A `proxy mode` setting of MANUAL_PROXY with no servers specified settings.setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY) settings.setValue('advanced/proxy http', '') settings.setValue('advanced/proxy https', '') settings.setValue('advanced/proxy username', 'user') settings.setValue('advanced/proxy password', 'pass') # WHEN: Calling `get_proxy_settings` result = get_proxy_settings() # THEN: The returned value should be the proxy servers set to None assert result == {'http': None, 'https': None} def test_mode_arg_specified(mock_settings): """ Test that the argument is used rather than reading the 'advanced/proxy mode' setting """ # GIVEN: Mocked settings # WHEN: Calling `get_proxy_settings` with the mode arg specified get_proxy_settings(mode=ProxyMode.NO_PROXY) # THEN: The mode arg should have been used rather than looking it up in the settings mock_settings.value.assert_not_called() def test_mode_incorrect_arg_specified(mock_settings): """ Test that the system settings are used when the mode arg specieied is invalid """ # GIVEN: Mocked settings # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified result = get_proxy_settings(mode='qwerty') # THEN: An None should be returned mock_settings.value.assert_not_called() assert result is None