diff --git a/src/mozanalysis/experiment.py b/src/mozanalysis/experiment.py index 85968c1d..15e8028b 100644 --- a/src/mozanalysis/experiment.py +++ b/src/mozanalysis/experiment.py @@ -1458,12 +1458,20 @@ class AnalysisWindow: @start.validator def _validate_start(self, attribute, value): - assert (value >= 0 and self.end >= 0) or (value < 0 and self.end < 0) + assert (value >= 0 and self.end >= 0) or (value < 0 and self.end < 0), ( + f"AnalysisWindow `start` invalid: {value} and {self.end} must share a sign", + "(either both positive/zero, or both negative)", + ) @end.validator def _validate_end(self, attribute, value): - assert value >= self.start - assert (value >= 0 and self.start >= 0) or (value < 0 and self.start < 0) + assert value >= self.start, ( + f"AnalysisWindow `end` invalid: {value} must be >= {self.start}" + ) + assert (value >= 0 and self.start >= 0) or (value < 0 and self.start < 0), ( + f"AnalysisWindow `end` invalid: {value} and {self.start} must share a sign", + "(either both positive/zero, or both negative)", + ) @attr.s(frozen=True, slots=True) diff --git a/tests/test_experiment.py b/tests/test_experiment.py index 5f47d5b6..dddd39c9 100644 --- a/tests/test_experiment.py +++ b/tests/test_experiment.py @@ -25,6 +25,12 @@ from mozanalysis.segments import Segment, SegmentDataSource +@pytest.mark.parametrize(("start", "end", "invalid"), [(-1, 1, "start"), (3, 1, "end")]) +def test_analysis_window_validation(start, end, invalid): + with pytest.raises(AssertionError, match=f"AnalysisWindow `{invalid}` invalid"): + AnalysisWindow(start, end) + + def test_time_limits_validates(): # Mainly check that the validation is running at all # No need to specify the same checks twice(?)