mirror of
https://github.com/sstent/FitTrack_GarminSync.git
synced 2026-01-29 18:41:44 +00:00
326 lines
12 KiB
Python
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"]) |