microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/1637-b-tracking-paths

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/tests/evals/Get-ChangedAIArtifact.Tests.ps1

224lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4
5BeforeAll {
6 $script:ModulePath = Join-Path $PSScriptRoot '../../evals/Modules/ArtifactDetection.psm1'
7 $script:ScriptPath = Join-Path $PSScriptRoot '../../evals/Get-ChangedAIArtifact.ps1'
8
9 Import-Module $script:ModulePath -Force
10}
11
12Describe 'ArtifactDetection module' -Tag 'Unit' {
13 Context 'Get-ArtifactDescriptor' {
14 It 'Classifies nested agent artifacts' {
15 $result = Get-ArtifactDescriptor -Path '.github/agents/hve-core/researcher.agent.md'
16 $result.kind | Should -Be 'agent'
17 $result.artifactId | Should -Be 'researcher'
18 $result.path | Should -Be '.github/agents/hve-core/researcher.agent.md'
19 }
20
21 It 'Classifies repo-root-only agent artifacts (no collection subdirectory)' {
22 $result = Get-ArtifactDescriptor -Path '.github/agents/local-only.agent.md'
23 $result.kind | Should -Be 'agent'
24 $result.artifactId | Should -Be 'local-only'
25 }
26
27 It 'Classifies prompt artifacts' {
28 $result = Get-ArtifactDescriptor -Path '.github/prompts/hve-core/task-research.prompt.md'
29 $result.kind | Should -Be 'prompt'
30 $result.artifactId | Should -Be 'task-research'
31 }
32
33 It 'Classifies instruction artifacts' {
34 $result = Get-ArtifactDescriptor -Path '.github/instructions/coding-standards/powershell/powershell.instructions.md'
35 $result.kind | Should -Be 'instruction'
36 $result.artifactId | Should -Be 'powershell'
37 }
38
39 It 'Classifies skill SKILL.md files' {
40 $result = Get-ArtifactDescriptor -Path '.github/skills/shared/pr-reference/SKILL.md'
41 $result.kind | Should -Be 'skill'
42 $result.artifactId | Should -Be 'pr-reference'
43 }
44
45 It 'Classifies repo-root-only skill SKILL.md (no collection subdirectory)' {
46 $result = Get-ArtifactDescriptor -Path '.github/skills/local-only/SKILL.md'
47 $result.kind | Should -Be 'skill'
48 $result.artifactId | Should -Be 'local-only'
49 }
50
51 It 'Normalizes backslash separators' {
52 $result = Get-ArtifactDescriptor -Path '.github\agents\hve-core\researcher.agent.md'
53 $result.kind | Should -Be 'agent'
54 $result.path | Should -Be '.github/agents/hve-core/researcher.agent.md'
55 }
56
57 It 'Returns null for non-artifact paths' {
58 Get-ArtifactDescriptor -Path 'scripts/evals/Test-EvalSpec.ps1' | Should -BeNullOrEmpty
59 Get-ArtifactDescriptor -Path 'docs/README.md' | Should -BeNullOrEmpty
60 Get-ArtifactDescriptor -Path '.github/skills/shared/pr-reference/scripts/helper.ps1' | Should -BeNullOrEmpty
61 Get-ArtifactDescriptor -Path '' | Should -BeNullOrEmpty
62 }
63
64 It 'Does not match files that merely contain artifact suffixes elsewhere' {
65 Get-ArtifactDescriptor -Path 'docs/agent.md' | Should -BeNullOrEmpty
66 Get-ArtifactDescriptor -Path '.github/agents/notes/README.md' | Should -BeNullOrEmpty
67 }
68 }
69
70 Context 'ConvertFrom-GitDiffNameStatus' {
71 It 'Parses added, modified, and deleted entries' {
72 $lines = @(
73 "A`tdocs/README.md",
74 "M`tscripts/run.ps1",
75 "D`tlegacy/old.txt"
76 )
77 $records = ConvertFrom-GitDiffNameStatus -Lines $lines
78 $records.Count | Should -Be 3
79 ($records | Where-Object { $_.status -eq 'A' }).path | Should -Be 'docs/README.md'
80 ($records | Where-Object { $_.status -eq 'M' }).path | Should -Be 'scripts/run.ps1'
81 ($records | Where-Object { $_.status -eq 'D' }).path | Should -Be 'legacy/old.txt'
82 }
83
84 It 'Parses rename entries and preserves both paths' {
85 $lines = @("R100`told/path.md`tnew/path.md")
86 $records = ConvertFrom-GitDiffNameStatus -Lines $lines
87 $records.Count | Should -Be 1
88 $records[0].status | Should -Be 'R'
89 $records[0].previousPath | Should -Be 'old/path.md'
90 $records[0].path | Should -Be 'new/path.md'
91 }
92
93 It 'Parses copy entries' {
94 $lines = @("C75`tsrc.md`tdst.md")
95 $records = ConvertFrom-GitDiffNameStatus -Lines $lines
96 $records[0].status | Should -Be 'C'
97 $records[0].previousPath | Should -Be 'src.md'
98 $records[0].path | Should -Be 'dst.md'
99 }
100
101 It 'Skips empty or malformed lines' {
102 $lines = @('', ' ', "A", "A`t")
103 $records = ConvertFrom-GitDiffNameStatus -Lines $lines
104 $records.Count | Should -Be 0
105 }
106
107 It 'Returns empty array for null input' {
108 $records = ConvertFrom-GitDiffNameStatus -Lines $null
109 $records.Count | Should -Be 0
110 }
111 }
112
113 Context 'Get-ChangedArtifactRecord' {
114 It 'Returns null for non-artifact paths' {
115 $change = @{ status = 'M'; path = 'scripts/foo.ps1'; previousPath = $null }
116 Get-ChangedArtifactRecord -Change $change | Should -BeNullOrEmpty
117 }
118
119 It 'Returns an artifact record for an added agent' {
120 $change = @{ status = 'A'; path = '.github/agents/hve-core/foo.agent.md'; previousPath = $null }
121 $rec = Get-ChangedArtifactRecord -Change $change
122 $rec.kind | Should -Be 'agent'
123 $rec.artifactId | Should -Be 'foo'
124 $rec.status | Should -Be 'A'
125 }
126
127 It 'Returns an artifact record for a deleted skill' {
128 $change = @{ status = 'D'; path = '.github/skills/shared/pr-reference/SKILL.md'; previousPath = $null }
129 $rec = Get-ChangedArtifactRecord -Change $change
130 $rec.kind | Should -Be 'skill'
131 $rec.artifactId | Should -Be 'pr-reference'
132 $rec.status | Should -Be 'D'
133 }
134
135 It 'Returns the destination as the artifact for a rename' {
136 $change = @{
137 status = 'R'
138 path = '.github/prompts/hve-core/new-name.prompt.md'
139 previousPath = '.github/prompts/hve-core/old-name.prompt.md'
140 }
141 $rec = Get-ChangedArtifactRecord -Change $change
142 $rec.kind | Should -Be 'prompt'
143 $rec.artifactId | Should -Be 'new-name'
144 $rec.status | Should -Be 'R'
145 $rec.previousPath | Should -Be '.github/prompts/hve-core/old-name.prompt.md'
146 }
147
148 It 'Treats a rename out of artifact space as a deletion' {
149 $change = @{
150 status = 'R'
151 path = 'archive/foo.md'
152 previousPath = '.github/prompts/hve-core/foo.prompt.md'
153 }
154 $rec = Get-ChangedArtifactRecord -Change $change
155 $rec.kind | Should -Be 'prompt'
156 $rec.artifactId | Should -Be 'foo'
157 $rec.status | Should -Be 'D'
158 }
159 }
160}
161
162Describe 'Get-ChangedAIArtifact.ps1 entry script' -Tag 'Integration' {
163 BeforeAll {
164 $script:gitAvailable = $null -ne (Get-Command git -ErrorAction SilentlyContinue)
165 }
166
167 It 'Emits a manifest classifying added artifacts between two commits' {
168 if (-not $script:gitAvailable) {
169 Set-ItResult -Skipped -Because 'git executable not available in test environment'
170 return
171 }
172
173 $repo = Join-Path $TestDrive ('repo-' + [Guid]::NewGuid())
174 New-Item -ItemType Directory -Path $repo | Out-Null
175
176 Push-Location $repo
177 try {
178 & git init --quiet --initial-branch=main 2>&1 | Out-Null
179 & git config user.email 'test@example.com' 2>&1 | Out-Null
180 & git config user.name 'Test User' 2>&1 | Out-Null
181 & git config commit.gpgsign false 2>&1 | Out-Null
182
183 New-Item -ItemType Directory -Path '.github/agents/hve-core' -Force | Out-Null
184 'seed' | Set-Content -LiteralPath 'README.md'
185 & git add . 2>&1 | Out-Null
186 & git commit --quiet -m 'baseline' 2>&1 | Out-Null
187 $baseSha = (& git rev-parse HEAD).Trim()
188
189 'agent body' | Set-Content -LiteralPath '.github/agents/hve-core/new-agent.agent.md'
190 New-Item -ItemType Directory -Path '.github/skills/shared/sample-skill' -Force | Out-Null
191 'skill body' | Set-Content -LiteralPath '.github/skills/shared/sample-skill/SKILL.md'
192 'unrelated change' | Set-Content -LiteralPath 'docs.md'
193 & git add . 2>&1 | Out-Null
194 & git commit --quiet -m 'add artifacts' 2>&1 | Out-Null
195 $headSha = (& git rev-parse HEAD).Trim()
196
197 $outFile = Join-Path $TestDrive ('manifest-' + [Guid]::NewGuid() + '.json')
198 & pwsh -NoProfile -File $script:ScriptPath `
199 -BaseRef $baseSha -HeadRef $headSha -OutFile $outFile -RepoRoot $repo *> $null
200 $LASTEXITCODE | Should -Be 0
201
202 $manifest = Get-Content -LiteralPath $outFile -Raw | ConvertFrom-Json
203 $manifest.artifacts.Count | Should -Be 2
204
205 $kinds = @($manifest.artifacts | ForEach-Object { $_.kind } | Sort-Object -Unique)
206 $kinds | Should -Contain 'agent'
207 $kinds | Should -Contain 'skill'
208
209 $agentEntry = $manifest.artifacts | Where-Object { $_.kind -eq 'agent' }
210 $agentEntry.artifactId | Should -Be 'new-agent'
211 $agentEntry.status | Should -Be 'A'
212
213 $skillEntry = $manifest.artifacts | Where-Object { $_.kind -eq 'skill' }
214 $skillEntry.artifactId | Should -Be 'sample-skill'
215 $skillEntry.status | Should -Be 'A'
216
217 $manifest.PSObject.Properties.Name | Should -Contain 'affectedAgents'
218 @($manifest.affectedAgents) | Should -Contain 'new-agent'
219 }
220 finally {
221 Pop-Location
222 }
223 }
224}