Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion forklet/infrastructure/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def setup_logger(

# Add console handler if requested
if console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

Expand Down
50 changes: 45 additions & 5 deletions forklet/interfaces/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Python API for Forklet GitHub Repository Downloader.
"""

import logging
from typing import Optional, List, Dict, Any, Callable
from pathlib import Path

Expand All @@ -14,25 +15,34 @@
DownloadRequest, DownloadResult, DownloadStrategy, FilterCriteria,
RepositoryInfo, GitReference, ProgressInfo, DownloadConfig
)


#####
class GitHubDownloader:
"""
Main API class for programmatic access to Forklet functionality.

Provides a clean, typed interface for downloading GitHub repository content.
"""
def __init__(self, auth_token: Optional[str] = None):

def __init__(self, auth_token: Optional[str] = None, verbose: bool = False):
"""
Initialize the downloader with optional authentication.

Args:
auth_token: GitHub personal access token for authentication
verbose: Enable detailed logging and progress information (default: False)
"""



self.verbose = verbose
self.auth_token = auth_token

# Configure logger based on verbose setting
if verbose:
logger.setLevel(logging.DEBUG)
logger.debug("Verbose logging enabled for GitHubDownloader")
else:
logger.setLevel(logging.INFO)

self.rate_limiter = RateLimiter()
self.retry_manager = RetryManager()

Expand Down Expand Up @@ -121,9 +131,18 @@ async def download(
"""

try:
logger.debug(f"Starting download for {owner}/{repo}@{ref}")
logger.debug(f"Destination: {destination}")
logger.debug(f"Strategy: {strategy}")
logger.debug(f"Include patterns: {include_patterns}")
logger.debug(f"Exclude patterns: {exclude_patterns}")

# Get repository information
repo_info = await self.get_repository_info(owner, repo)
logger.debug(f"Repository info: {repo_info.full_name}, size: {repo_info.size}KB")

git_ref = await self.resolve_reference(owner, repo, ref)
logger.debug(f"Resolved reference {ref} to {git_ref.ref_type}: {git_ref.sha}")

# Create filter criteria
filters = FilterCriteria(
Expand All @@ -148,8 +167,14 @@ async def download(
)

# Execute download
logger.debug("Starting download execution...")

result = await self.orchestrator.execute_download(request)

logger.debug(f"Download completed: {len(result.downloaded_files)} files, "
f"{len(result.failed_files)} failures, "
f"{result.progress.downloaded_bytes} bytes")

return result

except Exception as e:
Expand Down Expand Up @@ -270,3 +295,18 @@ async def get_download_progress(self) -> Optional[ProgressInfo]:
"""

return await self.orchestrator.get_current_progress()

def set_verbose(self, verbose: bool) -> None:
"""
Enable or disable verbose logging at runtime.

Args:
verbose: True to enable verbose logging, False to disable
"""
self.verbose = verbose
if verbose:
logger.setLevel(logging.DEBUG)
logger.debug("Verbose logging enabled")
else:
logger.setLevel(logging.INFO)
logger.info("Verbose logging disabled")
95 changes: 95 additions & 0 deletions tests/interfaces/test_verbose_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
Unit tests for verbose logging functionality in GitHubDownloader API.
"""

import pytest
import logging
from unittest.mock import Mock, patch
from forklet.interfaces.api import GitHubDownloader
from forklet.infrastructure.logger import logger


class TestVerboseLogging:
"""Test cases for verbose logging functionality."""

def test_default_initialization(self):
"""Test that GitHubDownloader initializes with verbose=False by default."""
downloader = GitHubDownloader()
assert downloader.verbose is False

def test_verbose_initialization(self):
"""Test that GitHubDownloader can be initialized with verbose=True."""
downloader = GitHubDownloader(verbose=True)
assert downloader.verbose is True

def test_verbose_with_auth_token(self):
"""Test that verbose mode works with authentication token."""
downloader = GitHubDownloader(auth_token="test_token", verbose=True)
assert downloader.verbose is True
assert downloader.auth_token == "test_token"

@patch('forklet.interfaces.api.logger')
def test_logger_level_verbose_true(self, mock_logger):
"""Test that logger level is set to DEBUG when verbose=True."""
GitHubDownloader(verbose=True)
mock_logger.setLevel.assert_called_with(logging.DEBUG)

@patch('forklet.interfaces.api.logger')
def test_logger_level_verbose_false(self, mock_logger):
"""Test that logger level is set to INFO when verbose=False."""
GitHubDownloader(verbose=False)
mock_logger.setLevel.assert_called_with(logging.INFO)

@patch('forklet.interfaces.api.logger')
def test_set_verbose_method_enable(self, mock_logger):
"""Test the set_verbose method when enabling verbose mode."""
downloader = GitHubDownloader(verbose=False)
downloader.set_verbose(True)

assert downloader.verbose is True
# Should be called twice: once in __init__, once in set_verbose
assert mock_logger.setLevel.call_count >= 2
mock_logger.setLevel.assert_called_with(logging.DEBUG)

@patch('forklet.interfaces.api.logger')
def test_set_verbose_method_disable(self, mock_logger):
"""Test the set_verbose method when disabling verbose mode."""
downloader = GitHubDownloader(verbose=True)
downloader.set_verbose(False)

assert downloader.verbose is False
# Should be called twice: once in __init__, once in set_verbose
assert mock_logger.setLevel.call_count >= 2
mock_logger.setLevel.assert_called_with(logging.INFO)

def test_verbose_mode_toggle(self):
"""Test toggling verbose mode multiple times."""
downloader = GitHubDownloader()

# Start with False
assert downloader.verbose is False

# Enable
downloader.set_verbose(True)
assert downloader.verbose is True

# Disable
downloader.set_verbose(False)
assert downloader.verbose is False

# Enable again
downloader.set_verbose(True)
assert downloader.verbose is True

@patch('forklet.interfaces.api.logger')
def test_verbose_logging_in_download_method(self, mock_logger):
"""Test that verbose logging occurs during download operations."""
# This test would require mocking the entire download chain
# For now, we'll just test that the downloader is properly configured
downloader = GitHubDownloader(verbose=True)

# Verify the downloader has verbose mode enabled
assert downloader.verbose is True

# Verify logger was configured for DEBUG level
mock_logger.setLevel.assert_called_with(logging.DEBUG)