second pass. low
This commit is contained in:
+19
-19
@@ -55,7 +55,7 @@ def test_parse_sidecar_frontmatter_only(tmp_path):
|
||||
# ── apply_sidecar ─────────────────────────────────────────────────────────────
|
||||
|
||||
BASE_DETAIL = {
|
||||
"id": "2024-01-01T08:00:00Z_cycling",
|
||||
"id": "2024-01-01T080000Z-morning-ride",
|
||||
"title": "Morning Ride",
|
||||
"sport": "cycling",
|
||||
"started_at": "2024-01-01T08:00:00Z",
|
||||
@@ -118,21 +118,21 @@ def data_dir(tmp_path):
|
||||
acts = tmp_path / "activities"
|
||||
acts.mkdir()
|
||||
# Two activities
|
||||
for act_id, title in [
|
||||
("2024-01-01T08:00:00Z_cycling", "Morning Ride"),
|
||||
("2024-01-02T09:00:00Z_running", "Easy Run"),
|
||||
for act_id, title, sport, started_at in [
|
||||
("2024-01-01T080000Z-morning-ride", "Morning Ride", "cycling", "2024-01-01T08:00:00Z"),
|
||||
("2024-01-02T090000Z-easy-run", "Easy Run", "running", "2024-01-02T09:00:00Z"),
|
||||
]:
|
||||
detail = {
|
||||
"id": act_id, "title": title, "sport": act_id.split("_")[1],
|
||||
"started_at": act_id.split("_")[0],
|
||||
"id": act_id, "title": title, "sport": sport,
|
||||
"started_at": started_at,
|
||||
"description": "", "privacy": "public", "custom": {},
|
||||
}
|
||||
(acts / f"{act_id}.json").write_text(json.dumps(detail))
|
||||
# Index
|
||||
index = {"activities": [
|
||||
{"id": "2024-01-01T08:00:00Z_cycling", "title": "Morning Ride",
|
||||
{"id": "2024-01-01T080000Z-morning-ride", "title": "Morning Ride",
|
||||
"sport": "cycling", "started_at": "2024-01-01T08:00:00Z", "privacy": "public", "custom": {}},
|
||||
{"id": "2024-01-02T09:00:00Z_running", "title": "Easy Run",
|
||||
{"id": "2024-01-02T090000Z-easy-run", "title": "Easy Run",
|
||||
"sport": "running", "started_at": "2024-01-02T09:00:00Z", "privacy": "public", "custom": {}},
|
||||
]}
|
||||
(tmp_path / "index.json").write_text(json.dumps(index))
|
||||
@@ -145,20 +145,20 @@ def test_merge_all_no_sidecars(data_dir):
|
||||
merged = data_dir / "_merged"
|
||||
assert merged.exists()
|
||||
# Unmodified files are symlinked
|
||||
detail_link = merged / "activities" / "2024-01-01T08:00:00Z_cycling.json"
|
||||
detail_link = merged / "activities" / "2024-01-01T080000Z-morning-ride.json"
|
||||
assert detail_link.is_symlink()
|
||||
|
||||
|
||||
def test_merge_all_applies_sidecar(data_dir):
|
||||
edits = data_dir / "edits"
|
||||
edits.mkdir()
|
||||
(edits / "2024-01-01T08:00:00Z_cycling.md").write_text(
|
||||
(edits / "2024-01-01T080000Z-morning-ride.md").write_text(
|
||||
"---\ntitle: Epic Ride\nhighlight: true\n---\n\nWhat a day!"
|
||||
)
|
||||
n = merge_all(data_dir)
|
||||
assert n == 1
|
||||
|
||||
merged_json = data_dir / "_merged" / "activities" / "2024-01-01T08:00:00Z_cycling.json"
|
||||
merged_json = data_dir / "_merged" / "activities" / "2024-01-01T080000Z-morning-ride.json"
|
||||
assert not merged_json.is_symlink()
|
||||
data = json.loads(merged_json.read_text())
|
||||
assert data["title"] == "Epic Ride"
|
||||
@@ -166,41 +166,41 @@ def test_merge_all_applies_sidecar(data_dir):
|
||||
assert data["description"] == "What a day!"
|
||||
|
||||
# Untouched activity is still a symlink
|
||||
run_link = data_dir / "_merged" / "activities" / "2024-01-02T09:00:00Z_running.json"
|
||||
run_link = data_dir / "_merged" / "activities" / "2024-01-02T090000Z-easy-run.json"
|
||||
assert run_link.is_symlink()
|
||||
|
||||
|
||||
def test_merge_all_private_filtered_from_index(data_dir):
|
||||
edits = data_dir / "edits"
|
||||
edits.mkdir()
|
||||
(edits / "2024-01-01T08:00:00Z_cycling.md").write_text("---\nprivate: true\n---\n")
|
||||
(edits / "2024-01-01T080000Z-morning-ride.md").write_text("---\nprivate: true\n---\n")
|
||||
merge_all(data_dir)
|
||||
|
||||
index = json.loads((data_dir / "_merged" / "index.json").read_text())
|
||||
ids = [a["id"] for a in index["activities"]]
|
||||
assert "2024-01-01T08:00:00Z_cycling" not in ids
|
||||
assert "2024-01-02T09:00:00Z_running" in ids
|
||||
assert "2024-01-01T080000Z-morning-ride" not in ids
|
||||
assert "2024-01-02T090000Z-easy-run" in ids
|
||||
|
||||
|
||||
def test_merge_all_highlight_sorts_first(data_dir):
|
||||
edits = data_dir / "edits"
|
||||
edits.mkdir()
|
||||
# Highlight the older activity — it should appear first
|
||||
(edits / "2024-01-01T08:00:00Z_cycling.md").write_text("---\nhighlight: true\n---\n")
|
||||
(edits / "2024-01-01T080000Z-morning-ride.md").write_text("---\nhighlight: true\n---\n")
|
||||
merge_all(data_dir)
|
||||
|
||||
index = json.loads((data_dir / "_merged" / "index.json").read_text())
|
||||
ids = [a["id"] for a in index["activities"]]
|
||||
assert ids[0] == "2024-01-01T08:00:00Z_cycling"
|
||||
assert ids[0] == "2024-01-01T080000Z-morning-ride"
|
||||
|
||||
|
||||
def test_merge_all_idempotent(data_dir):
|
||||
edits = data_dir / "edits"
|
||||
edits.mkdir()
|
||||
(edits / "2024-01-01T08:00:00Z_cycling.md").write_text("---\ntitle: Renamed\n---\n")
|
||||
(edits / "2024-01-01T080000Z-morning-ride.md").write_text("---\ntitle: Renamed\n---\n")
|
||||
merge_all(data_dir)
|
||||
merge_all(data_dir) # second run should not error or double-apply
|
||||
data = json.loads(
|
||||
(data_dir / "_merged" / "activities" / "2024-01-01T08:00:00Z_cycling.json").read_text()
|
||||
(data_dir / "_merged" / "activities" / "2024-01-01T080000Z-morning-ride.json").read_text()
|
||||
)
|
||||
assert data["title"] == "Renamed"
|
||||
|
||||
@@ -11,6 +11,16 @@ def test_running_variants():
|
||||
assert normalise_sport(raw) == "running", raw
|
||||
|
||||
|
||||
def test_skiing_variants():
|
||||
for raw in ("skiing", "alpine_skiing", "nordic_skiing", "backcountry_ski"):
|
||||
assert normalise_sport(raw) == "skiing", raw
|
||||
|
||||
|
||||
def test_swimming_variants():
|
||||
for raw in ("swimming", "swim", "open_water_swimming", "lap_swimming"):
|
||||
assert normalise_sport(raw) == "swimming", raw
|
||||
|
||||
|
||||
def test_unknown_falls_back_to_other():
|
||||
assert normalise_sport("yoga") == "other"
|
||||
assert normalise_sport(None) == "other"
|
||||
|
||||
+51
-1
@@ -1,4 +1,5 @@
|
||||
from bincio.extract.writer import make_activity_id, _slugify
|
||||
from bincio.extract.writer import make_activity_id, build_summary, _slugify
|
||||
from bincio.extract.metrics import ComputedMetrics
|
||||
from bincio.extract.models import ParsedActivity, DataPoint
|
||||
from datetime import datetime, timezone
|
||||
|
||||
@@ -31,3 +32,52 @@ def test_slugify():
|
||||
assert _slugify("Morning Ride!") == "morning-ride"
|
||||
assert _slugify(" Vélo ") == "velo" # é → e via NFKD + ASCII
|
||||
assert _slugify("") == ""
|
||||
|
||||
|
||||
def test_id_utc_conversion():
|
||||
"""Non-UTC timestamps should be converted to UTC in the ID."""
|
||||
from datetime import timedelta
|
||||
tz_plus2 = timezone(timedelta(hours=2))
|
||||
ts = datetime(2024, 6, 1, 9, 30, 12, tzinfo=tz_plus2) # 07:30:12 UTC
|
||||
act = ParsedActivity(
|
||||
points=[DataPoint(timestamp=ts)],
|
||||
sport="cycling",
|
||||
started_at=ts,
|
||||
source_file="test.fit",
|
||||
source_hash="sha256:abc",
|
||||
)
|
||||
assert make_activity_id(act) == "2024-06-01T073012Z"
|
||||
|
||||
|
||||
def test_build_summary_required_fields():
|
||||
"""build_summary should include all fields needed by the schema."""
|
||||
act = _dummy_activity("Test Ride")
|
||||
metrics = ComputedMetrics(
|
||||
distance_m=10000.0,
|
||||
duration_s=3600,
|
||||
moving_time_s=3500,
|
||||
elevation_gain_m=100.0,
|
||||
elevation_loss_m=95.0,
|
||||
avg_speed_kmh=10.0,
|
||||
max_speed_kmh=20.0,
|
||||
avg_hr_bpm=None,
|
||||
max_hr_bpm=None,
|
||||
avg_cadence_rpm=None,
|
||||
avg_power_w=None,
|
||||
max_power_w=None,
|
||||
bbox=None,
|
||||
start_latlng=None,
|
||||
end_latlng=None,
|
||||
mmp=None,
|
||||
best_efforts=None,
|
||||
best_climb_m=None,
|
||||
)
|
||||
summary = build_summary(act, metrics, "2024-06-01T073012Z-test-ride")
|
||||
# Required fields per schema
|
||||
assert summary["id"] == "2024-06-01T073012Z-test-ride"
|
||||
assert summary["title"] == "Test Ride"
|
||||
assert summary["sport"] == "cycling"
|
||||
assert "started_at" in summary
|
||||
assert "privacy" in summary
|
||||
assert "detail_url" in summary
|
||||
assert "track_url" in summary
|
||||
|
||||
Reference in New Issue
Block a user