microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/context-working

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/tests/linting/Invoke-PythonLint.Tests.ps1

299lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6 Pester tests for Invoke-PythonLint.ps1 script
7.DESCRIPTION
8 Tests for Python linting wrapper script:
9 - Parameter validation
10 - Tool availability checks
11 - Skill discovery via pyproject.toml
12 - Ruff execution and result handling
13 - Output file generation
14#>
15
16BeforeAll {
17 $script:ScriptPath = Join-Path $PSScriptRoot '../../linting/Invoke-PythonLint.ps1'
18
19 # Create stub function for ruff so it can be mocked even when not installed
20 function global:ruff { '' }
21
22 . $script:ScriptPath
23}
24
25AfterAll {
26 Remove-Item -Path 'Function:\ruff' -Force -ErrorAction SilentlyContinue
27}
28
29#region Parameter Validation Tests
30
31Describe 'Invoke-PythonLint Parameter Validation' -Tag 'Unit' {
32 Context 'RepoRoot parameter' {
33 BeforeEach {
34 Mock Get-PythonSkill { @() }
35 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
36 Mock Push-Location {}
37 Mock Pop-Location {}
38 }
39
40 It 'Accepts custom RepoRoot' {
41 $repoRoot = Join-Path $TestDrive 'test-repo'
42 New-Item -ItemType Directory -Path $repoRoot -Force | Out-Null
43 { Invoke-PythonLint -RepoRoot $repoRoot } | Should -Not -Throw
44 }
45 }
46
47 Context 'OutputPath parameter' {
48 BeforeEach {
49 Mock Get-PythonSkill { @() }
50 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
51 Mock Push-Location {}
52 Mock Pop-Location {}
53 }
54
55 It 'Accepts custom OutputPath' {
56 $outputPath = Join-Path $TestDrive 'lint-output.json'
57 { Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath } | Should -Not -Throw
58 }
59 }
60}
61
62#endregion
63
64#region Tool Availability Tests
65
66Describe 'ruff Tool Availability' -Tag 'Unit' {
67 Context 'Tool not installed' {
68 BeforeEach {
69 Mock Push-Location {}
70 Mock Pop-Location {}
71 Mock Get-PythonSkill { @((Join-Path $TestDrive 'skill1')) }
72 Mock Get-Command { $null } -ParameterFilter { $Name -eq 'ruff' }
73 }
74
75 It 'Returns failure when ruff not available' {
76 $result = Invoke-PythonLint -RepoRoot $TestDrive
77 $result.success | Should -BeFalse
78 }
79
80 It 'Reports skill path in errors' {
81 $result = Invoke-PythonLint -RepoRoot $TestDrive
82 $result.errors | Should -Contain (Join-Path $TestDrive 'skill1')
83 }
84
85 It 'Reports zero skills checked when ruff missing' {
86 $result = Invoke-PythonLint -RepoRoot $TestDrive
87 $result.skillsChecked | Should -Be 0
88 }
89 }
90
91 Context 'Tool installed' {
92 BeforeEach {
93 Mock Push-Location {}
94 Mock Pop-Location {}
95 Mock Get-PythonSkill { @() }
96 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
97 }
98
99 It 'Proceeds when ruff available' {
100 { Invoke-PythonLint -RepoRoot $TestDrive } | Should -Not -Throw
101 }
102 }
103}
104
105#endregion
106
107#region Skill Discovery Tests
108
109Describe 'Python Skill Discovery' -Tag 'Unit' {
110 Context 'No Python skills found' {
111 BeforeEach {
112 Mock Push-Location {}
113 Mock Pop-Location {}
114 Mock Get-PythonSkill { @() }
115 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
116 }
117
118 It 'Returns success with zero skills when no pyproject.toml found' {
119 $result = Invoke-PythonLint -RepoRoot $TestDrive
120 $result.success | Should -BeTrue
121 $result.skillsChecked | Should -Be 0
122 }
123 }
124
125 Context 'Python skills found' {
126 BeforeEach {
127 Mock Push-Location {}
128 Mock Pop-Location {}
129 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
130 Mock ruff { $global:LASTEXITCODE = 0; '' }
131 }
132
133 It 'Discovers skills via pyproject.toml' {
134 $skillDir = Join-Path $TestDrive 'skill1'
135 Mock Get-PythonSkill { @($skillDir) }
136
137 $result = Invoke-PythonLint -RepoRoot $TestDrive
138 $result.skillsChecked | Should -Be 1
139 }
140
141 It 'Discovers multiple skills' {
142 $skill1Dir = Join-Path $TestDrive 'skill1'
143 $skill2Dir = Join-Path $TestDrive 'skill2'
144 Mock Get-PythonSkill { @($skill1Dir, $skill2Dir) }
145
146 $result = Invoke-PythonLint -RepoRoot $TestDrive
147 $result.skillsChecked | Should -Be 2
148 }
149
150 It 'Excludes node_modules from discovery' {
151 # Get-PythonSkill applies the node_modules filter; mock returns post-filter result.
152 Mock Get-PythonSkill { @() }
153
154 $result = Invoke-PythonLint -RepoRoot $TestDrive
155 $result.skillsChecked | Should -Be 0
156 }
157 }
158}
159
160#endregion
161
162#region Lint Execution Tests
163
164Describe 'Ruff Lint Execution' -Tag 'Unit' {
165 BeforeAll {
166 $script:SkillDir = Join-Path $TestDrive 'lint-skill'
167 }
168
169 BeforeEach {
170 Mock Push-Location {}
171 Mock Pop-Location {}
172 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
173 Mock Get-PythonSkill { @($script:SkillDir) }
174 }
175
176 Context 'Lint passes' {
177 BeforeEach {
178 Mock ruff { $global:LASTEXITCODE = 0; '' }
179 }
180
181 It 'Returns success when ruff reports no issues' {
182 $result = Invoke-PythonLint -RepoRoot $TestDrive
183 $result.success | Should -BeTrue
184 }
185
186 It 'Marks skill as passed in details' {
187 $result = Invoke-PythonLint -RepoRoot $TestDrive
188 $result.details[0].passed | Should -BeTrue
189 }
190
191 It 'Reports no errors' {
192 $result = Invoke-PythonLint -RepoRoot $TestDrive
193 $result.errors | Should -HaveCount 0
194 }
195 }
196
197 Context 'Lint fails' {
198 BeforeEach {
199 Mock ruff { $global:LASTEXITCODE = 1; 'error: E501 line too long' }
200 }
201
202 It 'Returns failure when ruff reports issues' {
203 $result = Invoke-PythonLint -RepoRoot $TestDrive
204 $result.success | Should -BeFalse
205 }
206
207 It 'Records skill path in errors' {
208 $result = Invoke-PythonLint -RepoRoot $TestDrive
209 $result.errors | Should -Contain $script:SkillDir
210 }
211
212 It 'Marks skill as failed in details' {
213 $result = Invoke-PythonLint -RepoRoot $TestDrive
214 $result.details[0].passed | Should -BeFalse
215 }
216 }
217
218 Context 'Ruff throws exception' {
219 BeforeEach {
220 Mock ruff { throw 'ruff crashed' }
221 }
222
223 It 'Handles ruff exception gracefully' {
224 $result = Invoke-PythonLint -RepoRoot $TestDrive
225 $result.success | Should -BeFalse
226 }
227
228 It 'Records error with skill path' {
229 $result = Invoke-PythonLint -RepoRoot $TestDrive
230 $result.errors | Should -Not -BeNullOrEmpty
231 }
232 }
233
234 Context 'Fix mode with -Fix switch' {
235 BeforeEach {
236 Mock ruff { $global:LASTEXITCODE = 0; '' }
237 }
238
239 It 'Invokes ruff with --fix argument' {
240 Invoke-PythonLint -Fix -RepoRoot $TestDrive
241 Should -Invoke ruff -ParameterFilter { $args -contains '--fix' }
242 }
243
244 It 'Invokes ruff with check subcommand' {
245 Invoke-PythonLint -Fix -RepoRoot $TestDrive
246 Should -Invoke ruff -ParameterFilter { $args -contains 'check' }
247 }
248
249 It 'Invokes ruff with format subcommand' {
250 Invoke-PythonLint -Fix -RepoRoot $TestDrive
251 Should -Invoke ruff -ParameterFilter { $args -contains 'format' }
252 }
253
254 It 'Records formatExitCode in skill detail' {
255 $result = Invoke-PythonLint -Fix -RepoRoot $TestDrive
256 $result.details[0].formatExitCode | Should -Be 0
257 }
258 }
259}
260
261#endregion
262
263#region Output Persistence Tests
264
265Describe 'Output Persistence' -Tag 'Unit' {
266 BeforeAll {
267 $script:OutputSkillDir = Join-Path $TestDrive 'output-skill'
268 }
269
270 BeforeEach {
271 Mock Push-Location {}
272 Mock Pop-Location {}
273 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
274 Mock Get-PythonSkill { @($script:OutputSkillDir) }
275 Mock ruff { $global:LASTEXITCODE = 0; '' }
276 }
277
278 Context 'OutputPath specified' {
279 It 'Writes JSON results to OutputPath' {
280 $outputPath = Join-Path $TestDrive 'lint-results.json'
281 Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
282 Test-Path $outputPath | Should -BeTrue
283 }
284
285 It 'Produces valid JSON output' {
286 $outputPath = Join-Path $TestDrive 'lint-results2.json'
287 Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
288 { Get-Content $outputPath -Raw | ConvertFrom-Json } | Should -Not -Throw
289 }
290 }
291
292 Context 'OutputPath not specified' {
293 It 'Does not throw when OutputPath omitted' {
294 { Invoke-PythonLint -RepoRoot $TestDrive } | Should -Not -Throw
295 }
296 }
297}
298
299#endregion
300