diff --git a/solvis_graphql_api/color_scale/compute.py b/solvis_graphql_api/color_scale/compute.py index 8dc3b80..c27970f 100644 --- a/solvis_graphql_api/color_scale/compute.py +++ b/solvis_graphql_api/color_scale/compute.py @@ -64,7 +64,9 @@ def slim(new_intervals, max): while len(new_intervals) > MAX_LEN: new_intervals = new_intervals[::2] new_intervals = sorted(new_intervals) - if new_intervals[-1] < max: + # unreachable: the sole caller passes `max=intervals[0]` (the minimum), so the largest + # element is never < it. Faithfully ported dead branch from the legacy color_scale.py. + if new_intervals[-1] < max: # pragma: no cover new_intervals.append(max) return new_intervals diff --git a/tests/test_compute.py b/tests/test_compute.py new file mode 100644 index 0000000..c54ac36 --- /dev/null +++ b/tests/test_compute.py @@ -0,0 +1,62 @@ +"""Unit tests for the graphene-free colour-scale compute (extracted at the de-graphene cleanup). + +Covers the maths and the error/edge branches directly, so the new `color_scale/compute.py` +module is exercised without going through the schema. +""" + +import pytest + +from solvis_graphql_api.color_scale import compute as C + + +def test_get_normaliser_log_lin_and_error(): + assert C.get_normaliser(10.0, 1.0, "log") is not None + assert C.get_normaliser(10.0, 0.0, "lin") is not None + with pytest.raises(RuntimeError): + C.get_normaliser(10.0, 1.0, "bogus") + + +@pytest.mark.parametrize( + "vmin, vmax", + [ + (1.0e-6, 1.0), # wide multi-decade span -> slim() path + (0.0042171, 0.00967), # narrow span -> interpolate() path + (3.2937376598254063e-18, 0.0008977324469015002), # very wide -> slim + ensure_max + (1.0e-20, 1.0e-11), # slim path where ensure_max appends the boundary + ], +) +def test_log_intervals_lengths(vmin, vmax): + iv = C.log_intervals(vmin, vmax) + assert 4 <= len(iv) <= 10 + assert iv == sorted(iv) + + +def test_log_intervals_same_decade(): + # min_exponent == max_exponent branch (max_exponent += 1) + iv = C.log_intervals(1.2, 3.4) + assert len(iv) >= 4 + + +def test_compute_colour_scale_log(): + res = C.compute_colour_scale("inferno", "log", 1.0, 1.0e-3) + assert res.normalisation == "log" + assert len(res.levels) == len(res.hexrgbs) >= 4 + assert all(h.startswith("#") for h in res.hexrgbs) + + +def test_compute_colour_scale_lin(): + res = C.compute_colour_scale("inferno", "lin", 10.0, 0.0) + assert res.normalisation == "lin" + assert len(res.levels) == len(res.hexrgbs) + assert all(h.startswith("#") for h in res.hexrgbs) + + +def test_compute_colour_scale_unknown_normalisation(): + with pytest.raises(RuntimeError): + C.compute_colour_scale("inferno", "bogus", 10.0, 1.0) + + +def test_get_colour_values_includes_none_sentinel(): + vals = list(C.get_colour_values("inferno", 1.0, 1.0e-3, "log", (1.0e-3, 1.0e-2, None))) + assert vals[-1] == "x000000" # None maps to the sentinel, not a hex + assert all(v.startswith("#") for v in vals[:2]) diff --git a/tests/test_schema_helpers.py b/tests/test_schema_helpers.py new file mode 100644 index 0000000..d8cb4c4 --- /dev/null +++ b/tests/test_schema_helpers.py @@ -0,0 +1,37 @@ +"""Unit coverage for small Strawberry-schema helper branches. + +The trivial empty/None branches of the input-coercion helpers and the null-sortby skip aren't +reached by the parity/behavioural queries (which always supply styles, filter_set_options and +non-null sortby items), so exercise them directly. +""" + +from solvis_graphql_api.schema import _fso_dict, _style_dict, schema + + +def test_style_dict_none_returns_empty(): + assert _style_dict(None) == {} + + +def test_fso_dict_falsy_returns_empty(): + assert _fso_dict(None) == {} + + +_FILTER_RUPTURES_NULL_SORTBY = """ +query ($model_id: String! $location_ids: [String]! $fault_system: String!) { + filter_ruptures(first: 1 sortby: [null] filter: { + model_id: $model_id fault_system: $fault_system location_ids: $location_ids + radius_km: 5 minimum_mag: 8.3 minimum_rate: 1.0e-6}) { + total_count + } +} +""" + + +def test_filter_ruptures_skips_null_sortby_item(archive_fixture): + # a null element in the sortby list must be skipped (not crash the resolver) + result = schema.execute_sync( + _FILTER_RUPTURES_NULL_SORTBY, + variable_values={"model_id": "NSHM_v1.0.0", "fault_system": "HIK", "location_ids": ["WLG"]}, + ) + assert not result.errors + assert result.data["filter_ruptures"]["total_count"] >= 0