
    siu                        d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
mZ ddlmZmZ ddlZ eej                   j#                  dd            Z ee      e	j(                  vr"e	j(                  j+                  d ee             edz  Z ee      e	j(                  vr"e	j(                  j+                  d ee             ddlmZ d	efd
Zd	efdZd	efdZd	efdZd	efdZd	efdZd	efdZd	efdZ d	efdZ!d	efdZ"d	efdZ#d	efdZ$d	efdZ%d	efdZ&d	efdZ'd	efdZ(d	efdZ)d	efdZ*d	efdZ+d	efdZ,d	efdZ-d Z.d  Z/d! Z0d" Z1d# Z2d$ Z3d	efd%Z4d	efd&Z5d	efd'Z6y)(u8  
test_auto_merge.py

auto_merge.py TDD 테스트 (토르 작성)

테스트 항목:
1. test_scan_done_files: .done 파일 감지, .done.clear 제외
2. test_try_claim_success: 원자적 선점 성공
3. test_try_claim_already_claimed: 이미 선점된 경우 False
4. test_parse_done_valid: 정상 JSON 파싱
5. test_parse_done_invalid: 비정상 JSON 처리
6. test_parse_done_legacy_format: 구형 포맷 (merge_needed 없음) 처리
7. test_analyze_report: mock으로 parse_report 결과 검증
8. test_resolve_project_path_from_report: 보고서에서 프로젝트 경로 추출
9. test_execute_merge_success: 머지 성공 시나리오 (subprocess mock)
10. test_execute_merge_conflict: 머지 충돌 시나리오
11. test_run_tests_pytest: pytest 실행 성공
12. test_run_tests_timeout: 타임아웃 처리
13. test_revert_merge: 되돌리기 테스트
14. test_log_result: merge-log.json 기록
15. test_dry_run: 드라이런 모드에서 실제 머지 없음
16. test_run_full_flow: 전체 흐름 통합 테스트 (mock 기반)
17. test_load_env_keys: .env.keys 파싱
    N)Path)	MagicMockpatchWORKSPACE_ROOT/home/jay/workspacescripts)
AutoMergertmp_pathc                    | dz  dz  }|j                  d       |dz  }|j                  dd       |d	z  }|j                  d
d       |dz  j                  dd       |dz  j                  dd       t        t        |             }|j	                         }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }||k(  }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}y)uF   미처리 .done 파일 스캔: .done.clear 없는 것만 반환한다.memoryeventsTparentsztask-100.1.donez+{"task_id": "task-100.1", "status": "done"}utf-8encodingztask-101.1.donez+{"task_id": "task-101.1", "status": "done"}ztask-101.1.done.clear{}ztask-102.1.done.clearworkspace_path   ==)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenresult)py0py1py3py6zassert %(py8)spy8Nr   )z%(py1)s == %(py3)sf1r   r   assert %(py5)spy5)mkdir
write_textr	   strscan_done_filesr   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)r
   
events_dirr    f2mergerr   @py_assert2@py_assert5@py_assert4@py_format7@py_format9@py_assert0@py_format4@py_format6s                 ,/home/jay/workspace/tests/test_auto_merge.pytest_scan_done_filesr<   3   s   H$x/JT" 
'	'BMM?'MR 
'	'BMM?'MR))55dW5M ))55dW5Ms8}5F##%Fv;!;!;!33vv;!!99?99    c                    | dz  dz  }|j                  d       |dz  }|j                  dd       t        t        |       	      }|j	                  |      }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}d}||z  }	|	j                  }
 |
       }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      t        j                  |      dz  }t        t        j                  |            dx}x}	x}
}d}||z  }	|	j                  }
 |
       }| }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      t        j                  |      dz  }t        t        j                  |            dx}x}	x}
x}}y)uL   원자적 선점: .done.merging 파일을 생성하고 True를 반환한다.r   r   Tr   ztask-200.1.donez{"task_id": "task-200.1"}r   r   r   isz%(py0)s is %(py3)sr   r   r   r"   r#   Nztask-200.1.done.mergingzMassert %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
}r0   )r   py2r#   py7ztask-200.1.done.clearzQassert not %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
})r$   r%   r	   r&   	try_claimr(   r)   r*   r+   r,   r-   r.   r/   exists)r
   r0   	done_filer2   r   r3   @py_assert1r9   r:   @py_assert3r5   @py_assert6@py_format8@py_assert8r7   s                  r;   test_try_claim_successrM   P   s   H$x/JT"..I4wGs8}5Fi(F6T>6T66T2<J22<2::<:<<<<<<<<J<<<J<<<2<<<:<<<<<<<<<<4>
