salt/several-fixes-for-tests-to-avoid-errors-and-failures.patch

558 lines
22 KiB
Diff

From 30fd274d606b565a0a63fbc7f2fd67aec3c495b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Mon, 27 May 2024 12:01:53 +0100
Subject: [PATCH] Several fixes for tests to avoid errors and failures
in some OSes
* test_pip_state: skip tests which requires virtualenv CLI
* test_syndic_eauth: skip when using an incompatible Docker version
* test_pip: skip tests which requires virtualenv CLI
* Some more extra fixes for tests
* Enhance paths to look for 'sftp-server'
* Do trigger errors on newer docker-py version
* Make test_search_not_found to not fail on transactional systems
* Add `@pytest.mark.flaky_jail` to `tests/pytests/integration/ssh/test_ssh_setup.py::test_setup`
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
* Prevent crashing if mountpoint does not exist anymore
* Skip failing tests on transactional systems
* test_consul.py: do not produce error if consul is not available
* Redefine max retries for some flaky tests
* test_virt.py: skip as CI containers are not compatible with Salt 3006
* test_schema.py: Adjust expectations to newer jsonschema versions
* Apply suggestions from code review
Co-authored-by: Pedro Algarvio <pedro@algarvio.me>
---------
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
Co-authored-by: Pedro Algarvio <palgarvio@vmware.com>
Co-authored-by: Pedro Algarvio <pedro@algarvio.me>
---
salt/modules/status.py | 11 +++---
tests/conftest.py | 2 +
tests/integration/modules/test_pip.py | 4 ++
tests/pytests/functional/cache/test_consul.py | 4 ++
tests/pytests/functional/modules/test_pip.py | 1 +
.../functional/states/test_pip_state.py | 11 ++++++
tests/pytests/functional/states/test_pkg.py | 4 ++
.../integration/cli/test_syndic_eauth.py | 4 +-
.../integration/daemons/test_memory_leak.py | 2 +-
.../integration/modules/test_cmdmod.py | 1 +
.../pytests/integration/modules/test_virt.py | 4 ++
.../integration/ssh/test_pre_flight.py | 4 ++
.../pytests/integration/ssh/test_ssh_setup.py | 1 +
tests/pytests/scenarios/setup/test_man.py | 6 +++
.../unit/modules/dockermod/test_module.py | 20 +++++-----
tests/unit/modules/test_zypperpkg.py | 1 +
tests/unit/utils/test_schema.py | 37 +++++++++++++++----
17 files changed, 92 insertions(+), 25 deletions(-)
diff --git a/salt/modules/status.py b/salt/modules/status.py
index 4b0a3b0d400..33e5d7b8df5 100644
--- a/salt/modules/status.py
+++ b/salt/modules/status.py
@@ -1052,11 +1052,12 @@ def diskusage(*args):
# query the filesystems disk usage
ret = {}
for path in selected:
- fsstats = os.statvfs(path)
- blksz = fsstats.f_bsize
- available = fsstats.f_bavail * blksz
- total = fsstats.f_blocks * blksz
- ret[path] = {"available": available, "total": total}
+ if os.path.exists(path):
+ fsstats = os.statvfs(path)
+ blksz = fsstats.f_bsize
+ available = fsstats.f_bavail * blksz
+ total = fsstats.f_blocks * blksz
+ ret[path] = {"available": available, "total": total}
return ret
diff --git a/tests/conftest.py b/tests/conftest.py
index a7777c2cea6..ad57b4adef4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1428,6 +1428,8 @@ def sshd_server(salt_factories, sshd_config_dir, salt_master, grains):
"/usr/libexec/openssh/sftp-server",
# Arch Linux
"/usr/lib/ssh/sftp-server",
+ # openSUSE Tumbleweed and SL Micro 6.0
+ "/usr/libexec/ssh/sftp-server",
]
sftp_server_path = None
for path in sftp_server_paths:
diff --git a/tests/integration/modules/test_pip.py b/tests/integration/modules/test_pip.py
index 83457b467c8..d57e9cd2aea 100644
--- a/tests/integration/modules/test_pip.py
+++ b/tests/integration/modules/test_pip.py
@@ -557,6 +557,10 @@ class PipModuleTest(ModuleCase):
@pytest.mark.skip_initial_gh_actions_failure(
reason="This was skipped on older golden images and is failing on newer."
)
+ @pytest.mark.skipif(
+ bool(salt.utils.path.which("transactional-update")),
+ reason="Skipping on transactional systems",
+ )
def test_system_pip3(self):
self.run_function(
diff --git a/tests/pytests/functional/cache/test_consul.py b/tests/pytests/functional/cache/test_consul.py
index 30dc6925f26..996a1e932b6 100644
--- a/tests/pytests/functional/cache/test_consul.py
+++ b/tests/pytests/functional/cache/test_consul.py
@@ -11,6 +11,10 @@ import salt.loader
from salt.utils.versions import Version
from tests.pytests.functional.cache.helpers import run_common_cache_tests
+pytest.importorskip(
+ "consul",
+ reason="Please install python-consul package to use consul data cache driver",
+)
docker = pytest.importorskip("docker")
log = logging.getLogger(__name__)
diff --git a/tests/pytests/functional/modules/test_pip.py b/tests/pytests/functional/modules/test_pip.py
index 9b87735b68f..e04baa7c43f 100644
--- a/tests/pytests/functional/modules/test_pip.py
+++ b/tests/pytests/functional/modules/test_pip.py
@@ -22,6 +22,7 @@ from tests.support.helpers import VirtualEnv
)
@pytest.mark.requires_network
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_list_available_packages(modules, pip_version, tmp_path):
with VirtualEnv(venv_dir=tmp_path, pip_requirement=pip_version) as virtualenv:
virtualenv.install("-U", pip_version)
diff --git a/tests/pytests/functional/states/test_pip_state.py b/tests/pytests/functional/states/test_pip_state.py
index 3fc6ac7a1df..1921751b5dc 100644
--- a/tests/pytests/functional/states/test_pip_state.py
+++ b/tests/pytests/functional/states/test_pip_state.py
@@ -94,6 +94,7 @@ def test_pip_installed_removed(modules, states):
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_pip_installed_removed_venv(tmp_path, create_virtualenv, states):
venv_dir = tmp_path / "pip_installed_removed"
create_virtualenv(str(venv_dir))
@@ -105,6 +106,7 @@ def test_pip_installed_removed_venv(tmp_path, create_virtualenv, states):
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_pip_installed_errors(tmp_path, modules, state_tree):
venv_dir = tmp_path / "pip-installed-errors"
# Since we don't have the virtualenv created, pip.installed will
@@ -141,6 +143,7 @@ pep8-pip:
assert state_return.result is True
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_pip_installed_name_test_mode(tmp_path, create_virtualenv, states):
"""
Test pip.installed state while test=true
@@ -154,6 +157,7 @@ def test_pip_installed_name_test_mode(tmp_path, create_virtualenv, states):
assert name in ret.comment
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_pip_installed_pkgs_test_mode(tmp_path, create_virtualenv, states):
"""
Test pip.installed state while test=true
@@ -168,6 +172,7 @@ def test_pip_installed_pkgs_test_mode(tmp_path, create_virtualenv, states):
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_issue_2028_pip_installed_state(
tmp_path, modules, state_tree, get_python_executable
):
@@ -226,6 +231,7 @@ pep8-pip:
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_issue_2087_missing_pip(tmp_path, create_virtualenv, modules):
venv_dir = tmp_path / "issue-2087-missing-pip"
@@ -271,6 +277,7 @@ pip.installed:
@pytest.mark.destructive_test
@pytest.mark.slow_test
@pytest.mark.skip_if_not_root
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_issue_6912_wrong_owner(tmp_path, create_virtualenv, modules, states):
# Setup virtual environment directory to be used throughout the test
venv_dir = tmp_path / "6912-wrong-owner"
@@ -338,6 +345,7 @@ def test_issue_6912_wrong_owner(tmp_path, create_virtualenv, modules, states):
@pytest.mark.skip_on_darwin(reason="Test is flaky on macosx")
@pytest.mark.slow_test
@pytest.mark.skip_if_not_root
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_issue_6912_wrong_owner_requirements_file(
tmp_path, create_virtualenv, state_tree, modules, states
):
@@ -409,6 +417,7 @@ def test_issue_6912_wrong_owner_requirements_file(
@pytest.mark.destructive_test
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_issue_6833_pip_upgrade_pip(tmp_path, create_virtualenv, modules, states):
# Create the testing virtualenv
if sys.platform == "win32":
@@ -465,6 +474,7 @@ def test_issue_6833_pip_upgrade_pip(tmp_path, create_virtualenv, modules, states
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_pip_installed_specific_env(
tmp_path, state_tree_prod, states, create_virtualenv
):
@@ -514,6 +524,7 @@ def test_pip_installed_specific_env(
@pytest.mark.slow_test
+@pytest.mark.skip_if_binaries_missing("virtualenv", reason="Needs virtualenv binary")
def test_22359_pip_installed_unless_does_not_trigger_warnings(
create_virtualenv, tmp_path, states
):
diff --git a/tests/pytests/functional/states/test_pkg.py b/tests/pytests/functional/states/test_pkg.py
index 864c1d025f3..9e5a8350ad9 100644
--- a/tests/pytests/functional/states/test_pkg.py
+++ b/tests/pytests/functional/states/test_pkg.py
@@ -19,6 +19,10 @@ pytestmark = [
pytest.mark.slow_test,
pytest.mark.skip_if_not_root,
pytest.mark.destructive_test,
+ pytest.mark.skipif(
+ bool(salt.utils.path.which("transactional-update")),
+ reason="Skipping on transactional systems",
+ ),
]
diff --git a/tests/pytests/integration/cli/test_syndic_eauth.py b/tests/pytests/integration/cli/test_syndic_eauth.py
index 218022b9e3c..8dcdd3fbd28 100644
--- a/tests/pytests/integration/cli/test_syndic_eauth.py
+++ b/tests/pytests/integration/cli/test_syndic_eauth.py
@@ -6,7 +6,9 @@ import time
import pytest
-docker = pytest.importorskip("docker")
+from tests.conftest import CODE_DIR
+
+docker = pytest.importorskip("docker", minversion="4.0.0")
INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container"
diff --git a/tests/pytests/integration/daemons/test_memory_leak.py b/tests/pytests/integration/daemons/test_memory_leak.py
index 8157091c44e..f2c5307f1a5 100644
--- a/tests/pytests/integration/daemons/test_memory_leak.py
+++ b/tests/pytests/integration/daemons/test_memory_leak.py
@@ -49,7 +49,7 @@ def file_add_delete_sls(testfile_path, base_env_state_tree_root_dir):
@pytest.mark.skip_on_darwin(reason="MacOS is a spawning platform, won't work")
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Test is failing in GitHub Actions")
-@pytest.mark.flaky(max_runs=4)
+@pytest.mark.flaky(max_runs=10)
def test_memory_leak(salt_cli, salt_minion, file_add_delete_sls):
max_usg = None
diff --git a/tests/pytests/integration/modules/test_cmdmod.py b/tests/pytests/integration/modules/test_cmdmod.py
index d9c326c3f0a..d0b993ddbcf 100644
--- a/tests/pytests/integration/modules/test_cmdmod.py
+++ b/tests/pytests/integration/modules/test_cmdmod.py
@@ -63,6 +63,7 @@ def test_avoid_injecting_shell_code_as_root(
@pytest.mark.slow_test
+@pytest.mark.flaky(max_runs=4)
def test_blacklist_glob(salt_call_cli):
"""
cmd_blacklist_glob
diff --git a/tests/pytests/integration/modules/test_virt.py b/tests/pytests/integration/modules/test_virt.py
index 572923764bb..a1cd577fe76 100644
--- a/tests/pytests/integration/modules/test_virt.py
+++ b/tests/pytests/integration/modules/test_virt.py
@@ -26,6 +26,10 @@ pytestmark = [
Version(docker.__version__) < Version("4.0.0"),
reason="Test does not work in this version of docker-py",
),
+ pytest.mark.skipif(
+ salt.version.__saltstack_version__.major <= 3006,
+ reason="CI containers are not compatible with this Salt version",
+ ),
]
diff --git a/tests/pytests/integration/ssh/test_pre_flight.py b/tests/pytests/integration/ssh/test_pre_flight.py
index 09c65d29430..ac32b8d90fd 100644
--- a/tests/pytests/integration/ssh/test_pre_flight.py
+++ b/tests/pytests/integration/ssh/test_pre_flight.py
@@ -235,6 +235,10 @@ def demote(user_uid, user_gid):
@pytest.mark.slow_test
+@pytest.mark.skipif(
+ bool(salt.utils.path.which("transactional-update")),
+ reason="Skipping on transactional systems",
+)
def test_ssh_pre_flight_perms(salt_ssh_cli, caplog, _create_roster, account):
"""
Test to ensure standard user cannot run pre flight script
diff --git a/tests/pytests/integration/ssh/test_ssh_setup.py b/tests/pytests/integration/ssh/test_ssh_setup.py
index 97494bed36b..3b4cede85f8 100644
--- a/tests/pytests/integration/ssh/test_ssh_setup.py
+++ b/tests/pytests/integration/ssh/test_ssh_setup.py
@@ -161,6 +161,7 @@ def salt_ssh_cli(
)
+@pytest.mark.flaky_jail
def test_setup(salt_ssh_cli, ssh_container_name, ssh_sub_container_name, ssh_password):
"""
Test salt-ssh setup works
diff --git a/tests/pytests/scenarios/setup/test_man.py b/tests/pytests/scenarios/setup/test_man.py
index 28f0d6285a3..f10fe711f2d 100644
--- a/tests/pytests/scenarios/setup/test_man.py
+++ b/tests/pytests/scenarios/setup/test_man.py
@@ -9,6 +9,9 @@ import pytest
import salt.utils.platform
from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES
+from tests.conftest import CODE_DIR
+
+MISSING_SETUP_PY_FILE = not CODE_DIR.joinpath("setup.py").exists()
pytestmark = [
pytest.mark.core_test,
@@ -16,6 +19,9 @@ pytestmark = [
pytest.mark.skip_on_aix,
pytest.mark.skip_initial_onedir_failure,
pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False),
+ pytest.mark.skipif(
+ MISSING_SETUP_PY_FILE, reason="This test only work if setup.py is available"
+ ),
]
diff --git a/tests/pytests/unit/modules/dockermod/test_module.py b/tests/pytests/unit/modules/dockermod/test_module.py
index 1ac7dff52a5..a87d1add167 100644
--- a/tests/pytests/unit/modules/dockermod/test_module.py
+++ b/tests/pytests/unit/modules/dockermod/test_module.py
@@ -357,7 +357,7 @@ def test_update_mine():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_list_networks():
@@ -381,7 +381,7 @@ def test_list_networks():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_create_network():
@@ -425,7 +425,7 @@ def test_create_network():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_remove_network():
@@ -447,7 +447,7 @@ def test_remove_network():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_inspect_network():
@@ -469,7 +469,7 @@ def test_inspect_network():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_connect_container_to_network():
@@ -494,7 +494,7 @@ def test_connect_container_to_network():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_disconnect_container_from_network():
@@ -516,7 +516,7 @@ def test_disconnect_container_from_network():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_list_volumes():
@@ -542,7 +542,7 @@ def test_list_volumes():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_create_volume():
@@ -572,7 +572,7 @@ def test_create_volume():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_remove_volume():
@@ -594,7 +594,7 @@ def test_remove_volume():
@pytest.mark.skipif(
- docker_mod.docker.version_info < (1, 5, 0),
+ docker_mod._get_docker_py_versioninfo() < (1, 5, 0),
reason="docker module must be installed to run this test or is too old. >=1.5.0",
)
def test_inspect_volume():
diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py
index 6e5ca88895f..bd67b16745c 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -2602,6 +2602,7 @@ pattern() = package-c"""
zypp_mock.assert_called_with(root=None, ignore_not_found=True)
xml_mock.nolock.noraise.xml.call.assert_called_with("search", "emacs")
+ @patch("salt.utils.files.is_fcntl_available", MagicMock(return_value=False))
def test_search_not_found(self):
"""Test zypperpkg.search()"""
ret = {
diff --git a/tests/unit/utils/test_schema.py b/tests/unit/utils/test_schema.py
index 113c6836e07..a531dd93111 100644
--- a/tests/unit/utils/test_schema.py
+++ b/tests/unit/utils/test_schema.py
@@ -531,7 +531,9 @@ class ConfigTestCase(TestCase):
jsonschema.validate(
{"personal_access_token": "foo"}, Requirements.serialize()
)
- if JSONSCHEMA_VERSION >= Version("3.0.0"):
+ if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version(
+ "4.8.0"
+ ):
self.assertIn(
"'ssh_key_file' is a required property", excinfo.exception.message
)
@@ -899,13 +901,20 @@ class ConfigTestCase(TestCase):
except jsonschema.exceptions.ValidationError as exc:
self.fail("ValidationError raised: {}".format(exc))
- with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
+ if JSONSCHEMA_VERSION < Version("4.0.0"):
+ with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
+ jsonschema.validate(
+ {"item": "3"},
+ TestConf.serialize(),
+ format_checker=jsonschema.FormatChecker(),
+ )
+ self.assertIn("is not a", excinfo.exception.message)
+ else:
jsonschema.validate(
{"item": "3"},
TestConf.serialize(),
format_checker=jsonschema.FormatChecker(),
)
- self.assertIn("is not a", excinfo.exception.message)
def test_datetime_config(self):
item = schema.DateTimeItem(title="Foo", description="Foo Item")
@@ -1878,7 +1887,9 @@ class ConfigTestCase(TestCase):
jsonschema.validate(
{"item": {"sides": "4", "color": "blue"}}, TestConf.serialize()
)
- if JSONSCHEMA_VERSION >= Version("3.0.0"):
+ if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version(
+ "4.8.0"
+ ):
self.assertIn("'4'", excinfo.exception.message)
self.assertIn("is not of type", excinfo.exception.message)
self.assertIn("'boolean'", excinfo.exception.message)
@@ -2003,7 +2014,9 @@ class ConfigTestCase(TestCase):
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({"item": ["maybe"]}, TestConf.serialize())
- if JSONSCHEMA_VERSION >= Version("3.0.0"):
+ if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version(
+ "4.8.0"
+ ):
self.assertIn("'maybe'", excinfo.exception.message)
self.assertIn("is not one of", excinfo.exception.message)
self.assertIn("'yes'", excinfo.exception.message)
@@ -2067,7 +2080,9 @@ class ConfigTestCase(TestCase):
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({"item": ["maybe"]}, TestConf.serialize())
- if JSONSCHEMA_VERSION >= Version("3.0.0"):
+ if JSONSCHEMA_VERSION >= Version("3.0.0") and JSONSCHEMA_VERSION < Version(
+ "4.8.0"
+ ):
self.assertIn("'maybe'", excinfo.exception.message)
self.assertIn("is not one of", excinfo.exception.message)
self.assertIn("'yes'", excinfo.exception.message)
@@ -2154,11 +2169,17 @@ class ConfigTestCase(TestCase):
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({"item": [True]}, TestConf.serialize())
- self.assertIn("is not allowed for", excinfo.exception.message)
+ if JSONSCHEMA_VERSION >= Version("4.0.0"):
+ self.assertIn("should not be valid under", excinfo.exception.message)
+ else:
+ self.assertIn("is not allowed for", excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({"item": [False]}, TestConf.serialize())
- self.assertIn("is not allowed for", excinfo.exception.message)
+ if JSONSCHEMA_VERSION >= Version("4.0.0"):
+ self.assertIn("should not be valid under", excinfo.exception.message)
+ else:
+ self.assertIn("is not allowed for", excinfo.exception.message)
def test_item_name_override_class_attrname(self):
class TestConf(schema.Schema):
--
2.44.0