@@ -94,50 +94,54 @@ describe("sandbox lifecycle MCP destroy boundaries", () => {
9494 onboardSessionState . recreate = null ;
9595 } ) ;
9696
97- for ( const marker of [ "destroyPreparedAt" , "destroyPendingAt" ] as const ) {
98- for ( const withBridge of [ false , true ] ) {
99- it ( `preserves ${ marker } and blocks absent-sandbox recreation${ withBridge ? " with bridges" : " without bridges" } ` , ( ) => {
100- const runCaptureOpenshell = vi . fn ( ( ) => null ) ;
101- registryState . sandbox = {
102- name : "alpha" ,
103- agent : "openclaw" ,
104- mcp : {
105- bridges : withBridge
106- ? {
107- github : {
108- server : "github" ,
109- agent : "openclaw" ,
110- adapter : "mcporter" ,
111- url : "https://mcp.example.test/mcp" ,
112- env : [ "GITHUB_TOKEN" ] ,
113- providerName : "alpha-mcp-github" ,
114- providerId : "provider-123" ,
115- policyName : "mcp-github" ,
116- addedAt : "2026-07-02T22:49:42.000Z" ,
117- } ,
118- }
119- : { } ,
120- [ marker ] : "2026-07-02T22:49:42.000Z" ,
121- } ,
122- } ;
123- const before = JSON . stringify ( registryState . sandbox ) ;
124- const helpers = createSandboxLifecycleHelpers ( {
125- runCaptureOpenshell,
126- fetchGatewayAuthTokenFromSandbox : ( ) => null ,
127- agentProductName : ( ) => "OpenClaw" ,
128- prompt : async ( ) => "no" ,
129- isAffirmativeAnswer : ( ) => false ,
130- } ) ;
131-
132- expect ( ( ) => helpers . inspectSandboxForCreate ( "alpha" ) ) . toThrow (
133- / i n c o m p l e t e M C P d e s t r o y t r a n s a c t i o n .* f i n i s h c l e a n u p b e f o r e r e c r e a t i n g / i,
134- ) ;
135- expect ( runCaptureOpenshell ) . not . toHaveBeenCalled ( ) ;
136- expect ( registryState . removeSandbox ) . not . toHaveBeenCalled ( ) ;
137- expect ( JSON . stringify ( registryState . sandbox ) ) . toBe ( before ) ;
97+ it . each ( [
98+ [ "destroyPreparedAt" , "without bridges" , false ] ,
99+ [ "destroyPreparedAt" , "with bridges" , true ] ,
100+ [ "destroyPendingAt" , "without bridges" , false ] ,
101+ [ "destroyPendingAt" , "with bridges" , true ] ,
102+ ] as const ) (
103+ "preserves %s and blocks absent-sandbox recreation %s" ,
104+ ( marker , _bridgeState , withBridge ) => {
105+ const runCaptureOpenshell = vi . fn ( ( ) => null ) ;
106+ registryState . sandbox = {
107+ name : "alpha" ,
108+ agent : "openclaw" ,
109+ mcp : {
110+ bridges : withBridge
111+ ? {
112+ github : {
113+ server : "github" ,
114+ agent : "openclaw" ,
115+ adapter : "mcporter" ,
116+ url : "https://mcp.example.test/mcp" ,
117+ env : [ "GITHUB_TOKEN" ] ,
118+ providerName : "alpha-mcp-github" ,
119+ providerId : "provider-123" ,
120+ policyName : "mcp-github" ,
121+ addedAt : "2026-07-02T22:49:42.000Z" ,
122+ } ,
123+ }
124+ : { } ,
125+ [ marker ] : "2026-07-02T22:49:42.000Z" ,
126+ } ,
127+ } ;
128+ const before = JSON . stringify ( registryState . sandbox ) ;
129+ const helpers = createSandboxLifecycleHelpers ( {
130+ runCaptureOpenshell,
131+ fetchGatewayAuthTokenFromSandbox : ( ) => null ,
132+ agentProductName : ( ) => "OpenClaw" ,
133+ prompt : async ( ) => "no" ,
134+ isAffirmativeAnswer : ( ) => false ,
138135 } ) ;
139- }
140- }
136+
137+ expect ( ( ) => helpers . inspectSandboxForCreate ( "alpha" ) ) . toThrow (
138+ / i n c o m p l e t e M C P d e s t r o y t r a n s a c t i o n .* f i n i s h c l e a n u p b e f o r e r e c r e a t i n g / i,
139+ ) ;
140+ expect ( runCaptureOpenshell ) . not . toHaveBeenCalled ( ) ;
141+ expect ( registryState . removeSandbox ) . not . toHaveBeenCalled ( ) ;
142+ expect ( JSON . stringify ( registryState . sandbox ) ) . toBe ( before ) ;
143+ } ,
144+ ) ;
141145
142146 it ( "keeps the source registry row when OpenShell reports no sandbox (#7736)" , ( ) => {
143147 const rows = new Map < string , SandboxEntry > ( [
0 commit comments