44>4<<><>>>>>>>>>>>
>>>
>>>4>>><>>>>>>>>>>>r=   c                 *   | dz  dz  }|j                  d       |dz  }|j                  dd       |d	z  j                  d
d       t        t        |             }|j	                  |      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)uT   원자적 선점: .done.merging 파일이 이미 존재하면 False를 반환한다.r   r   Tr   ztask-201.1.donez{"task_id": "task-201.1"}r   r   ztask-201.1.done.mergingr   r   Fr?   rA   r   rB   r"   r#   N)r$   r%   r	   r&   rE   r(   r)   r*   r+   r,   r-   r.   r/   )	r
   r0   rG   r2   r   r3   rH   r9   r:   s	            r;   test_try_claim_already_claimedrO   g   s    H$x/JT"..I4wG++77w7Os8}5Fi(F6U?6U66Ur=   c                    | dz  dz  }|j                  d       |dz  }ddddd	d
}|j                  t        j                  |      d       t	        t        |             }|j                  |      }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }d	}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)uK   정상 JSON .done 파일 파싱: task_id, team_id, merge_needed 등 추출.r   r   Tr   task-391.1.done
task-391.1donetask/task-391.1-dev1dev1task_idstatusmerge_neededmerge_branchteam_idr   r   r   rW   r   z%(py1)s == %(py4)sr   py4assert %(py6)sr   Nr[   rY   r?   z%(py1)s is %(py4)srZ   )r$   r%   jsondumpsr	   r&   
parse_doner(   r)   r-   r.   r/   )r
   r0   rG   	done_datar2   r   r8   rI   r3   @py_format5r6   s              r;   test_parse_done_validrf   |   s   H$x/JT"..I.I I.As8}5Fy)F),,,,,,,,,,,,,,,,,,,)&&&&&&&&&&&&&&&&&&&.!)T)!T))))!T)))!)))T))))))).!;%;;!%;;;;;!%;;;;!;;;%;;;;;;;;r=   c                    | dz  dz  }|j                  d       |dz  }|j                  dd       t        t        |       	      }t	        j
                  |d
      5 }t        j                  t        d      5  |j                  |       ddd       |j                          ddd       y# 1 sw Y   "xY w# 1 sw Y   yxY w)uC   비정상 JSON: escalate를 호출하고 예외를 발생시킨다.r   r   Tr   ztask-bad.donezNOT VALID JSON{{{{r   r   r   escalateJSONmatchN)r$   r%   r	   r&   r   objectpytestraises
ValueErrorrc   assert_called_once)r
   r0   rG   r2   mock_escalates        r;   test_parse_done_invalidrr      s    H$x/JT"_,I-@s8}5F	fj	) +]]]:V4 	)i(	)((*+ +	) 	)+ +s$   B9:B-B9-B6	2B99Cc           	         | dz  dz  }|j                  d       |dz  }dddd	}|j                  t        j                  |      d
       t	        t        |             }|j                  |      }|j                  }d}d} |||      }	d}
|	|
u }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}	x}}
|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)uG   구형 포맷: merge_needed 없는 파일도 올바르게 파싱한다.r   r   Tr   ztask-old.donedev2z2026-03-01T10:00:00g      ^@)r[   end_timeduration_secondsr   r   r   rY   Fr?   )zS%(py8)s
{%(py8)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s, %(py6)s)
} is %(py11)sr   )r   rC   r^   r   r   py11zassert %(py13)spy13Nr[   r   r\   r]   r_   r   rW   ztask-old)r$   r%   ra   rb   r	   r&   rc   getr(   r)   r*   r+   r,   r-   r.   r/   )r
   r0   rG   legacy_datar2   r   rH   rI   r4   @py_assert7@py_assert10@py_assert9@py_format12@py_format14r8   r3   re   r6   s                     r;   test_parse_done_legacy_formatr      s   H$x/JT"_,I)!K
 K07Cs8}5Fy)F ::5n5e5:ne,55,5555,55555565556555:555n555e555,55555555555)&&&&&&&&&&&&&&&&&&&)*
*
****
******
*******r=   c                 
   | dz  dz  }|j                  d       |dz  }|j                  dd       d	d
