microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/987-enable-json-log-lint-version-consistency

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

304lines · 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-ChildItem { @() }
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-ChildItem { @() }
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-ChildItem {
72 @([PSCustomObject]@{
73 FullName = (Join-Path $TestDrive 'skill1/pyproject.toml')
74 Directory = [PSCustomObject]@{ FullName = (Join-Path $TestDrive 'skill1') }
75 })
76 }
77 Mock Get-Command { $null } -ParameterFilter { $Name -eq 'ruff' }
78 }
79
80 It 'Returns failure when ruff not installed' {
81 $result = Invoke-PythonLint -RepoRoot $TestDrive
82 $result.success | Should -BeFalse
83 $result.errors | Should -Contain 'ruff not installed'
84 }
85
86 It 'Reports zero skills checked when ruff missing' {
87 $result = Invoke-PythonLint -RepoRoot $TestDrive
88 $result.skillsChecked | Should -Be 0
89 }
90 }
91
92 Context 'Tool installed' {
93 BeforeEach {
94 Mock Push-Location {}
95 Mock Pop-Location {}
96 Mock Get-ChildItem { @() }
97 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
98 }
99
100 It 'Proceeds when ruff available' {
101 { Invoke-PythonLint -RepoRoot $TestDrive } | Should -Not -Throw
102 }
103 }
104}
105
106#endregion
107
108#region Skill Discovery Tests
109
110Describe 'Python Skill Discovery' -Tag 'Unit' {
111 Context 'No Python skills found' {
112 BeforeEach {
113 Mock Push-Location {}
114 Mock Pop-Location {}
115 Mock Get-ChildItem { @() }
116 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
117 }
118
119 It 'Returns success with zero skills when no pyproject.toml found' {
120 $result = Invoke-PythonLint -RepoRoot $TestDrive
121 $result.success | Should -BeTrue
122 $result.skillsChecked | Should -Be 0
123 }
124 }
125
126 Context 'Python skills found' {
127 BeforeEach {
128 Mock Push-Location {}
129 Mock Pop-Location {}
130 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
131 Mock ruff { $global:LASTEXITCODE = 0; '' }
132 }
133
134 It 'Discovers skills via pyproject.toml' {
135 $skillDir = Join-Path $TestDrive 'skill1'
136 Mock Get-ChildItem {
137 @([PSCustomObject]@{
138 FullName = (Join-Path $skillDir 'pyproject.toml')
139 Directory = [PSCustomObject]@{ FullName = $skillDir }
140 })
141 }
142
143 $result = Invoke-PythonLint -RepoRoot $TestDrive
144 $result.skillsChecked | Should -Be 1
145 }
146
147 It 'Discovers multiple skills' {
148 $skill1Dir = Join-Path $TestDrive 'skill1'
149 $skill2Dir = Join-Path $TestDrive 'skill2'
150 Mock Get-ChildItem {
151 @(
152 [PSCustomObject]@{
153 FullName = (Join-Path $skill1Dir 'pyproject.toml')
154 Directory = [PSCustomObject]@{ FullName = $skill1Dir }
155 },
156 [PSCustomObject]@{
157 FullName = (Join-Path $skill2Dir 'pyproject.toml')
158 Directory = [PSCustomObject]@{ FullName = $skill2Dir }
159 }
160 )
161 }
162
163 $result = Invoke-PythonLint -RepoRoot $TestDrive
164 $result.skillsChecked | Should -Be 2
165 }
166
167 It 'Excludes node_modules from discovery' {
168 Mock Get-ChildItem {
169 @([PSCustomObject]@{
170 FullName = (Join-Path $TestDrive 'node_modules/pkg/pyproject.toml')
171 Directory = [PSCustomObject]@{ FullName = (Join-Path $TestDrive 'node_modules/pkg') }
172 })
173 }
174
175 $result = Invoke-PythonLint -RepoRoot $TestDrive
176 $result.skillsChecked | Should -Be 0
177 }
178 }
179}
180
181#endregion
182
183#region Lint Execution Tests
184
185Describe 'Ruff Lint Execution' -Tag 'Unit' {
186 BeforeAll {
187 $script:SkillDir = Join-Path $TestDrive 'lint-skill'
188 }
189
190 BeforeEach {
191 Mock Push-Location {}
192 Mock Pop-Location {}
193 Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
194 Mock Get-ChildItem {
195 @([PSCustomObject]@{
196 FullName = (Join-Path $script:SkillDir 'pyproject.toml')
197 Directory = [PSCustomObject]@{ FullName = $script:SkillDir }
198 })
199 }
200 }
201
202 Context 'Lint passes' {
203 BeforeEach {
204 Mock ruff { $global:LASTEXITCODE = 0; '' }
205 }
206
207 It 'Returns success when ruff reports no issues' {
208 $result = Invoke-PythonLint -RepoRoot $TestDrive
209 $result.success | Should -BeTrue
210 }
211
212 It 'Marks skill as passed in details' {
213 $result = Invoke-PythonLint -RepoRoot $TestDrive
214 $result.details[0].passed | Should -BeTrue
215 }
216
217 It 'Reports no errors' {
218 $result = Invoke-PythonLint -RepoRoot $TestDrive
219 $result.errors | Should -HaveCount 0
220 }
221 }
222
223 Context 'Lint fails' {
224 BeforeEach {
225 Mock ruff { $global:LASTEXITCODE = 1; 'error: E501 line too long' }
226 }
227
228 It 'Returns failure when ruff reports issues' {
229 $result = Invoke-PythonLint -RepoRoot $TestDrive
230 $result.success | Should -BeFalse
231 }
232
233 It 'Records skill path in errors' {
234 $result = Invoke-PythonLint -RepoRoot $TestDrive
235 $result.errors | Should -Contain $script:SkillDir
236 }
237
238 It 'Marks skill as failed in details' {
239 $result = Invoke-PythonLint -RepoRoot $TestDrive
240 $result.details[0].passed | Should -BeFalse
241 }
242 }
243
244 Context 'Ruff throws exception' {
245 BeforeEach {
246 Mock ruff { throw 'ruff crashed' }
247 }
248
249 It 'Handles ruff exception gracefully' {
250 $result = Invoke-PythonLint -RepoRoot $TestDrive
251 $result.success | Should -BeFalse
252 }
253
254 It 'Records error with skill path' {
255 $result = Invoke-PythonLint -RepoRoot $TestDrive
256 $result.errors | Should -Not -BeNullOrEmpty
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-ChildItem {
275 @([PSCustomObject]@{
276 FullName = (Join-Path $script:OutputSkillDir 'pyproject.toml')
277 Directory = [PSCustomObject]@{ FullName = $script:OutputSkillDir }
278 })
279 }
280 Mock ruff { $global:LASTEXITCODE = 0; '' }
281 }
282
283 Context 'OutputPath specified' {
284 It 'Writes JSON results to OutputPath' {
285 $outputPath = Join-Path $TestDrive 'lint-results.json'
286 Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
287 Test-Path $outputPath | Should -BeTrue
288 }
289
290 It 'Produces valid JSON output' {
291 $outputPath = Join-Path $TestDrive 'lint-results2.json'
292 Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
293 { Get-Content $outputPath -Raw | ConvertFrom-Json } | Should -Not -Throw
294 }
295 }
296
297 Context 'OutputPath not specified' {
298 It 'Does not throw when OutputPath omitted' {
299 { Invoke-PythonLint -RepoRoot $TestDrive } | Should -Not -Throw
300 }
301 }
302}
303
304#endregion
305