microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/brd-skills-refactor

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/tests/plugins/Generate-Plugins.Tests.ps1

625lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4
5BeforeAll {
6 . $PSScriptRoot/../../plugins/Generate-Plugins.ps1
7 # Re-import CollectionHelpers after dot-sourcing because PluginHelpers internally
8 # imports CollectionHelpers with -Force, removing it from the caller's scope.
9 Import-Module (Join-Path $PSScriptRoot '../../collections/Modules/CollectionHelpers.psm1') -Force
10}
11
12Describe 'Get-AllowedCollectionMaturities' {
13 It 'Returns only stable for Stable channel' {
14 $result = Get-AllowedCollectionMaturities -Channel 'Stable'
15 $result | Should -Be @('stable')
16 }
17
18 It 'Returns stable, preview, and experimental for PreRelease channel' {
19 $result = Get-AllowedCollectionMaturities -Channel 'PreRelease'
20 $result | Should -Contain 'stable'
21 $result | Should -Contain 'preview'
22 $result | Should -Contain 'experimental'
23 }
24
25 It 'Does not include deprecated for either channel' {
26 $stable = Get-AllowedCollectionMaturities -Channel 'Stable'
27 $preRelease = Get-AllowedCollectionMaturities -Channel 'PreRelease'
28 $stable | Should -Not -Contain 'deprecated'
29 $preRelease | Should -Not -Contain 'deprecated'
30 }
31
32 It 'Does not include removed for either channel' {
33 $stable = Get-AllowedCollectionMaturities -Channel 'Stable'
34 $preRelease = Get-AllowedCollectionMaturities -Channel 'PreRelease'
35 $stable | Should -Not -Contain 'removed'
36 $preRelease | Should -Not -Contain 'removed'
37 }
38}
39
40Describe 'Select-CollectionItemsByChannel' {
41 It 'Includes stable items on Stable channel' {
42 $collection = @{
43 id = 'test'
44 items = @(
45 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' }
46 )
47 }
48 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'Stable'
49 $result.items.Count | Should -Be 1
50 }
51
52 It 'Excludes preview items on Stable channel' {
53 $collection = @{
54 id = 'test'
55 items = @(
56 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' },
57 @{ kind = 'agent'; path = '.github/agents/b.agent.md'; maturity = 'preview' }
58 )
59 }
60 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'Stable'
61 $result.items.Count | Should -Be 1
62 }
63
64 It 'Includes preview and experimental items on PreRelease channel' {
65 $collection = @{
66 id = 'test'
67 items = @(
68 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' },
69 @{ kind = 'prompt'; path = '.github/prompts/b.prompt.md'; maturity = 'preview' },
70 @{ kind = 'instruction'; path = '.github/instructions/c.instructions.md'; maturity = 'experimental' }
71 )
72 }
73 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'PreRelease'
74 $result.items.Count | Should -Be 3
75 }
76
77 It 'Excludes deprecated items on PreRelease channel' {
78 $collection = @{
79 id = 'test'
80 items = @(
81 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' },
82 @{ kind = 'agent'; path = '.github/agents/old.agent.md'; maturity = 'deprecated' }
83 )
84 }
85 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'PreRelease'
86 $result.items.Count | Should -Be 1
87 }
88
89 It 'Excludes removed items on Stable channel' {
90 $collection = @{
91 id = 'test'
92 items = @(
93 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' },
94 @{ kind = 'agent'; path = '.github/agents/gone.agent.md'; maturity = 'removed' }
95 )
96 }
97 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'Stable'
98 $result.items.Count | Should -Be 1
99 }
100
101 It 'Excludes removed items on PreRelease channel' {
102 $collection = @{
103 id = 'test'
104 items = @(
105 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' },
106 @{ kind = 'agent'; path = '.github/agents/gone.agent.md'; maturity = 'removed' }
107 )
108 }
109 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'PreRelease'
110 $result.items.Count | Should -Be 1
111 }
112
113 It 'Defaults to stable when maturity is null' {
114 $collection = @{
115 id = 'test'
116 items = @(
117 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = $null }
118 )
119 }
120 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'Stable'
121 $result.items.Count | Should -Be 1
122 }
123
124 It 'Preserves non-items keys from collection' {
125 $collection = @{
126 id = 'test'
127 name = 'Test Collection'
128 description = 'desc'
129 items = @(
130 @{ kind = 'agent'; path = '.github/agents/a.agent.md'; maturity = 'stable' }
131 )
132 }
133 $result = Select-CollectionItemsByChannel -Collection $collection -Channel 'Stable'
134 $result.id | Should -Be 'test'
135 $result.name | Should -Be 'Test Collection'
136 $result.description | Should -Be 'desc'
137 }
138}
139
140Describe 'Invoke-PluginGeneration - collection-level maturity' {
141 BeforeAll {
142 $script:maturityDir = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
143 New-Item -ItemType Directory -Path $script:maturityDir -Force | Out-Null
144
145 # Create package.json
146 @{
147 name = 'hve-core'
148 version = '1.0.0'
149 description = 'test'
150 author = 'test-author'
151 } | ConvertTo-Json | Set-Content -Path (Join-Path $script:maturityDir 'package.json')
152
153 # Create collections directory
154 $collectionsDir = Join-Path $script:maturityDir 'collections'
155 New-Item -ItemType Directory -Path $collectionsDir -Force | Out-Null
156
157 # Create .github structure with a test artifact
158 $ghDir = Join-Path $script:maturityDir '.github'
159 $agentsDir = Join-Path $ghDir 'agents/col'
160 New-Item -ItemType Directory -Path $agentsDir -Force | Out-Null
161 @'
162---
163description: "Test agent"
164---
165'@ | Set-Content -Path (Join-Path $agentsDir 'test.agent.md')
166
167 # Create shared directories for symlinks
168 New-Item -ItemType Directory -Path (Join-Path $script:maturityDir 'docs/templates') -Force | Out-Null
169 New-Item -ItemType Directory -Path (Join-Path $script:maturityDir 'scripts/lib') -Force | Out-Null
170
171 # Create plugins directory
172 New-Item -ItemType Directory -Path (Join-Path $script:maturityDir 'plugins') -Force | Out-Null
173
174 # Create .github/plugin directory
175 New-Item -ItemType Directory -Path (Join-Path $script:maturityDir '.github/plugin') -Force | Out-Null
176
177 # hve-core-all collection (required by Update-HveCoreAllCollection)
178 @"
179id: hve-core-all
180name: hve-core
181description: All artifacts
182tags: []
183items:
184 - path: .github/agents/col/test.agent.md
185 kind: agent
186display: {}
187"@ | Set-Content -Path (Join-Path $collectionsDir 'hve-core-all.collection.yml')
188
189 # Deprecated collection
190 @"
191id: deprecated-col
192name: Deprecated Collection
193description: A deprecated collection
194maturity: deprecated
195items:
196 - path: .github/agents/col/test.agent.md
197 kind: agent
198"@ | Set-Content -Path (Join-Path $collectionsDir 'deprecated-col.collection.yml')
199
200 # Experimental collection
201 @"
202id: experimental-col
203name: Experimental Collection
204description: An experimental collection
205maturity: experimental
206items:
207 - path: .github/agents/col/test.agent.md
208 kind: agent
209"@ | Set-Content -Path (Join-Path $collectionsDir 'experimental-col.collection.yml')
210 }
211
212 AfterAll {
213 Remove-Item -Path $script:maturityDir -Recurse -Force -ErrorAction SilentlyContinue
214 }
215
216 It 'Skips deprecated collection during generation' {
217 Invoke-PluginGeneration -RepoRoot $script:maturityDir -CollectionIds @('deprecated-col') -Refresh -Channel 'PreRelease' | Out-Null
218 $pluginDir = Join-Path $script:maturityDir 'plugins/deprecated-col'
219 Test-Path $pluginDir | Should -BeFalse
220 }
221
222 It 'Generates experimental collection on PreRelease channel' {
223 Invoke-PluginGeneration -RepoRoot $script:maturityDir -CollectionIds @('experimental-col') -Refresh -Channel 'PreRelease' | Out-Null
224 $pluginDir = Join-Path $script:maturityDir 'plugins/experimental-col'
225 Test-Path $pluginDir | Should -BeTrue
226 }
227}
228
229Describe 'Invoke-PluginGeneration' {
230 BeforeAll {
231 $script:tempDir = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
232 New-Item -ItemType Directory -Path $script:tempDir -Force | Out-Null
233
234 # Create package.json
235 @{
236 name = 'hve-core'
237 version = '1.0.0'
238 description = 'test'
239 author = 'test-author'
240 } | ConvertTo-Json | Set-Content -Path (Join-Path $script:tempDir 'package.json')
241
242 # Create collections directory with manifests
243 $collectionsDir = Join-Path $script:tempDir 'collections'
244 New-Item -ItemType Directory -Path $collectionsDir -Force | Out-Null
245
246 # Create .github structure with artifacts
247 $ghDir = Join-Path $script:tempDir '.github'
248 $agentsDir = Join-Path $ghDir 'agents'
249 $promptsDir = Join-Path $ghDir 'prompts'
250 $instrDir = Join-Path $ghDir 'instructions'
251 $skillsDir = Join-Path $ghDir 'skills/test-skill'
252 New-Item -ItemType Directory -Path $agentsDir -Force | Out-Null
253 New-Item -ItemType Directory -Path $promptsDir -Force | Out-Null
254 New-Item -ItemType Directory -Path $instrDir -Force | Out-Null
255 New-Item -ItemType Directory -Path $skillsDir -Force | Out-Null
256
257 @'
258---
259description: "Test agent"
260---
261'@ | Set-Content -Path (Join-Path $agentsDir 'test.agent.md')
262
263 @'
264---
265description: "Test prompt"
266---
267'@ | Set-Content -Path (Join-Path $promptsDir 'test.prompt.md')
268
269 @'
270---
271description: "Test instruction"
272applyTo: "**/*.ps1"
273---
274'@ | Set-Content -Path (Join-Path $instrDir 'test.instructions.md')
275
276 @'
277---
278name: test-skill
279description: "Test skill"
280---
281'@ | Set-Content -Path (Join-Path $skillsDir 'SKILL.md')
282
283 # Create docs/templates and scripts directories for shared symlinking
284 New-Item -ItemType Directory -Path (Join-Path $script:tempDir 'docs/templates') -Force | Out-Null
285 New-Item -ItemType Directory -Path (Join-Path $script:tempDir 'scripts/lib') -Force | Out-Null
286
287 # Create plugins directory
288 New-Item -ItemType Directory -Path (Join-Path $script:tempDir 'plugins') -Force | Out-Null
289
290 # Create .github/plugin directory for marketplace manifest
291 New-Item -ItemType Directory -Path (Join-Path $script:tempDir '.github/plugin') -Force | Out-Null
292
293 # hve-core-all collection
294 @"
295id: hve-core-all
296name: hve-core
297description: All artifacts
298tags:
299 - copilot
300items:
301 - path: .github/agents/test.agent.md
302 kind: agent
303 - path: .github/prompts/test.prompt.md
304 kind: prompt
305 - path: .github/instructions/test.instructions.md
306 kind: instruction
307 - path: .github/skills/test-skill
308 kind: skill
309display:
310 color: blue
311"@ | Set-Content -Path (Join-Path $collectionsDir 'hve-core-all.collection.yml')
312 }
313
314 AfterAll {
315 Remove-Item -Path $script:tempDir -Recurse -Force -ErrorAction SilentlyContinue
316 }
317
318 It 'Generates plugins successfully' {
319 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -Refresh -Channel 'PreRelease'
320 $result.Success | Should -BeTrue
321 $result.PluginCount | Should -BeGreaterOrEqual 1
322 }
323
324 It 'Creates plugin directory' {
325 $pluginDir = Join-Path $script:tempDir 'plugins/hve-core-all'
326 Test-Path $pluginDir | Should -BeTrue
327 }
328
329 It 'Generates plugin.json manifest' {
330 $manifestPath = Join-Path $script:tempDir 'plugins/hve-core-all/.github/plugin/plugin.json'
331 Test-Path $manifestPath | Should -BeTrue
332 $manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json
333 $manifest.name | Should -Be 'hve-core-all'
334 }
335
336 It 'Generates README.md' {
337 $readmePath = Join-Path $script:tempDir 'plugins/hve-core-all/README.md'
338 Test-Path $readmePath | Should -BeTrue
339 }
340
341 It 'Writes one Included Artifacts heading to collection markdown' {
342 $headingRepo = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
343 New-Item -ItemType Directory -Path $headingRepo -Force | Out-Null
344 New-Item -ItemType Directory -Path (Join-Path $headingRepo 'collections') -Force | Out-Null
345 New-Item -ItemType Directory -Path (Join-Path $headingRepo '.github/agents') -Force | Out-Null
346 New-Item -ItemType Directory -Path (Join-Path $headingRepo '.github/plugin') -Force | Out-Null
347 New-Item -ItemType Directory -Path (Join-Path $headingRepo 'plugins') -Force | Out-Null
348 New-Item -ItemType Directory -Path (Join-Path $headingRepo 'docs/templates') -Force | Out-Null
349 New-Item -ItemType Directory -Path (Join-Path $headingRepo 'scripts/lib') -Force | Out-Null
350
351 @{ name = 'hve-core'; version = '1.0.0'; description = 'test'; author = 'test-author' } |
352 ConvertTo-Json | Set-Content -Path (Join-Path $headingRepo 'package.json') -Encoding utf8NoBOM
353
354 @'
355---
356description: "Test agent"
357---
358'@ | Set-Content -Path (Join-Path $headingRepo '.github/agents/test.agent.md') -Encoding utf8NoBOM
359
360 @'
361id: hve-core-all
362name: hve-core
363description: All artifacts
364tags: []
365items: []
366display: {}
367'@ | Set-Content -Path (Join-Path $headingRepo 'collections/hve-core-all.collection.yml') -Encoding utf8NoBOM
368
369 $collectionYmlPath = Join-Path $headingRepo 'collections/heading-test.collection.yml'
370 @"
371id: heading-test
372name: Heading Test
373description: Heading writeback test
374items:
375 - path: .github/agents/test.agent.md
376 kind: agent
377"@ | Set-Content -Path $collectionYmlPath -Encoding utf8NoBOM
378
379 $collectionMdPath = Join-Path $headingRepo 'collections/heading-test.collection.md'
380 @"
381# Heading Test
382
383Intro text.
384
385## Included Artifacts
386
387<!-- BEGIN AUTO-GENERATED ARTIFACTS -->
388
389Old content.
390
391<!-- END AUTO-GENERATED ARTIFACTS -->
392"@ | Set-Content -Path $collectionMdPath -Encoding utf8NoBOM
393
394 $result = Invoke-PluginGeneration -RepoRoot $headingRepo -CollectionIds @('heading-test') -Refresh -Channel 'PreRelease'
395
396 $result.Success | Should -BeTrue
397 $collectionContent = Get-Content -Path $collectionMdPath -Raw
398 ([regex]::Matches($collectionContent, '(?m)^## Included Artifacts$')).Count | Should -Be 1
399 $collectionContent | Should -Not -Match 'Old content'
400 $collectionContent | Should -Match '### Chat Agents'
401 }
402
403 It 'Filters to specific collection IDs when provided' {
404 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -Refresh -Channel 'PreRelease'
405 $result.PluginCount | Should -Be 1
406 }
407
408 It 'Warns for non-existent collection IDs' {
409 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('nonexistent') -Refresh -Channel 'PreRelease' 3>&1
410 $warnings = @($result | Where-Object { $_ -is [System.Management.Automation.WarningRecord] })
411 $warnings.Count | Should -BeGreaterOrEqual 1
412 }
413
414 It 'Supports DryRun mode' {
415 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -DryRun -Channel 'PreRelease'
416 $result.Success | Should -BeTrue
417 }
418
419 It 'Returns zero plugins when no collections found' {
420 $emptyRoot = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
421 New-Item -ItemType Directory -Path (Join-Path $emptyRoot 'collections') -Force | Out-Null
422 New-Item -ItemType Directory -Path (Join-Path $emptyRoot 'plugins') -Force | Out-Null
423 @{ name = 'test'; version = '1.0.0'; description = 'test'; author = 'test' } |
424 ConvertTo-Json | Set-Content -Path (Join-Path $emptyRoot 'package.json')
425
426 # Create minimal .github structure for auto-update
427 New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.github/agents') -Force | Out-Null
428 @"
429id: hve-core-all
430name: hve-core
431description: test
432tags: []
433items: []
434display: {}
435"@ | Set-Content -Path (Join-Path $emptyRoot 'collections/hve-core-all.collection.yml')
436
437 $result = Invoke-PluginGeneration -RepoRoot $emptyRoot -CollectionIds @('missing-id') -Channel 'PreRelease' 3>&1
438 $hashtableResult = $result | Where-Object { $_ -is [hashtable] }
439 if ($hashtableResult) {
440 $hashtableResult.PluginCount | Should -Be 0
441 }
442 }
443
444 It 'Applies channel filtering to items' {
445 # Add a collection with mixed maturities
446 $mixedPath = Join-Path (Join-Path $script:tempDir 'collections') 'mixed.collection.yml'
447 @"
448id: mixed
449name: Mixed Collection
450description: Mixed maturity test
451items:
452 - path: .github/agents/test.agent.md
453 kind: agent
454 maturity: stable
455 - path: .github/prompts/test.prompt.md
456 kind: prompt
457 maturity: experimental
458"@ | Set-Content -Path $mixedPath
459
460 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('mixed') -Refresh -Channel 'Stable'
461 $result.Success | Should -BeTrue
462 }
463
464 It 'Removes orphan files on Refresh' {
465 # Create a stale file in plugin dir
466 $staleDir = Join-Path $script:tempDir 'plugins/hve-core-all/stale'
467 New-Item -ItemType Directory -Path $staleDir -Force | Out-Null
468 'stale' | Set-Content -Path (Join-Path $staleDir 'file.txt')
469
470 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -Refresh -Channel 'PreRelease'
471 $result.Success | Should -BeTrue
472 # Orphan file and its now-empty parent directory are removed
473 Test-Path (Join-Path $staleDir 'file.txt') | Should -BeFalse
474 Test-Path $staleDir | Should -BeFalse
475 # Plugin directory itself still exists with generated files
476 $pluginDir = Join-Path $script:tempDir 'plugins/hve-core-all'
477 Test-Path $pluginDir | Should -BeTrue
478 Test-Path (Join-Path $pluginDir 'README.md') | Should -BeTrue
479 }
480
481 It 'Logs DryRun message when refreshing existing plugin' {
482 # Create orphan file so DryRun has something to report
483 $pluginDir = Join-Path $script:tempDir 'plugins/hve-core-all'
484 $orphanDir = Join-Path $pluginDir 'stale-dry'
485 New-Item -ItemType Directory -Path $orphanDir -Force | Out-Null
486 'stale' | Set-Content -Path (Join-Path $orphanDir 'file.txt')
487
488 $output = Invoke-PluginGeneration -RepoRoot $script:tempDir `
489 -CollectionIds @('hve-core-all') `
490 -Refresh -DryRun -Channel 'PreRelease' 6>&1
491
492 $dryRunMessages = @($output | Where-Object { "$_" -match 'DRY RUN.*Would remove orphan' })
493 $dryRunMessages.Count | Should -BeGreaterOrEqual 1
494 }
495
496 It 'Warns when collections directory has no matching YAML files' {
497 $emptyRoot = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString())
498 $emptyCollDir = Join-Path $emptyRoot 'collections'
499 New-Item -ItemType Directory -Path $emptyCollDir -Force | Out-Null
500 New-Item -ItemType Directory -Path (Join-Path $emptyRoot 'plugins') -Force | Out-Null
501 New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.github/agents') -Force | Out-Null
502 @{ name = 'test'; version = '1.0.0'; description = 'test'; author = 'test' } |
503 ConvertTo-Json | Set-Content -Path (Join-Path $emptyRoot 'package.json')
504
505 # Mock Update-HveCoreAllCollection to avoid file-not-found errors
506 Mock Update-HveCoreAllCollection { return @{ ItemCount = 0; AddedCount = 0; RemovedCount = 0 } }
507
508 $result = Invoke-PluginGeneration -RepoRoot $emptyRoot -Channel 'PreRelease' 3>&1
509 $warnings = @($result | Where-Object { $_ -is [System.Management.Automation.WarningRecord] })
510 $warnings.Count | Should -BeGreaterOrEqual 1
511 $warnings[0].Message | Should -Match 'No collection manifests found'
512 }
513
514 It 'Outputs verbose symlink capability detection' {
515 $output = Invoke-PluginGeneration -RepoRoot $script:tempDir `
516 -CollectionIds @('hve-core-all') `
517 -Channel 'PreRelease' -Verbose 4>&1
518
519 $capMsg = @($output | Where-Object { "$_" -match 'Symlink capability' })
520 $capMsg.Count | Should -BeGreaterOrEqual 1
521 }
522
523 Context 'Orphan Cleanup' {
524 It 'Removes orphan files after overwrite-in-place' {
525 $staleDir = Join-Path $script:tempDir 'plugins/hve-core-all/orphan-test'
526 New-Item -ItemType Directory -Path $staleDir -Force | Out-Null
527 'stale' | Set-Content -Path (Join-Path $staleDir 'leftover.txt')
528
529 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -Refresh -Channel 'PreRelease'
530 $result.Success | Should -BeTrue
531 Test-Path (Join-Path $staleDir 'leftover.txt') | Should -BeFalse
532 Test-Path (Join-Path $script:tempDir 'plugins/hve-core-all/README.md') | Should -BeTrue
533 }
534
535 It 'Preserves generated files during cleanup' {
536 # Run a fresh Refresh to get clean state
537 Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -Refresh -Channel 'PreRelease' | Out-Null
538
539 $pluginDir = Join-Path $script:tempDir 'plugins/hve-core-all'
540 Test-Path (Join-Path $pluginDir 'README.md') | Should -BeTrue
541 Test-Path (Join-Path $pluginDir '.github/plugin/plugin.json') | Should -BeTrue
542 Test-Path (Join-Path $pluginDir 'docs/templates') | Should -BeTrue
543 Test-Path (Join-Path $pluginDir 'scripts/lib') | Should -BeTrue
544 }
545
546 It 'Removes empty directories after orphan cleanup' {
547 $nestedOrphan = Join-Path $script:tempDir 'plugins/hve-core-all/stale-dir/nested'
548 New-Item -ItemType Directory -Path $nestedOrphan -Force | Out-Null
549 'leftover' | Set-Content -Path (Join-Path $nestedOrphan 'leftover.txt')
550
551 $result = Invoke-PluginGeneration -RepoRoot $script:tempDir -CollectionIds @('hve-core-all') -Refresh -Channel 'PreRelease'
552 $result.Success | Should -BeTrue
553 Test-Path (Join-Path $script:tempDir 'plugins/hve-core-all/stale-dir') | Should -BeFalse
554 }
555
556 It 'DryRun logs orphan files without removing them' {
557 $orphanDir = Join-Path $script:tempDir 'plugins/hve-core-all/dry-orphan'
558 New-Item -ItemType Directory -Path $orphanDir -Force | Out-Null
559 'keep-me' | Set-Content -Path (Join-Path $orphanDir 'persist.txt')
560
561 $output = Invoke-PluginGeneration -RepoRoot $script:tempDir `
562 -CollectionIds @('hve-core-all') `
563 -Refresh -DryRun -Channel 'PreRelease' 6>&1
564
565 $dryRunMessages = @($output | Where-Object { "$_" -match 'DRY RUN.*Would remove orphan' })
566 $dryRunMessages.Count | Should -BeGreaterOrEqual 1
567 # File still exists after DryRun
568 Test-Path (Join-Path $orphanDir 'persist.txt') | Should -BeTrue
569 }
570 }
571}
572
573Describe 'Start-PluginGeneration' {
574 It 'Returns 0 on successful generation' {
575 Mock Invoke-PluginGeneration { return @{ Success = $true; PluginCount = 2 } }
576 Mock Get-Module { return @{ Name = 'PowerShell-Yaml' } } -ParameterFilter { $ListAvailable -and $Name -eq 'PowerShell-Yaml' }
577 Mock Import-Module {}
578
579 $scriptPath = "$PSScriptRoot/../../plugins/Generate-Plugins.ps1"
580 $exitCode = Start-PluginGeneration -ScriptPath $scriptPath -Channel 'PreRelease'
581 $exitCode | Should -Be 0
582 }
583
584 It 'Returns 1 when Invoke-PluginGeneration reports failure' {
585 Mock Invoke-PluginGeneration { return @{ Success = $false; PluginCount = 0; ErrorMessage = 'Generation failed' } }
586 Mock Get-Module { return @{ Name = 'PowerShell-Yaml' } } -ParameterFilter { $ListAvailable -and $Name -eq 'PowerShell-Yaml' }
587 Mock Import-Module {}
588
589 $scriptPath = "$PSScriptRoot/../../plugins/Generate-Plugins.ps1"
590 $output = Start-PluginGeneration -ScriptPath $scriptPath -Channel 'PreRelease' -ErrorAction SilentlyContinue
591 $exitCode = @($output) | Where-Object { $_ -is [int] } | Select-Object -Last 1
592 $exitCode | Should -Be 1
593 }
594
595 It 'Returns 1 when PowerShell-Yaml module is missing' {
596 Mock Get-Module { return $null } -ParameterFilter { $ListAvailable -and $Name -eq 'PowerShell-Yaml' }
597
598 $scriptPath = "$PSScriptRoot/../../plugins/Generate-Plugins.ps1"
599 $output = Start-PluginGeneration -ScriptPath $scriptPath -Channel 'PreRelease' -ErrorAction SilentlyContinue
600 $exitCode = @($output) | Where-Object { $_ -is [int] } | Select-Object -Last 1
601 $exitCode | Should -Be 1
602 }
603
604 It 'Defaults to refresh when no CollectionIds, Refresh, or DryRun provided' {
605 Mock Get-Module { return @{ Name = 'PowerShell-Yaml' } } -ParameterFilter { $ListAvailable -and $Name -eq 'PowerShell-Yaml' }
606 Mock Import-Module {}
607 Mock Invoke-PluginGeneration { return @{ Success = $true; PluginCount = 1 } }
608
609 $scriptPath = "$PSScriptRoot/../../plugins/Generate-Plugins.ps1"
610 Start-PluginGeneration -ScriptPath $scriptPath -Channel 'PreRelease' | Out-Null
611
612 Should -Invoke Invoke-PluginGeneration -Times 1 -ParameterFilter { $Refresh -eq $true }
613 }
614
615 It 'Does not force refresh when CollectionIds are provided' {
616 Mock Get-Module { return @{ Name = 'PowerShell-Yaml' } } -ParameterFilter { $ListAvailable -and $Name -eq 'PowerShell-Yaml' }
617 Mock Import-Module {}
618 Mock Invoke-PluginGeneration { return @{ Success = $true; PluginCount = 1 } }
619
620 $scriptPath = "$PSScriptRoot/../../plugins/Generate-Plugins.ps1"
621 Start-PluginGeneration -ScriptPath $scriptPath -CollectionIds @('test') -Channel 'PreRelease' | Out-Null
622
623 Should -Invoke Invoke-PluginGeneration -Times 1 -ParameterFilter { $Refresh -eq $false }
624 }
625}
626