@@ -11,6 +11,16 @@ import (
1111 "github.com/MiniMax-AI/OpenAgentCore/services/core/internal/sandbox"
1212)
1313
14+ // retirementFenceLease bounds only the cancellation attempt. The concurrent
15+ // owner query retains the production lease deadline and must remain usable.
16+ type retirementFenceLease struct { Ownership }
17+
18+ func (l retirementFenceLease ) CancelOperations (ctx context.Context , cancel context.CancelFunc ) error {
19+ ctx , stop := context .WithTimeout (ctx , 100 * time .Millisecond )
20+ defer stop ()
21+ return l .Ownership .CancelOperations (ctx , cancel )
22+ }
23+
1424func TestFailedInventoryRetirementClosesAdmissionAndRetainsGate (t * testing.T ) {
1525 for _ , mode := range []string {"gate_timeout" , "lease_loss" } {
1626 t .Run (mode , func (t * testing.T ) {
@@ -43,13 +53,12 @@ func TestFailedInventoryRetirementClosesAdmissionAndRetainsGate(t *testing.T) {
4353 go func () { defer m .active .Done (); defer finish (); <- operation .Done (); <- releaseProvider }()
4454 var queryDone chan error
4555 if mode == "gate_timeout" {
46- // This independent owner caller has its own bounded query context. Unlike
47- // a lifecycle scan, it is not canceled by the manager's shutdown context.
48- queryCtx , cancel := context .WithTimeout (t .Context (), 10 * time .Second )
49- defer cancel ()
56+ // Both lease operations have the same production deadline. Give only
57+ // the later fence a shorter caller deadline so it expires first.
58+ m .lease = retirementFenceLease {Ownership : owner .Lease }
5059 queryDone = make (chan error , 1 )
5160 armed .Store (true )
52- go func () { queryDone <- owner .Lease .CheckOwnership (queryCtx ) }()
61+ go func () { queryDone <- owner .Lease .CheckOwnership (t . Context () ) }()
5362 select {
5463 case <- reading :
5564 case <- time .After (2 * time .Second ):
@@ -114,13 +123,16 @@ func TestFailedInventoryRetirementClosesAdmissionAndRetainsGate(t *testing.T) {
114123 unblockRead ()
115124 select {
116125 case err := <- queryDone :
126+ queryDone = nil
117127 if err != nil {
118128 t .Fatal ("fence timeout damaged unrelated owner read" , err )
119129 }
120130 case <- time .After (2 * time .Second ):
121131 t .Fatal ("owner read did not settle" )
122132 }
123- queryDone = nil
133+ if err := owner .Lease .CheckOwnership (t .Context ()); err != nil {
134+ t .Fatal ("fence timeout lost the execution lease" , err )
135+ }
124136 }
125137 // Shutdown can now cancel the original work, but cannot call it settled
126138 // merely because cancellation was requested or a serial gate became free.
0 commit comments