Spaces:
Running
Running
Download tests/test_climate.py from lucashudsn/wavereader: direct link, hf CLI and curl.
- Browser
- Download file 8.65 kB
-
https://huggingface.co/spaces/lucashudsn/wavereader/resolve/main/tests/test_climate.py
- Command line
-
hf download hf://spaces/lucashudsn/wavereader/tests/test_climate.py
-
curl -L -o test_climate.py https://huggingface.co/spaces/lucashudsn/wavereader/resolve/main/tests/test_climate.py
8.65 kB
| """Offline tests for wavereader.climate (Worker B slice). No network.""" | |
| import json | |
| import sys | |
| from pathlib import Path | |
| # Bootstrap: repo root on sys.path so `wavereader.climate` imports without | |
| # requiring Worker A's `wavereader/__init__.py` (absent on v2-climate branch). | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| import pytest | |
| from wavereader.climate import ( | |
| COMPASS16, | |
| audit_break, | |
| break_climate, | |
| build_profile, | |
| climate_rose_fig, | |
| compute_monthly_stats, | |
| compute_rose, | |
| direction_rose, | |
| direction_to_bin, | |
| dominant_directions, | |
| filter_existing_slugs, | |
| ideal_window_label, | |
| load_climate, | |
| monthly_medians, | |
| monthly_window_pct, | |
| month_to_season, | |
| rose_top_share, | |
| slugify, | |
| summarize, | |
| top_months_by_window, | |
| top_rose_bins, | |
| ) | |
| FIXTURE = Path(__file__).parent / "fixtures" / "climate_bells.json" | |
| BELLS_BREAK = { | |
| "id": "Bells Beach | Victoria | Surf Coast", | |
| "name": "Bells Beach", | |
| "idealSwell": {"direction": ["SW", "S", "SSW"], "sizeRangeFt": {"min": 4, "max": 12}}, | |
| "bestSeason": ["autumn", "winter"], | |
| } | |
| def bells_climate(): | |
| return json.loads(FIXTURE.read_text(encoding="utf-8")) | |
| def test_direction_to_bin_edges(): | |
| assert direction_to_bin(0) == "N" | |
| assert direction_to_bin(360) == "N" | |
| assert direction_to_bin(22.5) == "NNE" | |
| assert direction_to_bin(225) == "SW" | |
| assert direction_to_bin(359) == "N" | |
| def test_compute_rose_known_distribution(): | |
| rose = compute_rose([90.0] * 50 + [180.0] * 25 + [None, None]) | |
| assert rose["E"] == pytest.approx(66.67, abs=0.01) | |
| assert rose["S"] == pytest.approx(33.33, abs=0.01) | |
| assert sum(rose.values()) == pytest.approx(100.0, abs=0.1) | |
| assert set(rose) == set(COMPASS16) | |
| def test_compute_rose_empty_and_nulls(): | |
| assert all(v == 0.0 for v in compute_rose([]).values()) | |
| assert all(v == 0.0 for v in compute_rose([None, None]).values()) | |
| assert sum(compute_rose([None, 45.0]).values()) == pytest.approx(100.0) | |
| def test_direction_rose_accessor(bells_climate): | |
| rose = direction_rose(bells_climate) | |
| assert set(rose) == set(COMPASS16) | |
| assert sum(rose.values()) == pytest.approx(100.0, abs=0.5) | |
| assert top_rose_bins(bells_climate, 3) == ["SW", "S", "SSW"] | |
| def test_monthly_medians_accessor(bells_climate): | |
| med = monthly_medians(bells_climate) | |
| assert set(med) == set(range(1, 13)) | |
| assert med[6]["median_swell_height_m"] == pytest.approx(2.5) | |
| assert med[1]["median_swell_period_s"] == pytest.approx(10.5) | |
| def test_monthly_stats_grouping_and_window(): | |
| dates = ["2021-01-05", "2021-01-20", "2021-06-10", "bad-date"] | |
| # window 1.0m..2.0m | |
| stats = compute_monthly_stats(dates, [1.5, 5.0, 1.2], [10.0, 11.0, 12.0], 1.0, 2.0) | |
| assert stats[1]["median_swell_height_m"] == pytest.approx(3.25) | |
| assert stats[1]["days_in_ideal_window"] == 1 | |
| assert stats[6]["days_in_ideal_window"] == 1 | |
| assert stats[6]["n_days"] == 1 | |
| assert stats[2]["n_days"] == 0 | |
| assert stats[2]["median_swell_height_m"] is None | |
| def test_top_months_by_window(bells_climate): | |
| assert top_months_by_window(bells_climate, 3) == [6, 7, 5] | |
| def test_monthly_window_pct(bells_climate): | |
| pct = monthly_window_pct(bells_climate) | |
| assert set(pct) == set(range(1, 13)) | |
| assert pct[10] == pytest.approx(22.6, abs=0.01) # 35/155, rounded to 0.1 | |
| assert pct[6] == pytest.approx(53.3, abs=0.01) # 80/150 | |
| empty = {"monthly": {"3": {"days_in_ideal_window": 0, "n_days": 0}}} | |
| assert monthly_window_pct(empty)[3] is None | |
| def test_dominant_directions_and_top_share(bells_climate): | |
| assert dominant_directions(bells_climate, 2) == [("SW", 28.0), ("S", 22.0)] | |
| assert rose_top_share(bells_climate, 2) == pytest.approx(50.0) | |
| assert rose_top_share({"direction_rose_pct": {}}) == 0.0 | |
| def test_ideal_window_label(bells_climate): | |
| assert ideal_window_label(bells_climate) == "4–12 ft" | |
| assert ideal_window_label({"ideal_window_ft": {"min": None, "max": None}}) == "" | |
| assert ideal_window_label({}) == "" | |
| def test_summarize_digest(bells_climate): | |
| s = summarize(bells_climate) | |
| assert s["ideal_window_ft"] == "4–12 ft" | |
| assert s["dominant_directions"] == [["SW", 28.0], ["S", 22.0]] | |
| assert s["top_directions_share_pct"] == pytest.approx(50.0) | |
| # by share of in-window days: 6 (53.3%) > 7 (48.4%) > 5 (45.2%) | |
| assert s["best_months_by_share"] == [6, 7, 5] | |
| assert set(s["monthly_window_pct"]) == set(range(1, 13)) | |
| assert s["monthly_window_pct"][6] == pytest.approx(53.3, abs=0.1) | |
| json.dumps(s) # must stay JSON-serializable for the agent tool surface | |
| def test_month_to_season(): | |
| assert month_to_season(1) == "summer" | |
| assert month_to_season(4) == "autumn" | |
| assert month_to_season(7) == "winter" | |
| assert month_to_season(10) == "spring" | |
| with pytest.raises(ValueError): | |
| month_to_season(13) | |
| def test_audit_agreement_bells(bells_climate): | |
| # Dataset SW/S/SSW == rose top-3; autumn/winter covers top months 6,7,5. | |
| assert audit_break(BELLS_BREAK, bells_climate) == [] | |
| def test_audit_direction_disagreement(bells_climate): | |
| bad = {**BELLS_BREAK, "idealSwell": {"direction": ["N", "NE"]}} | |
| findings = audit_break(bad, bells_climate) | |
| by_field = {f["field"]: f for f in findings} | |
| assert by_field["idealSwell.direction"]["severity"] == "high" | |
| assert by_field["idealSwell.direction"]["dataset_value"] == ["N", "NE"] | |
| assert by_field["idealSwell.direction"]["climate_value"] == ["SW", "S", "SSW"] | |
| # season still agrees -> no season finding | |
| assert "bestSeason" not in by_field | |
| def test_audit_season_disagreement(bells_climate): | |
| bad = {**BELLS_BREAK, "bestSeason": ["summer"]} | |
| findings = audit_break(bad, bells_climate) | |
| by_field = {f["field"]: f for f in findings} | |
| assert by_field["bestSeason"]["severity"] == "medium" | |
| assert by_field["bestSeason"]["dataset_value"] == ["summer"] | |
| assert by_field["bestSeason"]["climate_value"] == [6, 7, 5] | |
| assert "idealSwell.direction" not in by_field | |
| def test_audit_no_data_returns_empty(): | |
| empty = {"n_valid_direction": 0, "direction_rose_pct": {}, "monthly": {}} | |
| assert audit_break(BELLS_BREAK, empty) == [] | |
| def test_slugify(): | |
| assert slugify("Bells Beach | Victoria | Surf Coast") == "bells-beach-victoria-surf-coast" | |
| assert slugify("Avoca Beach | New South Wales | Central Coast") == ( | |
| "avoca-beach-new-south-wales-central-coast" | |
| ) | |
| def test_load_climate_fixture(bells_climate): | |
| loaded = load_climate("climate_bells", Path(__file__).parent / "fixtures") | |
| assert loaded == bells_climate | |
| with pytest.raises(FileNotFoundError): | |
| load_climate("nope", Path(__file__).parent / "fixtures") | |
| def test_break_climate_prefers_embedded(bells_climate, tmp_path): | |
| break_ = dict(BELLS_BREAK, state="Victoria", region="Surf Coast", climate=bells_climate) | |
| # embedded copy wins even when the fallback dir holds nothing | |
| assert break_climate(break_, tmp_path) == bells_climate | |
| def test_break_climate_file_fallback(bells_climate, tmp_path): | |
| import shutil | |
| shutil.copy(FIXTURE, tmp_path / "bells-beach-victoria-surf-coast.json") | |
| break_ = {k: v for k, v in BELLS_BREAK.items() if k != "id"} | |
| break_["state"] = "Victoria" | |
| break_["region"] = "Surf Coast" | |
| assert break_climate(break_, tmp_path) == bells_climate | |
| with pytest.raises(FileNotFoundError): | |
| break_climate({"name": "Nowhere", "state": "X", "region": "Y"}, tmp_path) | |
| def test_filter_existing_slugs_resumable(tmp_path): | |
| (tmp_path / "a.json").write_text("{}", encoding="utf-8") | |
| assert filter_existing_slugs(["a", "b"], tmp_path) == ["b"] | |
| assert filter_existing_slugs(["a", "b"], tmp_path, overwrite=True) == ["a", "b"] | |
| def test_build_profile_roundtrip(): | |
| dates = ["2021-01-01", "2021-01-02", "2021-07-01"] | |
| prof = build_profile( | |
| slug="x", break_id="X", name="X", latitude=-38.5, longitude=144.3, | |
| start="2021-01-01", end="2021-12-31", dates=dates, | |
| heights_m=[1.5, None, 2.0], directions_deg=[225.0, 225.0, None], | |
| periods_s=[12.0, 11.0, 13.0], window_min_ft=4, window_max_ft=12, | |
| ) | |
| assert prof["n_days"] == 3 | |
| assert prof["n_valid_direction"] == 2 | |
| assert prof["direction_rose_pct"]["SW"] == pytest.approx(100.0) | |
| assert prof["monthly"]["1"]["n_days"] == 2 | |
| def test_climate_rose_fig(bells_climate): | |
| fig = climate_rose_fig(bells_climate, "Bells Beach") | |
| assert len(fig.data) == 1 | |
| assert len(fig.data[0].r) == 16 | |
| assert list(fig.data[0].theta) == COMPASS16 | |
| assert max(fig.data[0].r) == pytest.approx(28.0) | |