Coverage for tests / unit / tools / core / test_install_context.py: 100%

61 statements  

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

1"""Tests for lintro.tools.core.install_context module. 

2 

3Hint generation tests have moved to test_install_strategies.py. 

4This file covers RuntimeContext construction, install context detection, 

5and CI detection. 

6""" 

7 

8from __future__ import annotations 

9 

10from unittest.mock import patch 

11 

12import pytest 

13from assertpy import assert_that 

14 

15from lintro.enums.install_context import CISystem, InstallContext, PackageManager 

16from lintro.tools.core.install_context import ( 

17 RuntimeContext, 

18 _detect_install_context, 

19 _is_ci, 

20) 

21from lintro.tools.core.install_strategies.environment import InstallEnvironment 

22 

23# --------------------------------------------------------------------------- 

24# Helpers 

25# --------------------------------------------------------------------------- 

26 

27 

28def _make_ctx( 

29 *, 

30 install_context: InstallContext = InstallContext.PIP, 

31 platform_label: str = "macOS arm64", 

32 managers: frozenset[PackageManager] = frozenset(PackageManager), 

33 is_ci: bool = False, 

34 ci_name: CISystem | None = None, 

35) -> RuntimeContext: 

36 """Build a RuntimeContext with sensible defaults for testing. 

37 

38 Args: 

39 install_context: How lintro was installed. 

40 platform_label: Platform description. 

41 managers: Available package manager names. 

42 is_ci: Running in CI. 

43 ci_name: CI system name. 

44 

45 Returns: 

46 A RuntimeContext instance. 

47 """ 

48 return RuntimeContext( 

49 install_context=install_context, 

50 platform_label=platform_label, 

51 environment=InstallEnvironment( 

52 install_context=install_context, 

53 available_managers=managers, 

54 ), 

55 is_ci=is_ci, 

56 ci_name=ci_name, 

57 ) 

58 

59 

60# --------------------------------------------------------------------------- 

61# RuntimeContext construction 

62# --------------------------------------------------------------------------- 

63 

64 

65def test_runtime_context_has_environment() -> None: 

66 """RuntimeContext carries an InstallEnvironment.""" 

67 ctx = _make_ctx() 

68 assert_that(ctx.environment).is_instance_of(InstallEnvironment) 

69 assert_that(ctx.environment.has(PackageManager.UV)).is_true() 

70 

71 

72def test_runtime_context_environment_reflects_managers() -> None: 

73 """InstallEnvironment correctly reports available managers.""" 

74 ctx = _make_ctx(managers=frozenset({PackageManager.CARGO})) 

75 assert_that(ctx.environment.has(PackageManager.CARGO)).is_true() 

76 assert_that(ctx.environment.has(PackageManager.UV)).is_false() 

77 

78 

79# --------------------------------------------------------------------------- 

80# _detect_install_context 

81# --------------------------------------------------------------------------- 

82 

83 

84def test_detect_docker_via_dockerenv() -> None: 

85 """Detect Docker context when /.dockerenv file exists.""" 

86 with patch( 

87 "lintro.tools.core.install_context.os.path.exists", 

88 return_value=True, 

89 ): 

90 result = _detect_install_context() 

91 

92 assert_that(result).is_equal_to(InstallContext.DOCKER) 

93 

94 

95def test_detect_docker_via_env_var( 

96 monkeypatch: pytest.MonkeyPatch, 

97) -> None: 

98 """Detect Docker context via LINTRO_DOCKER=1 env var.""" 

99 monkeypatch.setenv("LINTRO_DOCKER", "1") 

100 with patch( 

101 "lintro.tools.core.install_context.os.path.exists", 

102 return_value=False, 

103 ): 

104 result = _detect_install_context() 

105 

106 assert_that(result).is_equal_to(InstallContext.DOCKER) 

107 

108 

109def test_detect_docker_via_container_env_var( 

110 monkeypatch: pytest.MonkeyPatch, 

111) -> None: 

112 """Detect Docker context via CONTAINER=docker env var. 

113 

114 Args: 

115 monkeypatch: Pytest monkeypatch fixture. 

116 """ 

117 monkeypatch.delenv("LINTRO_DOCKER", raising=False) 

118 monkeypatch.setenv("CONTAINER", "docker") 

119 with patch( 

120 "lintro.tools.core.install_context.os.path.exists", 

121 return_value=False, 

122 ): 

123 result = _detect_install_context() 

124 

125 assert_that(result).is_equal_to(InstallContext.DOCKER) 

126 

127 

128def test_detect_pip_default( 

129 monkeypatch: pytest.MonkeyPatch, 

130) -> None: 

131 """Default to PIP context when no special indicators are present.""" 

132 monkeypatch.delenv("LINTRO_DOCKER", raising=False) 

