"""
Unit tests for MaintenanceService.

Tests storage maintenance and diagnostics including:
- Cleanup expired files
- Storage analysis
- File counting and sizing
- Monitoring counts
- Binary storage stats

Source: src/langbot/pkg/api/http/service/maintenance.py
"""

from __future__ import annotations

import pytest
from unittest.mock import AsyncMock, Mock, patch, MagicMock
from types import SimpleNamespace
import datetime
from pathlib import Path

from langbot.pkg.api.http.service.maintenance import MaintenanceService


pytestmark = pytest.mark.asyncio


def _create_mock_result(scalar_value=None):
    """Create mock result object for persistence queries."""
    result = Mock()
    result.scalar = Mock(return_value=scalar_value)
    return result


class TestMaintenanceServiceCleanupExpiredFiles:
    """Tests for cleanup_expired_files method."""

    async def test_cleanup_expired_files_default_retention(self):
        """Uses default retention days when config not set."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {}
        ap.storage_mgr = SimpleNamespace()

        # Create a proper mock object with __class__.__name__
        storage_provider = MagicMock()
        storage_provider.__class__.__name__ = 'LocalStorageProvider'
        ap.storage_mgr.storage_provider = storage_provider

        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Mock the internal cleanup methods - one is async, one is not
        service._cleanup_expired_uploaded_files = AsyncMock(return_value=0)
        service._cleanup_expired_log_files = Mock(return_value=0)  # NOT async!

        # Execute
        result = await service.cleanup_expired_files()

        # Verify - returns counts
        assert 'uploaded_files' in result
        assert 'log_files' in result
        assert result['uploaded_files'] == 0
        assert result['log_files'] == 0

    async def test_cleanup_expired_files_custom_retention(self):
        """Uses custom retention days from config."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {
            'storage': {
                'cleanup': {
                    'uploaded_file_retention_days': 14,
                    'log_retention_days': 7,
                }
            }
        }
        ap.storage_mgr = SimpleNamespace()

        storage_provider = MagicMock()
        storage_provider.__class__.__name__ = 'LocalStorageProvider'
        ap.storage_mgr.storage_provider = storage_provider

        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Mock the internal cleanup methods
        service._cleanup_expired_uploaded_files = AsyncMock(return_value=2)
        service._cleanup_expired_log_files = Mock(return_value=3)  # NOT async

        # Execute
        result = await service.cleanup_expired_files()

        # Verify
        assert result['uploaded_files'] == 2
        assert result['log_files'] == 3

    async def test_cleanup_expired_files_s3_provider(self):
        """Handles S3StorageProvider correctly."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {}
        ap.storage_mgr = SimpleNamespace()

        # Mock S3 provider
        s3_provider = MagicMock()
        s3_provider.__class__.__name__ = 'S3StorageProvider'
        s3_provider.delete = AsyncMock()
        ap.storage_mgr.storage_provider = s3_provider
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Mock the internal cleanup methods
        service._cleanup_expired_uploaded_files = AsyncMock(return_value=1)
        service._cleanup_expired_log_files = Mock(return_value=0)  # NOT async

        # Execute
        result = await service.cleanup_expired_files()

        # Verify
        assert result['uploaded_files'] == 1
        assert result['log_files'] == 0

    async def test_cleanup_expired_files_invalid_retention(self):
        """Uses default for invalid retention config."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {
            'storage': {
                'cleanup': {
                    'uploaded_file_retention_days': 'invalid',  # Invalid
                    'log_retention_days': 0,  # Invalid (less than 1)
                }
            }
        }
        ap.storage_mgr = SimpleNamespace()

        storage_provider = MagicMock()
        storage_provider.__class__.__name__ = 'LocalStorageProvider'
        ap.storage_mgr.storage_provider = storage_provider

        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Mock the internal cleanup methods
        service._cleanup_expired_uploaded_files = AsyncMock(return_value=0)
        service._cleanup_expired_log_files = Mock(return_value=0)  # NOT async

        # Execute
        result = await service.cleanup_expired_files()

        # Verify - warning logged, defaults used
        assert ap.logger.warning.called
        assert 'uploaded_files' in result


