microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
hve-core-v3.3.27

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

383lines · modeblame

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