Files
FitTrack2/FitnessSync/backend/tests/test_segments_verification.py
2026-01-09 12:10:58 -08:00

116 lines
3.7 KiB
Python

from unittest.mock import MagicMock
import sys
# Mock fitdecode before imports since it might not be installed in local env (running in docker)
sys.modules['fitdecode'] = MagicMock()
import pytest
from src.utils.geo import ramer_douglas_peucker, haversine_distance, calculate_bounds
from src.services.segment_matcher import SegmentMatcher
from src.models.activity import Activity
from src.models.segment import Segment
from datetime import datetime, timedelta
import json
from unittest.mock import patch
def test_haversine():
# Dist between (0,0) and (0,1) deg is ~111km
d = haversine_distance(0, 0, 0, 1)
# 1 deg lat ~ 111.32 km
assert 110000 < d < 112000
def test_rdp_simple():
# Points on a line
points = [[0,0], [1,1], [2,2]]
# Should simplify to [0,0], [2,2]
simplified = ramer_douglas_peucker(points, epsilon=0.1)
assert len(simplified) == 2
assert simplified[0] == [0,0]
assert simplified[1] == [2,2]
def test_rdp_peak():
# Triangle
points = [[0,0], [1,10], [2,0]] # [lon, lat] note: RDP expects [lon, lat] usually?
# My RDP implementation uses x,y so order doesn't matter for geometric shape
simplified = ramer_douglas_peucker(points, epsilon=1.0)
assert len(simplified) == 3
def test_bounds():
points = [[0,0], [10, 10], [-5, 5]]
bounds = calculate_bounds(points)
assert bounds['min_lat'] == 0 # wait, index 1 is lat? check utils
# If points are [lon, lat]
# 0,0: lat=0
# 10,10: lat=10
# -5,5: lat=5
# bounds are min_lat=0, max_lat=10. min_lon=-5, max_lon=10
# My calculate_bounds implementation assumes [lon, lat]
assert bounds['min_lat'] == 0
assert bounds['max_lat'] == 10
assert bounds['min_lon'] == -5
assert bounds['max_lon'] == 10
def test_matcher_logic():
# Mock DB session
mock_session = MagicMock()
# Create segment [0,0] -> [0, 0.01] (approx 1.1km north)
segment_points = [[0,0], [0, 0.01]]
segment = Segment(
id=1,
name="Test Seg",
points=json.dumps(segment_points),
bounds=json.dumps(calculate_bounds(segment_points)),
distance=1110.0,
activity_type='cycling'
)
mock_session.query.return_value.filter.return_value.all.return_value = [segment]
matcher = SegmentMatcher(mock_session)
# Create activity trace that covers this
# 0,0 at T=0
# 0,0.01 at T=100s
act_points = [[0,0], [0, 0.005], [0, 0.01]]
# Mock activity
activity = Activity(id=100, activity_start_time=datetime.now())
# Matcher needs to use parsers internally? Or uses slice of points?
# Matcher logic (_match_segment) uses points list passed to match_activity
# Wait, _match_segment needs timestamps to calc elapsed time.
# We need to mock extract_timestamps_from_file or patch it
from unittest.mock import patch
with patch('src.services.segment_matcher.extract_timestamps_from_file') as mock_extract:
# 0,0@T0, 0,0.005@T50, 0,0.01@T100
start_time = datetime.now()
timestamps = [start_time, start_time + timedelta(seconds=50), start_time + timedelta(seconds=100)]
mock_extract.return_value = timestamps
# Add dummy content
activity.file_content = b'dummy'
activity.file_type = 'fit'
# Run match
efforts = matcher.match_activity(activity, act_points)
assert len(efforts) == 1
effort = efforts[0]
assert effort.segment_id == 1
assert effort.elapsed_time == 100.0
if __name__ == "__main__":
# verification
try:
test_haversine()
test_rdp_simple()
test_bounds()
print("Geo Utils Passed")
except Exception as e:
print(f"Failed: {e}")