microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/collections-overview-docs

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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