@@ -380,6 +380,23 @@ describe("update-cli", () => {
380380 return calls [ index ] ;
381381 } ;
382382
383+ const syncPluginCall = ( index = 0 ) => {
384+ const calls = syncPluginsForUpdateChannel . mock . calls as unknown as Array <
385+ [ { channel ?: string ; config ?: OpenClawConfig } ]
386+ > ;
387+ return calls [ index ] ?. [ 0 ] ;
388+ } ;
389+
390+ const npmPluginUpdateCall = ( index = 0 ) => {
391+ const calls = updateNpmInstalledPlugins . mock . calls as unknown as Array <
392+ [ { timeoutMs ?: number } ]
393+ > ;
394+ return calls [ index ] ?. [ 0 ] ;
395+ } ;
396+
397+ const pluginWarning = ( result ?: UpdateRunResult ) => result ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ;
398+ const pluginOutcome = ( result ?: UpdateRunResult ) => result ?. postUpdate ?. plugins ?. npm . outcomes [ 0 ] ;
399+
383400 const expectPackageInstallSpec = ( spec : string ) => {
384401 expect ( runGatewayUpdate ) . not . toHaveBeenCalled ( ) ;
385402 const call = (
@@ -799,18 +816,13 @@ describe("update-cli", () => {
799816
800817 await updateCommand ( { channel : "dev" , yes : true , restart : false } ) ;
801818
802- expect ( spawn ) . toHaveBeenCalledWith (
803- expect . stringMatching ( / n o d e / ) ,
804- [ entrypoints [ 0 ] , "update" , "--no-restart" , "--yes" ] ,
805- expect . objectContaining ( {
806- stdio : "inherit" ,
807- env : expect . objectContaining ( {
808- OPENCLAW_UPDATE_POST_CORE : "1" ,
809- OPENCLAW_UPDATE_POST_CORE_CHANNEL : "dev" ,
810- OPENCLAW_UPDATE_POST_CORE_REQUESTED_CHANNEL : "dev" ,
811- } ) ,
812- } ) ,
813- ) ;
819+ const call = spawnCall ( ) ;
820+ expect ( call ?. [ 0 ] ) . toMatch ( / n o d e / ) ;
821+ expect ( call ?. [ 1 ] ) . toEqual ( [ entrypoints [ 0 ] , "update" , "--no-restart" , "--yes" ] ) ;
822+ expect ( call ?. [ 2 ] ?. stdio ) . toBe ( "inherit" ) ;
823+ expect ( call ?. [ 2 ] ?. env ?. OPENCLAW_UPDATE_POST_CORE ) . toBe ( "1" ) ;
824+ expect ( call ?. [ 2 ] ?. env ?. OPENCLAW_UPDATE_POST_CORE_CHANNEL ) . toBe ( "dev" ) ;
825+ expect ( call ?. [ 2 ] ?. env ?. OPENCLAW_UPDATE_POST_CORE_REQUESTED_CHANNEL ) . toBe ( "dev" ) ;
814826 expect ( replaceConfigFile ) . not . toHaveBeenCalled ( ) ;
815827 expect ( syncPluginsForUpdateChannel ) . not . toHaveBeenCalled ( ) ;
816828 expect ( updateNpmInstalledPlugins ) . not . toHaveBeenCalled ( ) ;
@@ -891,10 +903,10 @@ describe("update-cli", () => {
891903 ) ;
892904
893905 expect ( runGatewayUpdate ) . not . toHaveBeenCalled ( ) ;
894- expect ( runCommandWithTimeout ) . not . toHaveBeenCalledWith (
895- [ "npm" , "i" , "-g" , expect . any ( String ) ] ,
896- expect . anything ( ) ,
897- ) ;
906+ const installCall = (
907+ vi . mocked ( runCommandWithTimeout ) . mock . calls as unknown as Array < [ string [ ] , unknown ] >
908+ ) . find ( ( [ argv ] ) => argv [ 0 ] === "npm" && argv [ 1 ] === "i" && argv [ 2 ] === "-g" ) ;
909+ expect ( installCall ) . toBeUndefined ( ) ;
898910 expect ( defaultRuntime . exit ) . toHaveBeenCalledWith ( 0 ) ;
899911 expect ( syncPluginsForUpdateChannel ) . toHaveBeenCalledTimes ( 1 ) ;
900912 expect ( updateNpmInstalledPlugins ) . toHaveBeenCalledTimes ( 1 ) ;
@@ -999,14 +1011,8 @@ describe("update-cli", () => {
9991011 } ,
10001012 baseHash : "stable-hash" ,
10011013 } ) ;
1002- expect ( syncPluginsForUpdateChannel ) . toHaveBeenCalledWith (
1003- expect . objectContaining ( {
1004- channel : "dev" ,
1005- config : expect . objectContaining ( {
1006- update : expect . objectContaining ( { channel : "dev" } ) ,
1007- } ) ,
1008- } ) ,
1009- ) ;
1014+ expect ( syncPluginCall ( ) ?. channel ) . toBe ( "dev" ) ;
1015+ expect ( syncPluginCall ( ) ?. config ?. update ?. channel ) . toBe ( "dev" ) ;
10101016 } ) ;
10111017
10121018 it ( "post-core resume mode retries update channel persistence after config hash drift" , async ( ) => {
@@ -1071,14 +1077,8 @@ describe("update-cli", () => {
10711077 } ,
10721078 baseHash : "newer-hash" ,
10731079 } ) ;
1074- expect ( syncPluginsForUpdateChannel ) . toHaveBeenCalledWith (
1075- expect . objectContaining ( {
1076- config : expect . objectContaining ( {
1077- meta : expect . objectContaining ( { lastTouchedVersion : "2026.4.30" } ) ,
1078- update : expect . objectContaining ( { channel : "dev" } ) ,
1079- } ) ,
1080- } ) ,
1081- ) ;
1080+ expect ( syncPluginCall ( ) ?. config ?. meta ?. lastTouchedVersion ) . toBe ( "2026.4.30" ) ;
1081+ expect ( syncPluginCall ( ) ?. config ?. update ?. channel ) . toBe ( "dev" ) ;
10821082 } ) ;
10831083
10841084 it ( "passes the update timeout budget into post-core plugin updates" , async ( ) => {
@@ -1092,9 +1092,7 @@ describe("update-cli", () => {
10921092 } ,
10931093 ) ;
10941094
1095- expect ( updateNpmInstalledPlugins ) . toHaveBeenCalledWith (
1096- expect . objectContaining ( { timeoutMs : 1_800_000 } ) ,
1097- ) ;
1095+ expect ( npmPluginUpdateCall ( ) ?. timeoutMs ) . toBe ( 1_800_000 ) ;
10981096 } ) ;
10991097
11001098 it ( "uses a fail-closed integrity policy for post-core plugin updates" , async ( ) => {
@@ -1200,16 +1198,12 @@ describe("update-cli", () => {
12001198 } ,
12011199 ] ) ;
12021200 expect ( jsonOutput ?. postUpdate ?. plugins ?. status ) . toBe ( "warning" ) ;
1203- expect ( jsonOutput ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ) . toMatchObject ( {
1204- pluginId : "demo" ,
1205- guidance : [
1206- "Run openclaw doctor --fix to attempt automatic repair." ,
1207- "Run openclaw plugins inspect demo --runtime --json for details." ,
1208- ] ,
1209- } ) ;
1210- expect ( jsonOutput ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ?. reason ) . toContain (
1211- "npm package integrity drift" ,
1212- ) ;
1201+ expect ( pluginWarning ( jsonOutput ) ?. pluginId ) . toBe ( "demo" ) ;
1202+ expect ( pluginWarning ( jsonOutput ) ?. guidance ) . toEqual ( [
1203+ "Run openclaw doctor --fix to attempt automatic repair." ,
1204+ "Run openclaw plugins inspect demo --runtime --json for details." ,
1205+ ] ) ;
1206+ expect ( pluginWarning ( jsonOutput ) ?. reason ) . toContain ( "npm package integrity drift" ) ;
12131207 expect ( jsonOutput ?. postUpdate ?. plugins ?. npm . outcomes [ 0 ] ?. status ) . toBe ( "error" ) ;
12141208 expect ( jsonOutput ?. postUpdate ?. plugins ?. npm . outcomes [ 0 ] ?. message ) . toContain (
12151209 "Run openclaw doctor --fix to attempt automatic repair." ,
@@ -1268,16 +1262,10 @@ describe("update-cli", () => {
12681262 | undefined ;
12691263 expect ( jsonOutput ?. status ) . toBe ( "ok" ) ;
12701264 expect ( jsonOutput ?. postUpdate ?. plugins ?. status ) . toBe ( "warning" ) ;
1271- expect ( jsonOutput ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ) . toMatchObject ( {
1272- pluginId : "demo" ,
1273- } ) ;
1274- expect ( jsonOutput ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ?. reason ) . toContain (
1275- "package.json is missing" ,
1276- ) ;
1277- expect ( jsonOutput ?. postUpdate ?. plugins ?. npm . outcomes [ 0 ] ) . toMatchObject ( {
1278- pluginId : "demo" ,
1279- status : "error" ,
1280- } ) ;
1265+ expect ( pluginWarning ( jsonOutput ) ?. pluginId ) . toBe ( "demo" ) ;
1266+ expect ( pluginWarning ( jsonOutput ) ?. reason ) . toContain ( "package.json is missing" ) ;
1267+ expect ( pluginOutcome ( jsonOutput ) ?. pluginId ) . toBe ( "demo" ) ;
1268+ expect ( pluginOutcome ( jsonOutput ) ?. status ) . toBe ( "error" ) ;
12811269 } ) ;
12821270
12831271 it ( "prints non-fatal plugin warnings in human update output" , async ( ) => {
@@ -1335,17 +1323,13 @@ describe("update-cli", () => {
13351323 | UpdateRunResult
13361324 | undefined ;
13371325 expect ( jsonOutput ?. postUpdate ?. plugins ?. status ) . toBe ( "warning" ) ;
1338- expect ( jsonOutput ?. postUpdate ?. plugins ?. warnings ?. [ 0 ] ) . toMatchObject ( {
1339- pluginId : "demo" ,
1340- guidance : [
1341- "Run openclaw doctor --fix to attempt automatic repair." ,
1342- "Run openclaw plugins inspect demo --runtime --json for details." ,
1343- ] ,
1344- } ) ;
1345- expect ( jsonOutput ?. postUpdate ?. plugins ?. npm . outcomes [ 0 ] ) . toMatchObject ( {
1346- pluginId : "demo" ,
1347- status : "skipped" ,
1348- } ) ;
1326+ expect ( pluginWarning ( jsonOutput ) ?. pluginId ) . toBe ( "demo" ) ;
1327+ expect ( pluginWarning ( jsonOutput ) ?. guidance ) . toEqual ( [
1328+ "Run openclaw doctor --fix to attempt automatic repair." ,
1329+ "Run openclaw plugins inspect demo --runtime --json for details." ,
1330+ ] ) ;
1331+ expect ( pluginOutcome ( jsonOutput ) ?. pluginId ) . toBe ( "demo" ) ;
1332+ expect ( pluginOutcome ( jsonOutput ) ?. status ) . toBe ( "skipped" ) ;
13491333 } ) ;
13501334
13511335 it ( "fails unexpected post-core plugin sync exceptions" , async ( ) => {
0 commit comments