From 525b102bd053a6653dff663845c98683fa78bbab Mon Sep 17 00:00:00 2001 From: Ivan Devat Date: Mon, 24 Jun 2024 17:19:58 +0200 Subject: [PATCH] fix tests for missing webui --- pcs_test/Makefile.am | 1 + pcs_test/smoke.sh.in | 23 ++++++-- pcs_test/tier0/daemon/app/fixtures_app.py | 53 +++---------------- .../tier0/daemon/app/fixtures_app_webui.py | 28 ++++++++++ pcs_test/tier0/daemon/app/test_app_gui.py | 37 ++++++++++--- pcs_test/tier0/daemon/app/test_app_spa.py | 26 +++++++-- pcs_test/tier0/daemon/test_env.py | 6 ++- pcs_test/tier0/daemon/test_session.py | 25 ++++++--- pcs_test/tools/misc.py | 9 ++++ 9 files changed, 138 insertions(+), 70 deletions(-) create mode 100644 pcs_test/tier0/daemon/app/fixtures_app_webui.py diff --git a/pcs_test/Makefile.am b/pcs_test/Makefile.am index 4e71fcda..6c5932b3 100644 --- a/pcs_test/Makefile.am +++ b/pcs_test/Makefile.am @@ -134,6 +134,7 @@ EXTRA_DIST = \ tier0/common/test_tools.py \ tier0/common/test_tools_xml_fromstring.py \ tier0/daemon/app/fixtures_app.py \ + tier0/daemon/app/fixtures_app_webui.py \ tier0/daemon/app/__init__.py \ tier0/daemon/app/test_api_v0.py \ tier0/daemon/app/test_api_v1.py \ diff --git a/pcs_test/smoke.sh.in b/pcs_test/smoke.sh.in index a4b3ac71..37eac345 100755 --- a/pcs_test/smoke.sh.in +++ b/pcs_test/smoke.sh.in @@ -66,10 +66,25 @@ cat < ${pcsd_settings_conf_path} EOF cat ${pcsd_settings_conf_path} pcs cluster start --all --wait -curl --insecure --data "username=${cluster_user}&password=${cluster_user_password}" --cookie-jar ${cookie_file} https://localhost:2224/ui/login -curl --insecure --cookie ${cookie_file} --header "X-Requested-With: XMLHttpRequest" --data "hidden[hidden_input]=&config[stonith-enabled]=false" https://localhost:2224/managec/${cluster_name}/update_cluster_settings > "${output_file}" -cat "${output_file}"; echo "" -[ "$(cat ${output_file})" = "Update Successful" ] + +webui_http_code_response=$( + curl --insecure --silent --output /dev/null --write-out "%{http_code}" \ + https://localhost:2224/ui/ +) +if [ "$webui_http_code_response" = "200" ]; then + # Webui backend check + curl --insecure --data "username=${cluster_user}&password=${cluster_user_password}" --cookie-jar ${cookie_file} https://localhost:2224/ui/login + curl --insecure --cookie ${cookie_file} --header "X-Requested-With: XMLHttpRequest" --data "hidden[hidden_input]=&config[stonith-enabled]=false" https://localhost:2224/managec/${cluster_name}/update_cluster_settings > "${output_file}" + cat "${output_file}"; echo "" + [ "$(cat ${output_file})" = "Update Successful" ] +elif [ "$webui_http_code_response" = "401" ]; then + curl --insecure https://localhost:2224/ui/ > "${output_file}" + cat "${output_file}"; echo "" + [ "$(cat "${output_file}")" = '{"notauthorized":"true"}' ] +else + echo "Unexpected response from https://localhost:2224/ui/ - http code: '${webui_http_code_response}'" + exit 1 +fi # Sanity check of API V1 curl -kb "token=${token}" https://localhost:2224/api/v1/resource-agent-get-agents-list/v1 --data '{}' > "${output_file}" diff --git a/pcs_test/tier0/daemon/app/fixtures_app.py b/pcs_test/tier0/daemon/app/fixtures_app.py index 3c0d6a4f..56acf473 100644 --- a/pcs_test/tier0/daemon/app/fixtures_app.py +++ b/pcs_test/tier0/daemon/app/fixtures_app.py @@ -1,16 +1,11 @@ from pprint import pformat from urllib.parse import urlencode -from tornado.httputil import ( - HTTPHeaders, - parse_cookie, -) +from tornado.httputil import HTTPHeaders from tornado.testing import AsyncHTTPTestCase from tornado.web import Application from pcs.daemon import ruby_pcsd -from pcs.daemon.app.webui import session -from pcs.daemon.app.webui.auth import PCSD_SESSION USER = "user" GROUPS = ["group1", "group2"] @@ -56,6 +51,13 @@ class AppTest(AsyncHTTPTestCase): def fetch(self, path, raise_error=False, **kwargs): if "follow_redirects" not in kwargs: kwargs["follow_redirects"] = False + + if "is_ajax" in kwargs: + if "headers" not in kwargs: + kwargs["headers"] = {} + kwargs["headers"]["X-Requested-With"] = "XMLHttpRequest" + del kwargs["is_ajax"] + response = super().fetch(path, raise_error=raise_error, **kwargs) # "Strict-Transport-Security" header is expected in every response self.assertTrue( @@ -91,45 +93,6 @@ class AppTest(AsyncHTTPTestCase): self.assert_headers_contains(response.headers, self.wrapper.headers) self.assertEqual(response.body, self.wrapper.body) - -class AppUiTestMixin(AppTest): - def setUp(self): - self.session_storage = session.Storage(lifetime_seconds=10) - super().setUp() - - def assert_session_in_response(self, response, sid=None): - self.assertTrue("Set-Cookie" in response.headers) - cookie = parse_cookie(response.headers["Set-Cookie"]) - self.assertTrue(PCSD_SESSION, cookie) - if sid: - self.assertEqual(cookie[PCSD_SESSION], sid) - return cookie[PCSD_SESSION] - - def fetch(self, path, raise_error=False, **kwargs): - if "sid" in kwargs: - if "headers" not in kwargs: - kwargs["headers"] = {} - kwargs["headers"]["Cookie"] = f"{PCSD_SESSION}={kwargs['sid']}" - del kwargs["sid"] - - if "is_ajax" in kwargs: - if "headers" not in kwargs: - kwargs["headers"] = {} - kwargs["headers"]["X-Requested-With"] = "XMLHttpRequest" - del kwargs["is_ajax"] - - if "follow_redirects" not in kwargs: - kwargs["follow_redirects"] = False - - return super().fetch(path, raise_error=raise_error, **kwargs) - - def create_login_session(self): - return self.session_storage.login(USER) - - def assert_success_response(self, response, expected_body): - self.assertEqual(response.code, 200) - self.assertEqual(response.body.decode(), expected_body) - def assert_unauth_ajax(self, response): self.assertEqual(response.code, 401) self.assertEqual(response.body, b'{"notauthorized":"true"}') diff --git a/pcs_test/tier0/daemon/app/fixtures_app_webui.py b/pcs_test/tier0/daemon/app/fixtures_app_webui.py new file mode 100644 index 00000000..e38ca3e7 --- /dev/null +++ b/pcs_test/tier0/daemon/app/fixtures_app_webui.py @@ -0,0 +1,28 @@ +try: + from pcs.daemon.app import webui +except ImportError: + # You need to skip tests in tests that uses AppTest in the case webui + # is not there. + webui = None + +from pcs_test.tier0.daemon.app import fixtures_app + + +class AppTest(fixtures_app.AppTest): + def setUp(self): + self.session_storage = webui.session.Storage(lifetime_seconds=10) + super().setUp() + + def fetch(self, path, raise_error=False, **kwargs): + if "sid" in kwargs: + if "headers" not in kwargs: + kwargs["headers"] = {} + kwargs["headers"][ + "Cookie" + ] = f"{webui.auth.PCSD_SESSION}={kwargs['sid']}" + del kwargs["sid"] + + return super().fetch(path, raise_error=raise_error, **kwargs) + + def create_login_session(self): + return self.session_storage.login(fixtures_app.USER) diff --git a/pcs_test/tier0/daemon/app/test_app_gui.py b/pcs_test/tier0/daemon/app/test_app_gui.py index b3446fdb..2317eea5 100644 --- a/pcs_test/tier0/daemon/app/test_app_gui.py +++ b/pcs_test/tier0/daemon/app/test_app_gui.py @@ -1,14 +1,24 @@ import logging from unittest import mock +from tornado.httputil import parse_cookie + from pcs.daemon import ruby_pcsd from pcs.daemon.app import sinatra_ui from pcs.daemon.app.auth import UnixSocketAuthProvider -from pcs.daemon.app.webui import sinatra_ui as sinatra_ui_webui + +try: + from pcs.daemon.app import webui +except ImportError: + webui = None from pcs.lib.auth.provider import AuthProvider from pcs.lib.auth.types import AuthUser -from pcs_test.tier0.daemon.app import fixtures_app +from pcs_test.tier0.daemon.app import ( + fixtures_app, + fixtures_app_webui, +) +from pcs_test.tools.misc import skip_unless_webui_installed # Don't write errors to test output. logging.getLogger("tornado.access").setLevel(logging.CRITICAL) @@ -33,21 +43,28 @@ def patch_get_unix_socket_user(user): auth_provider = AuthProvider(logging.getLogger("test logger")) -class AppTest(fixtures_app.AppUiTestMixin): +@skip_unless_webui_installed() +@patch_login_user() +class SinatraAjaxProtectedSession(fixtures_app_webui.AppTest): def setUp(self): self.wrapper = fixtures_app.RubyPcsdWrapper(ruby_pcsd.SINATRA) super().setUp() - -@patch_login_user() -class SinatraAjaxProtectedSession(AppTest): def get_routes(self): - return sinatra_ui_webui.get_routes( + return webui.sinatra_ui.get_routes( self.session_storage, auth_provider, self.wrapper, ) + def assert_session_in_response(self, response, sid=None): + self.assertTrue("Set-Cookie" in response.headers) + cookie = parse_cookie(response.headers["Set-Cookie"]) + self.assertTrue(webui.auth.PCSD_SESSION, cookie) + if sid: + self.assertEqual(cookie[webui.auth.PCSD_SESSION], sid) + return cookie[webui.auth.PCSD_SESSION] + def test_deal_without_authentication(self): self.assert_unauth_ajax(self.get("/some-ajax", is_ajax=True)) @@ -63,7 +80,11 @@ class SinatraAjaxProtectedSession(AppTest): @patch_login_user() -class SinatraAjaxProtectedUnixSocket(AppTest): +class SinatraAjaxProtectedUnixSocket(fixtures_app.AppTest): + def setUp(self): + self.wrapper = fixtures_app.RubyPcsdWrapper(ruby_pcsd.SINATRA) + super().setUp() + def get_routes(self): return sinatra_ui.get_routes(auth_provider, self.wrapper) diff --git a/pcs_test/tier0/daemon/app/test_app_spa.py b/pcs_test/tier0/daemon/app/test_app_spa.py index 89a5ab0e..6acf3c7d 100644 --- a/pcs_test/tier0/daemon/app/test_app_spa.py +++ b/pcs_test/tier0/daemon/app/test_app_spa.py @@ -2,12 +2,22 @@ import logging import os from unittest import mock -from pcs.daemon.app import webui +try: + from pcs.daemon.app import webui +except ImportError: + webui = None + from pcs.lib.auth.provider import AuthProvider from pcs.lib.auth.types import AuthUser -from pcs_test.tier0.daemon.app import fixtures_app -from pcs_test.tools.misc import get_tmp_dir +from pcs_test.tier0.daemon.app import ( + fixtures_app, + fixtures_app_webui, +) +from pcs_test.tools.misc import ( + get_tmp_dir, + skip_unless_webui_installed, +) LOGIN_BODY = {"username": fixtures_app.USER, "password": fixtures_app.PASSWORD} PREFIX = "/ui/" @@ -16,7 +26,7 @@ PREFIX = "/ui/" logging.getLogger("tornado.access").setLevel(logging.CRITICAL) -class AppTest(fixtures_app.AppUiTestMixin): +class AppTest(fixtures_app_webui.AppTest): def setUp(self): self.public_dir = get_tmp_dir("tier0_daemon_app_spa") self.spa_dir_path = os.path.join(self.public_dir.name, "ui") @@ -41,7 +51,12 @@ class AppTest(fixtures_app.AppUiTestMixin): auth_provider=AuthProvider(logging.Logger("test logger")), ) + def assert_success_response(self, response, expected_body): + self.assertEqual(response.code, 200) + self.assertEqual(response.body.decode(), expected_body) + +@skip_unless_webui_installed() class Static(AppTest): def test_index(self): self.assert_success_response( @@ -50,6 +65,7 @@ class Static(AppTest): ) +@skip_unless_webui_installed() class Fallback(AppTest): def setUp(self): super().setUp() @@ -65,6 +81,7 @@ class Fallback(AppTest): ) +@skip_unless_webui_installed() class Login(AppTest): def setUp(self): super().setUp() @@ -94,6 +111,7 @@ class Login(AppTest): ) +@skip_unless_webui_installed() class Logout(AppTest): def test_can_logout(self): session1 = self.create_login_session() diff --git a/pcs_test/tier0/daemon/test_env.py b/pcs_test/tier0/daemon/test_env.py index 40f0c402..0eff3660 100644 --- a/pcs_test/tier0/daemon/test_env.py +++ b/pcs_test/tier0/daemon/test_env.py @@ -8,7 +8,10 @@ from unittest import ( from pcs import settings from pcs.daemon import env -from pcs_test.tools.misc import create_setup_patch_mixin +from pcs_test.tools.misc import ( + create_setup_patch_mixin, + skip_unless_webui_installed, +) def webui_fallback(public_dir): @@ -177,6 +180,7 @@ class Prepare(TestCase, create_setup_patch_mixin(env)): {env.PCSD_DEBUG: "false"} ) + @skip_unless_webui_installed() def test_errors_on_missing_paths(self): self.path_exists.return_value = False self.assert_environ_produces_modified_pcsd_env( diff --git a/pcs_test/tier0/daemon/test_session.py b/pcs_test/tier0/daemon/test_session.py index a893c01d..e8eff4cd 100644 --- a/pcs_test/tier0/daemon/test_session.py +++ b/pcs_test/tier0/daemon/test_session.py @@ -1,23 +1,31 @@ from contextlib import contextmanager from unittest import TestCase -from pcs.daemon.app.webui import session -from pcs.daemon.app.webui.session import Session +try: + from pcs.daemon.app import webui +except ImportError: + webui = None -from pcs_test.tools.misc import create_setup_patch_mixin +from pcs_test.tools.misc import ( + create_setup_patch_mixin, + skip_unless_webui_installed, +) SID = "abc" USER = "user" GROUPS = ["group1", "group2"] -PatchSessionMixin = create_setup_patch_mixin(session) +# If webui is None, the tests in this file using these mixins are skipped, +# passing a dummy object instead to make this executable +PatchSessionMixin = create_setup_patch_mixin(webui) if webui else object +@skip_unless_webui_installed() class SessionTest(TestCase, PatchSessionMixin): def setUp(self): - self.now = self.setup_patch("now", return_value=0) - self.session = Session(SID, USER) + self.now = self.setup_patch("session.now", return_value=0) + self.session = webui.session.Session(SID, USER) def test_session_grows_older(self): self.now.return_value = 10.1 @@ -42,10 +50,11 @@ class SessionTest(TestCase, PatchSessionMixin): session1.sid +@skip_unless_webui_installed() class StorageTest(TestCase, PatchSessionMixin): def setUp(self): - self.now = self.setup_patch("now", return_value=0) - self.storage = session.Storage(lifetime_seconds=10) + self.now = self.setup_patch("session.now", return_value=0) + self.storage = webui.session.Storage(lifetime_seconds=10) def test_does_not_accept_foreign_sid(self): self.assertIsNone(self.storage.get("unknown_sid")) diff --git a/pcs_test/tools/misc.py b/pcs_test/tools/misc.py index 28e03411..7a048a4d 100644 --- a/pcs_test/tools/misc.py +++ b/pcs_test/tools/misc.py @@ -15,6 +15,11 @@ from pcs.cli.common.parse_args import InputModifiers from pcs.common import str_tools from pcs.lib.external import CommandRunner +try: + from pcs.daemon.app import webui +except ImportError: + webui = None # type: ignore + from pcs_test import TEST_ROOT from pcs_test import settings as tests_settings from pcs_test.tools.custom_mock import MockLibraryReportProcessor @@ -292,6 +297,10 @@ def skip_unless_booth_resource_agent_installed(): ) +def skip_unless_webui_installed(): + return skipUnless(webui, "test requires webui which is not installed") + + def create_patcher(target_prefix_or_module): """ Return function for patching tests with preconfigured target prefix -- 2.45.2