microsoft/hve-core

Public

mirrored from https://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
a5c38379bc5d730200b9be8d7b8d86f9bd2bcd21

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

308lines · modeblame

0a90f573Jason5 months ago1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6Pester tests for Invoke-PythonLint.ps1 script
7.DESCRIPTION
8Tests 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
20function global:ruff { '' }
21
22. $script:ScriptPath
23}
24
25AfterAll {
26Remove-Item -Path 'Function:\ruff' -Force -ErrorAction SilentlyContinue
27}
28
29#region Parameter Validation Tests
30
31Describe 'Invoke-PythonLint Parameter Validation' -Tag 'Unit' {
32Context 'RepoRoot parameter' {
33BeforeEach {
34Mock Get-ChildItem { @() }
35Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
36Mock Push-Location {}
37Mock Pop-Location {}
38}
39
40It 'Accepts custom RepoRoot' {
41$repoRoot = Join-Path $TestDrive 'test-repo'
42New-Item -ItemType Directory -Path $repoRoot -Force | Out-Null
43{ Invoke-PythonLint -RepoRoot $repoRoot } | Should -Not -Throw
44}
45}
46
47Context 'OutputPath parameter' {
48BeforeEach {
49Mock Get-ChildItem { @() }
50Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
51Mock Push-Location {}
52Mock Pop-Location {}
53}
54
55It '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' {
67Context 'Tool not installed' {
68BeforeEach {
69Mock Push-Location {}
70Mock Pop-Location {}
71Mock Get-ChildItem {
72@([PSCustomObject]@{
73FullName = (Join-Path $TestDrive 'skill1/pyproject.toml')
74Directory = [PSCustomObject]@{ FullName = (Join-Path $TestDrive 'skill1') }
75})
76}
77Mock Get-Command { $null } -ParameterFilter { $Name -eq 'ruff' }
78}
79
3b922278Bill Berry4 months ago80It 'Returns failure when ruff not available' {
0a90f573Jason5 months ago81$result = Invoke-PythonLint -RepoRoot $TestDrive
82$result.success | Should -BeFalse
3b922278Bill Berry4 months ago83}
84
85It 'Reports skill path in errors' {
86$result = Invoke-PythonLint -RepoRoot $TestDrive
87$result.errors | Should -Contain (Join-Path $TestDrive 'skill1')
0a90f573Jason5 months ago88}
89
90It 'Reports zero skills checked when ruff missing' {
91$result = Invoke-PythonLint -RepoRoot $TestDrive
92$result.skillsChecked | Should -Be 0
93}
94}
95
96Context 'Tool installed' {
97BeforeEach {
98Mock Push-Location {}
99Mock Pop-Location {}
100Mock Get-ChildItem { @() }
101Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
102}
103
104It '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' {
115Context 'No Python skills found' {
116BeforeEach {
117Mock Push-Location {}
118Mock Pop-Location {}
119Mock Get-ChildItem { @() }
120Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
121}
122
123It '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
130Context 'Python skills found' {
131BeforeEach {
132Mock Push-Location {}
133Mock Pop-Location {}
134Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
135Mock ruff { $global:LASTEXITCODE = 0; '' }
136}
137
138It 'Discovers skills via pyproject.toml' {
139$skillDir = Join-Path $TestDrive 'skill1'
140Mock Get-ChildItem {
141@([PSCustomObject]@{
142FullName = (Join-Path $skillDir 'pyproject.toml')
143Directory = [PSCustomObject]@{ FullName = $skillDir }
144})
145}
146
147$result = Invoke-PythonLint -RepoRoot $TestDrive
148$result.skillsChecked | Should -Be 1
149}
150
151It 'Discovers multiple skills' {
152$skill1Dir = Join-Path $TestDrive 'skill1'
153$skill2Dir = Join-Path $TestDrive 'skill2'
154Mock Get-ChildItem {
155@(
156[PSCustomObject]@{
157FullName = (Join-Path $skill1Dir 'pyproject.toml')
158Directory = [PSCustomObject]@{ FullName = $skill1Dir }
159},
160[PSCustomObject]@{
161FullName = (Join-Path $skill2Dir 'pyproject.toml')
162Directory = [PSCustomObject]@{ FullName = $skill2Dir }
163}
164)
165}
166
167$result = Invoke-PythonLint -RepoRoot $TestDrive
168$result.skillsChecked | Should -Be 2
169}
170
171It 'Excludes node_modules from discovery' {
172Mock Get-ChildItem {
173@([PSCustomObject]@{
174FullName = (Join-Path $TestDrive 'node_modules/pkg/pyproject.toml')
175Directory = [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' {
190BeforeAll {
191$script:SkillDir = Join-Path $TestDrive 'lint-skill'
192}
193
194BeforeEach {
195Mock Push-Location {}
196Mock Pop-Location {}
197Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
198Mock Get-ChildItem {
199@([PSCustomObject]@{
200FullName = (Join-Path $script:SkillDir 'pyproject.toml')
201Directory = [PSCustomObject]@{ FullName = $script:SkillDir }
202})
203}
204}
205
206Context 'Lint passes' {
207BeforeEach {
208Mock ruff { $global:LASTEXITCODE = 0; '' }
209}
210
211It 'Returns success when ruff reports no issues' {
212$result = Invoke-PythonLint -RepoRoot $TestDrive
213$result.success | Should -BeTrue
214}
215
216It 'Marks skill as passed in details' {
217$result = Invoke-PythonLint -RepoRoot $TestDrive
218$result.details[0].passed | Should -BeTrue
219}
220
221It 'Reports no errors' {
222$result = Invoke-PythonLint -RepoRoot $TestDrive
223$result.errors | Should -HaveCount 0
224}
225}
226
227Context 'Lint fails' {
228BeforeEach {
229Mock ruff { $global:LASTEXITCODE = 1; 'error: E501 line too long' }
230}
231
232It 'Returns failure when ruff reports issues' {
233$result = Invoke-PythonLint -RepoRoot $TestDrive
234$result.success | Should -BeFalse
235}
236
237It 'Records skill path in errors' {
238$result = Invoke-PythonLint -RepoRoot $TestDrive
239$result.errors | Should -Contain $script:SkillDir
240}
241
242It 'Marks skill as failed in details' {
243$result = Invoke-PythonLint -RepoRoot $TestDrive
244$result.details[0].passed | Should -BeFalse
245}
246}
247
248Context 'Ruff throws exception' {
249BeforeEach {
250Mock ruff { throw 'ruff crashed' }
251}
252
253It 'Handles ruff exception gracefully' {
254$result = Invoke-PythonLint -RepoRoot $TestDrive
255$result.success | Should -BeFalse
256}
257
258It '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' {
270BeforeAll {
271$script:OutputSkillDir = Join-Path $TestDrive 'output-skill'
272}
273
274BeforeEach {
275Mock Push-Location {}
276Mock Pop-Location {}
277Mock Get-Command { [PSCustomObject]@{ Source = 'ruff' } } -ParameterFilter { $Name -eq 'ruff' }
278Mock Get-ChildItem {
279@([PSCustomObject]@{
280FullName = (Join-Path $script:OutputSkillDir 'pyproject.toml')
281Directory = [PSCustomObject]@{ FullName = $script:OutputSkillDir }
282})
283}
284Mock ruff { $global:LASTEXITCODE = 0; '' }
285}
286
287Context 'OutputPath specified' {
288It 'Writes JSON results to OutputPath' {
289$outputPath = Join-Path $TestDrive 'lint-results.json'
290Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
291Test-Path $outputPath | Should -BeTrue
292}
293
294It 'Produces valid JSON output' {
295$outputPath = Join-Path $TestDrive 'lint-results2.json'
296Invoke-PythonLint -RepoRoot $TestDrive -OutputPath $outputPath
297{ Get-Content $outputPath -Raw | ConvertFrom-Json } | Should -Not -Throw
298}
299}
300
301Context 'OutputPath not specified' {
302It 'Does not throw when OutputPath omitted' {
303{ Invoke-PythonLint -RepoRoot $TestDrive } | Should -Not -Throw
304}
305}
306}
307
308#endregion