@@ -82,10 +82,11 @@ func TestStatusStableChannelIgnoresRC(t *testing.T) {
8282 t .Parallel ()
8383
8484 snaps := & fakeSnapshots {snap : Snapshot {
85- LatestStable : rel ("v0.2.8" ),
86- LatestRC : rc ("v0.2.9-rc.1" ),
87- FetchedAt : testPublishedAt ,
88- Available : true ,
85+ LatestStable : rel ("v0.2.8" ),
86+ LatestRC : rc ("v0.2.9-rc.1" ),
87+ FetchedAt : testPublishedAt ,
88+ StableAvailable : true ,
89+ RCAvailable : true ,
8990 }}
9091 svc , _ := newTestService (t , "v0.2.8" , snaps , newFakeChannelStore ())
9192
@@ -111,10 +112,11 @@ func TestStatusStableAndRCOffersNewestRC(t *testing.T) {
111112 t .Parallel ()
112113
113114 snaps := & fakeSnapshots {snap : Snapshot {
114- LatestStable : rel ("v0.2.8" ),
115- LatestRC : rc ("v0.2.9-rc.1" ),
116- FetchedAt : testPublishedAt ,
117- Available : true ,
115+ LatestStable : rel ("v0.2.8" ),
116+ LatestRC : rc ("v0.2.9-rc.1" ),
117+ FetchedAt : testPublishedAt ,
118+ StableAvailable : true ,
119+ RCAvailable : true ,
118120 }}
119121 store := newFakeChannelStore ()
120122 store .channels [1 ] = string (ChannelStableAndRC )
@@ -129,17 +131,78 @@ func TestStatusStableAndRCOffersNewestRC(t *testing.T) {
129131 assert .True (t , status .LatestEligible .Prerelease )
130132}
131133
134+ func TestStatusRCOnlySnapshotRemainsAvailableForStableAndRC (t * testing.T ) {
135+ t .Parallel ()
136+
137+ snaps := & fakeSnapshots {snap : Snapshot {
138+ LatestStable : rel ("v9.0.0" ), // cached data whose revalidation failed
139+ LatestRC : rc ("v0.2.9-rc.1" ),
140+ FetchedAt : testPublishedAt ,
141+ StableAvailable : false ,
142+ RCAvailable : true ,
143+ }}
144+
145+ stableStore := newFakeChannelStore ()
146+ stableSvc , _ := newTestService (t , "v0.2.8" , snaps , stableStore )
147+ stableStatus , err := stableSvc .GetUpdateStatus (context .Background (), 1 )
148+ require .NoError (t , err )
149+ assert .False (t , stableStatus .StatusAvailable )
150+ assert .False (t , stableStatus .UpdateAvailable )
151+ assert .Nil (t , stableStatus .LatestEligible )
152+
153+ rcStore := newFakeChannelStore ()
154+ rcStore .channels [1 ] = string (ChannelStableAndRC )
155+ rcSvc , _ := newTestService (t , "v0.2.8" , snaps , rcStore )
156+ rcStatus , err := rcSvc .GetUpdateStatus (context .Background (), 1 )
157+ require .NoError (t , err )
158+ assert .True (t , rcStatus .StatusAvailable )
159+ assert .True (t , rcStatus .UpdateAvailable )
160+ require .NotNil (t , rcStatus .LatestEligible )
161+ assert .Equal (t , "v0.2.9-rc.1" , rcStatus .LatestEligible .Version ,
162+ "the unverified cached stable must not outrank the fresh RC" )
163+ }
164+
165+ func TestIncompleteRCViewOnlySuppressesStableAndRCChannel (t * testing.T ) {
166+ t .Parallel ()
167+
168+ snaps := & fakeSnapshots {snap : Snapshot {
169+ LatestStable : rel ("v0.2.9" ),
170+ LatestRC : rc ("v9.0.0-rc.1" ), // cached data whose revalidation failed
171+ FetchedAt : testPublishedAt ,
172+ StableAvailable : true ,
173+ RCAvailable : false ,
174+ }}
175+
176+ stableSvc , _ := newTestService (t , "v0.2.8" , snaps , newFakeChannelStore ())
177+ stableStatus , err := stableSvc .GetUpdateStatus (context .Background (), 1 )
178+ require .NoError (t , err )
179+ assert .True (t , stableStatus .StatusAvailable )
180+ assert .True (t , stableStatus .UpdateAvailable )
181+ require .NotNil (t , stableStatus .LatestEligible )
182+ assert .Equal (t , "v0.2.9" , stableStatus .LatestEligible .Version )
183+
184+ rcStore := newFakeChannelStore ()
185+ rcStore .channels [1 ] = string (ChannelStableAndRC )
186+ rcSvc , _ := newTestService (t , "v0.2.8" , snaps , rcStore )
187+ rcStatus , err := rcSvc .GetUpdateStatus (context .Background (), 1 )
188+ require .NoError (t , err )
189+ assert .False (t , rcStatus .StatusAvailable )
190+ assert .False (t , rcStatus .UpdateAvailable )
191+ assert .Nil (t , rcStatus .LatestEligible )
192+ }
193+
132194// Running an RC when its stable lands must offer the stable
133195// on BOTH channels — semver ranks v0.2.9 above v0.2.9-rc.5, and on
134196// stable_and_rc the max-compare picks the stable over the RC.
135197func TestStatusRCPromotedToStable (t * testing.T ) {
136198 t .Parallel ()
137199
138200 snaps := & fakeSnapshots {snap : Snapshot {
139- LatestStable : rel ("v0.2.9" ),
140- LatestRC : rc ("v0.2.9-rc.5" ),
141- FetchedAt : testPublishedAt ,
142- Available : true ,
201+ LatestStable : rel ("v0.2.9" ),
202+ LatestRC : rc ("v0.2.9-rc.5" ),
203+ FetchedAt : testPublishedAt ,
204+ StableAvailable : true ,
205+ RCAvailable : true ,
143206 }}
144207
145208 for _ , channel := range []Channel {ChannelStable , ChannelStableAndRC } {
@@ -162,10 +225,11 @@ func TestStatusNonSemverCurrentNeverOffers(t *testing.T) {
162225 t .Parallel ()
163226
164227 snaps := & fakeSnapshots {snap : Snapshot {
165- LatestStable : rel ("v9.9.9" ),
166- LatestRC : rc ("v9.9.10-rc.1" ),
167- FetchedAt : testPublishedAt ,
168- Available : true ,
228+ LatestStable : rel ("v9.9.9" ),
229+ LatestRC : rc ("v9.9.10-rc.1" ),
230+ FetchedAt : testPublishedAt ,
231+ StableAvailable : true ,
232+ RCAvailable : true ,
169233 }}
170234 store := newFakeChannelStore ()
171235 store .channels [1 ] = string (ChannelStableAndRC )
@@ -190,10 +254,11 @@ func TestStatusCurrentNewerThanEveryRelease(t *testing.T) {
190254 t .Parallel ()
191255
192256 snaps := & fakeSnapshots {snap : Snapshot {
193- LatestStable : rel ("v0.2.9" ),
194- LatestRC : rc ("v0.2.9-rc.5" ),
195- FetchedAt : testPublishedAt ,
196- Available : true ,
257+ LatestStable : rel ("v0.2.9" ),
258+ LatestRC : rc ("v0.2.9-rc.5" ),
259+ FetchedAt : testPublishedAt ,
260+ StableAvailable : true ,
261+ RCAvailable : true ,
197262 }}
198263
199264 for _ , channel := range []Channel {ChannelStable , ChannelStableAndRC } {
@@ -215,10 +280,11 @@ func TestChannelFlipDropsPendingRC(t *testing.T) {
215280 t .Parallel ()
216281
217282 snaps := & fakeSnapshots {snap : Snapshot {
218- LatestStable : rel ("v0.2.8" ),
219- LatestRC : rc ("v0.2.9-rc.1" ),
220- FetchedAt : testPublishedAt ,
221- Available : true ,
283+ LatestStable : rel ("v0.2.8" ),
284+ LatestRC : rc ("v0.2.9-rc.1" ),
285+ FetchedAt : testPublishedAt ,
286+ StableAvailable : true ,
287+ RCAvailable : true ,
222288 }}
223289 store := newFakeChannelStore ()
224290 svc , _ := newTestService (t , "v0.2.8" , snaps , store )
@@ -243,9 +309,10 @@ func TestInstallCommandExactTemplate(t *testing.T) {
243309 t .Parallel ()
244310
245311 snaps := & fakeSnapshots {snap : Snapshot {
246- LatestStable : rel ("v0.2.9" ),
247- FetchedAt : testPublishedAt ,
248- Available : true ,
312+ LatestStable : rel ("v0.2.9" ),
313+ FetchedAt : testPublishedAt ,
314+ StableAvailable : true ,
315+ RCAvailable : true ,
249316 }}
250317 svc , _ := newTestService (t , "v0.2.8" , snaps , newFakeChannelStore ())
251318
@@ -334,8 +401,9 @@ func TestStatusAvailableAfterSuccessfulEmptyFetch(t *testing.T) {
334401 t .Parallel ()
335402
336403 snaps := & fakeSnapshots {snap : Snapshot {
337- FetchedAt : testPublishedAt ,
338- Available : true ,
404+ FetchedAt : testPublishedAt ,
405+ StableAvailable : true ,
406+ RCAvailable : true ,
339407 }}
340408 svc , _ := newTestService (t , "v0.2.8" , snaps , newFakeChannelStore ())
341409
@@ -351,9 +419,10 @@ func TestUnavailableStatusDoesNotOfferCachedRelease(t *testing.T) {
351419 t .Parallel ()
352420
353421 snaps := & fakeSnapshots {snap : Snapshot {
354- LatestStable : rel ("v0.2.9" ),
355- FetchedAt : testPublishedAt ,
356- Available : false ,
422+ LatestStable : rel ("v0.2.9" ),
423+ FetchedAt : testPublishedAt ,
424+ StableAvailable : false ,
425+ RCAvailable : false ,
357426 }}
358427 svc , _ := newTestService (t , "v0.2.8" , snaps , newFakeChannelStore ())
359428
@@ -393,9 +462,10 @@ func TestStatusPropagatesChannelReadError(t *testing.T) {
393462 t .Parallel ()
394463
395464 snaps := & fakeSnapshots {snap : Snapshot {
396- LatestStable : rel ("v0.2.9" ),
397- FetchedAt : testPublishedAt ,
398- Available : true ,
465+ LatestStable : rel ("v0.2.9" ),
466+ FetchedAt : testPublishedAt ,
467+ StableAvailable : true ,
468+ RCAvailable : true ,
399469 }}
400470 store := newFakeChannelStore ()
401471 store .getErr = errors .New ("connection reset by peer" )
@@ -429,10 +499,11 @@ func TestNoncanonicalCandidateNeverOffered(t *testing.T) {
429499
430500 snaps := & fakeSnapshots {snap : Snapshot {
431501 // "0.3.0" (no leading v) is invalid for golang.org/x/mod/semver.
432- LatestStable : rel ("0.3.0" ),
433- LatestRC : rc ("nightly-20260727-abc" ),
434- FetchedAt : testPublishedAt ,
435- Available : true ,
502+ LatestStable : rel ("0.3.0" ),
503+ LatestRC : rc ("nightly-20260727-abc" ),
504+ FetchedAt : testPublishedAt ,
505+ StableAvailable : true ,
506+ RCAvailable : true ,
436507 }}
437508 for _ , channel := range []Channel {ChannelStable , ChannelStableAndRC } {
438509 store := newFakeChannelStore ()
0 commit comments