diff --git a/src/test/unit/web_interface/test_filter.py b/src/test/unit/web_interface/test_filter.py index a415b0633..9c76284d1 100644 --- a/src/test/unit/web_interface/test_filter.py +++ b/src/test/unit/web_interface/test_filter.py @@ -4,6 +4,7 @@ from zlib import compress import pytest +from semver import Version import web_interface.filter as flt from test.common_helper import create_test_file_object @@ -578,3 +579,45 @@ def test_sort_dict_list_by_key(input_, expected_result): ) def test_sort_ip_list(input_, expected_result): assert flt.sort_ip_list(input_) == expected_result + + +@pytest.mark.parametrize( + ('version', 'other_version', 'expected_result', 'forgiving'), + [ + ('1.2.3', '1.2.3', True, False), + ('1.2.3', '1.2.4', True, False), + ('1.2.3', '1.3.4', True, False), + ('1.2.3', '2.3.4', False, False), + # if both versions are not semver compatible, they are only considered compatible if equal + ('1.2', '1.2', True, False), + ('1.2', '1.3', False, False), + # with forgiving=True versions not strictly compatible with semver should still work + ('1.2', '1.2.3', True, True), + ('1.2', '1.2.3', False, False), + # below 1.0.0 the versions are considered incompatible + ('0.1.2', '0.2.3', False, False), + # the semver Version class should also work + (Version(1, 2, 3), '1.2.4', True, False), + ('1.2.3', Version(1, 2, 4), True, False), + (Version(1, 2, 3), Version(1, 2, 4), True, False), + ], +) +def test_version_is_compatible(version, other_version, expected_result, forgiving): + assert flt.version_is_compatible(version, other_version, forgiving=forgiving) == expected_result + + +def test_version_is_compatible_error(): + with pytest.raises(ValueError, match='not a valid version'): + assert flt.version_is_compatible('1.2.3', 'a.b.c') + + +@pytest.mark.parametrize( + ('input_', 'expected_result'), + [ + (None, None), + ('foobar', 'foobar'), + ({'foo': 'bar'}, '{\n "foo": "bar"\n}'), + ], +) +def test_render_query_title(input_, expected_result): + assert flt.render_query_title(input_) == expected_result diff --git a/src/web_interface/filter.py b/src/web_interface/filter.py index 6406e4c34..6d08546a7 100644 --- a/src/web_interface/filter.py +++ b/src/web_interface/filter.py @@ -16,12 +16,13 @@ from re import Match from string import ascii_letters from time import localtime, strftime, struct_time, time -from typing import TYPE_CHECKING, Any, Iterable, Union +from typing import TYPE_CHECKING, Any, Iterable import packaging.version import semver from common_helper_files import human_readable_file_size from flask import render_template +from semver import Version from helperFunctions.compare_sets import remove_duplicates_from_list from helperFunctions.data_conversion import make_unicode_string @@ -550,55 +551,47 @@ def get_searchable_crypto_block(crypto_material: str) -> str: return sorted(blocks, key=len, reverse=True)[0] -def version_is_compatible( - version: Union[str, semver.Version], - other: Union[str, semver.Version], - forgiving: bool = False, -) -> bool: - """A warpper around ``semver.Version.is_compatible`` that allows non semver versions. - If :paramref:`forgiving` is True non semver versions will try to be coerced to semver versions. +def version_is_compatible(version: str | Version, other: str | Version, forgiving: bool = False) -> bool: + """A wrapper around ``semver.Version.is_compatible`` that allows non semver versions. + If :paramref:`forgiving` is True, non semver versions will try to be coerced to semver versions. If this does not succeed or :paramref:`forgiving` is False then any semver version will be considered incompatible to any other non semver version. So for example '1.1.0' would not be compatible '1.2' if forgiving is False. - Otherwise it would be coerced from '1.2' to '1.2.0'. + Otherwise, it would be coerced from '1.2' to '1.2.0'. If both versions are not semver they are only compatible if they are equal. - :param version: The version to check compatiblity for. + :param version: The version to check compatibility for. :param other: The version to compare to. + :param forgiving: Whether the version check is forgiving with versions that are not compatible to semver.. :return: If :paramref:`version` is compatible with :paramref:`other`. :raises ValueError: If both versions are neither semver nor ``packaging.version.Version`` versions. """ - version_is_semver = True try: - if isinstance(version, str): - version = semver.Version.parse(version) - except ValueError: - version_is_semver = forgiving - version = _coerce_version(version) - - other_is_semver = True - try: - if isinstance(other, str): - other = semver.Version.parse(other) - except ValueError: - other_is_semver = forgiving - other = _coerce_version(other) + version, version_is_semver = _convert_to_semver(version, forgiving) + other, other_is_semver = _convert_to_semver(other, forgiving) + except packaging.version.InvalidVersion as invalid_version: + raise ValueError(f'Either {version} or {other} is not a valid version') from invalid_version if version_is_semver ^ other_is_semver: return False - if not version_is_semver and not other_is_semver: - try: - return packaging.version.Version(version) == packaging.version.Version(other) - except packaging.version.InvalidVersion as invalid_version: - raise ValueError from invalid_version + return version == other return version.is_compatible(other) +def _convert_to_semver(version: str | Version, forgiving: bool) -> tuple[Version, bool]: + try: + if isinstance(version, str): + version = Version.parse(version) + return version, True + except ValueError: + return _coerce_version(version), forgiving + + def _coerce_version(version: str) -> semver.Version: coerced = packaging.version.Version(version) return semver.Version(