# [DEF:__tests__/test_task_logger:Module]
# @RELATION: VERIFIES -> ../task_logger.py
# @PURPOSE: Contract testing for TaskLogger
# [/DEF:__tests__/test_task_logger:Module]

import pytest
from unittest.mock import MagicMock
from src.core.task_manager.task_logger import TaskLogger

# @TEST_FIXTURE: valid_task_logger -> {"task_id": "test_123", "add_log_fn": lambda *args: None, "source": "test_plugin"}
@pytest.fixture
def mock_add_log():
    return MagicMock()

@pytest.fixture
def task_logger(mock_add_log):
    return TaskLogger(task_id="test_123", add_log_fn=mock_add_log, source="test_plugin")

# @TEST_CONTRACT: TaskLoggerModel -> Invariants
def test_task_logger_initialization(task_logger):
    """Verify TaskLogger is bound to specific task_id and source."""
    assert task_logger._task_id == "test_123"
    assert task_logger._default_source == "test_plugin"

# @TEST_CONTRACT: invariants -> "All specific log methods (info, error) delegate to _log"
def test_log_methods_delegation(task_logger, mock_add_log):
    """Verify info, error, warning, debug delegate to internal _log."""
    task_logger.info("info message", metadata={"k": "v"})
    mock_add_log.assert_called_with(
        task_id="test_123",
        level="INFO",
        message="info message",
        source="test_plugin",
        metadata={"k": "v"}
    )
    
    task_logger.error("error message", source="override")
    mock_add_log.assert_called_with(
        task_id="test_123",
        level="ERROR",
        message="error message",
        source="override",
        metadata=None
    )

    task_logger.warning("warning message")
    mock_add_log.assert_called_with(
        task_id="test_123",
        level="WARNING",
        message="warning message",
        source="test_plugin",
        metadata=None
    )

    task_logger.debug("debug message")
    mock_add_log.assert_called_with(
        task_id="test_123",
        level="DEBUG",
        message="debug message",
        source="test_plugin",
        metadata=None
    )

# @TEST_CONTRACT: invariants -> "with_source creates a new logger with the same task_id"
def test_with_source(task_logger):
    """Verify with_source returns a new instance with updated default source."""
    new_logger = task_logger.with_source("new_source")
    assert isinstance(new_logger, TaskLogger)
    assert new_logger._task_id == "test_123"
    assert new_logger._default_source == "new_source"
    assert new_logger is not task_logger

# @TEST_EDGE: missing_task_id -> raises TypeError
def test_missing_task_id():
    with pytest.raises(TypeError):
        TaskLogger(add_log_fn=lambda x: x)

# @TEST_EDGE: invalid_add_log_fn -> raises TypeError
# (Python doesn't strictly enforce this at init, but let's verify it fails on call if not callable)
def test_invalid_add_log_fn():
    logger = TaskLogger(task_id="msg", add_log_fn=None)
    with pytest.raises(TypeError):
        logger.info("test")

# @TEST_INVARIANT: consistent_delegation
def test_progress_log(task_logger, mock_add_log):
    """Verify progress method correctly formats metadata."""
    task_logger.progress("Step 1", 45.5)
    mock_add_log.assert_called_with(
        task_id="test_123",
        level="INFO",
        message="Step 1",
        source="test_plugin",
        metadata={"progress": 45.5}
    )
    
    # Boundary checks
    task_logger.progress("Step high", 150)
    assert mock_add_log.call_args[1]["metadata"]["progress"] == 100
    
    task_logger.progress("Step low", -10)
    assert mock_add_log.call_args[1]["metadata"]["progress"] == 0