class TestMaintenanceServiceGetStorageAnalysis:
    """Tests for get_storage_analysis method."""

    async def test_get_storage_analysis_basic(self):
        """Returns basic storage analysis."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {'database': {'use': 'sqlite', 'sqlite': {'path': 'data/langbot.db'}}}
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()
        ap.task_mgr = SimpleNamespace()
        ap.task_mgr.get_stats = Mock(return_value={'running': 0})

        # Mock monitoring counts
        count_result = _create_mock_result(scalar_value=10)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        # Mock file operations
        service._path_size = Mock(return_value=1000)
        service._file_count = Mock(return_value=5)
        service._monitoring_counts = AsyncMock(return_value={'messages': 10, 'errors': 0})
        service._binary_storage_stats = AsyncMock(return_value={'count': 5, 'size_bytes': 500})
        service._expired_uploaded_candidates = AsyncMock(return_value=[])
        service._expired_log_candidates = Mock(return_value=[])

        # Execute
        result = await service.get_storage_analysis()

        # Verify
        assert 'generated_at' in result
        assert 'cleanup_policy' in result
        assert 'sections' in result
        assert 'database' in result
        assert 'cleanup_candidates' in result

    async def test_get_storage_analysis_sections(self):
        """Returns all storage sections."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {'database': {'use': 'postgresql'}}
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()
        ap.task_mgr = None

        count_result = _create_mock_result(scalar_value=0)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        service._path_size = Mock(return_value=0)
        service._file_count = Mock(return_value=0)
        service._monitoring_counts = AsyncMock(return_value={})
        service._binary_storage_stats = AsyncMock(return_value={'count': 0, 'size_bytes': 0})
        service._expired_uploaded_candidates = AsyncMock(return_value=[])
        service._expired_log_candidates = Mock(return_value=[])

        # Execute
        result = await service.get_storage_analysis()

        # Verify - all sections present
        sections = {s['key'] for s in result['sections']}
        assert 'database' in sections
        assert 'logs' in sections
        assert 'storage' in sections
        assert 'vector_store' in sections
        assert 'plugins' in sections
        assert 'mcp' in sections
        assert 'temp' in sections

    async def test_get_storage_analysis_postgresql(self):
        """Handles PostgreSQL database type."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {'database': {'use': 'postgresql'}}
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()
        ap.task_mgr = None

        count_result = _create_mock_result(scalar_value=0)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        service._path_size = Mock(return_value=0)
        service._file_count = Mock(return_value=0)
        service._monitoring_counts = AsyncMock(return_value={})
        service._binary_storage_stats = AsyncMock(return_value={'count': 0, 'size_bytes': None})
        service._expired_uploaded_candidates = AsyncMock(return_value=[])
        service._expired_log_candidates = Mock(return_value=[])

        # Execute
        result = await service.get_storage_analysis()

        # Verify
        assert result['database']['type'] == 'postgresql'

    async def test_get_storage_analysis_with_cleanup_candidates(self):
        """Returns cleanup candidates in analysis."""
        # Setup
        ap = SimpleNamespace()
        ap.instance_config = SimpleNamespace()
        ap.instance_config.data = {}
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()
        ap.task_mgr = None

        count_result = _create_mock_result(scalar_value=0)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        service._path_size = Mock(return_value=0)
        service._file_count = Mock(return_value=0)
        service._monitoring_counts = AsyncMock(return_value={})
        service._binary_storage_stats = AsyncMock(return_value={'count': 0, 'size_bytes': 0})
        service._expired_uploaded_candidates = AsyncMock(return_value=[{'key': 'old_file', 'size_bytes': 100}])
        service._expired_log_candidates = Mock(return_value=[{'name': 'old_log', 'size_bytes': 50}])

        # Execute
        result = await service.get_storage_analysis()

        # Verify
        assert len(result['cleanup_candidates']['uploaded_files']) == 1
        assert len(result['cleanup_candidates']['log_files']) == 1


class TestMaintenanceServiceMonitoringCounts:
    """Tests for _monitoring_counts method."""

    async def test_monitoring_counts_returns_counts(self):
        """Returns counts for all monitoring tables."""
        # Setup
        ap = SimpleNamespace()
        ap.persistence_mgr = SimpleNamespace()

        count_result = _create_mock_result(scalar_value=42)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        # Execute
        result = await service._monitoring_counts()

        # Verify - all table keys present
        assert 'messages' in result
        assert 'llm_calls' in result
        assert 'embedding_calls' in result
        assert 'errors' in result
        assert 'sessions' in result
        assert 'feedback' in result

    async def test_monitoring_counts_zero_results(self):
        """Returns zero counts when tables empty."""
        # Setup
        ap = SimpleNamespace()
        ap.persistence_mgr = SimpleNamespace()

        count_result = _create_mock_result(scalar_value=0)
        ap.persistence_mgr.execute_async = AsyncMock(return_value=count_result)

        service = MaintenanceService(ap)

        # Execute
        result = await service._monitoring_counts()

        # Verify - all zero
        assert all(v == 0 for v in result.values())


class TestMaintenanceServiceBinaryStorageStats:
    """Tests for _binary_storage_stats method."""

    async def test_binary_storage_stats_returns_stats(self):
        """Returns count and size for binary storage."""
        # Setup
        ap = SimpleNamespace()
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        # Mock count result
        count_result = _create_mock_result(scalar_value=10)
        # Mock size result
        size_result = _create_mock_result(scalar_value=5000)

        call_count = 0

        async def mock_execute(query):
            nonlocal call_count
            call_count += 1
            if call_count == 1:
                return count_result
            return size_result

        ap.persistence_mgr.execute_async = AsyncMock(side_effect=mock_execute)

        service = MaintenanceService(ap)

        # Execute
        result = await service._binary_storage_stats()

        # Verify
        assert result['count'] == 10
        assert result['size_bytes'] == 5000

    async def test_binary_storage_stats_size_error(self):
        """Handles error when calculating size."""
        # Setup
        ap = SimpleNamespace()
        ap.persistence_mgr = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        count_result = _create_mock_result(scalar_value=5)

        call_count = 0

        async def mock_execute(query):
            nonlocal call_count
            call_count += 1
            if call_count == 1:
                return count_result
            raise Exception('Size calculation error')

        ap.persistence_mgr.execute_async = AsyncMock(side_effect=mock_execute)

        service = MaintenanceService(ap)

        # Execute
        result = await service._binary_storage_stats()

        # Verify - warning logged, size_bytes None or 0
        assert ap.logger.warning.called
        assert result['count'] == 5


class TestMaintenanceServicePathSize:
    """Tests for _path_size method."""

    def test_path_size_nonexistent_path(self):
        """Returns 0 for nonexistent path."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        # Execute
        result = service._path_size(Path('/nonexistent/path'))

        # Verify
        assert result == 0

    def test_path_size_single_file(self):
        """Returns size for single file."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        # Mock file
        mock_stat = Mock()
        mock_stat.st_size = 100

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'is_file', return_value=True):
                with patch.object(Path, 'stat', return_value=mock_stat):
                    result = service._path_size(Path('test.txt'))

        # Verify
        assert result == 100

    def test_path_size_directory(self):
        """Returns total size for directory."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        # Mock os.walk
        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'is_file', return_value=False):
                with patch('os.walk') as mock_walk:
                    mock_walk.return_value = [
                        ('/test_dir', [], ['file1.txt', 'file2.txt']),
                    ]

                    # Mock file stat
                    mock_stat = Mock()
                    mock_stat.st_size = 50

                    with patch.object(Path, 'stat', return_value=mock_stat):
                        result = service._path_size(Path('/test_dir'))

        # Verify - 2 files * 50 bytes
        assert result == 100


