@@ -450,6 +450,14 @@ describe("qa cli runtime", () => {
450450 it ( "keeps telegram exit code clear when --allow-failures is set" , async ( ) => {
451451 const priorExitCode = process . exitCode ;
452452 process . exitCode = undefined ;
453+ await fs . writeFile (
454+ telegramSummaryPath ,
455+ JSON . stringify ( {
456+ counts : { total : 1 , passed : 0 , failed : 1 } ,
457+ scenarios : [ { status : "fail" } ] ,
458+ } ) ,
459+ "utf8" ,
460+ ) ;
453461 runTelegramQaLive . mockResolvedValueOnce ( {
454462 outputDir : telegramArtifactsDir ,
455463 reportPath : path . join ( telegramArtifactsDir , "report.md" ) ,
@@ -534,6 +542,39 @@ describe("qa cli runtime", () => {
534542 }
535543 } ) ;
536544
545+ it ( "sets a failing exit code when host suite scenarios are skipped" , async ( ) => {
546+ const priorExitCode = process . exitCode ;
547+ process . exitCode = undefined ;
548+ await fs . writeFile (
549+ suiteSummaryPath ,
550+ JSON . stringify ( {
551+ counts : {
552+ total : 1 ,
553+ passed : 0 ,
554+ failed : 0 ,
555+ skipped : 1 ,
556+ } ,
557+ scenarios : [ { name : "channel chat baseline" , status : "skip" } ] ,
558+ } ) ,
559+ "utf8" ,
560+ ) ;
561+ runQaSuiteFromRuntime . mockResolvedValueOnce ( {
562+ watchUrl : "http://127.0.0.1:43124" ,
563+ reportPath : suiteReportPath ,
564+ summaryPath : suiteSummaryPath ,
565+ scenarios : [ ] ,
566+ } ) ;
567+
568+ try {
569+ await runQaSuiteCommand ( {
570+ repoRoot : "/tmp/openclaw-repo" ,
571+ } ) ;
572+ expect ( process . exitCode ) . toBe ( 1 ) ;
573+ } finally {
574+ process . exitCode = priorExitCode ;
575+ }
576+ } ) ;
577+
537578 it ( "keeps host suite exit code clear when --allow-failures is set" , async ( ) => {
538579 const priorExitCode = process . exitCode ;
539580 process . exitCode = undefined ;
@@ -720,7 +761,7 @@ describe("qa cli runtime", () => {
720761 repoRoot : "/tmp/openclaw-repo" ,
721762 preflight : true ,
722763 } ) ,
723- ) . rejects . toThrow ( "QA parity preflight failed with 1 failing scenario." ) ;
764+ ) . rejects . toThrow ( "QA parity preflight failed with 1 failing or skipped scenario." ) ;
724765 } ) ;
725766
726767 it ( "keeps parity preflight exit code clear when --allow-failures is set" , async ( ) => {
@@ -1503,6 +1544,46 @@ describe("qa cli runtime", () => {
15031544 }
15041545 } ) ;
15051546
1547+ it ( "sets a failing exit code when multipass summary reports skipped scenarios" , async ( ) => {
1548+ const repoRoot = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "qa-multipass-summary-" ) ) ;
1549+ const summaryPath = path . join ( repoRoot , "qa-suite-summary.json" ) ;
1550+ await fs . writeFile (
1551+ summaryPath ,
1552+ JSON . stringify ( {
1553+ counts : {
1554+ total : 2 ,
1555+ passed : 1 ,
1556+ failed : 0 ,
1557+ skipped : 1 ,
1558+ } ,
1559+ } ) ,
1560+ "utf8" ,
1561+ ) ;
1562+ runQaMultipass . mockResolvedValueOnce ( {
1563+ outputDir : repoRoot ,
1564+ reportPath : path . join ( repoRoot , "qa-suite-report.md" ) ,
1565+ summaryPath,
1566+ hostLogPath : path . join ( repoRoot , "multipass-host.log" ) ,
1567+ bootstrapLogPath : path . join ( repoRoot , "multipass-guest-bootstrap.log" ) ,
1568+ guestScriptPath : path . join ( repoRoot , "multipass-guest-run.sh" ) ,
1569+ vmName : "openclaw-qa-test" ,
1570+ scenarioIds : [ "channel-chat-baseline" ] ,
1571+ } ) ;
1572+ const priorExitCode = process . exitCode ;
1573+ process . exitCode = undefined ;
1574+
1575+ try {
1576+ await runQaSuiteCommand ( {
1577+ repoRoot : "/tmp/openclaw-repo" ,
1578+ runner : "multipass" ,
1579+ } ) ;
1580+ expect ( process . exitCode ) . toBe ( 1 ) ;
1581+ } finally {
1582+ process . exitCode = priorExitCode ;
1583+ await fs . rm ( repoRoot , { recursive : true , force : true } ) ;
1584+ }
1585+ } ) ;
1586+
15061587 it ( "rejects malformed multipass summary JSON" , async ( ) => {
15071588 const repoRoot = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "qa-multipass-summary-" ) ) ;
15081589 const summaryPath = path . join ( repoRoot , "qa-suite-summary.json" ) ;
@@ -1577,7 +1658,7 @@ describe("qa cli runtime", () => {
15771658 repoRoot : "/tmp/openclaw-repo" ,
15781659 runner : "multipass" ,
15791660 } ) ,
1580- ) . rejects . toThrow ( "did not include counts.failed or scenarios[].status" ) ;
1661+ ) . rejects . toThrow ( "did not include counts.failed, counts.skipped, or scenarios[].status" ) ;
15811662 } finally {
15821663 await fs . rm ( repoRoot , { recursive : true , force : true } ) ;
15831664 }
0 commit comments