class MixinTest(AsyncHTTPTestCase, AssertMixin, create_setup_patch_mixin(app_session), UserAuthMixin): init_session = None auto_init_session = True groups_valid = False response_sid_expectation = None """ The app_session.Mixin is tested via Handler(RequestHandler) that mix it. The particular tests acts inside the handler. """ def setUp(self): Handler.test = self self.storage = session.Storage(lifetime_seconds=10) self.setup_patch("check_user_groups", self.check_user_groups) self.setup_patch("authorize_user", self.authorize_user) self.fetch_args = {} self.sid = None self.response = None super().setUp() def get_app(self): return Application([("/", Handler, dict(session_storage=self.storage)) ]) @property def session_dict(self): # pylint: disable=protected-access return self.storage._Storage__sessions def sid_from_body(self, response): self.assertIn("Set-Cookie", response.headers) cookie = parse_cookie(response.headers["Set-Cookie"]) self.assertIn(app_session.PCSD_SESSION, cookie) return cookie[app_session.PCSD_SESSION] def extra_checks(self, response): pass async def on_handle(self, handler): pass def test(self): if self.init_session == VANILLA_SESSION: self.sid = self.storage.provide().sid self.fetch_args = headers_with_sid(self.sid) elif self.init_session == AUTHENTICATED_SESSION: self.sid = self.storage.login(sid=None, username=USER, groups=[]).sid self.fetch_args = headers_with_sid(self.sid) response = self.fetch("/", **self.fetch_args) self.assertEqual(response.code, 200) self.assertEqual(response.body, b"") if self.response_sid_expectation == RESPONSE_WITH_SID: self.assertEqual(self.sid_from_body(response), self.sid) elif self.response_sid_expectation == RESPONSE_WITHOUT_SID: self.assertNotIn("Set-Cookie", response.headers) self.extra_checks(response)
class AuthorizeUserSync(TestCase, create_setup_patch_mixin(auth)): def setUp(self): self.authenticate_by_pam = self.setup_patch("authenticate_by_pam") self.get_user_groups_sync = self.setup_patch("get_user_groups_sync") def test_success_authorization(self): self.authenticate_by_pam.return_value = True self.get_user_groups_sync.return_value = [auth.HA_ADM_GROUP] user_auth_info = auth.authorize_user_sync(USER, PASSWORD) self.assertEqual(user_auth_info.name, USER) self.assertTrue(user_auth_info.is_authorized) def test_failed_pam_authorization(self): self.authenticate_by_pam.return_value = False user_auth_info = auth.authorize_user_sync(USER, PASSWORD) self.assertEqual(user_auth_info.name, USER) self.assertFalse(user_auth_info.is_authorized) def test_failed_on_groups(self): self.authenticate_by_pam.return_value = True self.get_user_groups_sync.return_value = [] user_auth_info = auth.authorize_user_sync(USER, PASSWORD) self.assertEqual(user_auth_info.name, USER) self.assertFalse(user_auth_info.is_authorized)
class Auth( AppTest, create_setup_patch_mixin(sinatra_remote), fixtures_app.UserAuthMixin, ): # pylint: disable=too-many-ancestors def setUp(self): self.setup_patch("authorize_user", self.authorize_user) super().setUp() def make_auth_request(self, valid=True): self.user_auth_info = fixtures_app.UserAuthInfo(valid=valid) return self.post( "/remote/auth", body={ "username": fixtures_app.USER, "password": fixtures_app.PASSWORD, }, ) def test_refuse_unknown_user(self): self.assertEqual(b"", self.make_auth_request(valid=False).body) def test_wraps_ruby_on_valid_user(self): self.assert_wrappers_response(self.make_auth_request())
class AppTest(fixtures_app.AppUiTestMixin, create_setup_patch_mixin(ui.app_session)): 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") os.makedirs(self.spa_dir_path) self.fallback_path = os.path.join(self.public_dir.name, "fallback.html") self.index_path = os.path.join(self.spa_dir_path, "index.html") self.index_content = "<html/>" with open(self.index_path, "w") as index: index.write(self.index_content) super().setUp() def tearDown(self): self.public_dir.cleanup() super().tearDown() def get_routes(self): return ui.get_routes( url_prefix=PREFIX, app_dir=self.spa_dir_path, fallback_page_path=self.fallback_path, session_storage=self.session_storage, )
class ManageTest(TestCase, create_setup_patch_mixin(http_server)): def setUp(self): self.server_list = [] self.pcsd_ssl = MagicMock(spec_set=PcsdSSL) self.setup_patch("HTTPServer", self.HTTPServer) # self.setup_patch("PcsdSSL", Mock(return_value=self.pcsd_ssl)) self.setup_patch("bind_sockets", lambda port, addr: addr2sock([addr])) self.app = MagicMock() self.https_server_manage = http_server.HttpsServerManage( Mock(return_value=self.app), PORT, BIND_ADDRESSES, self.pcsd_ssl, ) self.assertEqual(0, len(self.server_list)) self.assertFalse(self.https_server_manage.server_is_running) def HTTPServer(self, app, ssl_options): # pylint: disable=invalid-name self.assertEqual(self.app, app) self.assertEqual(self.pcsd_ssl.create_context.return_value, ssl_options) self.server_list.append(MagicMock(spec_set=HTTPServer)) return self.server_list[-1] def test_starting_and_stopping_new_http_server(self): self.https_server_manage.start() self.server_list[0].add_sockets.assert_called_once_with(BIND_SOCKETS) self.assertTrue(self.https_server_manage.server_is_running) self.https_server_manage.stop() self.server_list[0].stop.assert_called_once() self.assertFalse(self.https_server_manage.server_is_running) def test_reload_certs_raises_when_server_not_started(self): self.assertRaises( http_server.HttpsServerManageException, self.https_server_manage.reload_certs, ) def test_reload_certs_exchanges_servers(self): self.https_server_manage.start() self.https_server_manage.reload_certs() self.server_list[0].stop.assert_called_once() self.server_list[1].add_sockets.assert_called_once_with(BIND_SOCKETS) self.assertTrue(self.https_server_manage.server_is_running)
class AppTest(fixtures_app.AppUiTest, create_setup_patch_mixin(sinatra_ui.app_session)): def setUp(self): self.wrapper = fixtures_app.RubyPcsdWrapper(ruby_pcsd.SINATRA_GUI) super().setUp() def get_routes(self): return sinatra_ui.get_routes( self.session_storage, self.wrapper, PUBLIC_DIR, ) def assert_is_redirect(self, response, location, status_code=302): self.assert_headers_contains(response.headers, {"Location": location}) self.assertEqual(response.code, status_code)
class AppTest(fixtures_app.AppUiTestMixin, create_setup_patch_mixin(ui.app_session)): def setUp(self): self.index_content = "<html/>" with open(INDEX, "w") as index: index.write(self.index_content) super().setUp() def tearDown(self): if os.path.isfile(INDEX): os.remove(INDEX) super().tearDown() def get_routes(self): return ui.get_routes(url_prefix=PREFIX, app_dir=SPA_DIR, fallback_page_path=FALLBACK, session_storage=self.session_storage)
class AppTest( fixtures_app.AppUiTestMixin, create_setup_patch_mixin(sinatra_ui.app_session), ): def setUp(self): self.wrapper = fixtures_app.RubyPcsdWrapper(ruby_pcsd.SINATRA_GUI) self.public_dir = get_tmp_dir("tier0_daemon_app_gui") super().setUp() def tearDown(self): super().tearDown() self.public_dir.cleanup() def get_routes(self): return sinatra_ui.get_routes( self.session_storage, self.wrapper, self.public_dir.name, ) def assert_is_redirect(self, response, location, status_code=302): self.assert_headers_contains(response.headers, {"Location": location}) self.assertEqual(response.code, status_code)
from pcs.common.reports import ReportItemSeverity as severities from pcs.common.reports import codes as report_codes from pcs.lib.cib.node import PacemakerNode from pcs.lib.cib.resource import guest_node from pcs.lib.cib.tools import IdProvider from pcs_test.tools.assertions import ( assert_report_item_list_equal, assert_xml_equal, ) from pcs_test.tools.misc import create_setup_patch_mixin # pylint: disable=no-self-use SetupPatchMixin = create_setup_patch_mixin(guest_node) class ValidateHostConflicts(TestCase): @staticmethod def validate(node_name, options): tree = etree.fromstring(""" <cib> <configuration> <resources> <primitive id="CONFLICT"/> <primitive id="A"> <meta_attributes> <nvpair name="remote-node" value="GUEST_CONFLICT" />
class AppTest(fixtures_app.AppTest, create_setup_patch_mixin(app_gui.app_session)): def setUp(self): self.user_auth_info = UserAuthInfo() self.groups_valid = True self.wrapper = fixtures_app.RubyPcsdWrapper(ruby_pcsd.SINATRA_GUI) self.session_storage = session.Storage(lifetime_seconds=10) self.setup_patch("check_user_groups", self.check_user_groups) super().setUp() async def check_user_groups(self, username): self.assertEqual(username, USER) return auth.UserAuthInfo(username, self.user_auth_info.groups, is_authorized=self.groups_valid) def get_routes(self): return app_gui.get_routes( self.session_storage, self.wrapper, PUBLIC_DIR, ) def extract_sid(self, response): return self.assert_session_in_response(response) def fetch(self, path, raise_error=False, **kwargs): if "sid" in kwargs: if "headers" not in kwargs: kwargs["headers"] = {} kwargs["headers"]["Cookie"] = ( f"{app_session.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(sid=None, username=USER, groups=GROUPS) 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(app_session.PCSD_SESSION, cookie) if sid: self.assertEqual(cookie[app_session.PCSD_SESSION], sid) return cookie[app_session.PCSD_SESSION] def assert_is_redirect(self, response, location, status_code=302): self.assert_headers_contains(response.headers, {"Location": location}) self.assertEqual(response.code, status_code) def assert_unauth_ajax(self, response): self.assertEqual(response.code, 401) self.assertEqual(response.body, b'{"notauthorized":"true"}') def assert_success_response(self, response, expected_body): self.assertEqual(response.code, 200) self.assertEqual(response.body.decode(), expected_body)
from unittest import TestCase from pcs_test.tools.assertions import assert_raise_library_error from pcs_test.tools.misc import create_setup_patch_mixin from pcs.common.reports import ReportItemSeverity as severity from pcs.common.reports import codes as report_codes from pcs.lib import node_communication_format SetupPatchMixin = create_setup_patch_mixin(node_communication_format) class PcmkAuthkeyFormat(TestCase, SetupPatchMixin): def test_create_expected_dict(self): b64encode = self.setup_patch("base64.b64encode") b64encode.return_value = "encoded_content".encode() self.assertEqual( node_communication_format.pcmk_authkey_format("content"), { "data": b64encode.return_value.decode("utf-8"), "type": "pcmk_remote_authkey", "rewrite_existing": True, }, ) class ServiceCommandFormat(TestCase): def test_create_expected_dict(self): self.assertEqual( node_communication_format.service_cmd_format("pcsd", "start"), {
create_setup_patch_mixin, ) from pcs_test.tools.xml import etree_to_str from pcs.common import report_codes from pcs.common.tools import Version from pcs.lib.env import LibraryEnvironment def mock_tmpfile(filename): mock_file = mock.MagicMock() mock_file.name = rc(filename) return mock_file SetupPatchMixin = create_setup_patch_mixin( partial(mock.patch.object, LibraryEnvironment)) class ManageCibAssertionMixin: def assert_raises_cib_error(self, callable_obj, message): with self.assertRaises(AssertionError) as context_manager: callable_obj() self.assertEqual(str(context_manager.exception), message) def assert_raises_cib_not_loaded(self, callable_obj): self.assert_raises_cib_error(callable_obj, "CIB has not been loaded") def assert_raises_cib_already_loaded(self, callable_obj): self.assert_raises_cib_error(callable_obj, "CIB has already been loaded")
from unittest import mock, TestCase from pcs_test.tools.misc import create_setup_patch_mixin from pcs.cli.booth import env from pcs.common import report_codes, env_file_role_codes from pcs.lib.errors import LibraryEnvError, ReportItem SetupPatchMixin = create_setup_patch_mixin(env) class BoothConfTest(TestCase, SetupPatchMixin): def setUp(self): self.write = self.setup_patch("env_file.write") self.read = self.setup_patch("env_file.read") self.process_no_existing_file_expectation = self.setup_patch( "env_file.process_no_existing_file_expectation") def test_sucessfully_care_about_local_file(self): def next_in_line(_env): _env.booth["modified_env"] = { "config_file": { "content": "file content", "no_existing_file_expected": False, }, "key_file": { "content": "key file content", "no_existing_file_expected": False, } } return "call result"
class Prepare(TestCase, create_setup_patch_mixin(env)): def setUp(self): self.path_exists = self.setup_patch("path_exists", return_value=True) self.logger = Logger() def assert_environ_produces_modified_pcsd_env(self, environ=None, specific_env_values=None, errors=None, warnings=None): pcsd_dir = partial(join_path, settings.pcsd_exec_location) default_env_values = { env.PCSD_PORT: settings.pcsd_default_port, env.PCSD_SSL_CIPHERS: settings.default_ssl_ciphers, env.PCSD_SSL_OPTIONS: env.str_to_ssl_options(settings.default_ssl_options, []), env.PCSD_BIND_ADDR: {None}, env.NOTIFY_SOCKET: None, env.PCSD_DEBUG: False, env.PCSD_DISABLE_GUI: False, env.PCSD_SESSION_LIFETIME: settings.gui_session_lifetime_seconds, env.PCSD_STATIC_FILES_DIR: pcsd_dir(env.PCSD_STATIC_FILES_DIR_NAME), env.PCSD_DEV: False, "has_errors": False, } if specific_env_values is None: specific_env_values = {} # compare as dict because of clearer error report self.assertEqual( dict(env.prepare_env(environ or {}, self.logger)._asdict()), { **default_env_values, **specific_env_values }, ) self.assertEqual(self.logger.errors, errors or []) self.assertEqual(self.logger.warnings, warnings or []) def test_nothing_in_environ(self): self.assert_environ_produces_modified_pcsd_env() def test_many_valid_environment_changes(self): pcsd_dir = partial(join_path, env.PCSD_LOCAL_DIR) session_lifetime = 10 environ = { env.PCSD_PORT: "1234", env.PCSD_SSL_CIPHERS: "DEFAULT:!3DES:@STRENGTH", env.PCSD_SSL_OPTIONS: "OP_NO_SSLv2", env.PCSD_BIND_ADDR: "abc", env.NOTIFY_SOCKET: "xyz", env.PCSD_DEBUG: "true", env.PCSD_DISABLE_GUI: "true", env.PCSD_SESSION_LIFETIME: str(session_lifetime), env.PCSD_DEV: "true", } self.assert_environ_produces_modified_pcsd_env( environ=environ, specific_env_values={ env.PCSD_PORT: environ[env.PCSD_PORT], env.PCSD_SSL_CIPHERS: environ[env.PCSD_SSL_CIPHERS], env.PCSD_SSL_OPTIONS: OP_NO_SSLv2, env.PCSD_BIND_ADDR: {environ[env.PCSD_BIND_ADDR]}, env.NOTIFY_SOCKET: environ[env.NOTIFY_SOCKET], env.PCSD_DEBUG: True, env.PCSD_DISABLE_GUI: True, env.PCSD_SESSION_LIFETIME: session_lifetime, env.PCSD_STATIC_FILES_DIR: pcsd_dir(env.PCSD_STATIC_FILES_DIR_NAME), env.PCSD_DEV: True, }, ) def test_error_on_noninteger_session_lifetime(self): environ = {env.PCSD_SESSION_LIFETIME: "invalid"} self.assert_environ_produces_modified_pcsd_env( environ, specific_env_values={ **environ, "has_errors": True }, errors=[ "Invalid PCSD_SESSION_LIFETIME value 'invalid'" " (it must be an integer)" ], ) def test_report_invalid_ssl_ciphers(self): environ = {env.PCSD_SSL_CIPHERS: "invalid ;@{}+ ciphers"} self.assert_environ_produces_modified_pcsd_env( environ, specific_env_values={ **environ, "has_errors": True }, errors=["Invalid ciphers: '('No cipher can be selected.',)'"], ) def test_report_invalid_ssl_options(self): self.assert_environ_produces_modified_pcsd_env( environ={env.PCSD_SSL_OPTIONS: "invalid"}, specific_env_values={ env.PCSD_SSL_OPTIONS: 0, "has_errors": True }, errors=["Ignoring unknown SSL option 'invalid'"], ) def test_report_invalid_ssl_options_warning(self): env.settings.default_ssl_options = "invalid" self.assert_environ_produces_modified_pcsd_env( specific_env_values={env.PCSD_SSL_OPTIONS: 0}, warnings=["Ignoring unknown SSL option 'invalid'"], ) def test_empty_bind_addresses(self): self.assert_environ_produces_modified_pcsd_env( environ={env.PCSD_BIND_ADDR: " "}, specific_env_values={env.PCSD_BIND_ADDR: {""}}, ) def test_no_disable_gui_explicitly(self): self.assert_environ_produces_modified_pcsd_env( environ={env.PCSD_DISABLE_GUI: "false"}, ) def test_debug_explicitly(self): self.assert_environ_produces_modified_pcsd_env( environ={env.PCSD_DEBUG: "true"}, specific_env_values={env.PCSD_DEBUG: True}, ) def test_no_debug_explicitly(self): self.assert_environ_produces_modified_pcsd_env( {env.PCSD_DEBUG: "false"}) def test_errors_on_missing_paths(self): self.path_exists.return_value = False pcsd_dir = partial(join_path, settings.pcsd_exec_location) self.assert_environ_produces_modified_pcsd_env( specific_env_values={"has_errors": True}, errors=[ "Directory with web UI assets" f" '{pcsd_dir(env.PCSD_STATIC_FILES_DIR_NAME)}'" " does not exist", ], ) def test_no_errors_on_missing_paths_disabled_gui(self): self.path_exists.return_value = False self.assert_environ_produces_modified_pcsd_env( environ={env.PCSD_DISABLE_GUI: "true"}, specific_env_values={ env.PCSD_DISABLE_GUI: True, "has_errors": False, }, errors=[], )
from unittest import mock, TestCase from pcs_test.tools.misc import create_patcher, create_setup_patch_mixin from pcs.cli.common import env_file from pcs.common import report_codes from pcs.lib.errors import ReportItem patch_env_file = create_patcher(env_file) SetupPatchMixin = create_setup_patch_mixin(patch_env_file) FILE_PATH = "/path/to/local/file" class Write(TestCase, SetupPatchMixin): def setUp(self): self.mock_open = mock.mock_open() self.mock_error = self.setup_patch("console_report.error") def assert_params_causes_calls(self, env_file_dict, calls, path=FILE_PATH): with patch_env_file("open", self.mock_open, create=True): env_file.write(env_file_dict, path) self.assertEqual(self.mock_open.mock_calls, calls) def test_sucessfully_write(self): self.assert_params_causes_calls({"content": "filecontent"}, [ mock.call(FILE_PATH, "w"), mock.call().write("filecontent"), mock.call().close(), ])
from unittest import TestCase from pcs_test.tools.assertions import assert_raise_library_error from pcs_test.tools.misc import create_setup_patch_mixin from pcs.common import report_codes from pcs.lib import node_communication_format from pcs.lib.errors import ReportItemSeverity as severity SetupPatchMixin = create_setup_patch_mixin(node_communication_format) class PcmkAuthkeyFormat(TestCase, SetupPatchMixin): def test_create_expected_dict(self): b64encode = self.setup_patch("base64.b64encode") b64encode.return_value = "encoded_content".encode() self.assertEqual( node_communication_format.pcmk_authkey_format("content"), { "data": b64encode.return_value.decode("utf-8"), "type": "pcmk_remote_authkey", "rewrite_existing": True, } ) class ServiceCommandFormat(TestCase): def test_create_expected_dict(self): self.assertEqual( node_communication_format.service_cmd_format("pcsd", "start"), { "type": "service_command",
from pcs_test.tools.assertions import( assert_xml_equal, assert_report_item_list_equal, ) from pcs_test.tools.misc import create_setup_patch_mixin from pcs.common import report_codes from pcs.lib.cib.node import PacemakerNode from pcs.lib.cib.resource import guest_node from pcs.lib.cib.tools import IdProvider from pcs.lib.errors import ReportItemSeverity as severities # pylint: disable=no-self-use SetupPatchMixin = create_setup_patch_mixin(guest_node) class ValidateHostConflicts(TestCase): @staticmethod def validate(node_name, options): tree = etree.fromstring(""" <cib> <configuration> <resources> <primitive id="CONFLICT"/> <primitive id="A"> <meta_attributes> <nvpair name="remote-node" value="GUEST_CONFLICT" /> </meta_attributes>
self.assertIsNone(_session.username) self.assertFalse(_session.is_authenticated) self.assertIsNone(_session.ajax_id) self.assertEqual(_session.groups, []) def assert_authenticated_session(self, _session, username, groups): self.assertEqual(_session.username, username) self.assertEqual(_session.groups, groups) self.assertTrue(_session.is_authenticated) self.assertTrue(_session.ajax_id is not None) def assert_login_failed_session(self, _session, username): self.assertEqual(_session.username, username) self.assertFalse(_session.is_authenticated) PatchSessionMixin = create_setup_patch_mixin(session) class SessionTest(TestCase, AssertMixin, PatchSessionMixin): def setUp(self): self.now = self.setup_patch("now", return_value=0) self.session = Session(SID) def test_session_grows_older(self): self.now.return_value = 10.1 self.assertTrue(self.session.was_unused_last(10)) self.assertFalse(self.session.was_unused_last(11)) @contextmanager def refresh_test(self): self.now.return_value = 10.1 yield self.session
from unittest import mock, TestCase from pcs_test.tools.misc import create_patcher, create_setup_patch_mixin from pcs.cli.common import env_file from pcs.common import report_codes from pcs.lib.errors import ReportItem patch_env_file = create_patcher(env_file) SetupPatchMixin = create_setup_patch_mixin(patch_env_file) FILE_PATH = "/path/to/local/file" class Write(TestCase, SetupPatchMixin): def setUp(self): self.mock_open = mock.mock_open() self.mock_error = self.setup_patch("console_report.error") def assert_params_causes_calls(self, env_file_dict, calls, path=FILE_PATH): with patch_env_file("open", self.mock_open, create=True): env_file.write(env_file_dict, path) self.assertEqual(self.mock_open.mock_calls, calls) def test_sucessfully_write(self): self.assert_params_causes_calls( {"content": "filecontent"}, [ mock.call(FILE_PATH, "w"), mock.call().write("filecontent"), mock.call().close(), ]
self.assertFalse(_session.is_authenticated) self.assertIsNone(_session.ajax_id) self.assertEqual(_session.groups, []) def assert_authenticated_session(self, _session, username, groups): self.assertEqual(_session.username, username) self.assertEqual(_session.groups, groups) self.assertTrue(_session.is_authenticated) self.assertTrue(_session.ajax_id is not None) def assert_login_failed_session(self, _session, username): self.assertEqual(_session.username, username) self.assertFalse(_session.is_authenticated) PatchSessionMixin = create_setup_patch_mixin(session) class SessionTest(TestCase, AssertMixin, PatchSessionMixin): def setUp(self): self.now = self.setup_patch("now", return_value=0) self.session = Session(SID) def test_session_grows_older(self): self.now.return_value = 10.1 self.assertTrue(self.session.was_unused_last(10)) self.assertFalse(self.session.was_unused_last(11)) @contextmanager def refresh_test(self): self.now.return_value = 10.1
create_setup_patch_mixin, ) from pcs_test.tools.xml import etree_to_str from pcs.common import report_codes from pcs.common.tools import Version from pcs.lib.env import LibraryEnvironment def mock_tmpfile(filename): mock_file = mock.MagicMock() mock_file.name = rc(filename) return mock_file SetupPatchMixin = create_setup_patch_mixin( partial(mock.patch.object, LibraryEnvironment) ) class ManageCibAssertionMixin: def assert_raises_cib_error(self, callable_obj, message): with self.assertRaises(AssertionError) as context_manager: callable_obj() self.assertEqual(str(context_manager.exception), message) def assert_raises_cib_not_loaded(self, callable_obj): self.assert_raises_cib_error( callable_obj, "CIB has not been loaded" )