microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e69486a5f809ede45c63c0a31358c12912bd5168

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

379lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6 Pester tests for Invoke-PythonTests.ps1 script
7.DESCRIPTION
8 Tests for Python testing wrapper script:
9 - Parameter validation
10 - Tool availability checks
11 - Skill discovery via pyproject.toml
12 - Tests directory detection
13 - Pytest execution and result handling
14 - Output file generation
15 - Summary counters
16#>
17
18BeforeAll {
19 $script:ScriptPath = Join-Path $PSScriptRoot '../../linting/Invoke-PythonTests.ps1'
20
21 # Create stub function for pytest so it can be mocked even when not installed
22 function global:pytest { '' }
23
24 . $script:ScriptPath
25}
26
27AfterAll {
28 Remove-Item -Path 'Function:\pytest' -Force -ErrorAction SilentlyContinue
29}
30
31#region Parameter Validation Tests
32
33Describe 'Invoke-PythonTests Parameter Validation' -Tag 'Unit' {
34 Context 'RepoRoot parameter' {
35 BeforeEach {
36 Mock Get-ChildItem { @() }
37 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
38 Mock Push-Location {}
39 Mock Pop-Location {}
40 }
41
42 It 'Accepts custom RepoRoot' {
43 $repoRoot = Join-Path $TestDrive 'test-repo'
44 New-Item -ItemType Directory -Path $repoRoot -Force | Out-Null
45 { Invoke-PythonTests -RepoRoot $repoRoot } | Should -Not -Throw
46 }
47 }
48
49 Context 'OutputPath parameter' {
50 BeforeEach {
51 Mock Get-ChildItem { @() }
52 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
53 Mock Push-Location {}
54 Mock Pop-Location {}
55 }
56
57 It 'Accepts custom OutputPath' {
58 $outputPath = Join-Path $TestDrive 'test-output.json'
59 { Invoke-PythonTests -RepoRoot $TestDrive -OutputPath $outputPath } | Should -Not -Throw
60 }
61 }
62
63 Context 'Verbosity parameter' {
64 BeforeEach {
65 Mock Get-ChildItem { @() }
66 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
67 Mock Push-Location {}
68 Mock Pop-Location {}
69 }
70
71 It 'Defaults to -v verbosity' {
72 { Invoke-PythonTests -RepoRoot $TestDrive } | Should -Not -Throw
73 }
74
75 It 'Accepts custom verbosity' {
76 { Invoke-PythonTests -RepoRoot $TestDrive -Verbosity '-vv' } | Should -Not -Throw
77 }
78 }
79}
80
81#endregion
82
83#region Tool Availability Tests
84
85Describe 'pytest Tool Availability' -Tag 'Unit' {
86 Context 'Tool not installed' {
87 BeforeEach {
88 Mock Push-Location {}
89 Mock Pop-Location {}
90 Mock Get-ChildItem {
91 @([PSCustomObject]@{
92 FullName = (Join-Path $TestDrive 'skill1/pyproject.toml')
93 Directory = [PSCustomObject]@{ FullName = (Join-Path $TestDrive 'skill1') }
94 })
95 }
96 Mock Get-Command { $null } -ParameterFilter { $Name -eq 'pytest' }
97 }
98
99 It 'Returns failure when pytest not installed' {
100 $result = Invoke-PythonTests -RepoRoot $TestDrive
101 $result.success | Should -BeFalse
102 $result.errors | Should -Contain 'pytest not installed'
103 }
104
105 It 'Reports zero skills tested when pytest missing' {
106 $result = Invoke-PythonTests -RepoRoot $TestDrive
107 $result.skillsTested | Should -Be 0
108 }
109
110 It 'Reports zero passed and failed' {
111 $result = Invoke-PythonTests -RepoRoot $TestDrive
112 $result.passed | Should -Be 0
113 $result.failed | Should -Be 0
114 }
115 }
116
117 Context 'Tool installed' {
118 BeforeEach {
119 Mock Push-Location {}
120 Mock Pop-Location {}
121 Mock Get-ChildItem { @() }
122 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
123 }
124
125 It 'Proceeds when pytest available' {
126 { Invoke-PythonTests -RepoRoot $TestDrive } | Should -Not -Throw
127 }
128 }
129}
130
131#endregion
132
133#region Skill Discovery Tests
134
135Describe 'Python Skill Discovery for Testing' -Tag 'Unit' {
136 Context 'No Python skills found' {
137 BeforeEach {
138 Mock Push-Location {}
139 Mock Pop-Location {}
140 Mock Get-ChildItem { @() }
141 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
142 }
143
144 It 'Returns success with zero skills' {
145 $result = Invoke-PythonTests -RepoRoot $TestDrive
146 $result.success | Should -BeTrue
147 $result.skillsTested | Should -Be 0
148 }
149
150 It 'Reports zero passed and failed' {
151 $result = Invoke-PythonTests -RepoRoot $TestDrive
152 $result.passed | Should -Be 0
153 $result.failed | Should -Be 0
154 }
155 }
156
157 Context 'Skill without tests directory' {
158 BeforeEach {
159 $script:NoTestsSkillDir = Join-Path $TestDrive 'no-tests-skill'
160 Mock Push-Location {}
161 Mock Pop-Location {}
162 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
163 Mock Get-ChildItem {
164 @([PSCustomObject]@{
165 FullName = (Join-Path $script:NoTestsSkillDir 'pyproject.toml')
166 Directory = [PSCustomObject]@{ FullName = $script:NoTestsSkillDir }
167 })
168 }
169 Mock Test-Path { $false } -ParameterFilter { $Path -like '*tests' }
170 Mock pytest { $global:LASTEXITCODE = 0; '' }
171 }
172
173 It 'Skips skill without tests directory' {
174 $result = Invoke-PythonTests -RepoRoot $TestDrive
175 $result.skillsTested | Should -Be 0
176 }
177
178 It 'Does not call pytest for skill without tests' {
179 Invoke-PythonTests -RepoRoot $TestDrive
180 Should -Invoke -CommandName pytest -Times 0
181 }
182 }
183
184 Context 'Excludes node_modules from discovery' {
185 BeforeEach {
186 Mock Push-Location {}
187 Mock Pop-Location {}
188 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
189 Mock Get-ChildItem {
190 @([PSCustomObject]@{
191 FullName = (Join-Path $TestDrive 'node_modules/pkg/pyproject.toml')
192 Directory = [PSCustomObject]@{ FullName = (Join-Path $TestDrive 'node_modules/pkg') }
193 })
194 }
195 }
196
197 It 'Filters out node_modules paths' {
198 $result = Invoke-PythonTests -RepoRoot $TestDrive
199 $result.skillsTested | Should -Be 0
200 }
201 }
202}
203
204#endregion
205
206#region Test Execution Tests
207
208Describe 'Pytest Execution' -Tag 'Unit' {
209 BeforeAll {
210 $script:SkillDir = Join-Path $TestDrive 'test-skill'
211 }
212
213 BeforeEach {
214 Mock Push-Location {}
215 Mock Pop-Location {}
216 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
217 Mock Get-ChildItem {
218 @([PSCustomObject]@{
219 FullName = (Join-Path $script:SkillDir 'pyproject.toml')
220 Directory = [PSCustomObject]@{ FullName = $script:SkillDir }
221 })
222 }
223 Mock Test-Path { $true } -ParameterFilter { $Path -like '*tests' }
224 }
225
226 Context 'Tests pass' {
227 BeforeEach {
228 Mock pytest { $global:LASTEXITCODE = 0; '3 passed' }
229 }
230
231 It 'Returns success when pytest passes' {
232 $result = Invoke-PythonTests -RepoRoot $TestDrive
233 $result.success | Should -BeTrue
234 }
235
236 It 'Increments passed counter' {
237 $result = Invoke-PythonTests -RepoRoot $TestDrive
238 $result.passed | Should -Be 1
239 }
240
241 It 'Reports one skill tested' {
242 $result = Invoke-PythonTests -RepoRoot $TestDrive
243 $result.skillsTested | Should -Be 1
244 }
245
246 It 'Reports zero failures' {
247 $result = Invoke-PythonTests -RepoRoot $TestDrive
248 $result.failed | Should -Be 0
249 }
250
251 It 'Marks skill as passed in details' {
252 $result = Invoke-PythonTests -RepoRoot $TestDrive
253 $result.details[0].passed | Should -BeTrue
254 }
255 }
256
257 Context 'Tests fail' {
258 BeforeEach {
259 Mock pytest { $global:LASTEXITCODE = 1; '1 failed, 2 passed' }
260 }
261
262 It 'Returns failure when pytest fails' {
263 $result = Invoke-PythonTests -RepoRoot $TestDrive
264 $result.success | Should -BeFalse
265 }
266
267 It 'Increments failed counter' {
268 $result = Invoke-PythonTests -RepoRoot $TestDrive
269 $result.failed | Should -Be 1
270 }
271
272 It 'Records skill path in errors' {
273 $result = Invoke-PythonTests -RepoRoot $TestDrive
274 $result.errors | Should -Contain $script:SkillDir
275 }
276
277 It 'Marks skill as failed in details' {
278 $result = Invoke-PythonTests -RepoRoot $TestDrive
279 $result.details[0].passed | Should -BeFalse
280 }
281 }
282
283 Context 'Pytest throws exception' {
284 BeforeEach {
285 Mock pytest { throw 'pytest crashed' }
286 }
287
288 It 'Handles pytest exception gracefully' {
289 $result = Invoke-PythonTests -RepoRoot $TestDrive
290 $result.success | Should -BeFalse
291 }
292
293 It 'Increments failed counter on exception' {
294 $result = Invoke-PythonTests -RepoRoot $TestDrive
295 $result.failed | Should -Be 1
296 }
297
298 It 'Records error with skill path' {
299 $result = Invoke-PythonTests -RepoRoot $TestDrive
300 $result.errors | Should -Not -BeNullOrEmpty
301 }
302 }
303
304 Context 'Multiple skills' {
305 BeforeEach {
306 $script:Skill1Dir = Join-Path $TestDrive 'skill-a'
307 $script:Skill2Dir = Join-Path $TestDrive 'skill-b'
308 Mock Get-ChildItem {
309 @(
310 [PSCustomObject]@{
311 FullName = (Join-Path $script:Skill1Dir 'pyproject.toml')
312 Directory = [PSCustomObject]@{ FullName = $script:Skill1Dir }
313 },
314 [PSCustomObject]@{
315 FullName = (Join-Path $script:Skill2Dir 'pyproject.toml')
316 Directory = [PSCustomObject]@{ FullName = $script:Skill2Dir }
317 }
318 )
319 }
320 Mock pytest { $global:LASTEXITCODE = 0; 'passed' }
321 }
322
323 It 'Tests all discovered skills' {
324 $result = Invoke-PythonTests -RepoRoot $TestDrive
325 $result.skillsTested | Should -Be 2
326 }
327
328 It 'Counts all passing skills' {
329 $result = Invoke-PythonTests -RepoRoot $TestDrive
330 $result.passed | Should -Be 2
331 }
332 }
333}
334
335#endregion
336
337#region Output Persistence Tests
338
339Describe 'Output Persistence' -Tag 'Unit' {
340 BeforeAll {
341 $script:OutputSkillDir = Join-Path $TestDrive 'output-skill'
342 }
343
344 BeforeEach {
345 Mock Push-Location {}
346 Mock Pop-Location {}
347 Mock Get-Command { [PSCustomObject]@{ Source = 'pytest' } } -ParameterFilter { $Name -eq 'pytest' }
348 Mock Get-ChildItem {
349 @([PSCustomObject]@{
350 FullName = (Join-Path $script:OutputSkillDir 'pyproject.toml')
351 Directory = [PSCustomObject]@{ FullName = $script:OutputSkillDir }
352 })
353 }
354 Mock Test-Path { $true } -ParameterFilter { $Path -like '*tests' }
355 Mock pytest { $global:LASTEXITCODE = 0; 'passed' }
356 }
357
358 Context 'OutputPath specified' {
359 It 'Writes JSON results to OutputPath' {
360 $outputPath = Join-Path $TestDrive 'test-results.json'
361 Invoke-PythonTests -RepoRoot $TestDrive -OutputPath $outputPath
362 Test-Path $outputPath | Should -BeTrue
363 }
364
365 It 'Produces valid JSON output' {
366 $outputPath = Join-Path $TestDrive 'test-results2.json'
367 Invoke-PythonTests -RepoRoot $TestDrive -OutputPath $outputPath
368 { Get-Content $outputPath -Raw | ConvertFrom-Json } | Should -Not -Throw
369 }
370 }
371
372 Context 'OutputPath not specified' {
373 It 'Does not throw when OutputPath omitted' {
374 { Invoke-PythonTests -RepoRoot $TestDrive } | Should -Not -Throw
375 }
376 }
377}
378
379#endregion
380