microsoft/hve-core
Publicmirrored fromhttps://github.com/microsoft/hve-coreAvailable
scripts/tests/release/Update-VersionFiles.Tests.ps1
295lines · modecode
| 1 | #Requires -Modules Pester |
| 2 | # Copyright (c) Microsoft Corporation. |
| 3 | # SPDX-License-Identifier: MIT |
| 4 | |
| 5 | BeforeAll { |
| 6 | $script:ScriptPath = Join-Path $PSScriptRoot '../../release/Update-VersionFiles.ps1' |
| 7 | # Pass a dummy version to satisfy the mandatory parameter during dot-source. |
| 8 | # The main execution guard prevents any file changes. |
| 9 | . $script:ScriptPath -Version '0.0.0' |
| 10 | Mock Write-Host {} |
| 11 | } |
| 12 | |
| 13 | Describe 'Resolve-RepoRoot' -Tag 'Unit' { |
| 14 | It 'Returns the supplied path when provided' { |
| 15 | $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "rr-$([guid]::NewGuid())" |
| 16 | New-Item -ItemType Directory -Path $tempDir -Force | Out-Null |
| 17 | try { |
| 18 | $result = Resolve-RepoRoot -Supplied $tempDir |
| 19 | $result | Should -Be (Resolve-Path $tempDir).Path |
| 20 | } |
| 21 | finally { |
| 22 | Remove-Item -Recurse -Force $tempDir |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | It 'Auto-detects repo root when no path is supplied' { |
| 27 | $result = Resolve-RepoRoot -Supplied '' |
| 28 | $result | Should -Not -BeNullOrEmpty |
| 29 | Test-Path (Join-Path $result '.git') | Should -BeTrue |
| 30 | } |
| 31 | |
| 32 | It 'Throws when auto-detection fails and no path is supplied' { |
| 33 | Mock Resolve-Path { return [PSCustomObject]@{ Path = '/nonexistent/path' } } -ParameterFilter { |
| 34 | $Path -like "*../..*" |
| 35 | } |
| 36 | Mock Test-Path { return $false } -ParameterFilter { |
| 37 | $Path -like "*/.git" |
| 38 | } |
| 39 | { Resolve-RepoRoot -Supplied '' } | Should -Throw "*Unable to determine repository root*" |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | Describe 'Update-JsonVersion' -Tag 'Unit' { |
| 44 | BeforeAll { |
| 45 | $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) "ujv-$([guid]::NewGuid())" |
| 46 | New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null |
| 47 | } |
| 48 | |
| 49 | AfterAll { |
| 50 | if (Test-Path $script:TempDir) { |
| 51 | Remove-Item -Recurse -Force $script:TempDir |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | It 'Updates a simple version field' { |
| 56 | $filePath = Join-Path $script:TempDir 'simple.json' |
| 57 | @{ version = '1.0.0'; name = 'test' } | ConvertTo-Json | Set-Content $filePath |
| 58 | |
| 59 | Update-JsonVersion -FilePath $filePath -Description 'simple.json' -Transform { |
| 60 | param($j) $j.version = '2.0.0'; $j |
| 61 | } |
| 62 | |
| 63 | $result = Get-Content -Raw $filePath | ConvertFrom-Json |
| 64 | $result.version | Should -Be '2.0.0' |
| 65 | $result.name | Should -Be 'test' |
| 66 | } |
| 67 | |
| 68 | It 'Skips without error when file does not exist' { |
| 69 | $missingPath = Join-Path $script:TempDir 'does-not-exist.json' |
| 70 | { Update-JsonVersion -FilePath $missingPath -Description 'missing' -Transform { param($j) $j } } | |
| 71 | Should -Not -Throw |
| 72 | } |
| 73 | |
| 74 | It 'Updates nested properties via transform' { |
| 75 | $filePath = Join-Path $script:TempDir 'nested.json' |
| 76 | @{ |
| 77 | metadata = @{ version = '1.0.0' } |
| 78 | plugins = @(@{ version = '1.0.0'; id = 'p1' }) |
| 79 | } | ConvertTo-Json -Depth 10 | Set-Content $filePath |
| 80 | |
| 81 | Update-JsonVersion -FilePath $filePath -Description 'nested.json' -Transform { |
| 82 | param($j) |
| 83 | $j.metadata.version = '3.0.0' |
| 84 | foreach ($p in $j.plugins) { $p.version = '3.0.0' } |
| 85 | $j |
| 86 | } |
| 87 | |
| 88 | $result = Get-Content -Raw $filePath | ConvertFrom-Json -Depth 10 |
| 89 | $result.metadata.version | Should -Be '3.0.0' |
| 90 | $result.plugins[0].version | Should -Be '3.0.0' |
| 91 | } |
| 92 | |
| 93 | It 'Preserves dot-key in release-please manifest' { |
| 94 | $filePath = Join-Path $script:TempDir 'manifest.json' |
| 95 | @{ '.' = '1.0.0' } | ConvertTo-Json | Set-Content $filePath |
| 96 | |
| 97 | Update-JsonVersion -FilePath $filePath -Description 'manifest' -Transform { |
| 98 | param($j) $j.'.' = '4.0.0'; $j |
| 99 | } |
| 100 | |
| 101 | $result = Get-Content -Raw $filePath | ConvertFrom-Json |
| 102 | $result.'.' | Should -Be '4.0.0' |
| 103 | } |
| 104 | |
| 105 | It 'Updates both version and packages[""].version in package-lock.json' { |
| 106 | $filePath = Join-Path $script:TempDir 'package-lock.json' |
| 107 | @{ |
| 108 | name = 'hve-core' |
| 109 | version = '1.0.0' |
| 110 | lockfileVersion = 3 |
| 111 | packages = @{ '' = @{ version = '1.0.0'; name = 'hve-core' } } |
| 112 | } | ConvertTo-Json -Depth 10 | Set-Content $filePath |
| 113 | |
| 114 | $targetVersion = '5.0.0' |
| 115 | Update-JsonVersion -FilePath $filePath -Description 'package-lock.json' -AsHashtable -Transform { |
| 116 | param($j) |
| 117 | $j['version'] = $targetVersion |
| 118 | if ($j.ContainsKey('packages') -and $j['packages'].ContainsKey('')) { |
| 119 | $j['packages']['']['version'] = $targetVersion |
| 120 | } |
| 121 | $j |
| 122 | } |
| 123 | |
| 124 | $result = Get-Content -Raw $filePath | ConvertFrom-Json -Depth 10 -AsHashtable |
| 125 | $result['version'] | Should -Be '5.0.0' |
| 126 | $result['packages']['']['version'] | Should -Be '5.0.0' |
| 127 | $result['name'] | Should -Be 'hve-core' |
| 128 | } |
| 129 | |
| 130 | It 'Updates only top-level version when packages[""] is absent' { |
| 131 | $filePath = Join-Path $script:TempDir 'lock-no-root-pkg.json' |
| 132 | @{ |
| 133 | name = 'hve-core' |
| 134 | version = '1.0.0' |
| 135 | lockfileVersion = 3 |
| 136 | } | ConvertTo-Json -Depth 10 | Set-Content $filePath |
| 137 | |
| 138 | $targetVersion = '6.0.0' |
| 139 | Update-JsonVersion -FilePath $filePath -Description 'package-lock.json' -AsHashtable -Transform { |
| 140 | param($j) |
| 141 | $j['version'] = $targetVersion |
| 142 | if ($j.ContainsKey('packages') -and $j['packages'].ContainsKey('')) { |
| 143 | $j['packages']['']['version'] = $targetVersion |
| 144 | } |
| 145 | $j |
| 146 | } |
| 147 | |
| 148 | $result = Get-Content -Raw $filePath | ConvertFrom-Json -Depth 10 -AsHashtable |
| 149 | $result['version'] | Should -Be '6.0.0' |
| 150 | $result['name'] | Should -Be 'hve-core' |
| 151 | } |
| 152 | |
| 153 | It 'Throws when file contains malformed JSON' { |
| 154 | $filePath = Join-Path $script:TempDir 'malformed.json' |
| 155 | Set-Content -Path $filePath -Value '{ invalid json }' |
| 156 | |
| 157 | { Update-JsonVersion -FilePath $filePath -Description 'malformed' -Transform { param($j) $j } } | |
| 158 | Should -Throw |
| 159 | } |
| 160 | |
| 161 | It 'Throws when file is empty' { |
| 162 | $filePath = Join-Path $script:TempDir 'empty.json' |
| 163 | Set-Content -Path $filePath -Value '' |
| 164 | |
| 165 | { Update-JsonVersion -FilePath $filePath -Description 'empty' -Transform { param($j) $j } } | |
| 166 | Should -Throw |
| 167 | } |
| 168 | |
| 169 | It 'Propagates errors thrown by the transform block' { |
| 170 | $filePath = Join-Path $script:TempDir 'transform-err.json' |
| 171 | @{ version = '1.0.0' } | ConvertTo-Json | Set-Content $filePath |
| 172 | |
| 173 | { Update-JsonVersion -FilePath $filePath -Description 'transform-err' -Transform { |
| 174 | param($j) throw 'deliberate transform error' |
| 175 | } } | Should -Throw '*deliberate transform error*' |
| 176 | } |
| 177 | |
| 178 | It 'Throws when file is read-only' -Skip:($IsWindows -eq $false -and (id -u) -eq 0) { |
| 179 | $filePath = Join-Path $script:TempDir 'readonly.json' |
| 180 | @{ version = '1.0.0' } | ConvertTo-Json | Set-Content $filePath |
| 181 | Set-ItemProperty -Path $filePath -Name IsReadOnly -Value $true |
| 182 | |
| 183 | try { |
| 184 | { Update-JsonVersion -FilePath $filePath -Description 'readonly' -Transform { |
| 185 | param($j) $j.version = '2.0.0'; $j |
| 186 | } } | Should -Throw |
| 187 | } |
| 188 | finally { |
| 189 | Set-ItemProperty -Path $filePath -Name IsReadOnly -Value $false |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | Describe 'Update-VersionFiles script execution' -Tag 'Unit' { |
| 195 | BeforeAll { |
| 196 | $script:FakeRoot = Join-Path ([System.IO.Path]::GetTempPath()) "uvf-$([guid]::NewGuid())" |
| 197 | New-Item -ItemType Directory -Path $script:FakeRoot -Force | Out-Null |
| 198 | New-Item -ItemType Directory -Path (Join-Path $script:FakeRoot '.git') -Force | Out-Null |
| 199 | New-Item -ItemType Directory -Path (Join-Path $script:FakeRoot 'extension/templates') -Force | Out-Null |
| 200 | New-Item -ItemType Directory -Path (Join-Path $script:FakeRoot '.github/plugin') -Force | Out-Null |
| 201 | New-Item -ItemType Directory -Path (Join-Path $script:FakeRoot 'plugins/hve-core/.github/plugin') -Force | Out-Null |
| 202 | New-Item -ItemType Directory -Path (Join-Path $script:FakeRoot 'plugins/ado/.github/plugin') -Force | Out-Null |
| 203 | |
| 204 | # Seed all 5 version file types at 1.0.0 |
| 205 | @{ version = '1.0.0'; name = 'hve-core' } | |
| 206 | ConvertTo-Json | Set-Content (Join-Path $script:FakeRoot 'package.json') |
| 207 | @{ version = '1.0.0' } | |
| 208 | ConvertTo-Json | Set-Content (Join-Path $script:FakeRoot 'extension/templates/package.template.json') |
| 209 | @{ |
| 210 | metadata = @{ version = '1.0.0' } |
| 211 | plugins = @( |
| 212 | @{ version = '1.0.0'; id = 'hve-core' } |
| 213 | @{ version = '1.0.0'; id = 'ado' } |
| 214 | ) |
| 215 | } | ConvertTo-Json -Depth 10 | Set-Content (Join-Path $script:FakeRoot '.github/plugin/marketplace.json') |
| 216 | @{ version = '1.0.0' } | |
| 217 | ConvertTo-Json | Set-Content (Join-Path $script:FakeRoot 'plugins/hve-core/.github/plugin/plugin.json') |
| 218 | @{ version = '1.0.0' } | |
| 219 | ConvertTo-Json | Set-Content (Join-Path $script:FakeRoot 'plugins/ado/.github/plugin/plugin.json') |
| 220 | @{ '.' = '1.0.0' } | |
| 221 | ConvertTo-Json | Set-Content (Join-Path $script:FakeRoot '.release-please-manifest.json') |
| 222 | @{ |
| 223 | name = 'hve-core' |
| 224 | version = '1.0.0' |
| 225 | lockfileVersion = 3 |
| 226 | packages = @{ '' = @{ version = '1.0.0'; name = 'hve-core' } } |
| 227 | } | ConvertTo-Json -Depth 10 | Set-Content (Join-Path $script:FakeRoot 'package-lock.json') |
| 228 | } |
| 229 | |
| 230 | AfterAll { |
| 231 | if (Test-Path $script:FakeRoot) { |
| 232 | Remove-Item -Recurse -Force $script:FakeRoot |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | It 'Updates all version files to the target version' { |
| 237 | & $script:ScriptPath -Version '2.5.0' -RepoRoot $script:FakeRoot -SkipPluginGenerate |
| 238 | |
| 239 | $pkg = Get-Content -Raw (Join-Path $script:FakeRoot 'package.json') | ConvertFrom-Json |
| 240 | $pkg.version | Should -Be '2.5.0' |
| 241 | $pkg.name | Should -Be 'hve-core' |
| 242 | |
| 243 | $tmpl = Get-Content -Raw (Join-Path $script:FakeRoot 'extension/templates/package.template.json') | ConvertFrom-Json |
| 244 | $tmpl.version | Should -Be '2.5.0' |
| 245 | |
| 246 | $mkt = Get-Content -Raw (Join-Path $script:FakeRoot '.github/plugin/marketplace.json') | ConvertFrom-Json -Depth 10 |
| 247 | $mkt.metadata.version | Should -Be '2.5.0' |
| 248 | $mkt.plugins[0].version | Should -Be '2.5.0' |
| 249 | $mkt.plugins[1].version | Should -Be '2.5.0' |
| 250 | |
| 251 | $manifest = Get-Content -Raw (Join-Path $script:FakeRoot '.release-please-manifest.json') | ConvertFrom-Json |
| 252 | $manifest.'.' | Should -Be '2.5.0' |
| 253 | |
| 254 | $lock = Get-Content -Raw (Join-Path $script:FakeRoot 'package-lock.json') | ConvertFrom-Json -Depth 10 -AsHashtable |
| 255 | $lock['version'] | Should -Be '2.5.0' |
| 256 | $lock['packages']['']['version'] | Should -Be '2.5.0' |
| 257 | } |
| 258 | |
| 259 | It 'Updates multiple plugin.json files under plugins/' { |
| 260 | $p1 = Get-Content -Raw (Join-Path $script:FakeRoot 'plugins/hve-core/.github/plugin/plugin.json') | ConvertFrom-Json |
| 261 | $p1.version | Should -Be '2.5.0' |
| 262 | |
| 263 | $p2 = Get-Content -Raw (Join-Path $script:FakeRoot 'plugins/ado/.github/plugin/plugin.json') | ConvertFrom-Json |
| 264 | $p2.version | Should -Be '2.5.0' |
| 265 | } |
| 266 | |
| 267 | It 'Succeeds when optional files are missing' { |
| 268 | $sparseRoot = Join-Path ([System.IO.Path]::GetTempPath()) "uvf-sparse-$([guid]::NewGuid())" |
| 269 | New-Item -ItemType Directory -Path $sparseRoot -Force | Out-Null |
| 270 | New-Item -ItemType Directory -Path (Join-Path $sparseRoot '.git') -Force | Out-Null |
| 271 | |
| 272 | # Only create package.json — other files are absent |
| 273 | @{ version = '1.0.0' } | ConvertTo-Json | Set-Content (Join-Path $sparseRoot 'package.json') |
| 274 | |
| 275 | try { |
| 276 | { & $script:ScriptPath -Version '3.0.0' -RepoRoot $sparseRoot -SkipPluginGenerate } | |
| 277 | Should -Not -Throw |
| 278 | |
| 279 | $pkg = Get-Content -Raw (Join-Path $sparseRoot 'package.json') | ConvertFrom-Json |
| 280 | $pkg.version | Should -Be '3.0.0' |
| 281 | } |
| 282 | finally { |
| 283 | Remove-Item -Recurse -Force $sparseRoot |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | It 'Rejects invalid version "<Version>"' -ForEach @( |
| 288 | @{ Version = 'abc' } |
| 289 | @{ Version = '1.2' } |
| 290 | @{ Version = 'v1.2.3' } |
| 291 | ) { |
| 292 | { & $script:ScriptPath -Version $Version -RepoRoot $script:FakeRoot -SkipPluginGenerate } | |
| 293 | Should -Throw |
| 294 | } |
| 295 | } |
| 296 | |