@@ -30,9 +30,16 @@ import (
3030 "sigs.k8s.io/promo-tools/v4/promoter/image/promotion"
3131 "sigs.k8s.io/promo-tools/v4/promoter/image/provenance"
3232 "sigs.k8s.io/promo-tools/v4/promoter/image/registry"
33+ "sigs.k8s.io/promo-tools/v4/promoter/image/schema"
3334 "sigs.k8s.io/promo-tools/v4/types/image"
3435)
3536
37+ // nonEmptyManifests returns a minimal manifest slice so that the pipeline
38+ // does not stop early due to an empty manifest list.
39+ func nonEmptyManifests () []schema.Manifest {
40+ return []schema.Manifest {{}}
41+ }
42+
3643func TestPromoteImages (t * testing.T ) {
3744 sut := imagepromoter.Promoter {}
3845 sut .SetProvenanceGenerator (& provenance.PromotionGenerator {})
@@ -51,7 +58,9 @@ func TestPromoteImages(t *testing.T) {
5158 // No errors
5259 shouldErr : false ,
5360 msg : "No errors" ,
54- prepare : func (_ * imagefakes.FakePromoterImplementation ) {},
61+ prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
62+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
63+ },
5564 },
5665 {
5766 // ValidateOptions fails
@@ -78,27 +87,31 @@ func TestPromoteImages(t *testing.T) {
7887 // GetPromotionEdges fails
7988 shouldErr : true ,
8089 prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
90+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
8191 fpi .GetPromotionEdgesReturns (nil , testErr )
8292 },
8393 },
8494 {
8595 // ValidateStagingSignatures fails
8696 shouldErr : true ,
8797 prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
98+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
8899 fpi .ValidateStagingSignaturesReturns (nil , testErr )
89100 },
90101 },
91102 {
92103 // PromoteImages fails
93104 shouldErr : true ,
94105 prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
106+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
95107 fpi .PromoteImagesReturns (testErr )
96108 },
97109 },
98110 {
99111 // SignImages fails
100112 shouldErr : true ,
101113 prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
114+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
102115 fpi .SignImagesReturns (testErr )
103116 },
104117 },
@@ -107,6 +120,7 @@ func TestPromoteImages(t *testing.T) {
107120 shouldErr : true ,
108121 msg : "WriteProvenanceAttestations fails" ,
109122 prepare : func (fpi * imagefakes.FakePromoterImplementation ) {
123+ fpi .ParseManifestsReturns (nonEmptyManifests (), nil )
110124 fpi .WriteProvenanceAttestationsReturns (testErr )
111125 },
112126 },
@@ -126,6 +140,7 @@ func TestPromoteImages(t *testing.T) {
126140func TestPromoteImagesParseOnly (t * testing.T ) {
127141 sut := imagepromoter.Promoter {}
128142 mock := imagefakes.FakePromoterImplementation {}
143+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
129144 sut .SetImplementation (& mock )
130145
131146 // ParseOnly should stop after plan phase with no error
@@ -141,6 +156,7 @@ func TestPromoteImagesParseOnly(t *testing.T) {
141156func TestPromoteImagesNonConfirm (t * testing.T ) {
142157 sut := imagepromoter.Promoter {}
143158 mock := imagefakes.FakePromoterImplementation {}
159+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
144160 sut .SetImplementation (& mock )
145161 sut .SetProvenanceVerifier (& fakeVerifier {
146162 result : & provenance.Result {Verified : true },
@@ -156,9 +172,25 @@ func TestPromoteImagesNonConfirm(t *testing.T) {
156172 require .Equal (t , 0 , mock .PromoteImagesCallCount ())
157173}
158174
175+ func TestPromoteImagesEmptyManifests (t * testing.T ) {
176+ sut := imagepromoter.Promoter {}
177+ mock := imagefakes.FakePromoterImplementation {}
178+ // Return empty manifests (e.g., prow diff found no digests)
179+ mock .ParseManifestsReturns ([]schema.Manifest {}, nil )
180+ sut .SetImplementation (& mock )
181+
182+ opts := & options.Options {Confirm : true }
183+ require .NoError (t , sut .PromoteImages (context .Background (), opts ))
184+
185+ // No downstream phases should have been called
186+ require .Equal (t , 0 , mock .GetPromotionEdgesCallCount ())
187+ require .Equal (t , 0 , mock .PromoteImagesCallCount ())
188+ }
189+
159190func TestPromoteImagesProvenanceAlwaysRuns (t * testing.T ) {
160191 sut := imagepromoter.Promoter {}
161192 mock := imagefakes.FakePromoterImplementation {}
193+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
162194 mock .GetPromotionEdgesReturns (map [promotion.Edge ]any {
163195 testEdge (): nil ,
164196 }, nil )
@@ -199,6 +231,7 @@ func testEdge() promotion.Edge {
199231func TestPromoteImagesProvenanceFails (t * testing.T ) {
200232 sut := imagepromoter.Promoter {}
201233 mock := imagefakes.FakePromoterImplementation {}
234+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
202235 // Return a non-empty edge set so provenance has something to check
203236 mock .GetPromotionEdgesReturns (map [promotion.Edge ]any {
204237 testEdge (): nil ,
@@ -223,6 +256,7 @@ func TestPromoteImagesProvenanceFails(t *testing.T) {
223256func TestPromoteImagesProvenanceVerifierError (t * testing.T ) {
224257 sut := imagepromoter.Promoter {}
225258 mock := imagefakes.FakePromoterImplementation {}
259+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
226260 mock .GetPromotionEdgesReturns (map [promotion.Edge ]any {
227261 testEdge (): nil ,
228262 }, nil )
@@ -325,6 +359,7 @@ func TestNewPromoter(t *testing.T) {
325359 // Verify that a promoter created via New() has the verifier and
326360 // generator configured by running a full pipeline with a mock impl.
327361 mock := imagefakes.FakePromoterImplementation {}
362+ mock .ParseManifestsReturns (nonEmptyManifests (), nil )
328363 p .SetImplementation (& mock )
329364
330365 require .NoError (t , p .PromoteImages (context .Background (), & options.Options {Confirm : true }))
0 commit comments