dddg d}t        t        |             }t	        d|      5  |j                  d	      }ddd       d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y# 1 sw Y   xY w)uF   analyze_report: parse_report를 mock으로 대체하여 결과 검증.r   reportsTr   task-391.1.mdu   # task-391.1 보고서
r   r   rR   rU   rT   z3/home/jay/projects/MyApp/.worktrees/task-391.1-dev1)rW   teamrY   rZ   merge_worktreefilesr   auto_merge.parse_reportreturn_valueNrY   r?   r`   r]   r_   r   rZ   r   r\   r   )r$   r%   r	   r&   r   analyze_reportr(   r)   r-   r.   r/   )r
   reports_dirreport_filemock_reportr2   r   r8   rI   r3   re   r6   s              r;   test_analyze_reportr      s   X%	1Kd# /K5H  .OK s8}5F	({	C 5&&|45 .!)T)!T))))!T)))!)))T))))))).!;%;;!%;;;;;!%;;;;!;;;%;;;;;;;;"#\'\\#'\\\\\#'\\\\#\\\'\\\\\\\\5 5s   G88Hc                    t        t        |             }ddgd}|j                  d|      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}}y)uO   보고서 데이터에서 /home/jay/projects/... 패턴 경로를 추출한다.r   z8/home/jay/projects/ThreadAuto/.worktrees/task-391.1-dev1z%/home/jay/projects/ThreadAuto/main.pyr   r   rR   /home/jay/projects/ThreadAutor   z%(py0)s == %(py3)sr   rB   r"   r#   Nr	   r&   resolve_project_pathr(   r)   r*   r+   r,   r-   r.   r/   r
   r2   report_datar   r3   rH   r9   r:   s           r;   %test_resolve_project_path_from_reportr      s    s8}5F U9:K
 (({CF44644444644444446444644444444444r=   c                    t        t        |             }dg d}|j                  d|      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}y)u<   merge_worktree에서 프로젝트 경로를 역추적한다.r   z7/home/jay/projects/MyProject/.worktrees/task-391.1-dev1r   rR   z/home/jay/projects/MyProjectr   r   r   rB   r"   r#   Nr   r   s           r;   'test_resolve_project_path_from_worktreer      s    s8}5F TK
 (({CF33633333633333336333633333333333r=   c                    t        t        |             }dg d}|j                  d|      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}}y)u5   경로를 찾을 수 없을 때 None을 반환한다.r   Nr   z
task-999.9r?   rA   r   rB   r"   r#   r   r   s           r;   test_resolve_project_path_noner     s    s8}5F K
 (({CF6T>6T66Tr=   c                    t        t        |             }t        j                  ddd      }t	               }d|_        ||_        d|_        t        d|      5  |j                  d	d
d      }ddd       d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   xY w)uC   머지 성공: worktree_manager.py subprocess 호출 결과 파싱.r   mergedrT   rX   branchr    subprocess.runr   /home/jay/projects/MyApprR   rU   NrX   r   r\   r]   r_   r   r   )r	   r&   ra   rb   r   
returncodestdoutstderrr   execute_merger(   r)   r-   r.   r/   )
r
   r2   mock_outputmock_resultr   r8   rI   r3   re   r6   s
             r;   test_execute_merge_successr     s'   s8}5F**<RSTK+KK$KK	k	: X%%&@,PVWX ('x'x''''x''''''x'''''''(55555555555555555555555	X Xs   E77Fc                 f   t        t        |             }t        j                  ddd      }t	               }d|_        ||_        d|_        t        d|      5  t        j                  t        d	
      5  |j                  ddd       ddd       ddd       y# 1 sw Y   xY w# 1 sw Y   yxY w)uE   머지 충돌: subprocess 오류 시 RuntimeError를 발생시킨다.r   errorz<Merge conflict detected while merging 'task/task-391.1-dev1')rX   messager   conflictr   r   zconflict|errorrj   r   rR   rU   N)r	   r&   ra   rb   r   r   r   r   r   rm   rn   RuntimeErrorr   )r
   r2   mock_error_outputr   s       r;   test_execute_merge_conflictr   .  s    s8}5F

'ef +KK*K#K	k	: S]]</?@ 	S  !;\6R	SS S	S 	SS Ss$   B'6B
B'B$	 B''B0c                 D   t        t        |             }| dz  }|j                          t               }d|_        d|_        d|_        t        d|      5  t        dd	      5  |j                  t        |            }d
d
d
       d
d
d
       d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}|d   }
t        |
t              }|sddt!        j"                         v st        j$                  t              rt        j                  t              ndt        j                  |
      dt!        j"                         v st        j$                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d
x}
}y
# 1 sw Y   
xY w# 1 sw Y   xY w)u1   pytest 실행 성공: passed=True, output 포함.r   	myprojectr   z5 passed in 1.23sr   r   r   shutil.which/usr/bin/pytestNpassedTr?   r`   r]   r_   r   outputin)z%(py1)s in %(py4)sdurationz5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstancefloat)r   rC   r   r#   )r	   r&   r$   r   r   r   r   r   	run_testsr(   r)   r-   r.   r/   r   r   r*   r+   r,   )r
   r2   project_dirr   r   r8   rI   r3   re   r6   rH   r5   r:   s                r;   test_run_tests_pytestr   E  s   s8}5F [(K+KK,KK	k	: 8>0AB 	8%%c+&67F	88 (#t#t####t######t#######'vh''8'''''8''''8'''''''''''Z(0:(%00000000:000:000(000000%000%0000000000	8 	88 8s$   J%J JJ	JJc                    ddl }t        t        |             }| dz  }|j                          t	        d|j                  dd            5  t	        d	d
      5  |j                  t        |            }ddd       ddd       d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d}|d   }|j                  }
 |
       }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}y# 1 sw Y   PxY w# 1 sw Y   UxY w)uB   테스트 타임아웃: passed=False, output에 timeout 메시지.r   Nr   r   r   rm   i,  )side_effectr   r   r   r   Fr?   r`   r]   r_   r   timeoutr   r   )zD%(py1)s in %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.lower
}()
})r   r^   r   r   assert %(py10)spy10)
subprocessr	   r&   r$   r   TimeoutExpiredr   r(   r)   r-   r.   r/   lower)r
   r   r2   r   r   r8   rI   r3   re   r6   r4   r{   r7   @py_format11s                 r;   test_run_tests_timeoutr   `  sb   s8}5F[(K	Z-F-FxQT-U	V 8>0AB 	8%%c+&67F	88 ($u$u$$$$u$$$$$$u$$$$$$$0x(0(..0.00900000900009000(000.000000000000		8 	88 8s$   GG5GG	GGc                     t        t        |             }t               }d|_        d|_        d|_        t        d|      5 }|j                  d      }ddd       d	}|u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}j                   d   d   }	d}
|
|	v }|st        j                  d|fd|
|	f      t        j                  |
      dt        j                         v st        j                  |	      rt        j                  |	      nddz  }dd|iz  }t        t        j                  |            dx}
}d}
|
|	v }|st        j                  d|fd|
|	f      t        j                  |
      dt        j                         v st        j                  |	      rt        j                  |	      nddz  }dd|iz  }t        t        j                  |            dx}
}d}
|
|	v }|st        j                  d|fd|
|	f      t        j                  |
      dt        j                         v st        j                  |	      rt        j                  |	      nddz  }dd|iz  }t        t        j                  |            dx}
}y# 1 sw Y   xY w)u;   머지 되돌리기: git reset --hard HEAD~1 호출 확인.r   r   zHEAD is now at abc1234r   r   r   r   NTr?   rA   r   rB   r"   r#   gitr   )z%(py1)s in %(py3)scalled_argsr!   resetz--hard)r	   r&   r   r   r   r   r   revert_merger(   r)   r*   r+   r,   r-   r.   r/   	call_args)r
   r2   r   mock_runr   r3   rH   r9   r:   r   r8   s              r;   test_revert_merger   v  s   s8}5F+KK1KK	k	: Ah$$%?@A 6T>6T66T$$Q'*K5K5K5KK!7k!!!!7k!!!7!!!!!!k!!!k!!!!!!!"8{""""8{"""8""""""{"""{"""""""A As   LLc           
      0   | dz  }|j                          |dz  }ddddgi}|j                  t        j                  |      d       t	        t        |       	      }d
ddddddddd	}|j                  d
|       t        j                  |j                  d            }|d   }t        |      }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t!        t        j"                  |            dx}x}x}
}	|d   d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd |iz  }t!        t        j"                  |            dx}x}}|d   d!   d   }d
}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd |iz  }t!        t        j"                  |            dx}x}}y)"uI   merge-log.json에 결과를 추가한다. 기존 entries는 유지된다.r   merge-log.jsonentriesz
task-100.1successrW   rX   r   r   r   rR   z2026-03-07T15:30:00+09:00Tauto_mergedr   rT   passg      )@)	rW   	timestamprY   actionprojectr   test_resultrv   rX      r   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr   r   rC   r^   rD   assert %(py9)spy9Nr   rW   r\   r]   r_   r   r   )r$   r%   ra   rb   r	   r&   
log_resultloads	read_textr   r(   r)   r*   r+   r,   r-   r.   r/   )r
   
memory_dir	merge_logexistingr2   	new_entrysavedrH   rI   rJ   r4   rK   @py_format10r8   r3   re   r6   s                    r;   test_log_resultr     s   H$J--I	JKLHH-@s8}5F02( 
I lI.JJy**G*<=EY%3 %A% A%%%% A%%%%%%3%%%3%%%%%% %%%A%%%%%%%Ay)9\9)\9999)\999)999\9999999Ay)9\9)\9999)\999)999\9999999r=   c                    | dz  }|j                          t        t        |             }ddd}|j                  d|       |dz  }|j                  } |       }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }t        t        j                  |            d
x}}t        j                  |j                  d            }|d   }t        |      }d}	||	k(  }
|
st        j                   d|
fd||	f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d
x}x}x}
}	|d   d   d   }d}||k(  }|slt        j                   d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}y
)u0   merge-log.json이 없으면 새로 생성한다.r   r   
task-500.1r   r   r   Aassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r   r   rC   r^   Nr   r   r   r   r   r   r   r   r   r   r   rW   r\   r]   r_   r   )r$   r	   r&   r   rF   r*   r+   r(   r,   r-   r.   r/   ra   r   r   r   r)   )r
   r   r2   r   r   rH   rI   re   r   rJ   r4   rK   r   r8   r3   r6   s                   r;   test_log_result_creates_filer     s   H$Js8}5F(I>I
lI.--I99JJy**G*<=EY%3 %A% A%%%% A%%%%%%3%%%3%%%%%% %%%A%%%%%%%Ay)9\9)\9999)\999)999\9999999r=   c                    | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }dddd	d
d}|j                  t        j                  |      d       |dz  }|j                  dd       ddd	ddgd}t	        t        |       d      }t        d|      5  t        j                  |d      5 }t        j                  |d      5 }	|j                         }
ddd       ddd       ddd       j                          	j                          
d   }d}||k\  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w) uA   드라이런 모드: 실제 머지 없이 분석만 수행한다.r   r   Tr   r   rQ   rR   rS   rT   rU   rV   r   r   r   # task-391.1
5/home/jay/projects/TestApp/.worktrees/task-391.1-dev1"/home/jay/projects/TestApp/main.pyrW   rY   rZ   r   r   )r   dry_runr   r   r   r   N	processedr   )>=)z%(py1)s >= %(py4)sr]   r_   r   )r$   r%   ra   rb   r	   r&   r   rl   runassert_not_calledr(   r)   r-   r.   r/   )r
   r0   r   rG   rd   r   r   r2   
mock_merge
mock_testsr   r8   rI   r3   re   r6   s                   r;   test_dry_runr     s   H$x/JT"X%	1Kd# ..I.I I.A /K+g>  .Q67K s8}dCF	({	C &\\&/2 	&jfk2 &j&	&&   "  " +#!#!####!######!#######& &	& 	&& &s<   ,F<F0F$+F03F<$F-)F00F9	5F<<Gc           
      h   | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }dddd	d
d}|j                  t        j                  |      d       |dz  }|j                  dd       ddd	ddgd}t	        t        |             }t        d|      5  t        j                  |ddd	d      5  t        j                  |ddddd      5  |j                         }ddd       ddd       ddd       d   }	d}
|	|
k(  }|slt        j                  d |fd!|	|
f      t        j                  |	      t        j                  |
      d"z  }d#d$|iz  }t        t        j                  |            dx}	x}}
|d   }	d}
|	|
k(  }|slt        j                  d |fd!|	|
f      t        j                  |	      t        j                  |
      d"z  }d#d$|iz  }t        t        j                  |            dx}	x}}
|d%   }	d&}
|	|
k(  }|slt        j                  d |fd!|	|
f      t        j                  |	      t        j                  |
      d"z  }d#d$|iz  }t        t        j                  |            dx}	x}}
|j                  } |       }
|
st        j                  d'      d(z   d)t!        j"                         v st        j$                  |      rt        j                  |      nd)t        j                  |      t        j                  |
      d*z  }t        t        j                  |            dx}}
| dz  d+z  }|j                  } |       }
|
sd,d-t!        j"                         v st        j$                  |      rt        j                  |      nd-t        j                  |      t        j                  |
      d*z  }t        t        j                  |            dx}}
t        j&                  |j)                  d            }|d.   }t+        |      }
d}|
|k(  }|st        j                  d |fd/|
|f      d0t!        j"                         v st        j$                  t*              rt        j                  t*              nd0t        j                  |      t        j                  |
      t        j                  |      d1z  }d2d3|iz  }t        t        j                  |            dx}x}
x}}|d.   d&   d4   }	d}
|	|
k(  }|slt        j                  d |fd!|	|
f      t        j                  |	      t        j                  |
      d"z  }d#d$|iz  }t        t        j                  |            dx}	x}}
y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)5u`   전체 흐름 통합 테스트: scan → claim → parse → analyze → merge → test → log.r   r   Tr   r   rQ   rR   rS   rT   rU   rV   r   r   r   r   r   r   r   r   r   r   r   r   r   r   5 passedg      ?r   r   r   Nr   r   r   r\   r]   r_   r   	escalatedr   uA   .done 파일은 아누가 처리할 때까지 유지되어야 함C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}rG   r   r   r   r   r   r   r   r   r   r   rW   )r$   r%   ra   rb   r	   r&   r   rl   r   r(   r)   r-   r.   r/   rF   _format_assertmsgr*   r+   r,   r   r   r   )r
   r0   r   rG   rd   r   r   r2   r   r8   rI   r3   re   r6   rH   r   log_datarJ   r4   rK   r   s                        r;   test_run_full_flowr    s,   H$x/JT"X%	1Kd# ..I.I I.A /K+g>  .Q67K s8}5F	({	C &\\&/8_u@vw 	&(,
