""" Unit tests for formatting utilities in intervals_mcp_server.utils.formatting. These tests verify that the formatting functions produce expected output strings for activities, workouts, wellness entries, events, and intervals. """ import json from intervals_mcp_server.utils.formatting import ( format_activity_summary, format_workout, format_wellness_entry, format_event_summary, format_event_details, format_intervals, format_power_curves, ) from tests.sample_data import INTERVALS_DATA def test_format_activity_summary(): """ Test that format_activity_summary returns a string containing the activity name and ID. """ data = { "name": "Morning Ride", "id": 1, "type": "Ride", "startTime": "2024-01-01T08:00:00Z", "distance": 1000, "duration": 3600, } result = format_activity_summary(data) assert "Activity: Morning Ride" in result assert "ID: 1" in result def test_format_workout(): """ Test that format_workout returns a string containing the workout name and interval count. """ workout = { "name": "Workout1", "description": "desc", "sport": "Ride", "duration": 3600, "tss": 50, "intervals": [1, 2, 3], } result = format_workout(workout) assert "Workout: Workout1" in result assert "Intervals: 3" in result def test_format_wellness_entry(): """ Test that format_wellness_entry returns a string containing the date and fitness (CTL). """ with open("tests/ressources/wellness_entry.json", "r", encoding="utf-8") as f: entry = json.load(f) result = format_wellness_entry(entry) with open("tests/ressources/wellness_entry_formatted.txt", "r", encoding="utf-8") as f: expected_result = f.read() assert result == expected_result def test_format_wellness_entry_computes_form_tsb(): """Form (TSB) is computed as CTL - ATL when both are present.""" result = format_wellness_entry({"id": "2024-06-01", "ctl": 50, "atl": 65}) assert "Form (TSB): -15.0" in result def test_format_wellness_entry_no_form_without_both_components(): """Form is omitted if either CTL or ATL is missing.""" result = format_wellness_entry({"id": "2024-06-01", "ctl": 50}) assert "Form (TSB)" not in result def test_format_wellness_entry_prefers_explicit_date_over_id(): """An explicit `date` field wins over `id` for the Date line.""" result = format_wellness_entry({"id": "2024-06-01", "date": "2024-06-02", "ctl": 50}) assert "Date: 2024-06-02" in result def test_format_wellness_entry_null_date_falls_back_to_id(): """A present-but-null `date` falls back to `id`, not 'None'.""" result = format_wellness_entry({"id": "2024-06-01", "date": None, "ctl": 50}) assert "Date: 2024-06-01" in result assert "Date: None" not in result def test_format_wellness_entry_non_numeric_ctl_atl_does_not_crash(): """Non-numeric ctl/atl must not raise; Form is simply omitted.""" result = format_wellness_entry({"id": "2024-06-01", "ctl": "n/a", "atl": "n/a"}) assert "Form (TSB)" not in result assert "Date: 2024-06-01" in result def test_format_wellness_entry_include_all_fields(): """ Test that format_wellness_entry with include_all_fields=True includes additional unknown fields. """ entry = { "id": "2024-06-01", "ctl": 80, "weight": 75, "customField1": "hello", "customField2": 42, "updated": "2024-06-01T10:00:00Z", } result = format_wellness_entry(entry, include_all_fields=True) assert "Date: 2024-06-01" in result assert "Fitness (CTL): 80" in result assert "Weight: 75 kg" in result assert "Other Fields:" in result assert "customField1: hello" in result assert "customField2: 42" in result # "updated" is a known built-in field, should not appear in Other Fields assert "updated:" not in result def test_format_wellness_entry_no_extra_fields_by_default(): """ Test that format_wellness_entry without include_all_fields does not include additional fields. """ entry = { "id": "2024-06-01", "ctl": 80, "customField1": "hello", } result = format_wellness_entry(entry) assert "Other Fields:" not in result assert "customField1" not in result def test_format_wellness_entry_macros_populated(): """ Test that format_wellness_entry renders native nutrition macros (carbohydrates, protein, fatTotal) in grams when present. """ entry = { "id": "2026-04-08", "carbohydrates": 310, "protein": 145, "fatTotal": 72, } result = format_wellness_entry(entry) assert "Nutrition & Hydration:" in result assert "- Carbohydrates: 310 g" in result assert "- Protein: 145 g" in result assert "- Fat: 72 g" in result def test_format_wellness_entry_macros_null_hidden(): """ Test that format_wellness_entry hides macro lines when the fields are null, preserving backward compatibility with older wellness records. """ entry = { "id": "2026-04-08", "ctl": 80, "carbohydrates": None, "protein": None, "fatTotal": None, } result = format_wellness_entry(entry) assert "Carbohydrates" not in result assert "Protein" not in result # "Fat" could legitimately appear inside e.g. "Body Fat" elsewhere, so # anchor the negative assertion on the line-prefix form we would emit. assert "- Fat:" not in result def test_format_event_summary(): """ Test that format_event_summary returns a string containing the event date and type. """ event = { "start_date_local": "2024-01-01", "id": "e1", "name": "Event1", "description": "desc", "race": True, } summary = format_event_summary(event) assert "Date: 2024-01-01" in summary assert "Type: Race" in summary def test_format_event_details(): """ Test that format_event_details returns a string containing event and workout details. """ event = { "id": "e1", "date": "2024-01-01", "name": "Event1", "description": "desc", "workout": { "id": "w1", "sport": "Ride", "duration": 3600, "tss": 50, "intervals": [1, 2], }, "race": True, "priority": "A", "result": "1st", "calendar": {"name": "Main"}, } details = format_event_details(event) assert "Event Details:" in details assert "Workout Information:" in details def test_format_intervals(): """ Test that format_intervals returns a string containing interval analysis and the interval label. """ result = format_intervals(INTERVALS_DATA) assert "Intervals Analysis:" in result assert "Rep 1" in result def test_format_power_curves(): """ Test that format_power_curves returns a concise string with curve labels, power values, W/kg values, and activity IDs. """ curves = [ { "id": "s0", "label": "This season", "start": "2025-09-29T00:00:00", "end": "2026-03-14T00:00:00", "data_points": [ {"secs": 5, "watts": 780, "activity_id": "i100", "watts_per_kg": 10.4, "wkg_activity_id": "i100"}, {"secs": 60, "watts": 380, "activity_id": "i102", "watts_per_kg": 5.07, "wkg_activity_id": "i102"}, {"secs": 3600, "watts": 210, "activity_id": "i107", "watts_per_kg": 2.8, "wkg_activity_id": "i107"}, ], }, ] result = format_power_curves(curves, "Ride", include_normalised=True) assert "Power Curves (Ride):" in result assert "This season" in result assert "5s: 780W" in result assert "10.40W/kg" in result assert "1m: 380W" in result assert "1h: 210W" in result assert "i100" in result assert "i107" in result def test_format_power_curves_without_normalised(): """ Test that format_power_curves without normalised data does not include W/kg. """ curves = [ { "id": "s0", "label": "This season", "start": "", "end": "", "data_points": [ {"secs": 5, "watts": 780, "activity_id": "i100"}, ], }, ] result = format_power_curves(curves, "Ride", include_normalised=False) assert "780W" in result assert "W/kg" not in result