"""A recomputed force sample: raw validity is the sensor's, newtons follow the model status. The SDK calibration-model contract says a frame the model cannot answer (no contact, no session zero, ...) has an empty calibrated output while the raw readout is kept. These tests pin that a missing session zero no longer invalidates the raw counts of every frame — the defect that made a whole analysed video read "unavailable" in both units. """ from __future__ import annotations import math from orisys_app_shared.analysis.model import ALGORITHM_VERSION, ForceSample from orisys_app_shared.analysis.service import AnalysisService def _reading(status: dict[str, str], *, normal: float, valid: bool = True) -> dict: return { "normal_force": normal, "shear_x": float("nan"), "shear_y": float("nan"), "normal_force_raw": 1234.0, "shear_x_raw": -5.0, "shear_y_raw": 7.0, "valid": valid, "force_calibrated": True, "calibrated_axes": ("fn",), "calibration_status": status, } def test_a_missing_session_zero_keeps_the_raw_counts_valid(): sample = ForceSample.from_reading( _reading({"fn": "zero_unavailable"}, normal=float("nan")) ) assert sample.valid, "a model without a session zero invalidated the raw readout" assert sample.raw() == (1234.0, -5.0, 7.0) assert math.isnan(sample.normal) assert sample.calibrated assert dict(sample.status) == {"fn": "zero_unavailable"} def test_a_non_ok_status_empties_the_newton_value_even_when_one_is_reported(): # ``out_of_range`` carries a number in the SDK, but outside the validated range. sample = ForceSample.from_reading(_reading({"fn": "out_of_range"}, normal=42.0)) assert math.isnan(sample.normal) assert sample.valid def test_an_ok_status_keeps_the_newton_value(): sample = ForceSample.from_reading(_reading({"fn": "ok"}, normal=3.5)) assert sample.normal == 3.5 assert sample.valid def test_a_failed_sensor_readout_is_still_invalid(): sample = ForceSample.from_reading(_reading({"fn": "ok"}, normal=3.5, valid=False)) assert not sample.valid def test_a_legacy_calibration_without_statuses_is_unchanged(): reading = _reading({}, normal=2.0) reading["calibrated_axes"] = None sample = ForceSample.from_reading(reading) assert sample.normal == 2.0 and sample.valid def test_the_status_is_not_part_of_the_stored_payload(): sample = ForceSample.from_reading( _reading({"fn": "zero_unavailable"}, normal=float("nan")) ) decoded = ForceSample.decode(sample.encode()) assert decoded.valid and decoded.raw() == sample.raw() assert decoded.status == () def test_the_pass_counts_why_a_calibrated_result_has_no_newtons(): counts: dict = {} missing = ForceSample.from_reading( _reading({"fn": "zero_unavailable"}, normal=float("nan")) ) for _ in range(3): AnalysisService._accumulate_force(counts, missing) assert counts["force_valid_frames"] == 3 assert counts["force_calibrated_frames"] == 3 assert counts["force_newton_frames"] == 0 assert counts["force_zero_unavailable_frames"] == 3 AnalysisService._accumulate_force( counts, ForceSample.from_reading(_reading({"fn": "ok"}, normal=1.0)) ) assert counts["force_newton_frames"] == 1 assert counts["force_zero_unavailable_frames"] == 3 def test_results_stored_under_the_old_validity_rule_are_not_served(): # Version 1 marked every frame invalid when the model lacked a zero; the cache key # carries this string, so those results must miss the cache and be recomputed. assert ALGORITHM_VERSION != "flow-hhd-contact-1"