class TestMaintenanceServiceFileCount:
    """Tests for _file_count method."""

    def test_file_count_nonexistent_path(self):
        """Returns 0 for nonexistent path."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        # Execute
        result = service._file_count(Path('/nonexistent/path'))

        # Verify
        assert result == 0

    def test_file_count_single_file(self):
        """Returns 1 for single file."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'is_file', return_value=True):
                result = service._file_count(Path('test.txt'))

        # Verify
        assert result == 1

    def test_file_count_directory(self):
        """Returns file count for directory."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'is_file', return_value=False):
                with patch('os.walk') as mock_walk:
                    mock_walk.return_value = [
                        ('/test_dir', [], ['file1.txt', 'file2.txt', 'file3.txt']),
                    ]
                    result = service._file_count(Path('/test_dir'))

        # Verify
        assert result == 3


class TestMaintenanceServicePositiveInt:
    """Tests for _positive_int helper method."""

    def test_positive_int_valid_value(self):
        """Returns valid positive integer."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Execute
        result = service._positive_int(7, 5, 'test_param')

        # Verify
        assert result == 7
        assert not ap.logger.warning.called

    def test_positive_int_invalid_string(self):
        """Returns default for invalid string."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Execute
        result = service._positive_int('invalid', 5, 'test_param')

        # Verify
        assert result == 5
        assert ap.logger.warning.called

    def test_positive_int_invalid_none(self):
        """Returns default for None."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Execute
        result = service._positive_int(None, 5, 'test_param')

        # Verify
        assert result == 5
        assert ap.logger.warning.called

    def test_positive_int_negative_value(self):
        """Returns default for negative value."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Execute
        result = service._positive_int(-1, 5, 'test_param')

        # Verify
        assert result == 5
        assert ap.logger.warning.called

    def test_positive_int_zero_value(self):
        """Returns default for zero value."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()
        ap.logger.warning = Mock()

        service = MaintenanceService(ap)

        # Execute
        result = service._positive_int(0, 5, 'test_param')

        # Verify
        assert result == 5
        assert ap.logger.warning.called