133 monkeypatch.delenv("CONTAINER", raising=False) 

134 

135 with ( 

136 patch( 

137 "lintro.tools.core.install_context.os.path.exists", 

138 return_value=False, 

139 ), 

140 patch( 

141 "lintro.tools.core.install_context.__file__", 

142 "/usr/lib/python3.11/site-packages/lintro/tools/core/install_context.py", 

143 create=True, 

144 ), 

145 patch( 

146 "lintro.tools.core.install_context.sys.executable", 

147 "/usr/bin/python3", 

148 ), 

149 ): 

150 result = _detect_install_context() 

151 

152 assert_that(result).is_equal_to(InstallContext.PIP) 

153 

154 

155# --------------------------------------------------------------------------- 

156# CI detection 

157# --------------------------------------------------------------------------- 

158 

159 

160def test_is_ci_with_ci_env_var( 

161 monkeypatch: pytest.MonkeyPatch, 

162) -> None: 

163 """Return True when generic CI env var is set.""" 

164 monkeypatch.setenv("CI", "true") 

165 for var in ( 

166 "GITHUB_ACTIONS", 

167 "GITLAB_CI", 

168 "CIRCLECI", 

169 "JENKINS_URL", 

170 "BUILDKITE", 

171 "TF_BUILD", 

172 ): 

173 monkeypatch.delenv(var, raising=False) 

174 

175 assert_that(_is_ci()).is_true() 

176 

177 

178def test_is_ci_false( 

179 monkeypatch: pytest.MonkeyPatch, 

180) -> None: 

181 """Return False when no CI env vars are set.""" 

182 monkeypatch.delenv("CI", raising=False) 

183 for var in ( 

184 "GITHUB_ACTIONS", 

185 "GITLAB_CI", 

186 "CIRCLECI", 

187 "JENKINS_URL", 

188 "BUILDKITE", 

189 "TF_BUILD", 

190 ): 

191 monkeypatch.delenv(var, raising=False) 

192 

193 assert_that(_is_ci()).is_false() 

194 

195 

196@pytest.mark.parametrize( 

197 ("env_var", "env_value", "expected"), 

198 [ 

199 ("GITHUB_ACTIONS", "1", CISystem.GITHUB_ACTIONS), 

200 ("GITLAB_CI", "1", CISystem.GITLAB_CI), 

201 ("CIRCLECI", "true", CISystem.CIRCLECI), 

202 ("JENKINS_URL", "https://ci.example.com", CISystem.JENKINS), 

203 ("BUILDKITE", "true", CISystem.BUILDKITE), 

204 ("TF_BUILD", "True", CISystem.AZURE_PIPELINES), 

205 ], 

206 ids=[ 

207 "github_actions", 

208 "gitlab_ci", 

209 "circleci", 

210 "jenkins", 

211 "buildkite", 

212 "azure_pipelines", 

213 ], 

214) 

215def test_detect_ci_system( 

216 env_var: str, 

217 env_value: str, 

218 expected: CISystem, 

219 monkeypatch: pytest.MonkeyPatch, 

220) -> None: 

221 """Detect specific CI system from its environment variable. 

222 

223 Args: 

224 env_var: The CI-specific env var to set. 

225 env_value: The value to set for the env var. 

226 expected: Expected CISystem enum member. 

227 monkeypatch: Pytest monkeypatch fixture. 

228 """ 

229 for var in ( 

230 "GITHUB_ACTIONS", 

231 "GITLAB_CI", 

232 "CIRCLECI", 

233 "JENKINS_URL", 

234 "BUILDKITE", 

235 "TF_BUILD", 

236 ): 

237 monkeypatch.delenv(var, raising=False) 

238 

239 monkeypatch.setenv(env_var, env_value) 

240 

241 assert_that(CISystem.detect()).is_equal_to(expected) 

242 

243 

244def test_detect_ci_system_none( 

245 monkeypatch: pytest.MonkeyPatch, 

246) -> None: 

247 """Return None when no CI env vars are set.""" 

248 for var in ( 

249 "GITHUB_ACTIONS", 

250 "GITLAB_CI", 

251 "CIRCLECI", 

252 "JENKINS_URL", 

253 "BUILDKITE", 

254 "TF_BUILD", 

255 ): 

256 monkeypatch.delenv(var, raising=False) 

257 

258 assert_that(CISystem.detect()).is_none() 

259 

260 

261def test_ci_system_is_str_enum() -> None: 

262 """CISystem values are human-readable strings.""" 

263 assert_that(str(CISystem.GITHUB_ACTIONS)).is_equal_to("GitHub Actions") 

264 assert_that(str(CISystem.AZURE_PIPELINES)).is_equal_to("Azure Pipelines")