@@ -157,9 +157,11 @@ describe("mantis Slack desktop smoke runtime", () => {
157157
158158 it ( "leases Convex Slack credentials for gateway setup and maps them into the VM env" , async ( ) => {
159159 const commands : { args : readonly string [ ] ; command : string ; env ?: NodeJS . ProcessEnv } [ ] = [ ] ;
160+ const events : string [ ] = [ ] ;
160161 const fetchMock = vi . fn ( async ( input : RequestInfo | URL , init ?: RequestInit ) => {
161162 const url = describeFetchInput ( input ) ;
162163 if ( url . endsWith ( "/acquire" ) ) {
164+ events . push ( "acquire" ) ;
163165 return new Response (
164166 JSON . stringify ( {
165167 credentialId : "cred-slack" ,
@@ -177,6 +179,7 @@ describe("mantis Slack desktop smoke runtime", () => {
177179 ) ;
178180 }
179181 if ( url . endsWith ( "/release" ) || url . endsWith ( "/heartbeat" ) ) {
182+ events . push ( url . endsWith ( "/release" ) ? "release" : "heartbeat" ) ;
180183 return new Response ( JSON . stringify ( { status : "ok" } ) , { status : 200 } ) ;
181184 }
182185 throw new Error ( `unexpected fetch: ${ url } ${ describeFetchBody ( init ?. body ) } ` ) ;
@@ -186,6 +189,7 @@ describe("mantis Slack desktop smoke runtime", () => {
186189 const runner = vi . fn (
187190 async ( command : string , args : readonly string [ ] , options : { env ?: NodeJS . ProcessEnv } ) => {
188191 commands . push ( { command, args, env : options . env } ) ;
192+ events . push ( `${ command } :${ args [ 0 ] } ` ) ;
189193 if ( command === "/tmp/crabbox" && args [ 0 ] === "warmup" ) {
190194 return { stdout : "ready lease cbx_c0ffee\n" , stderr : "" } ;
191195 }
@@ -244,6 +248,17 @@ describe("mantis Slack desktop smoke runtime", () => {
244248 } ) ;
245249
246250 expect ( result . status ) . toBe ( "pass" ) ;
251+ expect ( events ) . toEqual (
252+ expect . arrayContaining ( [
253+ "/tmp/crabbox:warmup" ,
254+ "/tmp/crabbox:inspect" ,
255+ "acquire" ,
256+ "/tmp/crabbox:run" ,
257+ "release" ,
258+ ] ) ,
259+ ) ;
260+ expect ( events . indexOf ( "acquire" ) ) . toBeGreaterThan ( events . indexOf ( "/tmp/crabbox:inspect" ) ) ;
261+ expect ( events . indexOf ( "acquire" ) ) . toBeLessThan ( events . indexOf ( "/tmp/crabbox:run" ) ) ;
247262 const runCommand = commands . find (
248263 ( entry ) => entry . command === "/tmp/crabbox" && entry . args [ 0 ] === "run" ,
249264 ) ;
@@ -274,6 +289,59 @@ describe("mantis Slack desktop smoke runtime", () => {
274289 expect ( summary . slackUrl ) . toBe ( "https://app.slack.com/client/TLEASED/CLEASED" ) ;
275290 } ) ;
276291
292+ it ( "stops a created no-keep lease when the remote Slack QA run fails" , async ( ) => {
293+ const commands : { args : readonly string [ ] ; command : string } [ ] = [ ] ;
294+ const runner = vi . fn ( async ( command : string , args : readonly string [ ] ) => {
295+ commands . push ( { command, args } ) ;
296+ if ( command === "/tmp/crabbox" && args [ 0 ] === "warmup" ) {
297+ return { stdout : "ready lease cbx_fade123\n" , stderr : "" } ;
298+ }
299+ if ( command === "/tmp/crabbox" && args [ 0 ] === "inspect" ) {
300+ return {
301+ stdout : `${ JSON . stringify ( {
302+ host : "203.0.113.10" ,
303+ id : "cbx_fade123" ,
304+ provider : "hetzner" ,
305+ sshKey : "/tmp/key" ,
306+ sshPort : "2222" ,
307+ sshUser : "crabbox" ,
308+ } ) } \n`,
309+ stderr : "" ,
310+ } ;
311+ }
312+ if ( command === "/tmp/crabbox" && args [ 0 ] === "run" ) {
313+ throw new Error ( "remote Slack QA failed" ) ;
314+ }
315+ if ( command === "rsync" ) {
316+ const outputDir = args . at ( - 1 ) ;
317+ await fs . mkdir ( outputDir as string , { recursive : true } ) ;
318+ if ( ! String ( outputDir ) . endsWith ( "slack-qa/" ) ) {
319+ await fs . writeFile ( path . join ( outputDir as string , "slack-desktop-smoke.png" ) , "png" ) ;
320+ await fs . writeFile ( path . join ( outputDir as string , "remote-metadata.json" ) , "{}\n" ) ;
321+ }
322+ }
323+ return { stdout : "" , stderr : "" } ;
324+ } ) ;
325+
326+ const result = await runMantisSlackDesktopSmoke ( {
327+ commandRunner : runner ,
328+ crabboxBin : "/tmp/crabbox" ,
329+ keepLease : false ,
330+ outputDir : ".artifacts/qa-e2e/mantis/slack-desktop-created-fail" ,
331+ repoRoot,
332+ } ) ;
333+
334+ expect ( result . status ) . toBe ( "fail" ) ;
335+ expect ( commands ) . toEqual (
336+ expect . arrayContaining ( [
337+ expect . objectContaining ( {
338+ args : [ "stop" , "--provider" , "hetzner" , "cbx_fade123" ] ,
339+ command : "/tmp/crabbox" ,
340+ } ) ,
341+ ] ) ,
342+ ) ;
343+ } ) ;
344+
277345 it ( "passes gateway setup when Crabbox returns non-zero after remote metadata proves success" , async ( ) => {
278346 const runner = vi . fn ( async ( command : string , args : readonly string [ ] ) => {
279347 if ( command === "/tmp/crabbox" && args [ 0 ] === "warmup" ) {
0 commit comments