@@ -276,4 +276,113 @@ describe('GitService - 프로세스 관리', () => {
276276 expect ( mockedSpawn ) . toHaveBeenCalledTimes ( 3 ) ;
277277 } ) ;
278278 } ) ;
279+
280+ describe ( 'parseCommand - 명령어 파싱 테스트' , ( ) => {
281+ it ( '따옴표가 포함된 URL을 올바르게 파싱해야 한다' , async ( ) => {
282+ // Given: 따옴표가 포함된 clone 명령어
283+ class MockChildProcess extends EventEmitter {
284+ stdout = new EventEmitter ( ) ;
285+ stderr = new EventEmitter ( ) ;
286+ stdin = { end : jest . fn ( ) } ;
287+ pid = 12345 ;
288+ kill = jest . fn ( ) ;
289+ }
290+
291+ const mockChild = new MockChildProcess ( ) ;
292+ mockedSpawn . mockReturnValue ( mockChild as any ) ;
293+
294+ // When: clone 실행
295+ const clonePromise = gitService . clone ( 'https://github.com/test/repo.git' , '/tmp/test-repo' ) ;
296+
297+ // 비동기 실행을 위해 대기
298+ process . nextTick ( ( ) => {
299+ mockChild . stderr . emit ( 'data' , 'Cloning into...\n' ) ;
300+ mockChild . emit ( 'close' , 0 ) ;
301+ } ) ;
302+
303+ await clonePromise ;
304+
305+ // Then: spawn이 올바른 인자로 호출되었는지 확인
306+ expect ( mockedSpawn ) . toHaveBeenCalledWith (
307+ 'git' ,
308+ [ 'clone' , 'https://github.com/test/repo.git' , '/tmp/test-repo' ] ,
309+ expect . any ( Object )
310+ ) ;
311+ } ) ;
312+
313+ it ( '공백이 포함된 경로를 올바르게 파싱해야 한다' , async ( ) => {
314+ // Given: 공백이 포함된 경로
315+ class MockChildProcess extends EventEmitter {
316+ stdout = new EventEmitter ( ) ;
317+ stderr = new EventEmitter ( ) ;
318+ stdin = { end : jest . fn ( ) } ;
319+ pid = 12345 ;
320+ kill = jest . fn ( ) ;
321+ }
322+
323+ const mockChild = new MockChildProcess ( ) ;
324+ mockedSpawn . mockReturnValue ( mockChild as any ) ;
325+
326+ // When: clone 실행 (공백 포함 경로)
327+ const clonePromise = gitService . clone ( 'https://github.com/test/repo.git' , '/tmp/test repo with spaces' ) ;
328+
329+ // 비동기 실행을 위해 대기
330+ process . nextTick ( ( ) => {
331+ mockChild . stderr . emit ( 'data' , 'Cloning into...\n' ) ;
332+ mockChild . emit ( 'close' , 0 ) ;
333+ } ) ;
334+
335+ await clonePromise ;
336+
337+ // Then: spawn이 올바른 인자로 호출되었는지 확인
338+ expect ( mockedSpawn ) . toHaveBeenCalledWith (
339+ 'git' ,
340+ [ 'clone' , 'https://github.com/test/repo.git' , '/tmp/test repo with spaces' ] ,
341+ expect . any ( Object )
342+ ) ;
343+ } ) ;
344+
345+ it ( '여러 인자가 있는 명령어를 올바르게 파싱해야 한다' , async ( ) => {
346+ // Given: worktree add 명령어
347+ class MockChildProcess extends EventEmitter {
348+ stdout = new EventEmitter ( ) ;
349+ stderr = new EventEmitter ( ) ;
350+ stdin = { end : jest . fn ( ) } ;
351+ pid = 12345 ;
352+ kill = jest . fn ( ) ;
353+ }
354+
355+ const mockChild = new MockChildProcess ( ) ;
356+ mockedSpawn . mockReturnValue ( mockChild as any ) ;
357+
358+ // 유효한 저장소로 만들기 위한 설정
359+ mockExecAsync . mockResolvedValueOnce ( { stdout : '' , stderr : '' } ) ; // isValidRepository
360+ mockExecAsync . mockResolvedValueOnce ( { stdout : '' , stderr : '' } ) ; // worktree list
361+ mockExecAsync . mockResolvedValueOnce ( { stdout : '' , stderr : '' } ) ; // worktree prune
362+ mockExecAsync . mockResolvedValueOnce ( { stdout : '' , stderr : '' } ) ; // branchExists
363+ mockExecAsync . mockResolvedValueOnce ( { stdout : 'main' , stderr : '' } ) ; // getMainBranchName
364+
365+ // When: worktree 생성
366+ const worktreePromise = gitService . createWorktree (
367+ '/tmp/main-repo' ,
368+ 'feature-branch' ,
369+ '/tmp/worktree path' ,
370+ 'main'
371+ ) ;
372+
373+ // 비동기 실행을 위해 대기
374+ process . nextTick ( ( ) => {
375+ mockChild . stderr . emit ( 'data' , 'Preparing worktree...\n' ) ;
376+ mockChild . emit ( 'close' , 0 ) ;
377+ } ) ;
378+
379+ await worktreePromise ;
380+
381+ // Then: spawn이 올바른 인자로 호출되었는지 확인 (마지막 호출)
382+ const lastCall = mockedSpawn . mock . calls [ mockedSpawn . mock . calls . length - 1 ] ;
383+ expect ( lastCall ) . toBeDefined ( ) ;
384+ expect ( lastCall ! [ 0 ] ) . toBe ( 'git' ) ;
385+ expect ( lastCall ! [ 1 ] ) . toEqual ( [ 'worktree' , 'add' , '-b' , 'feature-branch' , '/tmp/worktree path' , 'main' ] ) ;
386+ } ) ;
387+ } ) ;
279388} ) ;
0 commit comments