diff --git a/tests/waterdata_test.py b/tests/waterdata_test.py index 06289ce4..6b2db974 100644 --- a/tests/waterdata_test.py +++ b/tests/waterdata_test.py @@ -1024,21 +1024,20 @@ def test_string_returned_unchanged(self): # Note: no hyphen requirement here — that's monitoring_location_id-specific. assert _normalize_str_iterable("dog", "parameter_code") == "dog" - def test_list_returned_unchanged(self): - assert _normalize_str_iterable(["00060", "00010"], "p") == ["00060", "00010"] - - def test_tuple_normalizes_to_list(self): - result = _normalize_str_iterable(("00060", "00010"), "p") - assert result == ["00060", "00010"] - assert isinstance(result, list) - - def test_pandas_series_normalizes_to_list(self): - result = _normalize_str_iterable(pd.Series(["00060", "00010"]), "p") - assert result == ["00060", "00010"] - assert isinstance(result, list) - - def test_numpy_array_normalizes_to_list(self): - result = _normalize_str_iterable(np.array(["00060", "00010"]), "p") + @pytest.mark.parametrize( + "value", + [ + ["00060", "00010"], + ("00060", "00010"), + pd.Series(["00060", "00010"]), + np.array(["00060", "00010"]), + ], + ids=["list", "tuple", "series", "ndarray"], + ) + def test_iterable_normalizes_to_list(self, value): + """Any iterable of strings (list / tuple / Series / ndarray) comes back + as a plain ``list``.""" + result = _normalize_str_iterable(value, "p") assert result == ["00060", "00010"] assert isinstance(result, list) diff --git a/tests/waterdata_utils_test.py b/tests/waterdata_utils_test.py index 4d568d1f..51dfcc57 100644 --- a/tests/waterdata_utils_test.py +++ b/tests/waterdata_utils_test.py @@ -671,64 +671,49 @@ def test_arrange_cols_keeps_geometry_when_present(): # --- _format_api_dates ------------------------------------------------------- -def test_format_api_dates_iso8601_with_z(): - """ISO 8601 datetimes with a 'Z' suffix must be parsed, not dropped to None.""" - assert _format_api_dates("2018-02-12T23:20:50Z") == "2018-02-12T23:20:50Z" - - -def test_format_api_dates_iso8601_with_fractional_seconds(): - assert _format_api_dates("2018-02-12T23:20:50.123Z") == "2018-02-12T23:20:50Z" - - -def test_format_api_dates_iso8601_with_offset(): - """Numeric offsets must be converted to UTC.""" - assert _format_api_dates("2018-02-12T19:20:50-04:00") == "2018-02-12T23:20:50Z" - - -def test_format_api_dates_iso8601_pair(): - """A list of two ISO 8601 datetimes must be parsed into a UTC interval.""" - result = _format_api_dates(["2018-02-12T23:20:50Z", "2018-03-18T12:31:12Z"]) - assert result == "2018-02-12T23:20:50Z/2018-03-18T12:31:12Z" - - -def test_format_api_dates_passthrough_interval(): - assert _format_api_dates("2018-02-12T00:00:00Z/..") == "2018-02-12T00:00:00Z/.." - - -def test_format_api_dates_passthrough_duration(): - assert _format_api_dates("P7D") == "P7D" - - -def test_format_api_dates_passthrough_time_only_duration(): - """ISO 8601 time-only durations (PT...) are passed through unchanged.""" - assert _format_api_dates("PT36H") == "PT36H" - - -def test_format_api_dates_word_with_p_is_not_a_duration(): - """Strings containing the letter 'p' must not be misclassified as durations.""" - assert _format_api_dates("Apr") is None - - -def test_format_api_dates_date_only(): - assert _format_api_dates("2024-01-01", date=True) == "2024-01-01" - - -def test_format_api_dates_date_only_pair(): - assert ( - _format_api_dates(["2024-01-01", "2024-02-01"], date=True) - == "2024-01-01/2024-02-01" - ) - - -def test_format_api_dates_space_separated_still_works(): - """The legacy space-separated format must still parse.""" - assert _format_api_dates("2024-01-01 00:00:00", date=True) == "2024-01-01" - - -def test_format_api_dates_open_ended_range_with_none(): - """A None / NaN endpoint becomes '..' in the output range.""" - assert _format_api_dates(["2024-01-01", None], date=True) == "2024-01-01/.." - assert _format_api_dates([None, "2024-01-01"], date=True) == "../2024-01-01" +@pytest.mark.parametrize( + "value, date, expected", + [ + ("2018-02-12T23:20:50Z", False, "2018-02-12T23:20:50Z"), + ("2018-02-12T23:20:50.123Z", False, "2018-02-12T23:20:50Z"), + ("2018-02-12T19:20:50-04:00", False, "2018-02-12T23:20:50Z"), + ( + ["2018-02-12T23:20:50Z", "2018-03-18T12:31:12Z"], + False, + "2018-02-12T23:20:50Z/2018-03-18T12:31:12Z", + ), + ("2018-02-12T00:00:00Z/..", False, "2018-02-12T00:00:00Z/.."), + ("P7D", False, "P7D"), + ("PT36H", False, "PT36H"), + ("Apr", False, None), + ("2024-01-01", True, "2024-01-01"), + (["2024-01-01", "2024-02-01"], True, "2024-01-01/2024-02-01"), + ("2024-01-01 00:00:00", True, "2024-01-01"), + (["2024-01-01", None], True, "2024-01-01/.."), + ([None, "2024-01-01"], True, "../2024-01-01"), + ], + ids=[ + "iso8601_z", + "fractional_seconds", + "offset_to_utc", + "iso8601_pair_to_interval", + "passthrough_interval", + "passthrough_duration", + "time_only_duration", + "word_with_p_not_duration", + "date_only", + "date_only_pair", + "space_separated", + "open_ended_none_end", + "open_ended_none_start", + ], +) +def test_format_api_dates(value, date, expected): + """``_format_api_dates`` normalizes ISO 8601 datetimes to UTC (dropping + fractional seconds, converting offsets), joins a pair into an interval, + passes durations / intervals through unchanged, renders a None endpoint as + ``..``, and returns None for a non-date word (e.g. ``"Apr"``).""" + assert _format_api_dates(value, date=date) == expected def test_format_api_dates_rejects_mapping():