""" Unit tests: Layer 1 training profile resolver scaffold. No database; pure template + algorithm + resolver behavior. """ import pytest from data_layer.training_profile import ( CalculationTemplate, DimensionSpec, FocusAreaMapping, TrainingEvaluationResult, resolve_for_base_profile, resolve_training_evaluation, ) from data_layer.training_profile.algorithms.registry import ( get_algorithm, list_algorithm_ids, register_algorithm, ) from data_layer.training_profile.models import AlgorithmRunResult from data_layer.training_profile.profiles.registry import get_training_base_profile from data_layer.training_profile.templates.registry import get_calculation_template class TestAlgorithmRegistry: def test_builtin_algorithms_registered(self): ids = list_algorithm_ids() assert "threshold_band" in ids assert "linear_range" in ids def test_get_algorithm_runs_threshold(self): fn = get_algorithm("threshold_band") r = fn( inputs={"avg_hr": 130.0}, params={ "value_key": "avg_hr", "bands": [ {"max": 120, "score": 0.2}, {"max": 150, "score": 0.8}, {"max": None, "score": 1.0}, ], }, ) assert r.normalized_score == 0.8 def test_duplicate_register_raises(self): def dummy(*, inputs, params): return AlgorithmRunResult(0.0, 0.0, []) with pytest.raises(ValueError, match="already registered"): register_algorithm("threshold_band", dummy) class TestResolver: def test_example_template_resolves(self): tpl = get_calculation_template("scaffold_example_aerobic_v1") result = resolve_training_evaluation( activity_inputs={ "avg_hr": 135.0, "duration_min": 45.0, "distance_km": 10.0, }, template=tpl, ) assert isinstance(result, TrainingEvaluationResult) assert result.template_id == "scaffold_example_aerobic_v1" assert result.confidence == "high" assert "aerobic_endurance" in result.focus_area_contributions assert len(result.dimension_results) == 2 for dr in result.dimension_results: assert dr.missing_inputs == [] def test_missing_required_input_skips_dimension(self): tpl = get_calculation_template("scaffold_example_aerobic_v1") result = resolve_training_evaluation( activity_inputs={"avg_hr": 135.0}, template=tpl, ) assert result.confidence in ("medium", "low", "insufficient") skipped = [d for d in result.dimension_results if d.evidence.get("skipped")] assert len(skipped) >= 1 def test_base_profile_filters_dimensions(self): profile = get_training_base_profile("scaffold_strength_base") tpl = get_calculation_template(profile.default_template_id) result = resolve_training_evaluation( activity_inputs={"duration_min": 50.0}, template=tpl, base_profile=profile, ) assert len(result.dimension_results) == 1 assert result.dimension_results[0].dimension_key == "effort" def test_resolve_for_base_profile_convenience(self): result = resolve_for_base_profile( activity_inputs={"duration_min": 40.0}, base_profile_key="scaffold_strength_base", include_trace=True, ) assert result.base_profile_key == "scaffold_strength_base" assert result.trace is not None assert "effort" in result.trace def test_to_serializable(self): tpl = get_calculation_template("scaffold_example_strength_v1") r = resolve_training_evaluation( activity_inputs={"duration_min": 45.0}, template=tpl, ) d = r.to_serializable() assert d["template_id"] == tpl.id assert "focus_area_contributions" in d assert isinstance(d["dimension_results"], list) class TestCustomTemplate: def test_unknown_algorithm_raises(self): bad = CalculationTemplate( id="bad", version="1", label="bad", dimensions=( DimensionSpec( key="x", algorithm_id="does_not_exist", inputs=("a",), params={}, maps_to=(FocusAreaMapping("strength", 1.0),), ), ), ) with pytest.raises(KeyError): resolve_training_evaluation( activity_inputs={"a": 1.0}, template=bad, )