Skip to content

Commit 2c4ecf6

Browse files
Reproduce shared-only claims and historical-prefix recovery baselines
Four isolated cases show unpublished claims bypassed, missing-tag proof misclassified, changed-prefix completed baselines rejected, and ambiguous baseline refs selected. Twelve existing controls remain green. (cherry picked from commit 136af13)
1 parent c2542d4 commit 2c4ecf6

1 file changed

Lines changed: 123 additions & 10 deletions

File tree

‎.github/actions/Resolve-PSModuleVersion/tests/Resolve-PSModuleVersion.Recovery.Tests.ps1‎

Lines changed: 123 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ Describe 'Gallery-only version selection requires original ownership proof' {
4949
ref = 'refs/tags/v1.2.4'
5050
object = [pscustomobject]@{ type = 'tag'; sha = '3333333333333333333333333333333333333333' }
5151
}
52+
VersionReference = [pscustomobject]@{
53+
ref = 'refs/tags/PSModule-Publication/testmodule/1.2.4'
54+
object = [pscustomobject]@{ type = 'tag'; sha = '3333333333333333333333333333333333333333' }
55+
}
5256
Annotation = [pscustomobject]@{
5357
sha = '3333333333333333333333333333333333333333'
5458
tag = 'v1.2.4'
@@ -61,11 +65,13 @@ Describe 'Gallery-only version selection requires original ownership proof' {
6165
}
6266
$state = @{
6367
Record = $record
68+
Records = [Collections.Generic.List[object]]::new()
6469
Claims = @($record.Reference)
6570
Calls = [Collections.Generic.List[string]]::new()
6671
ModulePath = $modulePath
6772
Claim = $claim
6873
}
74+
$state.Records.Add($record)
6975
$script:state = $state
7076
$reader = @{
7177
GetSource = {
@@ -80,36 +86,53 @@ Describe 'Gallery-only version selection requires original ownership proof' {
8086
GetTagReference = {
8187
param($Tag)
8288
$state.Calls.Add("tag-read:$Tag")
83-
if ($Tag -ceq $state.Record.Annotation.tag) { $state.Record.Reference.object }
89+
foreach ($item in $state.Records) {
90+
if ($Tag -ceq $item.Annotation.tag) { return $item.Reference.object }
91+
}
8492
}.GetNewClosure()
8593
GetVersionReference = {
8694
param($Name, $Version)
87-
if ($Name -cne 'TestModule' -or $Version -cne '1.2.4') {
88-
throw 'Unexpected module/version coordinate lookup.'
89-
}
95+
if ($Name -cne 'TestModule') { throw 'Unexpected module/version coordinate lookup.' }
9096
$state.Calls.Add('module-version-coordinate-read')
91-
$state.Record.Reference.object
97+
foreach ($item in $state.Records) {
98+
if ($item.VersionReference.ref -ceq "refs/tags/PSModule-Publication/testmodule/$Version") {
99+
return $item.VersionReference.object
100+
}
101+
}
92102
}.GetNewClosure()
93103
GetTagObject = {
94104
param($Sha)
95-
if ($Sha -cne $state.Record.Annotation.sha) { throw 'Unexpected annotated tag lookup.' }
96-
$state.Record.Annotation
105+
foreach ($item in $state.Records) {
106+
if ($Sha -ceq $item.Annotation.sha) { return $item.Annotation }
107+
}
108+
throw 'Unexpected annotated tag lookup.'
97109
}.GetNewClosure()
98110
GetRelease = {
99111
param($Tag)
100112
$state.Calls.Add("release-read:$Tag")
101-
if ($Tag -ceq $state.Record.Annotation.tag) { $state.Record.Release }
113+
foreach ($item in $state.Records) {
114+
if ($Tag -ceq $item.Annotation.tag) { return $item.Release }
115+
}
102116
}.GetNewClosure()
103117
GetPackage = {
104118
param($Name, $Version)
105119
$state.Calls.Add("package-read:$Version")
106120
if ($Name -cne 'TestModule') { throw 'Unexpected module lookup.' }
107-
if ($Version -ceq '1.2.4') { $state.Record.Package }
121+
foreach ($item in $state.Records) {
122+
if ($item.VersionReference.ref -ceq "refs/tags/PSModule-Publication/testmodule/$Version") {
123+
return $item.Package
124+
}
125+
}
108126
}.GetNewClosure()
109127
GetPackageArtifactDigest = {
110128
param($Name, $Version)
111129
$state.Calls.Add("raw-package-read:$Version")
112-
Get-PSModulePackageArtifactDigest -Path $state.Record.PackagePath -ModuleName $Name -GalleryVersion $Version
130+
foreach ($item in $state.Records) {
131+
if ($item.VersionReference.ref -ceq "refs/tags/PSModule-Publication/testmodule/$Version") {
132+
return Get-PSModulePackageArtifactDigest -Path $item.PackagePath -ModuleName $Name -GalleryVersion $Version
133+
}
134+
}
135+
throw 'Unexpected raw package lookup.'
113136
}.GetNewClosure()
114137
}
115138
$script:parameters = @{
@@ -154,6 +177,54 @@ Describe 'Gallery-only version selection requires original ownership proof' {
154177
(Get-ResolvedModuleVersion @script:parameters).ToString() | Should -BeExactly 'v1.2.4'
155178
}
156179

180+
It 'deduplicates shared and release refs for one exact original publication' {
181+
$script:state.Claims = @($script:state.Record.Reference, $script:state.Record.VersionReference)
182+
183+
(Get-ResolvedModuleVersion @script:parameters).ToString() | Should -BeExactly 'v1.2.4'
184+
}
185+
186+
It 'rejects an unpublished shared-only claim before a later completed baseline can select a new version' {
187+
$original = $script:state.Record
188+
$original.Reference = $null
189+
$original.Package = $null
190+
$later = [pscustomobject]@{
191+
Reference = [pscustomobject]@{
192+
ref = 'refs/tags/v1.2.5'
193+
object = [pscustomobject]@{ type = 'tag'; sha = '4444444444444444444444444444444444444444' }
194+
}
195+
VersionReference = [pscustomobject]@{
196+
ref = 'refs/tags/PSModule-Publication/testmodule/1.2.5'
197+
object = [pscustomobject]@{ type = 'tag'; sha = '4444444444444444444444444444444444444444' }
198+
}
199+
Annotation = [pscustomobject]@{
200+
sha = '4444444444444444444444444444444444444444'; tag = 'v1.2.5'; message = 'Completed later legacy baseline.'
201+
object = [pscustomobject]@{ type = 'commit'; sha = '2222222222222222222222222222222222222222' }
202+
}
203+
Release = [pscustomobject]@{
204+
tag_name = 'v1.2.5'; prerelease = $false; draft = $false
205+
assets = @([pscustomobject]@{ name = 'TestModule-1.2.5.zip'; state = 'uploaded' })
206+
}
207+
}
208+
$script:state.Records.Add($later)
209+
$script:state.Claims = @($original.VersionReference, $later.Reference)
210+
$script:parameters.GitHubVersion = New-PSSemVer -Version '1.2.5'
211+
$script:parameters.PSGalleryVersion = New-PSSemVer -Version '1.2.5'
212+
Mock -CommandName Get-NextModuleVersion -ModuleName Resolve-PSModuleVersion.Helpers -MockWith {
213+
New-PSSemVer -Version 'v1.2.6'
214+
}
215+
216+
{ Get-ResolvedModuleVersion @script:parameters } | Should -Throw '*original source claim*no published raw package proof*'
217+
218+
Should -Invoke -CommandName Get-NextModuleVersion -ModuleName Resolve-PSModuleVersion.Helpers -Times 0 -Exactly
219+
}
220+
221+
It 'rejects a shared-only published package whose observed release tag is missing' {
222+
$script:state.Record.Reference = $null
223+
$script:state.Claims = @($script:state.Record.VersionReference)
224+
225+
{ Get-ResolvedModuleVersion @script:parameters } | Should -Throw '*no verifiable source/artifact ownership proof*'
226+
}
227+
157228
It 'rejects a Gallery-only version with unavailable legacy proof instead of inventing a fallback' {
158229
$script:state.Record.Annotation.message = 'Legacy tag without artifact proof.'
159230

@@ -186,6 +257,48 @@ Describe 'Gallery-only version selection requires original ownership proof' {
186257
(Get-ResolvedModuleVersion @script:parameters).ToString() | Should -BeExactly 'v1.2.5'
187258
}
188259

260+
It 'uses the actual completed historical tag when the new source changes its prefix' {
261+
$script:state.Record.Annotation.object.sha = '2222222222222222222222222222222222222222'
262+
$script:state.Record.Annotation.message = 'Completed legacy tag.'
263+
$script:state.Record.Release = [pscustomobject]@{
264+
tag_name = 'v1.2.4'; prerelease = $false; draft = $false
265+
assets = @([pscustomobject]@{ name = 'TestModule-1.2.4.zip'; state = 'uploaded' })
266+
}
267+
$script:parameters.Configuration.VersionPrefix = 'release-'
268+
269+
(Get-ResolvedModuleVersion @script:parameters).ToString() | Should -BeExactly 'release-1.2.5'
270+
271+
$script:state.Calls | Should -Contain 'release-read:v1.2.4'
272+
$script:state.Calls | Should -Not -Contain 'release-read:release-1.2.4'
273+
}
274+
275+
It 'rejects ambiguous completed historical baseline tags instead of choosing a prefix' {
276+
$script:state.Record.Annotation.object.sha = '2222222222222222222222222222222222222222'
277+
$script:state.Record.Annotation.message = 'Completed legacy tag.'
278+
$script:state.Record.Release = [pscustomobject]@{
279+
tag_name = 'v1.2.4'; prerelease = $false; draft = $false
280+
assets = @([pscustomobject]@{ name = 'TestModule-1.2.4.zip'; state = 'uploaded' })
281+
}
282+
$other = [pscustomobject]@{
283+
Reference = [pscustomobject]@{
284+
ref = 'refs/tags/release-1.2.4'
285+
object = [pscustomobject]@{ type = 'tag'; sha = '4444444444444444444444444444444444444444' }
286+
}
287+
Annotation = [pscustomobject]@{
288+
sha = '4444444444444444444444444444444444444444'; tag = 'release-1.2.4'; message = 'Another completed legacy baseline.'
289+
object = [pscustomobject]@{ type = 'commit'; sha = '2222222222222222222222222222222222222222' }
290+
}
291+
Release = [pscustomobject]@{
292+
tag_name = 'release-1.2.4'; prerelease = $false; draft = $false
293+
assets = @([pscustomobject]@{ name = 'TestModule-1.2.4.zip'; state = 'uploaded' })
294+
}
295+
}
296+
$script:state.Records.Add($other)
297+
$script:state.Claims = @($script:state.Record.Reference, $other.Reference)
298+
299+
{ Get-ResolvedModuleVersion @script:parameters } | Should -Throw '*ambiguous*baseline*'
300+
}
301+
189302
It 'rejects changed package bytes despite matching source, version, and PR' {
190303
[IO.File]::WriteAllText(
191304
(Join-Path -Path $script:state.ModulePath -ChildPath 'TestModule.psm1'), '# Different package payload.'

0 commit comments

Comments
 (0)