PST &
  &	&& +#!#!####!######!#######( q q    q      q       +#!#!####!######!####### bbbbbbbbbbb9bbb9bbbbbbbbbbbb 8#&66I99zz)--w-?@H	"(3"#(q(#q((((#q((((((3(((3((("(((#(((q(((((((Iq!),<<,<<<<,<<<,<<<<<<<<<<'& &	& 	&& &s<   +V'V$V5V=V'VVV$	V''V1c           
      R   | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }dddd	d
}|j                  t        j                  |      d       |dz  }|j                  dd       dddddgd}t	        t        |             }t        d|      5  t        j                  |dddd      5  t        j                  |ddddd      5  t        j                  |dd      5 }t        j                  |d      5 }	|j                         }
d d d        d d d        d d d        d d d        d d d        j                          	j                          
d!   }d"}||k(  }|slt        j                  d#|fd$||f      t        j                  |      t        j                  |      d%z  }d&d'|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)(u<   테스트 실패 시 머지 revert + escalate 호출 확인.r   r   Tr   r   ztask-392.1.donez
task-392.1rS   rt   rW   rX   rY   r[   r   r   ztask-392.1.mdz# task-392.1
ztask/task-392.1-dev2z5/home/jay/projects/TestApp/.worktrees/task-392.1-dev2r   r   r   r   r   r   r   r   r   FzFAILED 2 errorsg       @r   r   rh   Nr   r   r   r\   r]   r_   r   )r$   r%   ra   rb   r	   r&   r   rl   r   rp   r(   r)   r-   r.   r/   )r
   r0   r   rG   rd   r   r   r2   mock_revertrq   r   r8   rI   r3   re   r6   s                   r;   test_run_full_flow_test_failurer  .  s   H$x/JT"X%	1Kd#..I	I I.A/K+g>  .Q67K s8}5F	({	C 	.\\&/8_u@vw 	.(-9JX[\ .
 \\&.tL .P[fj9 .]!'...	.	. ""$$$&+#!#!####!######!#######. .. .. .	. 	.	. 	.sl   *HH#H<G9	G-$G9	,H4H<H-G62G9	9H>HH
