@@ -45,7 +45,8 @@ vi.mock("./headers");
45
45
vi . mock ( "./inbox" ) ;
46
46
vi . mock ( "./sshConfig" ) ;
47
47
vi . mock ( "./sshSupport" ) ;
48
- vi . mock ( "./storage" ) ;
48
+ // Don't mock storage - we'll create real instances in tests
49
+ // vi.mock("./storage");
49
50
vi . mock ( "./util" ) ;
50
51
vi . mock ( "./workspaceMonitor" ) ;
51
52
vi . mock ( "fs/promises" ) ;
@@ -123,6 +124,7 @@ describe("remote", () => {
123
124
} ,
124
125
} as unknown as typeof vscode ;
125
126
127
+ // Storage import not needed here since we use mocks
126
128
mockStorage = {
127
129
getSessionTokenPath :vi . fn ( ) . mockReturnValue ( "/mock/session/path" ) ,
128
130
writeToCoderOutputChannel :vi . fn ( ) ,
@@ -1283,4 +1285,150 @@ describe("remote", () => {
1283
1285
expect ( findProcess . default ) . toHaveBeenCalledWith ( "port" , 9999 ) ;
1284
1286
} ) ;
1285
1287
} ) ;
1288
+
1289
+ describe ( "Logger integration" , ( ) => {
1290
+ it ( "should use Logger when set on Storage for logging messages" , async ( ) => {
1291
+ // Import the factory function for creating logger with mock
1292
+ const { createMockOutputChannelWithLogger} = await import (
1293
+ "./test-helpers"
1294
+ ) ;
1295
+ const { mockOutputChannel, logger} = createMockOutputChannelWithLogger ( ) ;
1296
+
1297
+ // Create a real Storage instance with the mock output channel
1298
+ const { Storage} = await import ( "./storage" ) ;
1299
+ const realStorage = new Storage (
1300
+ mockOutputChannel as never ,
1301
+ { } as never ,
1302
+ { } as never ,
1303
+ { } as never ,
1304
+ { } as never ,
1305
+ ) ;
1306
+
1307
+ // Set the logger on storage
1308
+ realStorage . setLogger ( logger ) ;
1309
+
1310
+ // Spy on storage methods we need
1311
+ vi . spyOn ( realStorage , "getSessionTokenPath" ) . mockReturnValue (
1312
+ "/mock/session/path" ,
1313
+ ) ;
1314
+ vi . spyOn ( realStorage , "migrateSessionToken" ) . mockResolvedValue ( undefined ) ;
1315
+ vi . spyOn ( realStorage , "readCliConfig" ) . mockResolvedValue ( {
1316
+ url :"https://test.coder.com" ,
1317
+ token :"test-token" ,
1318
+ } ) ;
1319
+ vi . spyOn ( realStorage , "getRemoteSSHLogPath" ) . mockResolvedValue ( undefined ) ;
1320
+ vi . spyOn ( realStorage , "fetchBinary" ) . mockResolvedValue ( "/path/to/coder" ) ;
1321
+ vi . spyOn ( realStorage , "getNetworkInfoPath" ) . mockReturnValue (
1322
+ "/mock/network/info" ,
1323
+ ) ;
1324
+ vi . spyOn ( realStorage , "getLogPath" ) . mockReturnValue ( "/mock/log/path" ) ;
1325
+ vi . spyOn ( realStorage , "getHeaders" ) . mockResolvedValue ( { } ) ;
1326
+
1327
+ // Create remote with the real storage that has logger
1328
+ remote = new Remote (
1329
+ mockVscodeProposed ,
1330
+ realStorage ,
1331
+ mockCommands ,
1332
+ vscode . ExtensionMode . Production ,
1333
+ ) ;
1334
+
1335
+ // Mock parseRemoteAuthority to return valid parts
1336
+ const { parseRemoteAuthority} = await import ( "./util" ) ;
1337
+ vi . mocked ( parseRemoteAuthority ) . mockReturnValue ( {
1338
+ host :"test.coder.com" ,
1339
+ label :"test-label" ,
1340
+ username :"test-user" ,
1341
+ workspace :"test-workspace" ,
1342
+ agent :undefined ,
1343
+ } ) ;
1344
+
1345
+ // Storage config already mocked above
1346
+
1347
+ // Mock needToken to return false
1348
+ const { needToken} = await import ( "./api" ) ;
1349
+ vi . mocked ( needToken ) . mockReturnValue ( false ) ;
1350
+
1351
+ // Mock makeCoderSdk to return workspace not found to exit early
1352
+ const mockWorkspaceRestClient = {
1353
+ getBuildInfo :vi . fn ( ) . mockResolvedValue ( { version :"v0.15.0" } ) ,
1354
+ getWorkspaceByOwnerAndName :vi . fn ( ) . mockRejectedValue ( {
1355
+ isAxiosError :true ,
1356
+ response :{ status :404 } ,
1357
+ } ) ,
1358
+ } as never ;
1359
+ const { makeCoderSdk} = await import ( "./api" ) ;
1360
+ vi . mocked ( makeCoderSdk ) . mockResolvedValue ( mockWorkspaceRestClient ) ;
1361
+
1362
+ // Mock cli.version
1363
+ const cli = await import ( "./cliManager" ) ;
1364
+ vi . mocked ( cli . version ) . mockResolvedValue ( "v0.15.0" ) ;
1365
+
1366
+ // Mock featureSetForVersion
1367
+ const { featureSetForVersion} = await import ( "./featureSet" ) ;
1368
+ vi . mocked ( featureSetForVersion ) . mockReturnValue ( {
1369
+ vscodessh :true ,
1370
+ } as never ) ;
1371
+
1372
+ // Mock user cancellation
1373
+ const showInfoMessageSpy = mockVscodeProposed . window
1374
+ . showInformationMessage as ReturnType < typeof vi . fn > ;
1375
+ showInfoMessageSpy . mockResolvedValue ( undefined ) ;
1376
+
1377
+ // Mock closeRemote
1378
+ vi . spyOn ( remote , "closeRemote" ) . mockResolvedValue ( ) ;
1379
+
1380
+ // Mock isAxiosError
1381
+ const { isAxiosError} = await import ( "axios" ) ;
1382
+ vi . mocked ( isAxiosError ) . mockReturnValue ( true ) ;
1383
+
1384
+ // Execute setup which should trigger logging
1385
+ await remote . setup ( "coder-vscode--test-label--test-user--test-workspace" ) ;
1386
+
1387
+ // Verify that messages were logged through the Logger
1388
+ const logs = logger . getLogs ( ) ;
1389
+ expect ( logs . length ) . toBeGreaterThan ( 0 ) ;
1390
+
1391
+ // Verify specific log messages were created
1392
+ const logMessages = logs . map ( ( log ) => log . message ) ;
1393
+ expect ( logMessages ) . toContain (
1394
+ "Setting up remote: test-user/test-workspace" ,
1395
+ ) ;
1396
+ expect ( logMessages ) . toContain (
1397
+ "Using deployment URL: https://test.coder.com" ,
1398
+ ) ;
1399
+ expect ( logMessages ) . toContain ( "Using deployment label: test-label" ) ;
1400
+ expect ( logMessages ) . toContain (
1401
+ "Got build info: v0.15.0 vscodessh feature: true" ,
1402
+ ) ;
1403
+
1404
+ // Verify messages were written to output channel with proper formatting
1405
+ expect ( mockOutputChannel . appendLine ) . toHaveBeenCalledWith (
1406
+ expect . stringMatching (
1407
+ / \[ .* \] \[ I N F O \] S e t t i n g u p r e m o t e : t e s t - u s e r \/ t e s t - w o r k s p a c e / ,
1408
+ ) ,
1409
+ ) ;
1410
+ } ) ;
1411
+
1412
+ it ( "should maintain backward compatibility with writeToCoderOutputChannel" , async ( ) => {
1413
+ // Import the factory function for creating logger with mock
1414
+ const { createMockOutputChannelWithLogger} = await import (
1415
+ "./test-helpers"
1416
+ ) ;
1417
+ const { mockOutputChannel, logger} = createMockOutputChannelWithLogger ( ) ;
1418
+
1419
+ // Test backward compatibility method
1420
+ logger . writeToCoderOutputChannel ( "Test backward compatibility" ) ;
1421
+
1422
+ // Verify it logs at INFO level
1423
+ const logs = logger . getLogs ( ) ;
1424
+ expect ( logs ) . toHaveLength ( 1 ) ;
1425
+ expect ( logs [ 0 ] . level ) . toBe ( "INFO" ) ;
1426
+ expect ( logs [ 0 ] . message ) . toBe ( "Test backward compatibility" ) ;
1427
+
1428
+ // Verify output format
1429
+ expect ( mockOutputChannel . appendLine ) . toHaveBeenCalledWith (
1430
+ expect . stringMatching ( / \[ .* \] \[ I N F O \] T e s t b a c k w a r d c o m p a t i b i l i t y / ) ,
1431
+ ) ;
1432
+ } ) ;
1433
+ } ) ;
1286
1434
} ) ;