Files
FitTrack_GarminSync/examples/Garmin_Analyser/tests/test_download_tracking.py

326 lines
12 KiB
Python

"""Tests for download tracking functionality with SQLite database."""
import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import patch, MagicMock
from clients.garmin_client import GarminClient
from db.session import SessionLocal
from db.models import ActivityDownload, Base
from config.settings import DATA_DIR, DB_PATH
class TestDownloadTracking:
"""Test download tracking functionality."""
@pytest.fixture(autouse=True)
def setup_and_teardown(self):
"""Set up test database and clean up after tests."""
# Create a temporary directory for test data
self.test_data_dir = Path(tempfile.mkdtemp())
# Create test database
self.test_db_path = self.test_data_dir / "test_garmin_analyser.db"
# Patch settings to use test paths
with patch('config.settings.DATA_DIR', self.test_data_dir), \
patch('config.settings.DB_PATH', self.test_db_path):
# Initialize test database
from db.session import engine
Base.metadata.create_all(bind=engine)
yield
# Clean up
if self.test_db_path.exists():
self.test_db_path.unlink()
if self.test_data_dir.exists():
shutil.rmtree(self.test_data_dir)
def test_upsert_activity_download_success(self):
"""Test upserting a successful download record."""
from clients.garmin_client import upsert_activity_download
# Create test data
activity_id = 12345
file_path = self.test_data_dir / "test_activity.fit"
file_path.write_bytes(b"test file content")
# Call the function
with SessionLocal() as db:
result = upsert_activity_download(
activity_id=activity_id,
source="garmin-connect",
file_path=file_path,
file_format="fit",
status="success",
size_bytes=len(file_path.read_bytes()),
checksum_sha256="test_checksum",
db_session=db
)
# Verify the record was created
record = db.query(ActivityDownload).filter_by(activity_id=activity_id).first()
assert record is not None
assert record.activity_id == activity_id
assert record.source == "garmin-connect"
assert record.status == "success"
assert record.file_format == "fit"
assert record.size_bytes == 18 # Length of "test file content"
assert record.checksum_sha256 == "test_checksum"
def test_upsert_activity_download_failure(self):
"""Test upserting a failed download record."""
from clients.garmin_client import upsert_activity_download
activity_id = 67890
# Call the function with failure status
with SessionLocal() as db:
result = upsert_activity_download(
activity_id=activity_id,
source="garmin-connect",
file_path=self.test_data_dir / "nonexistent.fit",
file_format="fit",
status="failed",
error_message="Download timeout",
http_status=500,
db_session=db
)
# Verify the record was created
record = db.query(ActivityDownload).filter_by(activity_id=activity_id).first()
assert record is not None
assert record.activity_id == activity_id
assert record.status == "failed"
assert record.error_message == "Download timeout"
assert record.http_status == 500
def test_upsert_activity_download_update_existing(self):
"""Test updating an existing download record."""
from clients.garmin_client import upsert_activity_download
activity_id = 11111
# Create initial record
with SessionLocal() as db:
initial_record = ActivityDownload(
activity_id=activity_id,
source="garmin-connect",
file_path=str(self.test_data_dir / "old.fit"),
file_format="fit",
status="success",
size_bytes=100,
checksum_sha256="old_checksum"
)
db.add(initial_record)
db.commit()
# Update the record
with SessionLocal() as db:
result = upsert_activity_download(
activity_id=activity_id,
source="garmin-connect",
file_path=self.test_data_dir / "new.fit",
file_format="fit",
status="success",
size_bytes=200,
checksum_sha256="new_checksum",
db_session=db
)
# Verify the record was updated
record = db.query(ActivityDownload).filter_by(activity_id=activity_id).first()
assert record is not None
assert record.size_bytes == 200
assert record.checksum_sha256 == "new_checksum"
def test_calculate_sha256(self):
"""Test SHA256 checksum calculation."""
from clients.garmin_client import calculate_sha256
# Create test file
test_file = self.test_data_dir / "test.txt"
test_content = b"Hello, world! This is a test file for SHA256 calculation."
test_file.write_bytes(test_content)
# Calculate checksum
checksum = calculate_sha256(test_file)
# Verify the checksum is correct
import hashlib
expected_checksum = hashlib.sha256(test_content).hexdigest()
assert checksum == expected_checksum
def test_should_skip_download_exists_and_matches(self):
"""Test skip logic when file exists and checksum matches."""
from clients.garmin_client import should_skip_download
activity_id = 22222
# Create test file
test_file = self.test_data_dir / "activity_22222.fit"
test_content = b"test workout data"
test_file.write_bytes(test_content)
# Create database record with matching checksum
with SessionLocal() as db:
record = ActivityDownload(
activity_id=activity_id,
source="garmin-connect",
file_path=str(test_file),
file_format="fit",
status="success",
size_bytes=len(test_content),
checksum_sha256=calculate_sha256(test_file)
)
db.add(record)
db.commit()
# Test should skip
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db)
assert should_skip is True
assert "already downloaded" in reason.lower()
def test_should_skip_download_exists_checksum_mismatch(self):
"""Test skip logic when file exists but checksum doesn't match."""
from clients.garmin_client import should_skip_download, calculate_sha256
activity_id = 33333
# Create test file with different content than recorded
test_file = self.test_data_dir / "activity_33333.fit"
test_content = b"new workout data - different from recorded"
test_file.write_bytes(test_content)
# Create database record with different checksum
with SessionLocal() as db:
record = ActivityDownload(
activity_id=activity_id,
source="garmin-connect",
file_path=str(test_file),
file_format="fit",
status="success",
size_bytes=100, # Different size
checksum_sha256="old_mismatched_checksum_12345"
)
db.add(record)
db.commit()
# Test should not skip
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db)
assert should_skip is False
assert "checksum mismatch" in reason.lower()
def test_should_skip_download_file_missing(self):
"""Test skip logic when database record exists but file is missing."""
from clients.garmin_client import should_skip_download
activity_id = 44444
# Create database record but no actual file
with SessionLocal() as db:
record = ActivityDownload(
activity_id=activity_id,
source="garmin-connect",
file_path=str(self.test_data_dir / "missing_activity.fit"),
file_format="fit",
status="success",
size_bytes=100,
checksum_sha256="some_checksum"
)
db.add(record)
db.commit()
# Test should not skip
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db)
assert should_skip is False
assert "file missing" in reason.lower()
def test_should_skip_download_no_record(self):
"""Test skip logic when no database record exists."""
from clients.garmin_client import should_skip_download
activity_id = 55555
# No record in database
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db)
assert should_skip is False
assert "no record" in reason.lower()
@patch('clients.garmin_client.GarminClient.authenticate')
@patch('clients.garmin_client.GarminClient.download_activity_original')
def test_download_activity_with_db_integration(self, mock_download, mock_authenticate):
"""Test download_activity_original with database integration."""
mock_authenticate.return_value = True
# Create test file content
test_content = b"FIT file content for testing"
test_file = self.test_data_dir / "activity_66666.fit"
# Mock the download to return our test file path
mock_download.return_value = test_file
# Create the test file
test_file.write_bytes(test_content)
# Create client and test download
client = GarminClient()
result = client.download_activity_original("66666")
# Verify download was called
mock_download.assert_called_once_with("66666", force_download=False, db_session=None)
# Verify database record was created
with SessionLocal() as db:
record = db.query(ActivityDownload).filter_by(activity_id=66666).first()
assert record is not None
assert record.status == "success"
assert record.size_bytes == len(test_content)
assert record.checksum_sha256 is not None
def test_force_download_override(self):
"""Test that force_download=True overrides skip logic."""
from clients.garmin_client import should_skip_download
activity_id = 77777
# Create existing record that would normally cause skip
with SessionLocal() as db:
record = ActivityDownload(
activity_id=activity_id,
source="garmin-connect",
file_path=str(self.test_data_dir / "activity_77777.fit"),
file_format="fit",
status="success",
size_bytes=100,
checksum_sha256="valid_checksum"
)
db.add(record)
db.commit()
# Create the file too
test_file = self.test_data_dir / "activity_77777.fit"
test_file.write_bytes(b"test content")
# Test with force_download=False (should skip)
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db, force_download=False)
assert should_skip is True
# Test with force_download=True (should not skip)
with SessionLocal() as db:
should_skip, reason = should_skip_download(activity_id, db, force_download=True)
assert should_skip is False
assert "force download" in reason.lower()
if __name__ == "__main__":
pytest.main([__file__, "-v"])