HH	HH&c           	         | dz  }|j                  dd       t        t        |             }|j                         }|d   }d}||k(  }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}d}|j                  }	 |	       }
t        |
      }||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |	      t	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}x}	x}
}y)u6   .env.keys 파일을 파싱하여 dict로 반환한다.z	.env.keysux   export COKACDIR_KEY_ANU=abc123def456
export COKACDIR_KEY_DEV1=xyz789
# 주석
export WORKSPACE_ROOT=/home/jay/workspace
r   r   r   COKACDIR_KEY_ANUabc123def456r   r\   r]   r_   r   NCOKACDIR_KEY_DEV1xyz789r   r   #)not in)zg%(py1)s not in %(py10)s
{%(py10)s = %(py3)s(%(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.keys
}()
})
}r&   r   )r   r   r^   r   r   r   zassert %(py12)spy12)r%   r	   r&   _load_env_keysr(   r)   r-   r.   r/   keysr*   r+   r,   )r
   env_filer2   r   r8   rI   r3   re   r6   r4   r{   r}   r   @py_format13s                 r;   test_load_env_keysr  `  s   +%H	6    s8}5F""$F$%77%7777%777%7777777777%&2(2&(2222&(222&222(2222222"#<'<<#'<<<<<#'<<<<#<<<'<<<<<<<<(&++(+-(c-((3(((((3((((3((((((c(((c((((((&(((&(((+(((-((((((((((((r=   c                  ,   t         j                  } d} | |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x} x}x}x}}t         j                  } d
} | |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x} x}x}x}}y	)u   'dev1-team' → 'dev1' 변환.	dev1-teamrU   r   zW%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s._team_id_to_short
}(%(py4)s)
} == %(py9)sr	   r   rC   r^   r   r   assert %(py11)srw   Nz	dev2-teamrt   
r	   _team_id_to_shortr(   r)   r*   r+   r,   r-   r.   r/   rH   rI   r4   rL   r{   r   r~   s          r;   !test_team_id_to_short_with_suffixr  z  s7   ''>>'4>>4>>>>4>>>>>>:>>>:>>>'>>>>>>4>>>>>>>>>>''>>'4>>4>>>>4>>>>>>:>>>:>>>'>>>>>>4>>>>>>>>>>>r=   c                  ,   t         j                  } d} | |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}x}x}}t         j                  } d	} | |      }d	}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}x}x}}y)
u+   이미 short 형식이면 그대로 반환.rU   r   r  r	   r  r  rw   Nrt   r  r  s          r;   #test_team_id_to_short_already_shortr     s7   ''99'/969/69999/6999999:999:999'999999/99969999999''99'/969/69999/6999999:999:999'999999/999699999999r=   c                  @   t         j                  } d} | |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}x}x}}t         j                  } d	} | |      }d	}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}x}x}}t         j                  } d
} | |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}x}x}}y)u<   빈 문자열, 'team'만 있는 경우 등 엣지 케이스.r   r   r  r	   r  r  rw   Nr   zcustom-teamcustomr  r  s          r;    test_team_id_to_short_edge_casesr#    s   ''11'+1r1+r1111+r111111:111:111'111111+111r1111111''99'/969/69999/6999999:999:999'999999/99969999999''BB'6B(B6(BBBB6(BBBBBB:BBB:BBB'BBBBBB6BBB(BBBBBBBBr=   c                  v   ddd} t         j                  } ||       }d}||k(  }|s
t        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |      dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}x}}y)u(   merge_branch에서 team_short 역추출.rT   NrZ   r   rU   r   ze%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s._extract_team_short_from_report
}(%(py3)s)
} == %(py8)sr	   r   r   rC   r   r#   r   r   r   
r	   _extract_team_short_from_reportr(   r)   r*   r+   r,   r-   r.   r/   r   rH   r5   r{   rJ   r7   r   s          r;   #test_extract_team_short_from_branchr+    s    #9TRK55L5kBLfLBfLLLLBfLLLLLL:LLL:LLL5LLLLLLkLLLkLLLBLLLfLLLLLLLr=   c                  v   ddd} t         j                  } ||       }d}||k(  }|s
t        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |      dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}x}}y)u*   merge_worktree에서 team_short 역추출.Nz1/home/jay/projects/App/.worktrees/task-391.1-dev2r%  rt   r   r&  r	   r   r'  r   r   r(  r*  s          r;   %test_extract_team_short_from_worktreer-    s     MK 55L5kBLfLBfLLLLBfLLLLLL:LLL:LLL5LLLLLLkLLLkLLLBLLLfLLLLLLLr=   c                  t   ddd} t         j                  } ||       }d}||u }|s
t        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |      dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}x}x}}y)
u   둘 다 없으면 None 반환.Nr%  r?   )ze%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s._extract_team_short_from_report
}(%(py3)s)
} is %(py8)sr	   r   r'  r   r   r(  r*  s          r;   test_extract_team_short_noner/    s    #'4@K55J5kBJdJBdJJJJBdJJJJJJ:JJJ:JJJ5JJJJJJkJJJkJJJBJJJdJJJJJJJr=   c                    | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }ddd	d
}|j                  t        j                  |      d       |dz  }|j                  dd       dd	ddg d}t	        t        |             }t        d|      5  |j                         }ddd       |dz  }	|	j                  }
 |
       }| }|st        j                  d      dz   dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |
      t        j                  |      dz  }t        t        j                   |            dx}
x}}|j                  }
 |
       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }t        t        j                   |            dx}
}d   }d}||k(  }|slt        j"                  d|fd||f      t        j                  |      t        j                  |      d z  }d!d"|iz  }t        t        j                   |            dx}x}}y# 1 sw Y   xY w)#ug   merge_needed=False인 경우 .done.clear를 생성하지 않고 .done 파일도 건드리지 않는다.r   r   Tr   r   ztask-500.1.doner   rS   F)rW   rX   rY   r   r   ztask-500.1.mdu   # task-500.1
머지 불필요
Nr   r   r   r   ztask-500.1.done.clearu3   .done.clear가 생성되면 안 됨 (경합 방지)zG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
clear_filer   uA   .done 파일이 유지되어야 함 (notify-completion에 위임)r  rG   skippedr   r   r\   r]   r_   r   )r$   r%   ra   rb   r	   r&   r   r   rF   r(   r  r*   r+   r,   r-   r.   r/   r)   )r
   r0   r   rG   rd   r   r   r2   r   r1  rH   rI   r4   r:   re   r8   r3   r6   s                     r;    test_merge_needed_false_no_claimr3    s.   H$x/JT"X%	1Kd# ..II
 I.A /K=P  K s8}5F	({	C  55J  Y "Y""Y"YY$YYYYYYYzYYYzYYY YYY"YYYYYY bbbbbbbbbbb9bbb9bbbbbbbbbbbb)!!!!!!!!!!!!!!!!!!! s   (KKc           
         | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }dddd	d
d}|j                  t        j                  |      d       |dz  }|j                  dd       ddd	dg d}t	        t        |             }t        d|      5  t        j                  |ddd	d      5  t        j                  |ddddd      5  t        j                  |d      5 }|j                         }	ddd       ddd       ddd       ddd       j                  d       	d   }
d}|
|k(  }|slt        j                  d|fd |
|f      t        j                  |
      t        j                  |      d!z  }d"d#|iz  }t        t        j                  |            dx}
x}}y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)$u9   머지 성공 후 notify_anu()가 호출되는지 확인.r   r   Tr   r   ztask-501.1.donez
task-501.1rS   ztask/task-501.1-dev1r  rV   r   r   ztask-501.1.mdz# task-501.1
z5/home/jay/projects/TestApp/.worktrees/task-501.1-dev1r   r   r   r   r   r   r   r   r   g      ?r   
notify_anuNr   r   r\   r]   r_   r   )r$   r%   ra   rb   r	   r&   r   rl   r   assert_called_once_withr(   r)   r-   r.   r/   )r
   r0   r   rG   rd   r   r   r2   mock_notifyr   r8   rI   r3   re   r6   s                  r;   "test_notify_anu_called_after_merger8    s   H$x/JT"X%	1Kd#..I.I I.A/K+g>  .QK s8}5F	({	C *\\$,8NO
 	*
 (,
PST *
 \\&,7 *;#ZZ\F**	** ''5( q q    q      q       	* ** *	* 	** *sT   *G!G#G	:F=	G	GG!=GG		GGG	G!!G*c           
         | dz  dz  }|j                  d       | dz  dz  }|j                  d       |dz  }dddd	d
}|j                  t        j                  |      d       |dz  }|j                  dd       dddddgd}t	        t        |             }t        d|      5  t        j                  |dddd      5 }t        j                  |ddddd      5  t        j                  |d      5  |j                          ddd       ddd       ddd       ddd       j                          |j                  d   }	|	d    }
d!}|
|k(  }|st        j                  d"|fd#|
|f      d$t        j                         v st        j                  |
      rt        j                   |
      nd$t        j                   |      d%z  }t        j"                  d&|
 d'      d(z   d)|iz  }t%        t        j&                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)*uN   execute_merge에 team_id가 아닌 team_short(dev1)가 전달되는지 확인.r   r   Tr   r   ztask-502.1.donez
task-502.1rS   r  r  r   r   ztask-502.1.mdz# task-502.1
ztask/task-502.1-dev1z5/home/jay/projects/TestApp/.worktrees/task-502.1-dev1r   r   r   r   r   r   r   r   r   okg      ?r   r5  Nr   r   rU   r   r   actual_team_shortrB   u)   team_short가 'dev1'이어야 하는데 ''z
>assert %(py5)sr#   )r$   r%   ra   rb   r	   r&   r   rl   r   rp   r   r(   r)   r*   r+   r,   r-   r  r.   r/   )r
   r0   r   rG   rd   r   r   r2   r   r   r;  r3   rH   r9   r:   s                  r;   %test_team_short_used_in_execute_merger=    s   H$x/JT"X%	1Kd#..I	I I.A/K+g>  .Q67K s8}5F	({	C !\\$,8NO
 	! (,#N !
 \\&,7 !JJL!!	!! !!#$$Q'I!! &h&hhhhhhhhhhhhhhhhhh*STeSffg(hhhhhhh! !! !	! 	!! !sT   *H7H*#H:H	HH*H7HHH'"H**H4	/H77I)7__doc__builtinsr*   _pytest.assertion.rewrite	assertionrewriter(   ra   ossyspathlibr   unittest.mockr   r   rm   environry   	WORKSPACEr&   pathinsertSCRIPTS
auto_merger	   r<   rM   rO   rf   rr   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r   r#  r+  r-  r/  r3  r8  r=   r=   r;   <module>rN     s  2   	 
  *   02GHI	y>!HHOOAs9~& i
w<sxxHHOOAs7|$ !4 :?T ?.T *<D <:+d +*+D +8]$ ]B
5D 
5
4d 
4
T 
$6 6.S$ S.1D 161T 1,# #2:d :>:4 :,*$4 *$d5= 5=p*$d *$d) )4?:CMMK("t ("`-! -!j/iD /ir=   