@@ -34,19 +34,33 @@ jest.mock('../data/repository/ai_repository', () => ({
3434 } ) ) ,
3535} ) ) ;
3636
37+ jest . mock ( '../utils/setup_files' , ( ) => {
38+ const actual = jest . requireActual < typeof import ( '../utils/setup_files' ) > ( '../utils/setup_files' ) ;
39+ return {
40+ ...actual ,
41+ hasValidSetupToken : jest . fn ( ( cwd : string ) => actual . hasValidSetupToken ( cwd ) ) ,
42+ } ;
43+ } ) ;
44+
3745describe ( 'CLI' , ( ) => {
3846 let exitSpy : jest . SpyInstance ;
47+ let consoleErrorSpy : jest . SpyInstance ;
48+ let consoleLogSpy : jest . SpyInstance ;
3949
4050 beforeEach ( ( ) => {
4151 jest . clearAllMocks ( ) ;
4252 exitSpy = jest . spyOn ( process , 'exit' ) . mockImplementation ( ( ( ) => { } ) as ( ) => never ) ;
4353 ( execSync as jest . Mock ) . mockReturnValue ( Buffer . from ( 'https://github.com/test-owner/test-repo.git' ) ) ;
4454 ( runLocalAction as jest . Mock ) . mockResolvedValue ( undefined ) ;
4555 mockIsIssue . mockResolvedValue ( true ) ;
56+ consoleErrorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
57+ consoleLogSpy = jest . spyOn ( console , 'log' ) . mockImplementation ( ( ) => { } ) ;
4658 } ) ;
4759
4860 afterEach ( ( ) => {
4961 exitSpy ?. mockRestore ( ) ;
62+ consoleErrorSpy ?. mockRestore ( ) ;
63+ consoleLogSpy ?. mockRestore ( ) ;
5064 } ) ;
5165
5266 describe ( 'think' , ( ) => {
@@ -265,8 +279,7 @@ describe('CLI', () => {
265279
266280 describe ( 'setup' , ( ) => {
267281 // Token check: hasValidSetupToken/setupEnvFileExists and message variants are covered in
268- // setup_files.test.ts and initial_setup_use_case.test.ts. Full "exit with proposal" path
269- // is hard to test here because Commander captures options.token default at CLI load time.
282+ // setup_files.test.ts and initial_setup_use_case.test.ts.
270283 it ( 'calls runLocalAction with INITIAL_SETUP' , async ( ) => {
271284 await program . parseAsync ( [ 'node' , 'cli' , 'setup' ] ) ;
272285
@@ -277,6 +290,19 @@ describe('CLI', () => {
277290 expect ( params [ INPUT_KEYS . WELCOME_TITLE ] ) . toContain ( 'Initial Setup' ) ;
278291 } ) ;
279292
293+ it ( 'proceeds when --token is provided even if env/.env has no token' , async ( ) => {
294+ const { hasValidSetupToken } = require ( '../utils/setup_files' ) ;
295+ ( hasValidSetupToken as jest . Mock ) . mockReturnValue ( false ) ;
296+
297+ await program . parseAsync ( [ 'node' , 'cli' , 'setup' , '--token' , 'ghp_abcdefghijklmnopqrstuvwxyz12' ] ) ;
298+
299+ expect ( exitSpy ) . not . toHaveBeenCalled ( ) ;
300+ expect ( runLocalAction ) . toHaveBeenCalledTimes ( 1 ) ;
301+ const params = ( runLocalAction as jest . Mock ) . mock . calls [ 0 ] [ 0 ] ;
302+ expect ( params [ INPUT_KEYS . TOKEN ] ) . toBe ( 'ghp_abcdefghijklmnopqrstuvwxyz12' ) ;
303+ expect ( params [ INPUT_KEYS . SINGLE_ACTION ] ) . toBe ( ACTIONS . INITIAL_SETUP ) ;
304+ } ) ;
305+
280306 it ( 'exits when not inside a git repo' , async ( ) => {
281307 ( execSync as jest . Mock ) . mockImplementation ( ( cmd : string ) => {
282308 if ( typeof cmd === 'string' && cmd . includes ( 'is-inside-work-tree' ) ) throw new Error ( 'not a repo' ) ;
0 commit comments