diff --git a/RUFAS/data_validator.py b/RUFAS/data_validator.py index b1716fb772..17e466986e 100644 --- a/RUFAS/data_validator.py +++ b/RUFAS/data_validator.py @@ -1709,7 +1709,6 @@ def extract_value_by_key_list( >>> DataValidator.extract_value_by_key_list(example_data, var_path) 'straw' """ - for key in variable_path: if isinstance(data, list) and 0 <= int(key) < len(data): data = data[int(key)] @@ -1748,6 +1747,9 @@ def __init__(self) -> None: "is_of_type": lambda left, right, eager_termination: self._evaluate_is_type(left, right, eager_termination), "is_null": lambda left, _right, _eager_termination: self._evaluate_is_null(left), "regex": lambda left, right, _eager_termination: self._evaluate_regex(left, right), + "is_equal_length": lambda left, right, _eager_termination: self._evaluate_equal_data_length( + left, right, _eager_termination + ), } def cross_validate_data( @@ -2170,6 +2172,25 @@ def _validate_relationship(self, relationship: Any, eager_termination: bool) -> else: return True + def _evaluate_equal_data_length(self, left_hand_value: Any, right_hand_value: Any, eager_termination: bool) -> bool: + """Evaluates if data lengths matches.""" + if not (isinstance(left_hand_value, list) and isinstance(right_hand_value, list)): + self._event_logs.append( + { + "error": "Invalid data length validation", + "message": "Both data have to be list type to validate their length.", + "info_map": { + "class": self.__class__.__name__, + "function": self._evaluate_equal_data_length.__name__, + }, + } + ) + if eager_termination: + raise ValueError("Cross-validation error: Invalid type comparison.") + return False + else: + return len(left_hand_value) == len(right_hand_value) + def _evaluate_equal_condition(self, left_hand_value: Any, right_hand_value: Any) -> bool: """Evaluates equal condition.""" return bool(left_hand_value == right_hand_value) @@ -2184,7 +2205,6 @@ def _evaluate_is_null(self, left_hand_value: Any) -> bool: def _evaluate_is_type(self, left_hand_value: Any, data_type: Any, eager_termination: bool) -> bool: """Evaluates the if_type condition""" - # TODO: Remove these type checks when cross validation inputs' validation is implemented - issue #2615 if not isinstance(data_type[0], str): self._event_logs.append( { diff --git a/changelog.md b/changelog.md index b1500f223d..41cb37f6d3 100644 --- a/changelog.md +++ b/changelog.md @@ -60,10 +60,10 @@ v1.0.0 - [2921](https://github.com/RuminantFarmSystems/RuFaS/pull/2921) - [minor change] [OutputManager][NoInputChange] [NoOutputChange] Override incorrect fill type for complex data structure data padding. - [2924](https://github.com/RuminantFarmSystems/RuFaS/pull/2924) - [minor change] [NoInputChange] [NoOutputChange] Updated advance purchase allowance to prevent excessive warnings for example run. - [2929](https://github.com/RuminantFarmSystems/RuFaS/pull/2929) - [minor change] [GraphGenerator] [NoInputChange] [NoOutputChange] Sanitizes non-numerical data sent to graph generator to allow graphing to occur despite. +- [2916](https://github.com/RuminantFarmSystems/RuFaS/pull/2916) - [minor change] [Corss Validation] [NoInputChange] [NoOutputChange] Added cross validation rules for the Crop and Soil module. - [2925](https://github.com/RuminantFarmSystems/RuFaS/pull/2925) - [minor change] [NoInputChange] [NoOutputChange] Fix the `graph_and_report` option in report_generation.py. - [2907](https://github.com/RuminantFarmSystems/RuFaS/pull/2907) - [minor change] [NoInputChange] [OutputChange] Fix the FarmGrownFeed Emissions unit issue. The mirror issue of [Fix FarmGrownFeed Emissions Unit on test #2908](https://github.com/RuminantFarmSystems/MASM/pull/2908) to update `dev`. - ### v1.0.0 - [2081](https://github.com/RuminantFarmSystems/RuFaS/pull/2081) - [minor change] [Crop & Soil] Break down the `_setup_field()` function in `FieldManager`. diff --git a/input/data/crop/example_alf_corn_silage_rotation.json b/input/data/crop/example_alf_corn_silage_rotation.json index 6b53739c95..d1e7b88b20 100644 --- a/input/data/crop/example_alf_corn_silage_rotation.json +++ b/input/data/crop/example_alf_corn_silage_rotation.json @@ -1,6 +1,6 @@ {"crop_schedules": [ - + { "crop_species": "corn_silage", "planting_days": [ diff --git a/input/metadata/cross_validation/crop_and_soil_cross_validation.json b/input/metadata/cross_validation/crop_and_soil_cross_validation.json new file mode 100644 index 0000000000..f9d61dea89 --- /dev/null +++ b/input/metadata/cross_validation/crop_and_soil_cross_validation.json @@ -0,0 +1,1534 @@ +{ + "cross-validation": [ + { + "description": "alfalfa_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "alfalfa_silage_minimum_temperature": "crop_configurations.crop_configurations.0.minimum_temperature", + "alfalfa_silage_optimal_temperature": "crop_configurations.crop_configurations.0.optimal_temperature", + "alfalfa_silage_minimum_harvest_index": "crop_configurations.crop_configurations.0.minimum_harvest_index", + "alfalfa_silage_optimal_harvest_index": "crop_configurations.crop_configurations.0.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "alfalfa_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "alfalfa_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "alfalfa_baleage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "alfalfa_baleage_minimum_temperature": "crop_configurations.crop_configurations.1.minimum_temperature", + "alfalfa_baleage_optimal_temperature": "crop_configurations.crop_configurations.1.optimal_temperature", + "alfalfa_baleage_minimum_harvest_index": "crop_configurations.crop_configurations.1.minimum_harvest_index", + "alfalfa_baleage_optimal_harvest_index": "crop_configurations.crop_configurations.1.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "alfalfa_baleage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_baleage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "alfalfa_baleage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_baleage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "alfalfa_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "alfalfa_hay_minimum_temperature": "crop_configurations.crop_configurations.2.minimum_temperature", + "alfalfa_hay_optimal_temperature": "crop_configurations.crop_configurations.2.optimal_temperature", + "alfalfa_hay_minimum_harvest_index": "crop_configurations.crop_configurations.2.minimum_harvest_index", + "alfalfa_hay_optimal_harvest_index": "crop_configurations.crop_configurations.2.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "alfalfa_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "alfalfa_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "alfalfa_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "cereal_rye_grain temperature and harvest index bounds", + "target_and_save": { + "variables": { + "cereal_rye_grain_minimum_temperature": "crop_configurations.crop_configurations.3.minimum_temperature", + "cereal_rye_grain_optimal_temperature": "crop_configurations.crop_configurations.3.optimal_temperature", + "cereal_rye_grain_minimum_harvest_index": "crop_configurations.crop_configurations.3.minimum_harvest_index", + "cereal_rye_grain_optimal_harvest_index": "crop_configurations.crop_configurations.3.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_grain_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_grain_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_grain_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_grain_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "cereal_rye_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "cereal_rye_silage_minimum_temperature": "crop_configurations.crop_configurations.4.minimum_temperature", + "cereal_rye_silage_optimal_temperature": "crop_configurations.crop_configurations.4.optimal_temperature", + "cereal_rye_silage_minimum_harvest_index": "crop_configurations.crop_configurations.4.minimum_harvest_index", + "cereal_rye_silage_optimal_harvest_index": "crop_configurations.crop_configurations.4.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "cereal_rye_baleage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "cereal_rye_baleage_minimum_temperature": "crop_configurations.crop_configurations.5.minimum_temperature", + "cereal_rye_baleage_optimal_temperature": "crop_configurations.crop_configurations.5.optimal_temperature", + "cereal_rye_baleage_minimum_harvest_index": "crop_configurations.crop_configurations.5.minimum_harvest_index", + "cereal_rye_baleage_optimal_harvest_index": "crop_configurations.crop_configurations.5.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_baleage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_baleage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_baleage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_baleage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "cereal_rye_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "cereal_rye_hay_minimum_temperature": "crop_configurations.crop_configurations.6.minimum_temperature", + "cereal_rye_hay_optimal_temperature": "crop_configurations.crop_configurations.6.optimal_temperature", + "cereal_rye_hay_minimum_harvest_index": "crop_configurations.crop_configurations.6.minimum_harvest_index", + "cereal_rye_hay_optimal_harvest_index": "crop_configurations.crop_configurations.6.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "cereal_rye_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "cereal_rye_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "corn_grain temperature and harvest index bounds", + "target_and_save": { + "variables": { + "corn_grain_minimum_temperature": "crop_configurations.crop_configurations.7.minimum_temperature", + "corn_grain_optimal_temperature": "crop_configurations.crop_configurations.7.optimal_temperature", + "corn_grain_minimum_harvest_index": "crop_configurations.crop_configurations.7.minimum_harvest_index", + "corn_grain_optimal_harvest_index": "crop_configurations.crop_configurations.7.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "corn_grain_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "corn_grain_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "corn_grain_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "corn_grain_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "corn_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "corn_silage_minimum_temperature": "crop_configurations.crop_configurations.8.minimum_temperature", + "corn_silage_optimal_temperature": "crop_configurations.crop_configurations.8.optimal_temperature", + "corn_silage_minimum_harvest_index": "crop_configurations.crop_configurations.8.minimum_harvest_index", + "corn_silage_optimal_harvest_index": "crop_configurations.crop_configurations.8.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "corn_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "corn_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "corn_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "corn_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soybean_grain temperature and harvest index bounds", + "target_and_save": { + "variables": { + "soybean_grain_minimum_temperature": "crop_configurations.crop_configurations.9.minimum_temperature", + "soybean_grain_optimal_temperature": "crop_configurations.crop_configurations.9.optimal_temperature", + "soybean_grain_minimum_harvest_index": "crop_configurations.crop_configurations.9.minimum_harvest_index", + "soybean_grain_optimal_harvest_index": "crop_configurations.crop_configurations.9.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "soybean_grain_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "soybean_grain_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "soybean_grain_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "soybean_grain_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soybean_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "soybean_hay_minimum_temperature": "crop_configurations.crop_configurations.10.minimum_temperature", + "soybean_hay_optimal_temperature": "crop_configurations.crop_configurations.10.optimal_temperature", + "soybean_hay_minimum_harvest_index": "crop_configurations.crop_configurations.10.minimum_harvest_index", + "soybean_hay_optimal_harvest_index": "crop_configurations.crop_configurations.10.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "soybean_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "soybean_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "soybean_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "soybean_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "tall_fescue_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "tall_fescue_silage_minimum_temperature": "crop_configurations.crop_configurations.11.minimum_temperature", + "tall_fescue_silage_optimal_temperature": "crop_configurations.crop_configurations.11.optimal_temperature", + "tall_fescue_silage_minimum_harvest_index": "crop_configurations.crop_configurations.11.minimum_harvest_index", + "tall_fescue_silage_optimal_harvest_index": "crop_configurations.crop_configurations.11.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "tall_fescue_baleage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "tall_fescue_baleage_minimum_temperature": "crop_configurations.crop_configurations.12.minimum_temperature", + "tall_fescue_baleage_optimal_temperature": "crop_configurations.crop_configurations.12.optimal_temperature", + "tall_fescue_baleage_minimum_harvest_index": "crop_configurations.crop_configurations.12.minimum_harvest_index", + "tall_fescue_baleage_optimal_harvest_index": "crop_configurations.crop_configurations.12.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_baleage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_baleage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_baleage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_baleage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "tall_fescue_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "tall_fescue_hay_minimum_temperature": "crop_configurations.crop_configurations.13.minimum_temperature", + "tall_fescue_hay_optimal_temperature": "crop_configurations.crop_configurations.13.optimal_temperature", + "tall_fescue_hay_minimum_harvest_index": "crop_configurations.crop_configurations.13.minimum_harvest_index", + "tall_fescue_hay_optimal_harvest_index": "crop_configurations.crop_configurations.13.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "tall_fescue_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "tall_fescue_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "triticale_grain temperature and harvest index bounds", + "target_and_save": { + "variables": { + "triticale_grain_minimum_temperature": "crop_configurations.crop_configurations.14.minimum_temperature", + "triticale_grain_optimal_temperature": "crop_configurations.crop_configurations.14.optimal_temperature", + "triticale_grain_minimum_harvest_index": "crop_configurations.crop_configurations.14.minimum_harvest_index", + "triticale_grain_optimal_harvest_index": "crop_configurations.crop_configurations.14.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "triticale_grain_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_grain_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "triticale_grain_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_grain_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "triticale_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "triticale_silage_minimum_temperature": "crop_configurations.crop_configurations.15.minimum_temperature", + "triticale_silage_optimal_temperature": "crop_configurations.crop_configurations.15.optimal_temperature", + "triticale_silage_minimum_harvest_index": "crop_configurations.crop_configurations.15.minimum_harvest_index", + "triticale_silage_optimal_harvest_index": "crop_configurations.crop_configurations.15.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "triticale_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "triticale_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "triticale_baleage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "triticale_baleage_minimum_temperature": "crop_configurations.crop_configurations.16.minimum_temperature", + "triticale_baleage_optimal_temperature": "crop_configurations.crop_configurations.16.optimal_temperature", + "triticale_baleage_minimum_harvest_index": "crop_configurations.crop_configurations.16.minimum_harvest_index", + "triticale_baleage_optimal_harvest_index": "crop_configurations.crop_configurations.16.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "triticale_baleage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_baleage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "triticale_baleage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_baleage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "triticale_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "triticale_hay_minimum_temperature": "crop_configurations.crop_configurations.17.minimum_temperature", + "triticale_hay_optimal_temperature": "crop_configurations.crop_configurations.17.optimal_temperature", + "triticale_hay_minimum_harvest_index": "crop_configurations.crop_configurations.17.minimum_harvest_index", + "triticale_hay_optimal_harvest_index": "crop_configurations.crop_configurations.17.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "triticale_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "triticale_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "triticale_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "winter_wheat_grain temperature and harvest index bounds", + "target_and_save": { + "variables": { + "winter_wheat_grain_minimum_temperature": "crop_configurations.crop_configurations.18.minimum_temperature", + "winter_wheat_grain_optimal_temperature": "crop_configurations.crop_configurations.18.optimal_temperature", + "winter_wheat_grain_minimum_harvest_index": "crop_configurations.crop_configurations.18.minimum_harvest_index", + "winter_wheat_grain_optimal_harvest_index": "crop_configurations.crop_configurations.18.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_grain_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_grain_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_grain_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_grain_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "winter_wheat_silage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "winter_wheat_silage_minimum_temperature": "crop_configurations.crop_configurations.19.minimum_temperature", + "winter_wheat_silage_optimal_temperature": "crop_configurations.crop_configurations.19.optimal_temperature", + "winter_wheat_silage_minimum_harvest_index": "crop_configurations.crop_configurations.19.minimum_harvest_index", + "winter_wheat_silage_optimal_harvest_index": "crop_configurations.crop_configurations.19.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_silage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_silage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_silage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_silage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "winter_wheat_baleage temperature and harvest index bounds", + "target_and_save": { + "variables": { + "winter_wheat_baleage_minimum_temperature": "crop_configurations.crop_configurations.20.minimum_temperature", + "winter_wheat_baleage_optimal_temperature": "crop_configurations.crop_configurations.20.optimal_temperature", + "winter_wheat_baleage_minimum_harvest_index": "crop_configurations.crop_configurations.20.minimum_harvest_index", + "winter_wheat_baleage_optimal_harvest_index": "crop_configurations.crop_configurations.20.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_baleage_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_baleage_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_baleage_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_baleage_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "winter_wheat_hay temperature and harvest index bounds", + "target_and_save": { + "variables": { + "winter_wheat_hay_minimum_temperature": "crop_configurations.crop_configurations.21.minimum_temperature", + "winter_wheat_hay_optimal_temperature": "crop_configurations.crop_configurations.21.optimal_temperature", + "winter_wheat_hay_minimum_harvest_index": "crop_configurations.crop_configurations.21.minimum_harvest_index", + "winter_wheat_hay_optimal_harvest_index": "crop_configurations.crop_configurations.21.optimal_harvest_index" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_hay_optimal_temperature" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_hay_minimum_temperature" + ] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": [ + "winter_wheat_hay_optimal_harvest_index" + ] + }, + "right_hand": { + "ordered_variables": [ + "winter_wheat_hay_minimum_harvest_index" + ] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "Corn-Alf-Silage crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "Corn-Alf-Silage_schedule_0_planting_years": "Corn-Alf-Silage.crop_schedules.0.planting_years", + "Corn-Alf-Silage_schedule_0_planting_days": "Corn-Alf-Silage.crop_schedules.0.planting_days", + "Corn-Alf-Silage_schedule_0_harvest_years": "Corn-Alf-Silage.crop_schedules.0.harvest_years", + "Corn-Alf-Silage_schedule_0_harvest_days": "Corn-Alf-Silage.crop_schedules.0.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_0_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_0_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_0_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_0_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "Corn-Alf-Silage crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "Corn-Alf-Silage_schedule_1_planting_years": "Corn-Alf-Silage.crop_schedules.1.planting_years", + "Corn-Alf-Silage_schedule_1_planting_days": "Corn-Alf-Silage.crop_schedules.1.planting_days", + "Corn-Alf-Silage_schedule_1_harvest_years": "Corn-Alf-Silage.crop_schedules.1.harvest_years", + "Corn-Alf-Silage_schedule_1_harvest_days": "Corn-Alf-Silage.crop_schedules.1.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_1_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_1_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_1_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_1_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "Corn-Alf-Silage crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "Corn-Alf-Silage_schedule_2_planting_years": "Corn-Alf-Silage.crop_schedules.2.planting_years", + "Corn-Alf-Silage_schedule_2_planting_days": "Corn-Alf-Silage.crop_schedules.2.planting_days", + "Corn-Alf-Silage_schedule_2_harvest_years": "Corn-Alf-Silage.crop_schedules.2.harvest_years", + "Corn-Alf-Silage_schedule_2_harvest_days": "Corn-Alf-Silage.crop_schedules.2.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_2_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_2_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_2_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_2_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "Corn-Alf-Silage crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "Corn-Alf-Silage_schedule_3_planting_years": "Corn-Alf-Silage.crop_schedules.3.planting_years", + "Corn-Alf-Silage_schedule_3_planting_days": "Corn-Alf-Silage.crop_schedules.3.planting_days", + "Corn-Alf-Silage_schedule_3_harvest_years": "Corn-Alf-Silage.crop_schedules.3.harvest_years", + "Corn-Alf-Silage_schedule_3_harvest_days": "Corn-Alf-Silage.crop_schedules.3.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_3_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_3_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_3_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "Corn-Alf-Silage_schedule_3_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "CornGrain-AlfHay crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "CornGrain-AlfHay_schedule_0_planting_years": "CornGrain-AlfHay.crop_schedules.0.planting_years", + "CornGrain-AlfHay_schedule_0_planting_days": "CornGrain-AlfHay.crop_schedules.0.planting_days", + "CornGrain-AlfHay_schedule_0_harvest_years": "CornGrain-AlfHay.crop_schedules.0.harvest_years", + "CornGrain-AlfHay_schedule_0_harvest_days": "CornGrain-AlfHay.crop_schedules.0.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_0_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_0_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_0_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_0_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "CornGrain-AlfHay crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "CornGrain-AlfHay_schedule_1_planting_years": "CornGrain-AlfHay.crop_schedules.1.planting_years", + "CornGrain-AlfHay_schedule_1_planting_days": "CornGrain-AlfHay.crop_schedules.1.planting_days", + "CornGrain-AlfHay_schedule_1_harvest_years": "CornGrain-AlfHay.crop_schedules.1.harvest_years", + "CornGrain-AlfHay_schedule_1_harvest_days": "CornGrain-AlfHay.crop_schedules.1.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_1_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_1_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_1_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_1_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "CornGrain-AlfHay crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "CornGrain-AlfHay_schedule_2_planting_years": "CornGrain-AlfHay.crop_schedules.2.planting_years", + "CornGrain-AlfHay_schedule_2_planting_days": "CornGrain-AlfHay.crop_schedules.2.planting_days", + "CornGrain-AlfHay_schedule_2_harvest_years": "CornGrain-AlfHay.crop_schedules.2.harvest_years", + "CornGrain-AlfHay_schedule_2_harvest_days": "CornGrain-AlfHay.crop_schedules.2.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_2_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_2_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_2_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_2_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "CornGrain-AlfHay crop schedules have equal planting and harvesting year/day lengths", + "target_and_save": { + "variables": { + "CornGrain-AlfHay_schedule_3_planting_years": "CornGrain-AlfHay.crop_schedules.3.planting_years", + "CornGrain-AlfHay_schedule_3_planting_days": "CornGrain-AlfHay.crop_schedules.3.planting_days", + "CornGrain-AlfHay_schedule_3_harvest_years": "CornGrain-AlfHay.crop_schedules.3.harvest_years", + "CornGrain-AlfHay_schedule_3_harvest_days": "CornGrain-AlfHay.crop_schedules.3.harvest_days" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_3_planting_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_3_planting_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + }, + { + "left_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_3_harvest_years" + ], + "apply_to": "individual" + }, + "right_hand": { + "ordered_variables": [ + "CornGrain-AlfHay_schedule_3_harvest_days" + ], + "apply_to": "individual" + }, + "relationship": "is_equal_length" + } + ] + }, + { + "description": "field_1 watering amount must be zero when watering interval is zero", + "target_and_save": { + "variables": { + "watering_amount_in_liters": "field_1.watering_amount_in_liters", + "watering_interval": "field_1.watering_interval" + }, + "constants": { + "zero": 0 + } + }, + "apply_when": [ + { + "left_hand": { + "ordered_variables": ["watering_interval"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "equal" + } + ], + "rules": [ + { + "left_hand": { + "ordered_variables": ["watering_amount_in_liters"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "equal" + } + ] + }, + { + "description": "field_1 watering interval must be greater than zero when watering amount is positive", + "apply_when": [ + { + "left_hand": { + "ordered_variables": ["watering_amount_in_liters"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "greater" + } + ], + "rules": [ + { + "left_hand": { + "ordered_variables": ["watering_interval"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "greater" + } + ] + }, + { + "description": "field_2 watering amount must be zero when watering interval is zero", + "target_and_save": { + "variables": { + "watering_amount_in_liters": "field_2.watering_amount_in_liters", + "watering_interval": "field_2.watering_interval" + }, + "constants": { + "zero": 0 + } + }, + "apply_when": [ + { + "left_hand": { + "ordered_variables": ["watering_interval"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "equal" + } + ], + "rules": [ + { + "left_hand": { + "ordered_variables": ["watering_amount_in_liters"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "equal" + } + ] + }, + { + "description": "field_2 watering interval must be greater than zero when watering amount is positive", + "apply_when": [ + { + "left_hand": { + "ordered_variables": ["watering_amount_in_liters"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "greater" + } + ], + "rules": [ + { + "left_hand": { + "ordered_variables": ["watering_interval"] + }, + "right_hand": { + "ordered_variables": ["zero"] + }, + "relationship": "greater" + } + ] + }, + { + "description": "soil_1 layer 0 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_1.soil_layers.0.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_1.soil_layers.0.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_1.soil_layers.0.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_1 layer 1 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_1.soil_layers.1.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_1.soil_layers.1.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_1.soil_layers.1.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_1 layer 2 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_1.soil_layers.2.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_1.soil_layers.2.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_1.soil_layers.2.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_2 layer 0 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_2.soil_layers.0.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_2.soil_layers.0.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_2.soil_layers.0.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_2 layer 1 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_2.soil_layers.1.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_2.soil_layers.1.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_2.soil_layers.1.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_2 layer 2 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_2.soil_layers.2.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_2.soil_layers.2.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_2.soil_layers.2.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + }, + { + "description": "soil_2 layer 3 water concentrations are ordered", + "target_and_save": { + "variables": { + "wilting_point_water_concentration": "soil_2.soil_layers.3.wilting_point_water_concentration", + "field_capacity_water_concentration": "soil_2.soil_layers.3.field_capacity_water_concentration", + "saturation_point_water_concentration": "soil_2.soil_layers.3.saturation_point_water_concentration" + } + }, + "rules": [ + { + "left_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["wilting_point_water_concentration"] + }, + "relationship": "greater_or_equal_to" + }, + { + "left_hand": { + "ordered_variables": ["saturation_point_water_concentration"] + }, + "right_hand": { + "ordered_variables": ["field_capacity_water_concentration"] + }, + "relationship": "greater_or_equal_to" + } + ] + } + ] +} diff --git a/tests/test_data_validator.py b/tests/test_data_validator.py index 8b8b7a2ebc..f4685adec6 100644 --- a/tests/test_data_validator.py +++ b/tests/test_data_validator.py @@ -2951,7 +2951,7 @@ def test_evaluate_expression_apply_to_group( @pytest.mark.parametrize( "relationship", - ["equal", "greater", "greater_or_equal_to", "not_equal", "is_of_type", "regex"], + ["equal", "greater", "greater_or_equal_to", "not_equal", "is_of_type", "regex", "is_equal_length"], ) @pytest.mark.parametrize("eager_termination", [True, False]) def test_validate_relationship_valid_values(relationship: str, eager_termination: bool) -> None: @@ -3123,6 +3123,64 @@ def test_evaluate_regex_fullmatch(text: str, pattern: str, expected: bool) -> No assert cv._evaluate_regex(text, pattern) is expected +@pytest.mark.parametrize( + "left,right,expected", + [ + ([], [], True), + ([1, 2, 3, 4], [3, 2, 1, 4], True), # longer lists + ([1, 2, 3, 4], [1, 1, 5], False), # left longer than right + ([1, 1, 5], [1, 2, 3, 4], False), # right longer than left + ], +) +@pytest.mark.parametrize("eager_termination", [True, False]) +def test_evaluate_equal_data_length_with_lists( + left: list[Any], right: list[Any], expected: bool, eager_termination: bool +) -> None: + """List lengths are compared directly with no event log on valid inputs.""" + cv = CrossValidator() + + assert cv._evaluate_equal_data_length(left, right, eager_termination) is expected + assert cv._event_logs == [] + + +@pytest.mark.parametrize( + "left,right", + [ + ("abc", [1, 2, 3]), + ([1, 2], {"a": 1}), + (1, 2), + ], +) +@pytest.mark.parametrize("eager_termination", [True, False]) +def test_evaluate_equal_data_length_invalid_types(left: Any, right: Any, eager_termination: bool) -> None: + """Non-list inputs should log and optionally raise.""" + cv = CrossValidator() + + if eager_termination: + with pytest.raises(ValueError, match=r"Cross-validation error: Invalid type comparison\."): + cv._evaluate_equal_data_length(left, right, eager_termination=True) + assert len(cv._event_logs) == 1 + else: + assert cv._evaluate_equal_data_length(left, right, eager_termination=False) is False + assert len(cv._event_logs) == 1 + + +@pytest.mark.parametrize("eager_termination", [True, False]) +def test_evaluate_condition_equal_length_branch(mocker: MockerFixture, eager_termination: bool) -> None: + """'is_equal_length' should dispatch to _evaluate_equal_data_length with eager flag preserved.""" + cv = CrossValidator() + mocker.patch.object(cv, "_validate_condition_clause", return_value=True) + mocker.patch.object(cv, "_evaluate_expression", side_effect=[([1, 2], True), ([3, 4], True)]) + mock_equal_length = mocker.patch.object(cv, "_evaluate_equal_data_length", return_value=True) + + result = cv._evaluate_condition( + {"relationship": "is_equal_length", "left_hand": {}, "right_hand": {}}, eager_termination + ) + + assert result is True + mock_equal_length.assert_called_once_with([1, 2], [3, 4], eager_termination) + + @pytest.mark.parametrize("eager_termination", [True, False]) def test_evaluate_condition_short_circuits_when_validation_fails( mocker: MockerFixture, eager_termination: bool