import pytest from dashboard_layout_schema import DashboardLayoutPayload, coalesce_effective_layout, default_layout_dict from dashboard_widget_config import validate_widget_entry_config def test_body_chart_days_bounds(): assert validate_widget_entry_config("body_overview", {"chart_days": 7}) == {"chart_days": 7} assert validate_widget_entry_config("body_overview", {"chart_days": 90}) == {"chart_days": 90} assert validate_widget_entry_config("body_overview", {"chart_days": 42.0}) == {"chart_days": 42} with pytest.raises(ValueError): validate_widget_entry_config("body_overview", {"chart_days": 6}) with pytest.raises(ValueError): validate_widget_entry_config("body_overview", {"chart_days": 91}) def test_body_history_viz_empty_expands_defaults(): d = validate_widget_entry_config("body_history_viz", {}) assert d["chart_days"] == 30 assert d["show_kpis"] is True assert d["show_weight_chart"] is True assert d["kpi_detail"] == "compact" assert d["show_body_fat_chart"] is False def test_body_history_viz_chart_days_and_merge(): d = validate_widget_entry_config("body_history_viz", {"chart_days": 60}) assert d["chart_days"] == 60 assert d["show_goals_strip"] is False with pytest.raises(ValueError): validate_widget_entry_config("body_history_viz", {"chart_days": 5}) def test_body_history_viz_requires_visible_block(): with pytest.raises(ValueError): validate_widget_entry_config( "body_history_viz", {"show_kpis": False, "show_weight_chart": False}, ) def test_body_history_viz_unknown_key(): with pytest.raises(ValueError): validate_widget_entry_config("body_history_viz", {"evil": True}) def test_nutrition_history_viz_empty_expands_defaults(): d = validate_widget_entry_config("nutrition_history_viz", {}) assert d["chart_days"] == 30 assert d["show_kpis"] is True assert d["show_kcal_vs_weight"] is True assert d["kpi_detail"] == "compact" assert d["show_calorie_balance_chart"] is False assert d["show_energy_protein_charts"] is False def test_nutrition_history_viz_chart_days_and_merge(): d = validate_widget_entry_config("nutrition_history_viz", {"chart_days": 45}) assert d["chart_days"] == 45 assert d["show_goals_strip"] is False with pytest.raises(ValueError): validate_widget_entry_config("nutrition_history_viz", {"chart_days": 5}) def test_nutrition_history_viz_requires_visible_block(): with pytest.raises(ValueError): validate_widget_entry_config( "nutrition_history_viz", {"show_kpis": False, "show_kcal_vs_weight": False, "show_macro_daily_bars": False, "show_macro_distribution_pair": False}, ) def test_nutrition_history_viz_unknown_key(): with pytest.raises(ValueError): validate_widget_entry_config("nutrition_history_viz", {"evil": True}) def test_welcome_config_rejected_unknown_key(): with pytest.raises(ValueError): validate_widget_entry_config("welcome", {"x": 1}) def test_body_unknown_key(): with pytest.raises(ValueError): validate_widget_entry_config("body_overview", {"chart_days": 30, "extra": 1}) def test_activity_chart_days(): assert validate_widget_entry_config("activity_overview", {"chart_days": 14}) == {"chart_days": 14} with pytest.raises(ValueError): validate_widget_entry_config("activity_overview", {"chart_days": 5}) def test_kpi_board_tiles(): assert validate_widget_entry_config("kpi_board", {}) == {} assert validate_widget_entry_config("kpi_board", {"tiles": []}) == {"tiles": []} assert validate_widget_entry_config( "kpi_board", {"tiles": [{"id": "body_fat"}, {"id": "avg_kcal"}, {"id": "ref:hr_max"}]}, ) == {"tiles": [{"id": "body_fat"}, {"id": "avg_kcal"}, {"id": "ref:hr_max"}]} with pytest.raises(ValueError): validate_widget_entry_config("kpi_board", {"tiles": [{"id": "unknown"}]}) with pytest.raises(ValueError): validate_widget_entry_config("kpi_board", {"tiles": [{"id": "body_fat"}, {"id": "body_fat"}]}) with pytest.raises(ValueError): validate_widget_entry_config("kpi_board", {"extra": 1}) def test_quick_capture_visibility(): assert validate_widget_entry_config("quick_capture", {}) == {} assert validate_widget_entry_config("quick_capture", {"show_weight": False}) == {"show_weight": False} full = { "show_weight": True, "show_resting_hr": False, "show_hrv": True, "show_vo2_max": False, } assert validate_widget_entry_config("quick_capture", full) == full with pytest.raises(ValueError): validate_widget_entry_config("quick_capture", {"show_weight": "yes"}) with pytest.raises(ValueError): validate_widget_entry_config( "quick_capture", { "show_weight": False, "show_resting_hr": False, "show_hrv": False, "show_vo2_max": False, }, ) with pytest.raises(ValueError): validate_widget_entry_config("quick_capture", {"extra": 1}) def test_nutrition_detail_charts_days(): assert validate_widget_entry_config("nutrition_detail_charts", {}) == {} assert validate_widget_entry_config("nutrition_detail_charts", {"chart_days": 60}) == {"chart_days": 60} with pytest.raises(ValueError): validate_widget_entry_config("nutrition_detail_charts", {"chart_days": 3}) def test_recovery_charts_panel_days(): assert validate_widget_entry_config("recovery_charts_panel", {}) == {} assert validate_widget_entry_config("recovery_charts_panel", {"chart_days": 28}) == {"chart_days": 28} with pytest.raises(ValueError): validate_widget_entry_config("recovery_charts_panel", {"chart_days": 99}) def test_trend_kcal_weight_chart_days(): assert validate_widget_entry_config("trend_kcal_weight", {}) == {} assert validate_widget_entry_config("trend_kcal_weight", {"chart_days": 30}) == {"chart_days": 30} with pytest.raises(ValueError): validate_widget_entry_config("trend_kcal_weight", {"chart_days": 6}) def test_kpi_board_legacy_chart_days_dropped(): """Nur chart_days (Alt-Layouts) → automatische Kachelwahl, kein Ø-Kal-Fenster mehr.""" assert validate_widget_entry_config("kpi_board", {"chart_days": 14}) == {} assert validate_widget_entry_config("kpi_board", {"chart_days": 5}) == {} def test_welcome_still_rejects_config(): with pytest.raises(ValueError): validate_widget_entry_config("welcome", {"chart_days": 30}) def test_layout_payload_with_chart_days_roundtrip(): p = DashboardLayoutPayload.model_validate( { "version": 1, "widgets": [ {"id": "welcome", "enabled": True}, { "id": "body_overview", "enabled": True, "config": {"chart_days": 42}, }, ], } ) d = p.to_stored_dict() assert d["widgets"][1]["config"]["chart_days"] == 42 def test_coalesce_rejects_invalid_widget_config(): raw = { "version": 1, "widgets": [ {"id": "welcome", "enabled": True, "config": {"evil": True}}, ], } custom, eff = coalesce_effective_layout(raw) assert custom is False assert eff == default_layout_dict()