From 3af65b2f452ae765cde4916507350b8c59ea2aea Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 30 Sep 2024 11:12:26 +0800 Subject: [PATCH] feat(api): add version comparison logic (#8902) --- api/controllers/console/version.py | 49 +++++++++++++++++-- .../controllers/test_compare_versions.py | 38 ++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 api/tests/unit_tests/controllers/test_compare_versions.py diff --git a/api/controllers/console/version.py b/api/controllers/console/version.py index 76adbfe6a..deda1a0d0 100644 --- a/api/controllers/console/version.py +++ b/api/controllers/console/version.py @@ -38,11 +38,52 @@ class VersionApi(Resource): return result content = json.loads(response.content) - result["version"] = content["version"] - result["release_date"] = content["releaseDate"] - result["release_notes"] = content["releaseNotes"] - result["can_auto_update"] = content["canAutoUpdate"] + if _has_new_version(latest_version=content["version"], current_version=f"{args.get('current_version')}"): + result["version"] = content["version"] + result["release_date"] = content["releaseDate"] + result["release_notes"] = content["releaseNotes"] + result["can_auto_update"] = content["canAutoUpdate"] return result +def _has_new_version(*, latest_version: str, current_version: str) -> bool: + def parse_version(version: str) -> tuple: + # Split version into parts and pre-release suffix if any + parts = version.split("-") + version_parts = parts[0].split(".") + pre_release = parts[1] if len(parts) > 1 else None + + # Validate version format + if len(version_parts) != 3: + raise ValueError(f"Invalid version format: {version}") + + try: + # Convert version parts to integers + major, minor, patch = map(int, version_parts) + return (major, minor, patch, pre_release) + except ValueError: + raise ValueError(f"Invalid version format: {version}") + + latest = parse_version(latest_version) + current = parse_version(current_version) + + # Compare major, minor, and patch versions + for latest_part, current_part in zip(latest[:3], current[:3]): + if latest_part > current_part: + return True + elif latest_part < current_part: + return False + + # If versions are equal, check pre-release suffixes + if latest[3] is None and current[3] is not None: + return True + elif latest[3] is not None and current[3] is None: + return False + elif latest[3] is not None and current[3] is not None: + # Simple string comparison for pre-release versions + return latest[3] > current[3] + + return False + + api.add_resource(VersionApi, "/version") diff --git a/api/tests/unit_tests/controllers/test_compare_versions.py b/api/tests/unit_tests/controllers/test_compare_versions.py new file mode 100644 index 000000000..87902b6d4 --- /dev/null +++ b/api/tests/unit_tests/controllers/test_compare_versions.py @@ -0,0 +1,38 @@ +import pytest + +from controllers.console.version import _has_new_version + + +@pytest.mark.parametrize( + ("latest_version", "current_version", "expected"), + [ + ("1.0.1", "1.0.0", True), + ("1.1.0", "1.0.0", True), + ("2.0.0", "1.9.9", True), + ("1.0.0", "1.0.0", False), + ("1.0.0", "1.0.1", False), + ("1.0.0", "2.0.0", False), + ("1.0.1", "1.0.0-beta", True), + ("1.0.0", "1.0.0-alpha", True), + ("1.0.0-beta", "1.0.0-alpha", True), + ("1.0.0", "1.0.0-rc1", True), + ("1.0.0", "0.9.9", True), + ("1.0.0", "1.0.0-dev", True), + ], +) +def test_has_new_version(latest_version, current_version, expected): + assert _has_new_version(latest_version=latest_version, current_version=current_version) == expected + + +def test_has_new_version_invalid_input(): + with pytest.raises(ValueError): + _has_new_version(latest_version="1.0", current_version="1.0.0") + + with pytest.raises(ValueError): + _has_new_version(latest_version="1.0.0", current_version="1.0") + + with pytest.raises(ValueError): + _has_new_version(latest_version="invalid", current_version="1.0.0") + + with pytest.raises(ValueError): + _has_new_version(latest_version="1.0.0", current_version="invalid")