class TestMaintenanceServiceIsUploadedFileKey:
    """Tests for _is_uploaded_file_key helper method."""

    def test_is_uploaded_file_key_valid(self):
        """Returns True for valid upload file key."""
        # Setup
        ap = SimpleNamespace()

        service = MaintenanceService(ap)

        # Execute - simple filename without path
        result = service._is_uploaded_file_key('uploaded_file.txt')

        # Verify
        assert result is True

    def test_is_uploaded_file_key_with_path(self):
        """Returns False for key with path separator."""
        # Setup
        ap = SimpleNamespace()

        service = MaintenanceService(ap)

        # Execute - key with path
        result = service._is_uploaded_file_key('path/to/file.txt')

        # Verify
        assert result is False

    def test_is_uploaded_file_key_plugin_config(self):
        """Returns False for plugin config prefix."""
        # Setup
        ap = SimpleNamespace()

        service = MaintenanceService(ap)

        # Execute - plugin config file
        result = service._is_uploaded_file_key('plugin_config_some_plugin.json')

        # Verify
        assert result is False


class TestMaintenanceServiceExpiredLogCandidates:
    """Tests for _expired_log_candidates method."""

    def test_expired_log_candidates_nonexistent_dir(self):
        """Returns empty list when logs dir not exists."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        with patch.object(Path, 'exists', return_value=False):
            result = service._expired_log_candidates(3)

        # Verify
        assert result == []

    def test_expired_log_candidates_matches_pattern(self):
        """Matches log file pattern correctly."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        # Mock directory with log files
        old_date = datetime.date.today() - datetime.timedelta(days=10)
        old_log_name = f'langbot-{old_date.isoformat()}.log'
        recent_log_name = f'langbot-{datetime.date.today().isoformat()}.log'

        mock_entry_old = Mock(spec=Path)
        mock_entry_old.is_file = Mock(return_value=True)
        mock_entry_old.name = old_log_name
        mock_stat = Mock()
        mock_stat.st_size = 1000
        mock_entry_old.stat = Mock(return_value=mock_stat)

        mock_entry_recent = Mock(spec=Path)
        mock_entry_recent.is_file = Mock(return_value=True)
        mock_entry_recent.name = recent_log_name
        mock_stat2 = Mock()
        mock_stat2.st_size = 500
        mock_entry_recent.stat = Mock(return_value=mock_stat2)

        # Non-log file
        mock_entry_other = Mock(spec=Path)
        mock_entry_other.is_file = Mock(return_value=True)
        mock_entry_other.name = 'other_file.txt'

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'iterdir') as mock_iterdir:
                mock_iterdir.return_value = [mock_entry_old, mock_entry_recent, mock_entry_other]
                result = service._expired_log_candidates(3)

        # Verify - only old log included
        assert len(result) == 1
        assert result[0]['name'] == old_log_name

    def test_expired_log_candidates_includes_path(self):
        """Includes path when include_paths=True."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        old_date = datetime.date.today() - datetime.timedelta(days=10)
        old_log_name = f'langbot-{old_date.isoformat()}.log'

        mock_entry = Mock(spec=Path)
        mock_entry.is_file = Mock(return_value=True)
        mock_entry.name = old_log_name
        mock_entry.__str__ = Mock(return_value='/data/logs/' + old_log_name)
        mock_stat = Mock()
        mock_stat.st_size = 1000
        mock_entry.stat = Mock(return_value=mock_stat)

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'iterdir') as mock_iterdir:
                mock_iterdir.return_value = [mock_entry]
                result = service._expired_log_candidates(3, include_paths=True)

        # Verify - path included
        assert 'path' in result[0]


class TestMaintenanceServiceExpiredLocalUploadCandidates:
    """Tests for _expired_local_upload_candidates method."""

    def test_expired_local_upload_candidates_nonexistent_dir(self):
        """Returns empty list when storage dir not exists."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)

        with patch.object(Path, 'exists', return_value=False):
            result = service._expired_local_upload_candidates(7)

        # Verify
        assert result == []

    def test_expired_local_upload_candidates_filters_uploaded(self):
        """Only returns uploaded files matching pattern."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)
        # Mock _is_uploaded_file_key
        service._is_uploaded_file_key = Mock(side_effect=lambda key: 'plugin_config_' not in key and '/' not in key)

        # Create mock files - one valid, one plugin config
        mock_entry_valid = Mock(spec=Path)
        mock_entry_valid.is_file = Mock(return_value=True)
        mock_entry_valid.name = 'valid_upload.txt'
        mock_stat = Mock()
        mock_stat.st_size = 100
        mock_stat.st_mtime = 0  # Very old
        mock_entry_valid.stat = Mock(return_value=mock_stat)

        mock_entry_plugin = Mock(spec=Path)
        mock_entry_plugin.is_file = Mock(return_value=True)
        mock_entry_plugin.name = 'plugin_config_test.json'
        mock_stat2 = Mock()
        mock_stat2.st_size = 200
        mock_stat2.st_mtime = 0
        mock_entry_plugin.stat = Mock(return_value=mock_stat2)

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'iterdir') as mock_iterdir:
                mock_iterdir.return_value = [mock_entry_valid, mock_entry_plugin]
                result = service._expired_local_upload_candidates(7)

        # Verify - only valid upload included
        assert len(result) == 1
        assert result[0]['key'] == 'valid_upload.txt'

    def test_expired_local_upload_candidates_includes_path(self):
        """Includes path when include_paths=True."""
        # Setup
        ap = SimpleNamespace()
        ap.logger = SimpleNamespace()

        service = MaintenanceService(ap)
        service._is_uploaded_file_key = Mock(return_value=True)

        mock_entry = Mock(spec=Path)
        mock_entry.is_file = Mock(return_value=True)
        mock_entry.name = 'old_file.txt'
        mock_entry.__str__ = Mock(return_value='/data/storage/old_file.txt')
        mock_stat = Mock()
        mock_stat.st_size = 100
        mock_stat.st_mtime = 0
        mock_entry.stat = Mock(return_value=mock_stat)

        with patch.object(Path, 'exists', return_value=True):
            with patch.object(Path, 'iterdir') as mock_iterdir:
                mock_iterdir.return_value = [mock_entry]
                result = service._expired_local_upload_candidates(7, include_paths=True)

        # Verify - path included
        assert 'path' in result[0]
