@@ -84,6 +84,16 @@ describe("policy channel remove/enable flows", () => {
8484 return { rebuildSandbox, removePreset, updateSandbox } ;
8585 }
8686
87+ function expectHermesSessionCleanup ( command : unknown ) {
88+ expect ( String ( command ) ) . toContain ( "/sandbox/.hermes/platforms/whatsapp" ) ;
89+ expect ( String ( command ) ) . toContain (
90+ "/sandbox/.hermes/profiles/dashboard-home/platforms/whatsapp/session" ,
91+ ) ;
92+ expect ( String ( command ) ) . toContain (
93+ "/sandbox/.hermes/dashboard-home/platforms/whatsapp/session" ,
94+ ) ;
95+ }
96+
8797 async function removeWhatsappNonInteractive ( ) {
8898 const previousNonInteractive = process . env . NEMOCLAW_NON_INTERACTIVE ;
8999 process . env . NEMOCLAW_NON_INTERACTIVE = "1" ;
@@ -137,14 +147,15 @@ describe("policy channel remove/enable flows", () => {
137147 ) . toBeLessThan ( updateSandbox . mock . invocationCallOrder [ 0 ] ) ;
138148 } ) ;
139149
140- it . each ( [ { scenario : "exec transport" } , { scenario : "SSH transport" } ] ) (
141- "clears every Hermes WhatsApp session path through the SSH fallback [$scenario]" ,
142- async ( { scenario } ) => {
150+ it . each ( [
151+ { scenario : "exec transport" , execStatus : 0 , usesSsh : false } ,
152+ { scenario : "SSH fallback" , execStatus : 1 , usesSsh : true } ,
153+ ] ) ( "clears every Hermes WhatsApp session path through $scenario" , async ( { execStatus, usesSsh } ) => {
143154 const { updateSandbox } = await arrangeHermesWhatsappRemoval ( ) ;
144155 vi . mocked ( processRecovery . executeSandboxExecCommand ) . mockReturnValue ( {
145- status : 1 ,
146- stdout : "" ,
147- stderr : "exec unavailable" ,
156+ status : execStatus ,
157+ stdout : execStatus === 0 ? "NEMOCLAW_CHANNEL_CLEAR_OK\n" : "" ,
158+ stderr : execStatus === 0 ? "" : "exec unavailable" ,
148159 } ) ;
149160 vi . mocked ( processRecovery . executeSandboxCommand ) . mockReturnValue ( {
150161 status : 0 ,
@@ -154,30 +165,24 @@ describe("policy channel remove/enable flows", () => {
154165
155166 await expect ( removeWhatsappNonInteractive ( ) ) . resolves . toBeUndefined ( ) ;
156167
157- const command = (
158- {
159- "exec transport" : vi . mocked ( processRecovery . executeSandboxExecCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
160- "SSH transport" : vi . mocked ( processRecovery . executeSandboxCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
161- } as const
162- ) [ scenario ] ! ;
163- expect ( String ( command ) ) . toContain ( "/sandbox/.hermes/platforms/whatsapp" ) ;
164- expect ( String ( command ) ) . toContain (
165- "/sandbox/.hermes/profiles/dashboard-home/platforms/whatsapp/session" ,
166- ) ;
167- expect ( String ( command ) ) . toContain (
168- "/sandbox/.hermes/dashboard-home/platforms/whatsapp/session" ,
169- ) ;
168+ const transport = usesSsh
169+ ? vi . mocked ( processRecovery . executeSandboxCommand )
170+ : vi . mocked ( processRecovery . executeSandboxExecCommand ) ;
171+ expectHermesSessionCleanup ( transport . mock . calls [ 0 ] ?. [ 1 ] ) ;
172+ const sshCleanupCommands = vi
173+ . mocked ( processRecovery . executeSandboxCommand )
174+ . mock . calls . filter ( ( [ , command ] ) =>
175+ String ( command ) . includes ( "/sandbox/.hermes/platforms/whatsapp" ) ,
176+ ) ;
177+ expect ( sshCleanupCommands ) . toHaveLength ( usesSsh ? 1 : 0 ) ;
170178
171179 expect ( updateSandbox ) . toHaveBeenCalled ( ) ;
172- expect (
173- vi . mocked ( processRecovery . executeSandboxCommand ) . mock . invocationCallOrder [ 0 ] ,
174- ) . toBeLessThan ( updateSandbox . mock . invocationCallOrder [ 0 ] ) ;
175- } ,
176- ) ;
180+ expect ( transport . mock . invocationCallOrder [ 0 ] ) . toBeLessThan (
181+ updateSandbox . mock . invocationCallOrder [ 0 ] ,
182+ ) ;
183+ } ) ;
177184
178- it . each ( [ { scenario : "exec transport" } , { scenario : "SSH transport" } ] ) (
179- "keeps channel state unchanged when both Hermes cleanup transports fail [$scenario]" ,
180- async ( { scenario } ) => {
185+ it ( "keeps channel state unchanged when both Hermes cleanup transports fail" , async ( ) => {
181186 const { rebuildSandbox, removePreset, updateSandbox } = await arrangeHermesWhatsappRemoval ( ) ;
182187 const runOpenshell = vi . spyOn ( openshellRuntime , "runOpenshell" ) ;
183188 vi . mocked ( processRecovery . executeSandboxExecCommand ) . mockReturnValue ( {
@@ -193,26 +198,18 @@ describe("policy channel remove/enable flows", () => {
193198
194199 await expect ( removeWhatsappNonInteractive ( ) ) . rejects . toThrow ( "process.exit(1)" ) ;
195200
196- const command = (
197- {
198- "exec transport" : vi . mocked ( processRecovery . executeSandboxExecCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
199- "SSH transport" : vi . mocked ( processRecovery . executeSandboxCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
200- } as const
201- ) [ scenario ] ! ;
202- expect ( String ( command ) ) . toContain ( "/sandbox/.hermes/platforms/whatsapp" ) ;
203- expect ( String ( command ) ) . toContain (
204- "/sandbox/.hermes/profiles/dashboard-home/platforms/whatsapp/session" ,
201+ expectHermesSessionCleanup (
202+ vi . mocked ( processRecovery . executeSandboxExecCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
205203 ) ;
206- expect ( String ( command ) ) . toContain (
207- "/sandbox/.hermes/dashboard-home/platforms/whatsapp/session" ,
204+ expectHermesSessionCleanup (
205+ vi . mocked ( processRecovery . executeSandboxCommand ) . mock . calls [ 0 ] ?. [ 1 ] ,
208206 ) ;
209207
210208 expect ( runOpenshell ) . not . toHaveBeenCalled ( ) ;
211209 expect ( updateSandbox ) . not . toHaveBeenCalled ( ) ;
212210 expect ( removePreset ) . not . toHaveBeenCalled ( ) ;
213211 expect ( rebuildSandbox ) . not . toHaveBeenCalled ( ) ;
214- } ,
215- ) ;
212+ } ) ;
216213
217214 it ( "supports stop dry runs for configured Hermes channels" , async ( ) => {
218215 vi . spyOn ( registry , "getSandbox" ) . mockReturnValue ( { name : "alpha" , agent : "hermes" } ) ;
0 commit comments