Coverage for tests / unit / plugins / conftest.py: 89%

53 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Pytest configuration for plugin unit tests.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from typing import TYPE_CHECKING, Any 

7 

8import pytest 

9 

10from lintro.models.core.tool_result import ToolResult 

11from lintro.plugins.base import BaseToolPlugin 

12from lintro.plugins.discovery import reset_discovery 

13from lintro.plugins.protocol import ToolDefinition 

14from lintro.plugins.registry import ToolRegistry 

15 

16if TYPE_CHECKING: 

17 from collections.abc import Generator 

18 

19 

20@dataclass 

21class FakeToolPlugin(BaseToolPlugin): 

22 """Fake tool plugin for testing.""" 

23 

24 _definition: ToolDefinition = field( 

25 default_factory=lambda: ToolDefinition( 

26 name="fake-tool", 

27 description="Fake tool for testing", 

28 file_patterns=["*.py"], 

29 can_fix=True, 

30 default_timeout=30, 

31 ), 

32 ) 

33 

34 @property 

35 def definition(self) -> ToolDefinition: 

36 """Return the tool definition. 

37 

38 Returns: 

39 The tool definition. 

40 """ 

41 return self._definition 

42 

43 def check(self, paths: list[str], options: dict[str, object]) -> ToolResult: 

44 """Fake check implementation. 

45 

46 Args: 

47 paths: List of file paths to check. 

48 options: Dictionary of options for the check. 

49 

50 Returns: 

51 A ToolResult indicating success. 

52 """ 

53 return ToolResult(name="fake-tool", success=True, output="OK", issues_count=0) 

54 

55 

56@pytest.fixture 

57def fake_tool_plugin() -> FakeToolPlugin: 

58 """Provide a FakeToolPlugin instance for testing. 

59 

60 Returns: 

61 A FakeToolPlugin instance. 

62 """ 

63 return FakeToolPlugin() 

64 

65 

66@pytest.fixture 

67def clean_registry() -> Generator[None]: 

68 """Save and restore registry state for test isolation. 

69 

70 This fixture saves the current registry state before the test 

71 and restores it after, ensuring tests don't pollute each other. 

72 

73 Yields: 

74 None: Saves and restores registry state. 

75 """ 

76 original_tools = dict(ToolRegistry._tools) 

77 original_instances = dict(ToolRegistry._instances) 

78 try: 

79 yield 

80 finally: 

81 ToolRegistry._tools = original_tools 

82 ToolRegistry._instances = original_instances 

83 

84 

85@pytest.fixture 

86def empty_registry() -> Generator[None]: 

87 """Provide an empty registry for testing. 

88 

89 Clears the registry before the test and restores it after. 

90 

91 Yields: 

92 None: Clears and restores registry state. 

93 """ 

94 original_tools = dict(ToolRegistry._tools) 

95 original_instances = dict(ToolRegistry._instances) 

96 ToolRegistry.clear() 

97 try: 

98 yield 

99 finally: 

100 ToolRegistry._tools = original_tools 

101 ToolRegistry._instances = original_instances 

102 

103 

104@pytest.fixture 

105def reset_discovery_state() -> Generator[None]: 

106 """Reset discovery state before and after test. 

107 

108 Yields: 

109 None: Resets discovery state. 

110 """ 

111 reset_discovery() 

112 try: 

113 yield 

114 finally: 

115 reset_discovery() 

116 

117 

118def create_fake_plugin( 

119 name: str = "fake-tool", 

120 description: str = "Fake tool for testing", 

121 file_patterns: list[str] | None = None, 

122 can_fix: bool = True, 

123) -> type[BaseToolPlugin]: 

124 """Factory function to create fake plugin classes with custom attributes. 

125 

126 Args: 

127 name: Tool name. 

128 description: Tool description. 

129 file_patterns: File patterns the tool handles. 

130 can_fix: Whether the tool can fix issues. 

131 

132 Returns: 

133 A new FakePlugin class with the specified attributes. 

134 """ 

135 patterns: list[str] = file_patterns if file_patterns is not None else ["*.py"] 

136 

137 @dataclass 

138 class DynamicFakePlugin(BaseToolPlugin): 

139 @property 

140 def definition(self) -> ToolDefinition: 

141 return ToolDefinition( 

142 name=name, 

143 description=description, 

144 file_patterns=patterns, 

145 can_fix=can_fix, 

146 ) 

147 

148 def check(self, paths: list[str], options: dict[str, Any]) -> ToolResult: 

149 return ToolResult(name=name, success=True, output="OK", issues_count=0) 

150 

151 return DynamicFakePlugin