Coverage for tests / unit / utils / test_timeout_utils.py: 98%
51 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Unit tests for timeout utilities."""
3import subprocess
4from typing import Any
5from unittest.mock import Mock
7import pytest
8from assertpy import assert_that
10from lintro.tools.core.timeout_utils import (
11 create_timeout_result,
12 get_timeout_value,
13 run_subprocess_with_timeout,
14)
17class MockDefinition:
18 """Mock definition for testing."""
20 def __init__(self, name: str) -> None:
21 """Initialize mock definition.
23 Args:
24 name: Tool name.
25 """
26 self.name = name
29class MockTool:
30 """Mock tool for testing timeout utilities."""
32 def __init__(self, name: str = "test_tool", default_timeout: int = 300) -> None:
33 """Initialize mock tool.
35 Args:
36 name: Tool name for testing.
37 default_timeout: Default timeout value in seconds.
38 """
39 self.definition = MockDefinition(name)
40 self._default_timeout = default_timeout
41 self.options: dict[str, Any] = {}
43 def _run_subprocess(
44 self,
45 cmd: list[str],
46 timeout: int | None = None,
47 cwd: str | None = None,
48 ) -> tuple[bool, str]:
49 """Mock subprocess runner.
51 Args:
52 cmd: Command to run.
53 timeout: Optional timeout value.
54 cwd: Optional working directory.
56 Returns:
57 tuple[bool, str]: Success status and output.
58 """
59 return True, "success"
62def test_get_timeout_value_with_option() -> None:
63 """Test getting timeout value when set in options."""
64 tool = MockTool()
65 tool.options["timeout"] = 60
67 assert_that(get_timeout_value(tool)).is_equal_to(60)
70def test_get_timeout_value_with_default() -> None:
71 """Test getting timeout value when using tool default."""
72 tool = MockTool(default_timeout=45)
74 assert_that(get_timeout_value(tool)).is_equal_to(45)
77def test_get_timeout_value_with_custom_default() -> None:
78 """Test getting timeout value with custom default parameter."""
79 tool = MockTool()
81 assert_that(get_timeout_value(tool, 120)).is_equal_to(120)
84def test_create_timeout_result() -> None:
85 """Test creating a timeout result object."""
86 tool = MockTool("pytest")
88 result = create_timeout_result(tool, 30, ["pytest", "test"])
90 assert_that(result.success).is_false()
91 assert_that(result.output).contains(
92 "pytest execution timed out (30s limit exceeded)",
93 )
94 assert_that(result.issues_count).is_equal_to(1)
95 assert_that(result.issues).is_empty()
96 assert_that(result.timed_out).is_true()
97 assert_that(result.timeout_seconds).is_equal_to(30)
100def test_run_subprocess_with_timeout_success() -> None:
101 """Test successful subprocess execution with timeout."""
102 tool = MockTool()
103 tool._run_subprocess = Mock(return_value=(True, "output")) # type: ignore[method-assign]
105 success, output = run_subprocess_with_timeout(tool, ["echo", "test"])
107 assert_that(success).is_true()
108 assert_that(output).is_equal_to("output")
109 tool._run_subprocess.assert_called_once_with(
110 cmd=["echo", "test"],
111 timeout=None,
112 cwd=None,
113 )
116def test_run_subprocess_with_timeout_exception() -> None:
117 """Test subprocess timeout exception handling."""
118 tool = MockTool()
120 # Mock subprocess to raise TimeoutExpired
121 def mock_run_subprocess(
122 cmd: list[str],
123 timeout: int | None = None,
124 cwd: str | None = None,
125 ) -> tuple[bool, str]:
126 raise subprocess.TimeoutExpired(
127 cmd=["slow", "command"],
128 timeout=10,
129 output="timeout occurred",
130 )
132 tool._run_subprocess = mock_run_subprocess # type: ignore[method-assign]
134 with pytest.raises(subprocess.TimeoutExpired) as exc_info:
135 run_subprocess_with_timeout(tool, ["slow", "command"], timeout=10)
137 # Verify the exception has enhanced message
138 assert_that(str(exc_info.value.output)).contains("test_tool execution timed out")
139 assert_that(str(exc_info.value.output)).contains("(10s limit exceeded)")