11import assert from "node:assert/strict" ;
22import { spawnSync } from "node:child_process" ;
33import fs from "node:fs" ;
4+ import os from "node:os" ;
45import path from "node:path" ;
56import test from "node:test" ;
67import { fileURLToPath } from "node:url" ;
@@ -12,6 +13,7 @@ const scripts = [
1213 "scripts/start-beauticode.ps1" ,
1314 "scripts/start-beauticode-engine.ps1" ,
1415 "scripts/codex-launch.ps1" ,
16+ "scripts/install-dsh-plugin.ps1" ,
1517 "apps/tray/start-tray.ps1" ,
1618] ;
1719
@@ -100,3 +102,92 @@ test("picker DryRun routes DSH to the tray and Codex to its launcher", () => {
100102 assert . match ( result . stdout , new RegExp ( needle . replace ( / [ . ] / g, "\\." ) ) ) ;
101103 }
102104} ) ;
105+
106+ test ( "install-dsh-plugin wires a missing DSH home and can uninstall" , ( ) => {
107+ const script = path . join ( repoRoot , "scripts/install-dsh-plugin.ps1" ) ;
108+ const pluginRoot = path . join ( repoRoot , "integrations/deepseek-harness" ) ;
109+ const home = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "bc-dsh-home-" ) ) ;
110+ try {
111+ const add = spawnSync (
112+ "powershell.exe" ,
113+ [
114+ "-NoProfile" ,
115+ "-ExecutionPolicy" ,
116+ "Bypass" ,
117+ "-File" ,
118+ script ,
119+ "-PluginRoot" ,
120+ pluginRoot ,
121+ "-DshHome" ,
122+ home ,
123+ ] ,
124+ { encoding : "utf8" , windowsHide : true } ,
125+ ) ;
126+ assert . equal ( add . status , 0 , add . stderr || add . stdout ) ;
127+ const homePatch = fs . readFileSync ( path . join ( home , "cordis.patch.yml" ) , "utf8" ) ;
128+ assert . match ( homePatch , / i d : b e a u t i c o d e - b r i d g e / ) ;
129+ assert . match ( homePatch , / f i l e : \/ \/ / ) ;
130+
131+ const web = path . join ( home , "profiles" , "web" ) ;
132+ fs . mkdirSync ( web , { recursive : true } ) ;
133+ fs . writeFileSync (
134+ path . join ( web , "package.json" ) ,
135+ JSON . stringify ( {
136+ name : "dsh-profile-web" ,
137+ private : true ,
138+ dependencies : { } ,
139+ dsh : { profile : { bundles : [ "@deepseek-ai/dsh-base" ] } } ,
140+ } ) ,
141+ "utf8" ,
142+ ) ;
143+ fs . writeFileSync ( path . join ( web , "cordis.patch.yml" ) , "[]\n" , "utf8" ) ;
144+ const migrate = spawnSync (
145+ "powershell.exe" ,
146+ [
147+ "-NoProfile" ,
148+ "-ExecutionPolicy" ,
149+ "Bypass" ,
150+ "-File" ,
151+ script ,
152+ "-PluginRoot" ,
153+ pluginRoot ,
154+ "-DshHome" ,
155+ home ,
156+ ] ,
157+ { encoding : "utf8" , windowsHide : true } ,
158+ ) ;
159+ assert . equal ( migrate . status , 0 , migrate . stderr || migrate . stdout ) ;
160+ const webPatch = fs . readFileSync ( path . join ( web , "cordis.patch.yml" ) , "utf8" ) ;
161+ assert . match ( webPatch , / @ b e a u t i c o d e \/ d s h - p l u g i n / ) ;
162+ assert . equal ( fs . existsSync ( path . join ( home , "cordis.patch.yml" ) ) , false ) ;
163+ assert . ok (
164+ fs . existsSync (
165+ path . join ( web , "node_modules" , "@beauticode" , "dsh-plugin" , "index.mjs" ) ,
166+ ) ,
167+ ) ;
168+
169+ const remove = spawnSync (
170+ "powershell.exe" ,
171+ [
172+ "-NoProfile" ,
173+ "-ExecutionPolicy" ,
174+ "Bypass" ,
175+ "-File" ,
176+ script ,
177+ "-PluginRoot" ,
178+ pluginRoot ,
179+ "-DshHome" ,
180+ home ,
181+ "-Remove" ,
182+ ] ,
183+ { encoding : "utf8" , windowsHide : true } ,
184+ ) ;
185+ assert . equal ( remove . status , 0 , remove . stderr || remove . stdout ) ;
186+ assert . equal (
187+ fs . existsSync ( path . join ( web , "node_modules" , "@beauticode" , "dsh-plugin" ) ) ,
188+ false ,
189+ ) ;
190+ } finally {
191+ fs . rmSync ( home , { recursive : true , force : true } ) ;
192+ }
193+ } ) ;
0 commit comments