Files
openrun/tests/unit/test_manual.py
2026-06-12 05:48:30 -04:00

111 lines
3.7 KiB
Python

"""Tests for `openrun.ingest.manual` — CSV import + idempotency."""
from __future__ import annotations
from pathlib import Path
import pytest
from openrun.ingest.manual import import_csv
def _write_csv(tmp_path: Path, text: str) -> Path:
p = tmp_path / "manual.csv"
p.write_text(text)
return p
def test_import_csv_inserts_required_columns(tmp_conn, tmp_path: Path) -> None:
csv = _write_csv(tmp_path, """\
activity_date,activity_type,distance_km,duration_min,training_load,notes
2026-05-15,strength,,45,30,upper body
2026-05-16,hike,8.0,120,40,morning hike
""")
result = import_csv(tmp_conn, csv)
assert result.inserted == 2
assert result.updated == 0
assert result.skipped == 0
assert result.errors == []
rows = tmp_conn.execute(
"SELECT activity_date, activity_type, distance_km, duration_min, training_load, notes "
"FROM manual_activities ORDER BY activity_date"
).fetchall()
assert rows[0]["activity_date"] == "2026-05-15"
assert rows[0]["activity_type"] == "strength"
assert rows[0]["distance_km"] is None
assert rows[0]["duration_min"] == 45
assert rows[0]["training_load"] == 30
assert rows[0]["notes"] == "upper body"
assert rows[1]["distance_km"] == 8.0
def test_import_csv_requires_header_columns(tmp_conn, tmp_path: Path) -> None:
csv = _write_csv(tmp_path, "date,type\n2026-05-15,strength\n") # wrong column names
with pytest.raises(ValueError, match="missing required columns"):
import_csv(tmp_conn, csv)
def test_import_csv_external_id_makes_reimport_idempotent(tmp_conn, tmp_path: Path) -> None:
csv = _write_csv(tmp_path, """\
activity_date,activity_type,training_load,external_id
2026-05-15,strength,30,strava-12345
""")
import_csv(tmp_conn, csv)
second = import_csv(tmp_conn, csv)
assert second.inserted == 0
assert second.updated == 1
count = tmp_conn.execute("SELECT COUNT(*) FROM manual_activities").fetchone()[0]
assert count == 1
def test_import_csv_reimport_with_new_tl_updates_value(tmp_conn, tmp_path: Path) -> None:
"""External_id upsert should reflect changed training_load on re-import."""
csv1 = _write_csv(tmp_path, """\
activity_date,activity_type,training_load,external_id
2026-05-15,strength,30,k1
""")
import_csv(tmp_conn, csv1)
csv2 = _write_csv(tmp_path, """\
activity_date,activity_type,training_load,external_id
2026-05-15,strength,55,k1
""")
import_csv(tmp_conn, csv2)
tl = tmp_conn.execute("SELECT training_load FROM manual_activities WHERE external_id='k1'").fetchone()[0]
assert tl == 55
def test_import_csv_blank_external_id_allows_duplicates(tmp_conn, tmp_path: Path) -> None:
"""No external_id = caller is intentionally logging without dedupe key. Two imports = two rows."""
csv = _write_csv(tmp_path, """\
activity_date,activity_type,training_load
2026-05-15,strength,30
""")
import_csv(tmp_conn, csv)
import_csv(tmp_conn, csv)
assert tmp_conn.execute("SELECT COUNT(*) FROM manual_activities").fetchone()[0] == 2
def test_import_csv_skips_bad_rows_but_keeps_going(tmp_conn, tmp_path: Path) -> None:
csv = _write_csv(tmp_path, """\
activity_date,activity_type,training_load
2026-05-15,strength,30
bad-date,hike,40
2026-05-17,run,
""")
result = import_csv(tmp_conn, csv)
assert result.inserted == 2
assert result.skipped == 1
assert len(result.errors) == 1
assert "line 3" in result.errors[0]
def test_import_csv_accepts_iso_datetime_in_date_column(tmp_conn, tmp_path: Path) -> None:
csv = _write_csv(tmp_path, """\
activity_date,activity_type,training_load
2026-05-15T08:30:00,strength,30
""")
import_csv(tmp_conn, csv)
d = tmp_conn.execute("SELECT activity_date FROM manual_activities").fetchone()[0]
assert d == "2026-05-15"