Coverage for tests / unit / config / test_config_loader.py: 100%

53 statements  

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

1"""Unit tests for lintro configuration loaders from pyproject.toml.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6from unittest.mock import patch 

7 

8import pytest 

9from assertpy import assert_that 

10 

11from lintro.config.config_loader import _load_pyproject_fallback 

12from lintro.utils.config import ( 

13 clear_pyproject_cache, 

14 load_lintro_tool_config, 

15) 

16 

17 

18def test_load_lintro_tool_config( 

19 tmp_path: Path, 

20 monkeypatch: pytest.MonkeyPatch, 

21) -> None: 

22 """Load tool-specific config sections from pyproject. 

23 

24 Args: 

25 tmp_path: Temporary directory for pyproject creation. 

26 monkeypatch: Pytest monkeypatch to chdir into temp dir. 

27 """ 

28 # Clear both LRU caches to ensure we load from the test directory 

29 clear_pyproject_cache() 

30 

31 pyproject = tmp_path / "pyproject.toml" 

32 pyproject.write_text( 

33 ( 

34 "[tool.lintro]\n" 

35 "[tool.lintro.ruff]\n" 

36 'select = ["E", "F"]\n' 

37 "line_length = 88\n" 

38 "[tool.lintro.prettier]\n" 

39 "single_quote = true\n" 

40 ), 

41 ) 

42 monkeypatch.chdir(tmp_path) 

43 ruff_cfg = load_lintro_tool_config("ruff") 

44 assert_that(ruff_cfg.get("line_length")).is_equal_to(88) 

45 assert_that(ruff_cfg.get("select")).is_equal_to(["E", "F"]) 

46 prettier_cfg = load_lintro_tool_config("prettier") 

47 assert_that(prettier_cfg.get("single_quote") is True).is_true() 

48 missing_cfg = load_lintro_tool_config("yamllint") 

49 assert_that(missing_cfg).is_equal_to({}) 

50 

51 

52def test_config_loader_handles_missing_and_malformed_pyproject( 

53 tmp_path: Path, 

54 monkeypatch: pytest.MonkeyPatch, 

55) -> None: 

56 """Validate loaders handle missing and malformed pyproject files. 

57 

58 Args: 

59 tmp_path: Temporary directory used to simulate project roots. 

60 monkeypatch: Pytest monkeypatch fixture for chdir and environment. 

61 """ 

62 # Clear both LRU caches to ensure we load from the test directory 

63 clear_pyproject_cache() 

64 

65 from lintro.utils import config as cfg 

66 

67 # 1) Missing pyproject.toml 

68 monkeypatch.chdir(tmp_path) 

69 assert_that(cfg.load_lintro_tool_config("ruff")).is_equal_to({}) 

70 assert_that(cfg.load_post_checks_config()).is_equal_to({}) 

71 

72 # 2) Malformed pyproject.toml should be handled gracefully 

73 (tmp_path / "pyproject.toml").write_text("not: [valid\n") 

74 assert_that(cfg.load_lintro_tool_config("ruff")).is_equal_to({}) 

75 assert_that(cfg.load_post_checks_config()).is_equal_to({}) 

76 

77 

78# ============================================================================= 

79# TOML Parse Error Logging Tests 

80# ============================================================================= 

81 

82 

83def test_load_pyproject_toml_parse_error_logs_warning( 

84 tmp_path: Path, 

85 monkeypatch: pytest.MonkeyPatch, 

86) -> None: 

87 """Verify TOML parse error logs warning with file path and error details. 

88 

89 Args: 

90 tmp_path: Pytest temporary directory fixture. 

91 monkeypatch: Pytest monkeypatch fixture for chdir. 

92 """ 

93 clear_pyproject_cache() 

94 

95 pyproject = tmp_path / "pyproject.toml" 

96 pyproject.write_text("invalid: [toml content") 

97 monkeypatch.chdir(tmp_path) 

98 

99 with patch("lintro.config.config_loader.logger") as mock_logger: 

100 result, path = _load_pyproject_fallback() 

101 

102 assert_that(result).is_equal_to({}) 

103 assert_that(path).is_none() 

104 mock_logger.warning.assert_called_once() 

105 warning_msg = mock_logger.warning.call_args[0][0] 

106 assert_that(warning_msg).contains("Failed to parse pyproject.toml") 

107 assert_that(warning_msg).contains(str(pyproject)) 

108 

109 

110def test_load_pyproject_os_error_logs_debug( 

111 tmp_path: Path, 

112 monkeypatch: pytest.MonkeyPatch, 

113) -> None: 

114 """Verify OS error reading pyproject.toml logs debug message. 

115 

116 Args: 

117 tmp_path: Pytest temporary directory fixture. 

118 monkeypatch: Pytest monkeypatch fixture for chdir. 

119 """ 

120 clear_pyproject_cache() 

121 

122 pyproject = tmp_path / "pyproject.toml" 

123 pyproject.write_text("[tool.lintro]") 

124 monkeypatch.chdir(tmp_path) 

125 

126 with ( 

127 patch("lintro.config.config_loader.logger") as mock_logger, 

128 patch("pathlib.Path.open", side_effect=OSError("Permission denied")), 

129 ): 

130 result, path = _load_pyproject_fallback() 

131 

132 assert_that(result).is_equal_to({}) 

133 assert_that(path).is_none() 

134 mock_logger.debug.assert_called_once() 

135 debug_msg = mock_logger.debug.call_args[0][0] 

136 assert_that(debug_msg).contains("Could not read pyproject.toml")