Skip to content

Commit

Permalink
feat: Implement VersionCompare evaluator for version string comparisons
Browse files Browse the repository at this point in the history
Added Version Compare evaluator
  • Loading branch information
AkashS0510 committed Dec 12, 2024
1 parent 32eace9 commit 051b181
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/tirith/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ def generate_evaluator_result(evaluator_obj, input_data, provider_module):
provider_inputs = evaluator_obj.get("provider_args")
condition = evaluator_obj.get("condition")
evaluator_name: str = condition.get("type")
evaluator_data = condition.get("value")
if evaluator_name is "VersionCompare":
evaluator_data = { "value": condition.get("value"), "operation": condition.get("operation") }
else:
evaluator_data = condition.get("value")
evaluator_error_tolerance: int = condition.get("error_tolerance", DEFAULT_ERROR_TOLERANCE)

if not condition:
Expand Down
2 changes: 2 additions & 0 deletions src/tirith/core/evaluators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .not_equals import NotEquals
from .not_contained_in import NotContainedIn
from .not_contains import NotContains
from .version_compare import VersionCompare

EVALUATORS_DICT: Dict[str, Type[BaseEvaluator]] = {
"ContainedIn": ContainedIn,
Expand All @@ -29,4 +30,5 @@
"NotEquals": NotEquals,
"NotContainedIn": NotContainedIn,
"NotContains": NotContains,
"VersionCompare": VersionCompare
}
79 changes: 79 additions & 0 deletions src/tirith/core/evaluators/version_compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from .base_evaluator import BaseEvaluator
from packaging.version import Version
import re


# Compares two version strings based on the provided operation.

# Args:
# evaluator_input (str): The first version string to compare.
# evaluator_data (str): The second version string to compare.
# operation (str): The comparison operation to perform.
# Supported values: "LessThan", "LessThanOrEquals", "Equals", "GreaterThan", "GreaterThanOrEquals".

# Returns:
# dict: A dictionary containing the comparison result (`passed`) and a descriptive message.

# Example:
#
# >>> comparator = VersionCompare()
# >>> comparator.evaluate("1.0.0", {"value": "1.0.1", "operation": "lessThan"})
# {'passed': True, 'message': '1.0.0 is less than 1.0.1'}
# >>> comparator.evaluate("1.0.0", {"value": "1.0.0", "operation": "equal"})
# {'passed': True, 'message': '1.0.0 is equal to 1.0.0'}
# >>> comparator.evaluate("2.0.0", {"value": "1.0.0", "operation": "greaterThan"})
# {'passed': True, 'message': '2.0.0 is greater than 1.0.0'}


# .. versionadded:: 1.0.0



class VersionCompare(BaseEvaluator):
"""Compares two version strings based on the provided operation."""

def parse_version(self, version_string):
"""Parses a version string and returns a comparable version object."""
# Extract the version part from the string
match = re.search(r'[\d]+(\.[\d]+)*(\-[a-zA-Z0-9]+)?$', version_string)
if match:
try:
return Version(match.group(0))
except Exception as e:
raise ValueError(f"Invalid version format: {version_string}. Error: {str(e)}")
else:
raise ValueError(f"Invalid version format: {version_string}")

def evaluate(self, evaluator_input, evaluator_data):
"""Compares two version strings based on the provided operation."""
evaluation_result = {"passed": False, "message": "Not evaluated"}
try:
v1 = self.parse_version(evaluator_input)
v2 = self.parse_version(evaluator_data['value'])
operation = evaluator_data['operation']

if operation == "LessThan":
evaluation_result["passed"] = v1 < v2
elif operation == "LessThanOrEquals":
evaluation_result["passed"] = v1 <= v2
elif operation == "Equals":
evaluation_result["passed"] = v1 == v2
elif operation == "GreaterThan":
evaluation_result["passed"] = v1 > v2
elif operation == "GreaterThanOrEquals":
evaluation_result["passed"] = v1 >= v2

else:
raise ValueError(f"Unsupported operation: {operation}")

if evaluation_result["passed"]:
evaluation_result["message"] = f"{evaluator_input} is {operation.replace('_', ' ')} {evaluator_data}"
else:
evaluation_result["message"] = f"{evaluator_input} is not {operation.replace('_', ' ')} {evaluator_data}"

return evaluation_result

except ValueError as e:
evaluation_result["message"] = str(e)
return evaluation_result

45 changes: 45 additions & 0 deletions tests/core/evaluators/test_version_compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from tirith.core.evaluators import VersionCompare
from pytest import mark

# Define test cases
checks_passing = [
("1.0.0", {"value": "1.0.1", "operation": "LessThan"}),
("1.0.0", {"value": "1.0.0", "operation": "Equals"}),
("2.0.0", {"value": "1.0.0", "operation": "GreaterThan"}),
("1.0.0", {"value": "1.1.0", "operation": "LessThanOrEquals"}),
("1.1.0", {"value": "1.1.0", "operation": "LessThanOrEquals"}),
("1.2.0", {"value": "1.1.0", "operation": "GreaterThanOrEquals"}),
("1.1.0", {"value": "1.1.0", "operation": "GreaterThanOrEquals"}),
]

checks_failing = [
("1.0.0", {"value": "1.0.1", "operation": "GreaterThan"}),
("1.0.0", {"value": "1.0.0", "operation": "LessThan"}),
("1.0.1", {"value": "1.0.0", "operation": "LessThan"}),
("1.1.0", {"value": "1.0.0", "operation": "Equals"}),
]


evaluator = VersionCompare()

# pytest -v -m passing
@mark.passing
@mark.parametrize("evaluator_input,evaluator_data", checks_passing)
def test_evaluate_passing(evaluator_input, evaluator_data):
result = evaluator.evaluate(evaluator_input, evaluator_data)
operation = evaluator_data["operation"]
assert result == {
"passed": True,
"message": f"{evaluator_input} is {operation} {evaluator_data['value']}"
}

# pytest -v -m failing
@mark.failing
@mark.parametrize("evaluator_input,evaluator_data", checks_failing)
def test_evaluate_failing(evaluator_input, evaluator_data):
result = evaluator.evaluate(evaluator_input, evaluator_data)
operation = evaluator_data["operation"]
assert result == {
"passed": False,
"message": f"{evaluator_input} is not {operation} {evaluator_data['value']}"
}

0 comments on commit 051b181

Please sign in to comment.