saving
This commit is contained in:
110
tests/unit/test_manual.py
Normal file
110
tests/unit/test_manual.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user