@@ -115,7 +115,7 @@ describe("GlobalContext", () => {
115115
116116 expect ( consoleWarnMock ) . toHaveBeenCalledWith (
117117 expect . stringContaining (
118- "Error updating global context, deleting corrupted file " ,
118+ "Error updating global context, attempting to salvage security-sensitive values " ,
119119 ) ,
120120 ) ;
121121
@@ -174,4 +174,154 @@ describe("GlobalContext", () => {
174174 it ( "should not crash or throw when getting a key that hasn't been set" , ( ) => {
175175 expect ( globalContext . get ( "hasAlreadyCreatedAPromptFile" ) ) . toBeUndefined ( ) ;
176176 } ) ;
177+
178+ describe ( "salvage functionality" , ( ) => {
179+ it ( "should salvage allowAnonymousTelemetry from corrupted sharedConfig" , ( ) => {
180+ // Create a file with partially corrupted JSON that contains salvageable sharedConfig
181+ const corruptedContent = `{
182+ "indexingPaused": true,
183+ "sharedConfig": {"allowAnonymousTelemetry": false},
184+ "corrupted": { invalid json
185+ }` ;
186+ fs . writeFileSync ( globalContextFilePath , corruptedContent ) ;
187+
188+ const consoleWarnMock = jest
189+ . spyOn ( console , "warn" )
190+ . mockImplementation ( ( ) => { } ) ;
191+
192+ // Try to update - this should salvage the allowAnonymousTelemetry value
193+ globalContext . update ( "indexingPaused" , false ) ;
194+
195+ expect ( consoleWarnMock ) . toHaveBeenCalledWith (
196+ expect . stringContaining (
197+ "Error updating global context, attempting to salvage security-sensitive values" ,
198+ ) ,
199+ ) ;
200+
201+ // Should have the new value
202+ expect ( globalContext . get ( "indexingPaused" ) ) . toBe ( false ) ;
203+
204+ // Should have salvaged the sharedConfig with allowAnonymousTelemetry
205+ const salvaged = globalContext . get ( "sharedConfig" ) ;
206+ expect ( salvaged ) . toEqual ( { allowAnonymousTelemetry : false } ) ;
207+
208+ consoleWarnMock . mockRestore ( ) ;
209+ } ) ;
210+
211+ it ( "should handle salvage when no sharedConfig is present" , ( ) => {
212+ const corruptedContent = `{
213+ "indexingPaused": true,
214+ "corrupted": { invalid json
215+ }` ;
216+ fs . writeFileSync ( globalContextFilePath , corruptedContent ) ;
217+
218+ const consoleWarnMock = jest
219+ . spyOn ( console , "warn" )
220+ . mockImplementation ( ( ) => { } ) ;
221+
222+ globalContext . update ( "hasAlreadyCreatedAPromptFile" , true ) ;
223+
224+ expect ( consoleWarnMock ) . toHaveBeenCalledWith (
225+ expect . stringContaining (
226+ "Error updating global context, attempting to salvage security-sensitive values" ,
227+ ) ,
228+ ) ;
229+
230+ // Should have the new value
231+ expect ( globalContext . get ( "hasAlreadyCreatedAPromptFile" ) ) . toBe ( true ) ;
232+
233+ // Should not have any sharedConfig since none was salvageable
234+ expect ( globalContext . get ( "sharedConfig" ) ) . toBeUndefined ( ) ;
235+
236+ consoleWarnMock . mockRestore ( ) ;
237+ } ) ;
238+
239+ it ( "should handle salvage when sharedConfig has invalid allowAnonymousTelemetry" , ( ) => {
240+ const corruptedContent = `{
241+ "indexingPaused": true,
242+ "sharedConfig": {"allowAnonymousTelemetry": "not-a-boolean"},
243+ "corrupted": { invalid json
244+ }` ;
245+ fs . writeFileSync ( globalContextFilePath , corruptedContent ) ;
246+
247+ const consoleWarnMock = jest
248+ . spyOn ( console , "warn" )
249+ . mockImplementation ( ( ) => { } ) ;
250+
251+ globalContext . update ( "indexingPaused" , false ) ;
252+
253+ // Should not have salvaged an invalid boolean value
254+ const salvaged = globalContext . get ( "sharedConfig" ) ;
255+ expect ( salvaged ) . toBeUndefined ( ) ;
256+
257+ consoleWarnMock . mockRestore ( ) ;
258+ } ) ;
259+
260+ it ( "should handle salvage with valid allowAnonymousTelemetry set to true" , ( ) => {
261+ const corruptedContent = `{
262+ "indexingPaused": true,
263+ "sharedConfig": {"allowAnonymousTelemetry": true, "otherField": "value"},
264+ "corrupted": { invalid json
265+ }` ;
266+ fs . writeFileSync ( globalContextFilePath , corruptedContent ) ;
267+
268+ const consoleWarnMock = jest
269+ . spyOn ( console , "warn" )
270+ . mockImplementation ( ( ) => { } ) ;
271+
272+ globalContext . update ( "hasAlreadyCreatedAPromptFile" , true ) ;
273+
274+ // Should have salvaged only the allowAnonymousTelemetry value
275+ const salvaged = globalContext . get ( "sharedConfig" ) ;
276+ expect ( salvaged ) . toEqual ( { allowAnonymousTelemetry : true } ) ;
277+
278+ consoleWarnMock . mockRestore ( ) ;
279+ } ) ;
280+
281+ it ( "should handle completely unparseable content gracefully" , ( ) => {
282+ fs . writeFileSync (
283+ globalContextFilePath ,
284+ "complete garbage not json at all" ,
285+ ) ;
286+
287+ const consoleWarnMock = jest
288+ . spyOn ( console , "warn" )
289+ . mockImplementation ( ( ) => { } ) ;
290+
291+ globalContext . update ( "indexingPaused" , true ) ;
292+
293+ // Should still work and create a new file
294+ expect ( globalContext . get ( "indexingPaused" ) ) . toBe ( true ) ;
295+ expect ( globalContext . get ( "sharedConfig" ) ) . toBeUndefined ( ) ;
296+
297+ consoleWarnMock . mockRestore ( ) ;
298+ } ) ;
299+
300+ it ( "should preserve multiple salvageable values when updating different key" , ( ) => {
301+ const corruptedContent = `{
302+ "indexingPaused": true,
303+ "sharedConfig": {"allowAnonymousTelemetry": false},
304+ "hasAlreadyCreatedAPromptFile": true,
305+ "corrupted": { invalid json
306+ }` ;
307+ fs . writeFileSync ( globalContextFilePath , corruptedContent ) ;
308+
309+ const consoleWarnMock = jest
310+ . spyOn ( console , "warn" )
311+ . mockImplementation ( ( ) => { } ) ;
312+
313+ // Update a different key - should salvage and preserve existing data
314+ globalContext . update ( "showConfigUpdateToast" , false ) ;
315+
316+ // Should have the new value
317+ expect ( globalContext . get ( "showConfigUpdateToast" ) ) . toBe ( false ) ;
318+
319+ // Should have salvaged the security-sensitive value
320+ expect ( globalContext . get ( "sharedConfig" ) ) . toEqual ( {
321+ allowAnonymousTelemetry : false ,
322+ } ) ;
323+
324+ consoleWarnMock . mockRestore ( ) ;
325+ } ) ;
326+ } ) ;
177327} ) ;
0 commit comments