@@ -27,6 +27,7 @@ import * as health from '../../src/bootstrap/health.js';
2727import * as repair from '../../src/bootstrap/repair.js' ;
2828import * as auth from '../../src/bootstrap/auth.js' ;
2929import * as keychain from '../../src/lib/keychain.js' ;
30+ import * as hooks from '../../src/bootstrap/hooks.js' ;
3031import * as stateManager from '../../src/lib/state-manager.js' ;
3132import { getDefaultState } from '../../src/lib/state.js' ;
3233import { setMockUi , getMockUiCalls , clearMockUiCalls } from '../../src/ui' ;
@@ -318,27 +319,42 @@ describe('integrateCommand: configuration validation', () => {
318319 setMockUi ( false ) ;
319320 } ) ;
320321
321- it ( 'exits 1 when server URL cannot be determined ' , async ( ) => {
322+ it ( 'exits 0 and installs hooks when no project key is configured (secrets-only mode) ' , async ( ) => {
322323 const discoverSpy = spyOn ( discovery , 'discoverProject' ) . mockResolvedValue ( FAKE_PROJECT_INFO ) ;
324+ const hooksSpy = spyOn ( hooks , 'installSecretScanningHooks' ) . mockResolvedValue ( undefined ) ;
323325 try {
324- await integrateCommand ( 'claude' , { project : 'my-project' } ) ;
325- expect ( mockExit ) . toHaveBeenCalledWith ( 1 ) ;
326- const errors = getMockUiCalls ( ) . filter ( c => c . method === 'error' ) . map ( c => String ( c . args [ 0 ] ) ) ;
327- expect ( errors . some ( m => m . includes ( 'Server URL' ) ) ) . toBe ( true ) ;
326+ await integrateCommand ( 'claude' , { } ) ;
327+ expect ( mockExit ) . toHaveBeenCalledWith ( 0 ) ;
328+ expect ( hooksSpy ) . toHaveBeenCalled ( ) ;
329+ } finally {
330+ discoverSpy . mockRestore ( ) ;
331+ hooksSpy . mockRestore ( ) ;
332+ }
333+ } ) ;
334+
335+ it ( 'shows secrets-only message when no project key is configured' , async ( ) => {
336+ const discoverSpy = spyOn ( discovery , 'discoverProject' ) . mockResolvedValue ( FAKE_PROJECT_INFO ) ;
337+ const hooksSpy = spyOn ( hooks , 'installSecretScanningHooks' ) . mockResolvedValue ( undefined ) ;
338+ try {
339+ await integrateCommand ( 'claude' , { } ) ;
340+ const texts = getMockUiCalls ( ) . filter ( c => c . method === 'text' ) . map ( c => String ( c . args [ 0 ] ) ) ;
341+ expect ( texts . some ( m => m . includes ( 'No project key' ) ) ) . toBe ( true ) ;
328342 } finally {
329343 discoverSpy . mockRestore ( ) ;
344+ hooksSpy . mockRestore ( ) ;
330345 }
331346 } ) ;
332347
333- it ( 'exits 1 when project key cannot be determined ' , async ( ) => {
348+ it ( 'exits 0 and installs hooks when server is set but no project key (secrets-only mode) ' , async ( ) => {
334349 const discoverSpy = spyOn ( discovery , 'discoverProject' ) . mockResolvedValue ( FAKE_PROJECT_INFO ) ;
350+ const hooksSpy = spyOn ( hooks , 'installSecretScanningHooks' ) . mockResolvedValue ( undefined ) ;
335351 try {
336352 await integrateCommand ( 'claude' , { server : 'https://sonarcloud.io' } ) ;
337- expect ( mockExit ) . toHaveBeenCalledWith ( 1 ) ;
338- const errors = getMockUiCalls ( ) . filter ( c => c . method === 'error' ) . map ( c => String ( c . args [ 0 ] ) ) ;
339- expect ( errors . some ( m => m . includes ( 'Project key' ) ) ) . toBe ( true ) ;
353+ expect ( mockExit ) . toHaveBeenCalledWith ( 0 ) ;
354+ expect ( hooksSpy ) . toHaveBeenCalled ( ) ;
340355 } finally {
341356 discoverSpy . mockRestore ( ) ;
357+ hooksSpy . mockRestore ( ) ;
342358 }
343359 } ) ;
344360
0 commit comments