@@ -266,3 +266,93 @@ func TestReconcileDeExpiryRestoresKey(t *testing.T) {
266266 assert .Empty (t , expirations )
267267 assert .Contains (t , r .AllCredentials (), SDKCredential (key ))
268268}
269+
270+ // findAcceptedKey returns the entry with the given value, or nil. Used because AcceptedKeys order is
271+ // unspecified.
272+ func findAcceptedKey (entries []AcceptedKey , value string ) * AcceptedKey {
273+ for i := range entries {
274+ if entries [i ].Value == value {
275+ return & entries [i ]
276+ }
277+ }
278+ return nil
279+ }
280+
281+ // TestAcceptedKeys verifies that AcceptedKeys returns the full accepted set — every server and mobile
282+ // key, including the anchor and primary mobile key — with type, identifier, and expiry populated.
283+ func TestAcceptedKeys (t * testing.T ) {
284+ t .Run ("single anchor plus primary mobile" , func (t * testing.T ) {
285+ r := newTestRotator ()
286+ r .Reconcile (mustBuild (t , NewAcceptedSetBuilder ().
287+ WithAnchor (SDKKeyParams {Value : "sdk-anchor" , Key : util .PtrOrNil ("default" )}).
288+ WithPrimaryMobileKey (MobileKeyParams {Value : "mob-primary" , Key : util .PtrOrNil ("mob-1" )})), time .Unix (0 , 0 ))
289+
290+ entries := r .AcceptedKeys ()
291+ require .Len (t , entries , 2 )
292+
293+ anchor := findAcceptedKey (entries , "sdk-anchor" )
294+ require .NotNil (t , anchor )
295+ assert .Equal (t , KeyTypeServer , anchor .Type )
296+ require .NotNil (t , anchor .Key )
297+ assert .Equal (t , "default" , * anchor .Key )
298+ assert .Nil (t , anchor .Expiry )
299+
300+ mob := findAcceptedKey (entries , "mob-primary" )
301+ require .NotNil (t , mob )
302+ assert .Equal (t , KeyTypeMobile , mob .Type )
303+ require .NotNil (t , mob .Key )
304+ assert .Equal (t , "mob-1" , * mob .Key )
305+ })
306+
307+ t .Run ("multiple keys include the anchor; expiry populated" , func (t * testing.T ) {
308+ r := newTestRotator ()
309+ expiry := time .Date (2099 , 6 , 1 , 0 , 0 , 0 , 0 , time .UTC )
310+ r .Reconcile (mustBuild (t , NewAcceptedSetBuilder ().
311+ WithAnchor (SDKKeyParams {Value : "sdk-anchor" , Key : util .PtrOrNil ("default" )}).
312+ WithSDKKey (SDKKeyParams {Value : "sdk-b" , Key : util .PtrOrNil ("b-service" )}).
313+ WithSDKKey (SDKKeyParams {Value : "sdk-old" , Key : util .PtrOrNil ("old-key" ), Expiry : util .PtrOrNil (expiry )}).
314+ WithPrimaryMobileKey (MobileKeyParams {Value : "mob-primary" })), time .Unix (0 , 0 ))
315+
316+ entries := r .AcceptedKeys ()
317+ require .Len (t , entries , 4 ) // anchor + sdk-b + sdk-old + mob-primary
318+ assert .NotNil (t , findAcceptedKey (entries , "sdk-anchor" ), "anchor must be present in the full set" )
319+
320+ old := findAcceptedKey (entries , "sdk-old" )
321+ require .NotNil (t , old )
322+ require .NotNil (t , old .Expiry )
323+ assert .Equal (t , expiry , * old .Expiry )
324+
325+ // A key with no identifier (the primary mobile here) carries a nil Key.
326+ mob := findAcceptedKey (entries , "mob-primary" )
327+ require .NotNil (t , mob )
328+ assert .Nil (t , mob .Key )
329+ })
330+ }
331+
332+ // TestReconcileClearsStaleKeyIdentifier verifies that when a later reconcile carries no identifier for
333+ // a key that previously had one (e.g. an old-format payload after a new-format one), the rotator
334+ // clears the stale identifier rather than retaining it — so /status never shows an identifier the
335+ // current credential set no longer carries.
336+ func TestReconcileClearsStaleKeyIdentifier (t * testing.T ) {
337+ r := newTestRotator ()
338+ now := time .Unix (0 , 0 )
339+
340+ // First reconcile: sdk-b carries the identifier "b-service".
341+ r .Reconcile (mustBuild (t , NewAcceptedSetBuilder ().
342+ WithAnchor (SDKKeyParams {Value : "sdk-anchor" }).
343+ WithSDKKey (SDKKeyParams {Value : "sdk-b" , Key : util .PtrOrNil ("b-service" )})), now )
344+
345+ b := findAcceptedKey (r .AcceptedKeys (), "sdk-b" )
346+ require .NotNil (t , b )
347+ require .NotNil (t , b .Key )
348+ assert .Equal (t , "b-service" , * b .Key )
349+
350+ // Second reconcile: same credential value, but no identifier this time.
351+ r .Reconcile (mustBuild (t , NewAcceptedSetBuilder ().
352+ WithAnchor (SDKKeyParams {Value : "sdk-anchor" }).
353+ WithSDKKey (SDKKeyParams {Value : "sdk-b" })), now )
354+
355+ b = findAcceptedKey (r .AcceptedKeys (), "sdk-b" )
356+ require .NotNil (t , b )
357+ assert .Nil (t , b .Key , "identifier must be cleared when the new payload carries none" )
358+ }
0 commit comments