@@ -747,6 +747,37 @@ func TestAdmit_PinnedBranchClosesExpiredOtherInstance(t *testing.T) {
747747 assert .Same (t , pinned , item .Value ())
748748}
749749
750+ // A lookup for a build whose pinned template has left the TTL cache must serve
751+ // and pin that template, not admit the candidate, and its release must drop
752+ // only its own pin.
753+ func TestAdmit_PinnedBranchPinsTheServedTemplate (t * testing.T ) {
754+ t .Parallel ()
755+
756+ c , _ := newPinTestCache (time .Hour )
757+ pinned := newPinTestTemplate (t , "build-pinned-lookup" )
758+ releaseFirst := pinForTest (t , c , pinned )
759+
760+ candidate := newPinTestTemplate (t , pinned .key )
761+ got , found , releaseLookup := c .lookupOrAdmit (t .Context (), pinned .key , candidate , time .Hour , true )
762+
763+ assert .True (t , found )
764+ assert .Same (t , pinned , got , "the pinned template must be served" )
765+ assert .Equal (t , int64 (2 ), c .footprint ().pinnedRefs , "the lookup must take its own pin" )
766+
767+ item := c .cache .Get (pinned .key , ttlcache .WithDisableTouchOnHit [string , Template ]())
768+ require .NotNil (t , item , "the pinned template must be re-admitted" )
769+ assert .Same (t , pinned , item .Value ())
770+
771+ releaseLookup ()
772+ assert .True (t , c .isPinned (pinned .key ), "the lookup's release must not drop the first pin" )
773+ assert .Equal (t , int64 (1 ), c .footprint ().pinnedRefs )
774+
775+ releaseFirst ()
776+ assert .False (t , c .isPinned (pinned .key ))
777+ assert .Zero (t , pinned .closes .Load ())
778+ assert .Zero (t , candidate .closes .Load ())
779+ }
780+
750781// The eviction callback must not hold extendMu across Close. extendMu is taken
751782// on every sandbox create and resume, and closeTemplate waits on the template's
752783// futures with no deadline, so a Close that blocks under the lock is a
0 commit comments