
    riqi                     V   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
Z
ddlmZ ddlmZ ddlmZmZmZmZ ddlZ eej*                  j-                  dd            Z ee      e	j2                  vr"e	j2                  j5                  d ee             edz  Z ee      e	j2                  vr"e	j2                  j5                  d ee             dd	lmZmZ d
edededededefdZ d
ededefdZ!deddfdZ" G d d      Z#ejH                  dedeeddf   fd       Z% G d d      Z&y)u  
test_auto_merge_stash_isolation.py

task-2418: PreFlightCheck T1+T2 stash 격리 fix 회귀 테스트
작성자: 모리건 (dev3 팀 테스터)

테스트 항목:
단위 테스트 (8개) - TestPreFlightCheckStashIsolationUnit
  1. test_simulate_merge_passes_affected_files_to_stash
  2. test_simulate_merge_metadata_missing_dirty_returns_conflict
  3. test_simulate_merge_metadata_missing_clean_skips_stash
  4. test_get_task_affected_files_parses_modify_and_create_only
  5. test_stash_pop_with_retry_succeeds_after_one_failure
  6. test_stash_pop_with_retry_raises_after_three_failures
  7. test_find_stash_by_msg_searches_correct_ref_among_multiple_stashes
  8. test_run_groups_task_ids_per_project_path

통합 테스트 (4개) - TestPreFlightCheckStashIsolationIntegration
  9.  test_integration_stash_whitelist_includes_untracked
  10. test_integration_concurrent_stash_apply_correct_ref
  11. test_integration_apply_drop_removes_stash
  12. test_integration_apply_conflict_triggers_notify
    N)Path)	Generator)	MagicMockMockcallpatchWORKSPACE_ROOTz/home/jay/workspacescripts)PreFlightCheckPreFlightCheckErrorbasetask_idmodify_filescreate_filesforbidden_filesreturnc                    | dz  dz  }|j                  dd       || dz  }d| dddg}|r-|j                  d	       |D ]  }|j                  d
| d        |r-|j                  d       |D ]  }|j                  d
| d        |r-|j                  d       |D ]  }|j                  d
| d        |j                  dj                  |      d       |S )u<   task md 파일을 ## affected_files 섹션과 함께 생성.memorytasksTparentsexist_ok.md# 
z## affected_files
u   ### 수정
z- `z`
u   ### 신규
u   ### 변경 금지
 utf-8encoding)mkdirappend
write_textjoin)	r   r   r   r   r   	tasks_dirmd_pathlinesfs	            </home/jay/workspace/tests/test_auto_merge_stash_isolation.py_make_task_md_with_affectedr)   3   s    x')IOOD4O0WIS/)G'"t%:;E^$ 	'ALL3qc&	'^$ 	'ALL3qc&	'*+  	'ALL3qc&	' rwwu~8N    c                 z    | dz  dz  }|j                  dd       || dz  }|j                  d| dd	       |S )
u6   ## affected_files 섹션 없는 task md 파일 생성.r   r   Tr   r   r   u1   

작업 내용만 있고 affected_files 없음.
r   r   )r    r"   )r   r   r$   r%   s       r(   _make_task_md_no_affectedr,   K   sS    x')IOOD4O0WIS/)GG9$XYdklNr*   	repo_pathc                    | j                  dd       t        j                  ddgt        |       dd       t        j                  g dt        |       dd       t        j                  g dt        |       dd       | d	z  }|j	                  d
d       t        j                  g dt        |       dd       t        j                  g dt        |       dd       y)u,   임시 git repo 초기화 및 초기 커밋.Tr   gitinitFcwdcapture_outputcheck)r/   configz
user.emailztest@test.com)r/   r5   z	user.nameTest	README.mdzinit
r   r   )r/   add.)r/   commit-mr0   N)r    
subprocessrunstrr"   )r-   readmes     r(   _git_init_repor@   T   s    OOD4O0NNE6?ItSXYNNCYhltyzNN9s9~^bjop$F
h1NN&C	N4W\]NN2IW[chir*   c                   p    e Zd Z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y)$TestPreFlightCheckStashIsolationUnitu%   단위 테스트 (mock 기반, 8개).tmp_pathc                    d}dg}dg}t        ||||g        t        |      }|j                  dt        |             t        t        |            }g fd}t	        d|      5  t	        j
                  |d	d
      5  |j                  |g |g       ddd       ddd       t              }	d}
|	|
k\  }|st        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  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}	x}}
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  }t        j                  d      dz   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  }t        j                  d!      dz   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  }t        j                  d#      dz   d|iz  }t        t        j                   |            dx}}	|j#                  d"      }||dz   d }||z   D ]  }||v }|st        j                  d|fd$||f      d%t        j                         v st        j                  |      rt        j                  |      nd%d&t        j                         v st        j                  |      rt        j                  |      nd&d'z  }t        j                  | d(      d)z   d*|iz  }t        t        j                   |            d} |j#                  d      }|j#                  d       }|j#                  d"      }||k  }||k  }	|r|	st        j                  d+||	fd,|||f      d-t        j                         v st        j                  |      rt        j                  |      nd-d.t        j                         v st        j                  |      rt        j                  |      nd.d/t        j                         v st        j                  |      rt        j                  |      nd/d0z  }t        j                  d1      d2z   d3|iz  }t        t        j                   |            dx}}	y# 1 sw Y   xY w# 1 sw Y   xY w)4uM   simulate_merge 호출 시 task affected_files만 stash push 인자에 전달.	task-1001scripts/foo.pytests/test_foo.pyr	   workspace_pathc                     t               }d|_        d|_        d|_        d| v rd| v rj	                  |        d|_        |S )Nr   r   stashpushstash_ref_capturedr   
returncodestdoutstderrr!   cmdkwargsresultstash_push_callss      r(   mock_subprocess_runztTestPreFlightCheckStashIsolationUnit.test_simulate_merge_passes_affected_files_to_stash.<locals>.mock_subprocess_run}   sG    VF !FFMFM#~&C- '', 4Mr*   subprocess.runside_effect_is_working_tree_dirtyTreturn_valuetask_idsN   >=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)slenrV   py0py1py3py6u4   git stash push가 최소 1회 호출되어야 한다
>assert %(py8)spy8r   --include-untrackedinz%(py1)s in %(py3)s
stash_callrf   rg   u$   --include-untracked 플래그 필요
>assert %(py5)spy5r;   u   -m 메시지 플래그 필요--u   -- 구분자 필요z%(py0)s in %(py2)sr'   	file_argsre   py2u&   이 stash push 파일 목록에 없음
>assert %(py4)spy4)<rz   )z%(py0)s < %(py3)sz%(py3)s < %(py4)sidx_untrackedidx_midx_dash_dash)re   rg   ry   u   플래그 순서가 잘못됨
>assert %(py6)srh   )r)   r>   setenvr   r   objectsimulate_mergerc   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationindex)selfrC   monkeypatchr   r   r   project_pathpfcrW   @py_assert2@py_assert5@py_assert4@py_format7@py_format9ro   @py_assert0@py_format4@py_format6dash_dash_idxru   r'   @py_assert1@py_format3@py_format5r{   r|   r}   rV   s                              @r(   2test_simulate_merge_passes_affected_files_to_stashzWTestPreFlightCheckStashIsolationUnit.test_simulate_merge_passes_affected_files_to_stashm   s0   ()+,#Hg|\SUV 8}+S];CM:
	 #1DE 	Ic#;$O I""<wi"HI	I
 #$aa$)aaa$aaaaaasaaasaaaaaa#aaa#aaa$aaaaaa+aaaaaaaa%a(
$Z$
2ZZZ$
ZZZ$ZZZZZZ
ZZZ
ZZZZ4ZZZZZZZBtz!BBBtzBBBtBBBBBBzBBBzBBBB#BBBBBBB8tz!888tz888t888888z888z8888#8888888 #((.}q012	, 	PA	>OOO1	OOOOOO1OOO1OOOOOO	OOO	OOOOaS(N#OOOOOOO	P #(()>?  &"((.u4Uu}4UUUU}u}UUUUUU}UUU}UUUUUUuUUUuUUUUUU}UUU}UUUU6UUUUUUU+I I	I 	Is$   (ZY4Z4Y>	9ZZc                 4   d}t        ||       t        |      }|j                  dt        |             t        t        |            }g t	        j
                  |dd      5  t	        j
                  |dfd	      5  |j                  |g |g
      }ddd       ddd       d   }d}||u }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }
t        j                  d      dz   d|
iz  }t        t        j                  |            dx}x}	}|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  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}x}}|d   d   d   }d}||k(  }	|	st        j                  d |	fd!||f      t        j                  |      t        j                  |      dz  }
t        j                  d"      dz   d|
iz  }t        t        j                  |            dx}x}	}t              }	d}|	|k(  }|st        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  }t        j                  d&      d'z   d(|iz  }t        t        j                  |            dx}	x}}y# 1 sw Y   kxY w# 1 sw Y   pxY w))uW   affected_files 없는 task + dirty working tree → metadata_missing conflict + 알림.z	task-2000r	   rH   r[   Tr\   _notify_chairman_stash_failurec                  &    j                  |       S Nr!   akwnotify_callss     r(   <lambda>zrTestPreFlightCheckStashIsolationUnit.test_simulate_merge_metadata_missing_dirty_returns_conflict.<locals>.<lambda>   s    bnbubuvwbx r*   rY   r^   NpassedF)is)z%(py1)s is %(py4)srf   ry   u   passed=False이어야 한다r~   rh   	conflictsr`   ra   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} >= %(py7)src   re   rw   ry   py7u(   conflicts가 최소 1개이어야 한다
>assert %(py9)spy9r   branch__metadata_missing__==z%(py1)s == %(py4)su.   branch가 __metadata_missing__이어야 한다z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   rd   =   _notify_chairman_stash_failure가 1회 호출되어야 한다ri   rj   )r,   r>   r   r   r   r   r   r   r   r   r   r   r   rc   r   r   r   )r   rC   r   r   r   r   rU   r   @py_assert3r   r   r   r   @py_assert6r   @py_format8@py_format10r   r   r   s                      @r(   ;test_simulate_merge_metadata_missing_dirty_returns_conflictz`TestPreFlightCheckStashIsolationUnit.test_simulate_merge_metadata_missing_dirty_returns_conflict   s   !(G48}+S];CM:\\#7dK 	Rc#CQxy R++L"y+QR	R hH5H5(HHH5HHHHHH5HHH*HHHHHHHH+&Xs&'X1X'1,XXX'1XXXXXXsXXXsXXX&XXX'XXX1XXX.XXXXXXXXk"1%h/{3I{/3II{{{/3I{{{/{{{3I{{{K{{{{{{{{< fAf A%fff Affffffsfffsffffff<fff<fff fffAfff'ffffffffR R	R 	Rs$   %PP P P
	PPc                    d}t        ||       t        |      }|j                  dt        |             t        t        |            }g fd}t	        j
                  |dd      5  t	        d|	      5  |j                  |g |g
      }ddd       ddd       t              }d}	||	k(  }
|
st        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  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}x}
}	d   }g }||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)uK   affected_files 없는 task + clean working tree → stash push 호출 0건.z	task-3000r	   rH   c                 x    t               }d|_        d|_        d|_        d| v rd| v rj	                  |        |S )Nr   r   rK   rL   rN   rR   s      r(   rW   zxTestPreFlightCheckStashIsolationUnit.test_simulate_merge_metadata_missing_clean_skips_stash.<locals>.mock_subprocess_run   s@    VF !FFMFM#~&C- '',Mr*   r[   Fr\   rX   rY   r^   Nr   r   r   rc   rV   rd   uK   clean working tree에서는 git stash push를 호출하지 않아야 한다ri   rj   r   r   r   u$   conflicts가 비어 있어야 한다r~   rh   )r,   r>   r   r   r   r   r   rc   r   r   r   r   r   r   r   r   r   )r   rC   r   r   r   r   rW   rU   r   r   r   r   r   r   r   r   rV   s                   @r(   6test_simulate_merge_metadata_missing_clean_skips_stashz[TestPreFlightCheckStashIsolationUnit.test_simulate_merge_metadata_missing_clean_skips_stash   s   !(G48}+S];CM:	 \\#7eL 	R'5HI R++L"y+QR	R #$xx$)xxx$xxxxxxsxxxsxxxxxx#xxx#xxx$xxxxxx+xxxxxxxxk"PbP"b(PPP"bPPP"PPPbPPP*PPPPPPPPR R	R 	Rs$   *I)8II)I&	!I))I3c                 n   d}dg}dg}ddg}t        |||||       |j                  dt        |             t        t        |            }|j	                  |g      }||v }	|	st        j                  d|	fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
dt        j                         v st        j                  |      rt        j                  |      nddz  }
t        j                  | d      dz   d|
iz  }t        t        j                  |            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  }t        j                  d      dz   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  }t        j                  d      dz   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  }t        j                  d      dz   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  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}}y)uA   수정/신규 섹션만 파싱하고 변경 금지 섹션 제외.z	task-4000rF   rG   zscripts/bar.pyzdispatch.pyr	   rH   rl   rt   r   rU   rv   u   가 결과에 없음rx   ry   Nrn   filesrp   u'   수정 파일이 포함되어야 한다rq   rr   u'   신규 파일이 포함되어야 한다not in)z%(py1)s not in %(py3)su.   변경 금지 파일이 제외되어야 한다)r)   r   r>   r   _get_task_affected_filesr   r   r   r   r   r   r   r   r   )r   rC   r   r   r   r   r   r   rU   r   r   r   r   r   r   r   r   s                    r(   :test_get_task_affected_files_parses_modify_and_create_onlyz_TestPreFlightCheckStashIsolationUnit.test_get_task_affected_files_parses_modify_and_create_only   s   ()+,+];#Hg|\Sbc+S];CM:--wi8& BBBw&BBBBBBwBBBwBBBBBB&BBB&BBBBWI-A"BBBBBBBwS5(SSS5SSSSSSSSS5SSS5SSSS*SSSSSSS"V"e+VVV"eVVV"VVVVVVeVVVeVVVV-VVVVVVV^u,^^^u^^^^^^^^^u^^^u^^^^.^^^^^^^[}E)[[[}E[[[}[[[[[[E[[[E[[[[+[[[[[[[r*   c           	         t        |      }|j                  dt        |             t        t        |            }dgfd}t        j                  |dd      5  t        d|	      5  t        d
      5  |j                  |dddg       ddd       ddd       ddd       d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   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)uK   stash apply 첫 번째 실패 → 두 번째 시도 성공 → drop 성공.r	   rH   r   c                     t               }d|_        d|_        d|_        d| v r2d| v r.dxx   dz  cc<   d   dk(  rd|_        d|_        |S d|_        |S d| v rd| v rd|_        |S )Nr   r   rK   applyr`   zapply faileddropr   rO   rP   rQ   )rS   rT   rU   
call_counts      r(   rW   zvTestPreFlightCheckStashIsolationUnit.test_stash_pop_with_retry_succeeds_after_one_failure.<locals>.mock_subprocess_run  s    VF !FFMFM#~'S.1"a=A%()F%$2FM M	 )*F% M CFcM$%!Mr*   _find_stash_by_msg	stash@{0}r\   rX   rY   
time.sleeppreflight-task-1001-123rE   N   r   r   r   uF   stash apply가 2회 호출되어야 한다 (1회 실패 + 1회 성공)r~   rh   )r>   r   r   r   r   _stash_pop_with_retryr   r   r   r   r   r   )r   rC   r   r   r   rW   r   r   r   r   r   r   s              @r(   4test_stash_pop_with_retry_succeeds_after_one_failurezYTestPreFlightCheckStashIsolationUnit.test_stash_pop_with_retry_succeeds_after_one_failure   s"   8}+S];CM:S
	$ \\#3+N 	s'5HI s<( s--lKIbepdqrss	s !}kk}!kkk}kkk}kkkkkk#kkkkkkkk	s ss s	s 	ss<   E+E7D<EE<EEE	EEc                    t        |      }|j                  dt        |             t        t        |            }dgfd}g t        j                  |dd      5  t        d|	      5  t        d
      5  t        j                  |dfd	      5  t        j                  t              5  |j                  |dddg       ddd       ddd       ddd       ddd       ddd       d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}x}}t              }d}||k(  }|st        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  }
t        j                  d      dz   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)uc   stash apply 3회 모두 실패 → _notify_chairman_stash_failure 1회 + PreFlightCheckError raise.r	   rH   r   c                 p    t               }d|_        d|_        d|_        d| v rd| v rdxx   dz  cc<   |S )Nr`   r   zapply conflictrK   r   r   r   )rS   rT   rU   apply_counts      r(   rW   zwTestPreFlightCheckStashIsolationUnit.test_stash_pop_with_retry_raises_after_three_failures.<locals>.mock_subprocess_run)  s@    VF !FFM,FM#~'S.A!#Mr*   r   r   r\   rX   rY   r   r   c                  &    j                  |       S r   r   r   s     r(   r   zlTestPreFlightCheckStashIsolationUnit.test_stash_pop_with_retry_raises_after_three_failures.<locals>.<lambda>9  s    jvj}j}~  kA r*   r   rE   N   r   r   r   u*   stash apply가 3회 시도되어야 한다r~   rh   r`   r   rc   r   rd   r   ri   rj   )r>   r   r   r   r   pytestraisesr   r   r   r   r   r   r   r   rc   r   r   r   )r   rC   r   r   r   rW   r   r   r   r   r   r   r   r   r   r   s                 @@r(   5test_stash_pop_with_retry_raises_after_three_failureszZTestPreFlightCheckStashIsolationUnit.test_stash_pop_with_retry_raises_after_three_failures   s
   8}+S];CM:c		 \\#3+N 	'5HI <( c+K  ZA  B #]]+>? 55 ,k;TWbVc	 1~PP~"PPP~PPP~PPPPPP$PPPPPPPP< fAf A%fff Affffffsfffsffffff<fff<fff fffAfff'ffffffff    	 	sl    K .K:KJ9	0J,J9	KKK ,J61J9	9K>KKKK	K  K*c                 p   t        |      }|j                  dt        |             t        t        |            }dfd}t        d|      5  |j	                  |d      }d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z   d|	iz  }
t        t        j                  |
            dx}}y# 1 sw Y   xY w)u-   stash list에서 msg로 정확한 ref 반환.r	   rH   zzstash@{0} On main: other-bot-stash
stash@{1} On main: preflight-task-1001-1234567890
stash@{2} On main: yet-another-stash
c                 F    t               }d|_        |_        d|_        |S )Nr   r   r   )rS   rT   rU   stash_list_outputs      r(   rW   zTestPreFlightCheckStashIsolationUnit.test_find_stash_by_msg_searches_correct_ref_among_multiple_stashes.<locals>.mock_subprocess_runS  s%    VF !F-FMFMMr*   rX   rY   zpreflight-task-1001-1234567890Nz	stash@{1}r   )z%(py0)s == %(py3)srefre   rg   u   예상 ref=stash@{1}, 실제=rq   rr   )r>   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   rC   r   r   r   rW   r   r   r   r   r   r   s              @r(   Btest_find_stash_by_msg_searches_correct_ref_among_multiple_stasheszgTestPreFlightCheckStashIsolationUnit.test_find_stash_by_msg_searches_correct_ref_among_multiple_stashesF  s    8}+S];CM:5 		 #1DE 	Y((7WXC	Y "Jsk!JJJskJJJJJJsJJJsJJJkJJJ%DSE#JJJJJJJ	Y 	Ys   D,,D5c                    |j                  dt        |             t        t        |            }ddddddddd	d
ddg}g d6fd	}t        j                  |d|      5  t        j                  |d|      5  |j                  d       ddd       ddd       t              }d}||k(  }|st        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  }	t        j                  dt               d      dz   d|	iz  }
t        t        j                  |
            dx}x}}D ci c]  }|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  }t        j                  d"      d#z   d$|iz  }t        t        j                  |            dx}}|d%   }t#        |      }ddh}||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  }t        j                  d)      d*z   d+|iz  }t        t        j                  |            dx}x}x}}|d,   }t#        |      }ddh}||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  }t        j                  d-      d*z   d+|iz  }t        t        j                  |            dx}x}x}}|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  }t        j                  d/      d#z   d$|iz  }t        t        j                  |            dx}}|d%   }d
g}||k(  }|st        j                  d|fd0||f      t        j                  |      t        j                  |      d1z  }t        j                  d2      d3z   d4|iz  }	t        t        j                  |	            dx}x}}|d,   }d	g}||k(  }|st        j                  d|fd0||f      t        j                  |      t        j                  |      d1z  }t        j                  d5      d3z   d4|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   $xY w# 1 sw Y   )xY wc c}w )7ud   같은 project_path 두 task → simulate_merge 1회 (task_ids 2개), 다른 path → 별도 호출.r	   rH   ztask-Azbranch-Az/proj/alpha)r   r   r   ztask-Bzbranch-Bztask-Czbranch-Cz
/proj/betaNc                 8    j                  | ||d       dg dS )N)r   branchesr_   T)r   r   r   )r   r   r_   simulate_callss      r(   mock_simulate_mergezkTestPreFlightCheckStashIsolationUnit.test_run_groups_task_ids_per_project_path.<locals>.mock_simulate_mergeq  s,    !! ,$$# 
 #44r*   find_merge_branchesr\   r   rY   zbatch-test-001r   r   r   rc   r   rd   u1   simulate_merge가 2회 호출되어야 하는데    회ri   rj   r   is notz%(py0)s is not %(py3)s
alpha_callr   u5   /proj/alpha에 대한 simulate_merge 호출이 없음rq   rr   r   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)ssetr   u   alpha branches가 맞지 않음r   r   r_   u   alpha task_ids가 맞지 않음	beta_callu4   /proj/beta에 대한 simulate_merge 호출이 없음r   r   u   beta branches가 맞지 않음r~   rh   u   beta task_ids가 맞지 않음r   )r   r>   r   r   r   r=   rc   r   r   r   r   r   r   r   r   r   getr   )r   rC   r   r   branch_infosr   r   r   r   r   r   ccalls_by_pathr   r   r   r   r   r   r   r   r   r   r   r   s                           @r(   )test_run_groups_task_ids_per_project_pathzNTestPreFlightCheckStashIsolationUnit.test_run_groups_task_ids_per_project_pathc  sf   +S];CM: !JV JV JU
 	5 \\#4<P 	*c#3ATU *()*	*
 >"uau"a'uuu"auuuuuusuuusuuuuuu>uuu>uuu"uuuauuu+\]`ao]p\qqt)uuuuuuuu 8FF!>*A-FF"&&}5
!%^z%^^^z^^^^^^z^^^z^^^^^^'^^^^^^^j)is)*iz:.Fi*.FFiii*.Fiiiiiisiiisiii)iii*iii.FiiiHiiiiiiiij)es)*ex.Be*.BBeee*.Beeeeeeseeeseee)eee*eee.BeeeDeeeeeeee!%%l3	 $\y$\\\y\\\\\\y\\\y\\\\\\&\\\\\\\$VV$4VVV$VVV$VVVVVV6VVVVVVVV$T
T$
2TTT$
TTT$TTT
TTT4TTTTTTTT#* *	* 	* Gs*   #Z=<Z0Z=[
0Z:	5Z==[N)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r   r    r*   r(   rB   rB   f   s|    /2V4 2VpgTX g2Qt QB\SW \4!lT !lN gd  gLK[_ K:(U$ (Ur*   rB   rC   c              #   4   K   | dz  }t        |       | yw)u4   실 임시 git repo 픽스처: init + 초기 커밋.repoN)r@   )rC   r   s     r(   git_repor     s      fD4
Js   c                   P    e Zd ZdZdedefdZdedefdZdedefdZdedefdZy)	+TestPreFlightCheckStashIsolationIntegrationu-   통합 테스트 (실 임시 git repo, 4개).r   rC   c           	      	   |j                  dt        |             |dz  }|j                  dd       |dz  }|j                  dd       t        t        |            }d	}|j	                  t        |      |ddg      }t        j                  g d
t        |      ddd      }	|	j                  }
|
j                  } |       }d}||k(  }|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                  |      dz  }t        j                  d|	j                         dz   d|iz  }t!        t        j"                  |            dx}
x}x}x}}t        j                  g dt        |      ddd      }|j                  }||v }
|
s t        j                  d|
fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d| d|j                         dz   d |iz  }t!        t        j"                  |            dx}
}|j                  }
|
j$                  }d!} ||      }d"}||k\  }|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  }t        j                  d&      d'z   d(|iz  }t!        t        j"                  |            dx}
x}x}x}x}}d}||u}
|
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.z   d/|iz  }t!        t        j"                  |            dx}
}y)0u@   tracked 수정 + untracked 추가 파일 모두 stash에 포함.r	   r7   zmodified content
r   r   znew_untracked.pyzprint('hello')
rH   zpreflight-task-1001-test)r/   statusz--porcelainTFr2   r3   textr4   r   r   )z`%(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.stdout
}.strip
}()
} == %(py9)sstatus_result)re   rw   ry   rh   r   u+   stash 후 working tree가 clean해야 함: z
>assert %(py11)spy11Nr/   rK   listrl   )z.%(py0)s in %(py4)s
{%(py4)s = %(py2)s.stdout
}	stash_msgstash_list_resultre   rw   ry   u   stash list에 u   가 없음: r~   rh   r   r`   ra   )zh%(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.stdout
}.count
}(%(py6)s)
} >= %(py11)s)re   rw   ry   rh   rj   r   u&   stash ref가 최소 1개 있어야 함z
>assert %(py13)spy13r   r   	stash_refr   u0   _stash_whitelist가 None을 반환하면 안 됨rq   rr   )r   r>   r"   r   _stash_whitelistr<   r=   rP   stripr   r   r   r   r   r   r   r   r   count)r   r   rC   r   tracked_fileuntracked_filer   r  r  r   r   r   r   @py_assert8@py_assert7r   @py_format12r  r   r   @py_assert10@py_assert9@py_format14r   r   r   s                             r(   3test_integration_stash_whitelist_includes_untrackedz_TestPreFlightCheckStashIsolationIntegration.test_integration_stash_whitelist_includes_untracked  sU   +S];  +- 4wG "$66!!"4w!GCM:.	((M,-
	 #,H
 ##w#))w)+wrw+r1www+rwwwwww}www}www#www)www+wwwrwww5`anauau`v3wwwwwwww 'NN$H
 .44xy44xxxy4xxxxxxyxxxyxxxxxx-xxx-xxx4xxxykQ]^o^v^v]w6xxxxxxx ''b'--bdb-d3bqb3q8bbb3qbbbbbb bbb bbb'bbb-bbbdbbb3bbbqbbb:bbbbbbbb $Xy$XXXyXXXXXXyXXXyXXXXXX&XXXXXXXr*   c           
      p	   |j                  dt        |             t        t        |            }|dz  }|j                  dd       t	        j
                  g dt        |      dd	
       d}t	        j
                  ddddd|ddgt        |      dd	
       |dz  }|j                  dd       d}t	        j
                  ddddd|ddgt        |      dd	
       |j                  t        |      |      }	d}
|	|
u}|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}}
d}
|	|
k7  }|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z   d|iz  }t        t        j                  |            dx}}
t!        j"                  |d"      5  |j%                  t        |      |	|d#g       ddd       t	        j
                  g d$t        |      ddd	%      j&                  }||v}|st        j                  d&|fd'||f      d(t        j                         v st        j                  |      rt        j                  |      nd(d)t        j                         v st        j                  |      rt        j                  |      nd)d*z  }t        j                  d+      d,z   d-|iz  }t        t        j                  |            d}||v }|st        j                  d.|fd/||f      d0t        j                         v st        j                  |      rt        j                  |      nd0d)t        j                         v st        j                  |      rt        j                  |      nd)d*z  }t        j                  d1      d,z   d-|iz  }t        t        j                  |            d}y# 1 sw Y   xY w)2ug   다그다 stash 후 다른 봇 stash → _find_stash_by_msg가 다그다 stash의 정확한 ref 반환.r	   rH   dagda.pyzdagda = True
r   r   )r/   r8   r  TFr1   zpreflight-task-1001-dagdar/   rK   rL   rk   r;   rs   zother_bot.pyzother = True
zpreflight-task-2002-otherNr   r   	found_refr   u*   다그다 stash ref를 찾지 못함 (msg=)rq   rr   r   !=)z%(py0)s != %(py3)suM   다른 봇이 stash@{0}이므로 다그다는 stash@{0}이 아니어야 함: r   rE   r  r   r   )z%(py0)s not in %(py2)sdagda_stash_msg
stash_listrv   u%   다그다 stash가 drop 되어야 함rx   ry   rl   rt   other_stash_msgu$   다른 봇 stash는 잔존해야 함)r   r>   r   r"   r<   r=   r   r   r   r   r   r   r   r   r   r   r   r   r   rP   )r   r   rC   r   r   
dagda_filer  
other_filer  r  r   r   r   r   r  r   r   s                    r(   3test_integration_concurrent_stash_apply_correct_refz_TestPreFlightCheckStashIsolationIntegration.test_integration_concurrent_stash_apply_correct_ref  s   +S];CM: 
*
.A1s8}UYafg5GV%:D/SWYcdH		
 .
.A5GV%:D/SWYghH		
 **3x=/J	 $ey$eeeyeeeeeeyeeeyeeeeee(RSbRccd&eeeeeee'  	IyK'  	I  	I  	IyK  	I  	I  	I  	I  	I  	Iy  	I  	I  	Iy  	I  	I  	IK  	I  	I  	I+|  ~G  }H  *I  	I  	I  	I  	I  	I  	I \\#?@ 	`%%c(mYR]Q^_	`  ^^$H
 & 	 j0YYYjYYYYYYYYYYYYYYYjYYYjYYYY2YYYYYYY *,TTT*TTTTTTTTTTTTTTT*TTT*TTTT.TTTTTTT	` 	`s   R++R5c                 t   |j                  dt        |             t        t        |            }|dz  }|j                  dd       d}|j	                  t        |      |dg      }d}||u}	|	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z   d|
iz  }t        t        j                  |            dx}	}g }	|j                  } |       }| }|}|s|j                  } |       }d}||k7  }|}|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }|	j!                  |       |st        j                  dfdf      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      t        j                  |      dz  }dd|iz  }|	j!                  |       t        j"                  |	d      i z  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}	x}x}x}x}x}x}}t%        j&                  |d      5  |j)                  t        |      ||dg       ddd       t+        j,                  g dt        |      ddd !      j.                  }|j0                  }	 |	       }d"}||k(  }|st        j                  d#|fd$||f      d%t        j                         v st        j                  |      rt        j                  |      nd%t        j                  |	      t        j                  |      t        j                  |      d&z  }t        j                  d'|       d(z   d)|iz  }t        t        j                  |            dx}	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}	}|j                  }	 |	       }d}||k(  }|st        j                  d#|fd-||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |      t        j                  |      d&z  }t        j                  d.|j                                d(z   d)|iz  }t        t        j                  |            dx}	x}x}}y# 1 sw Y    xY w)/uB   stash → apply + drop → stash list 비어 있고 변경 복원.r	   rH   z	target.pyzx = 42
r   r   zpreflight-task-5000-apply-dropNr   r   r  r      stash_ref가 None이면 안 됨rq   rr   z>not %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s.exists
}()
}	test_file)rw   ry   rh   r  )zM%(py13)s
{%(py13)s = %(py11)s
{%(py11)s = %(py9)s.read_text
}()
} != %(py16)s)r   r   r  py16z%(py18)spy18r`   u;   stash 후 target.py가 이전 상태이거나 없어야 함z
>assert %(py21)spy21r   z	task-5000r  TFr   r   r   )zD%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.strip
}()
} == %(py7)sr  r   u9   stash apply+drop 후 stash list가 비어 있어야 함: r   r   u    target.py가 복원되어야 함zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r  )zH%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.read_text
}()
} == %(py7)su)   target.py 내용이 복원되어야 함: )r   r>   r   r"   r  r   r   r   r   r   r   r   r   r   exists	read_textr!   _format_boolopr   r   r   r<   r=   rP   r	  )r   r   rC   r   r   r"  r  r  r   r   r   r   r   r   r  r   r  @py_assert12@py_assert15@py_assert14r   @py_format17@py_format19@py_format20@py_format22r  r   r   r   s                                r(   )test_integration_apply_drop_removes_stashzUTestPreFlightCheckStashIsolationIntegration.test_integration_apply_drop_removes_stash  s   +S];CM: {*	Z':4	 ((X	K=Q	 $Gy$GGGyGGGGGGyGGGyGGGGGG&GGGGGGG	J9## 	J#% 	J%% 	J% 	J%)<)< 	J)<)> 	J* 	J)>*)L 	J 	JCI6	J 	J1I1I  	J 	J@I	  	J 	J@I	 $ 	J 	J@I	 & 	J 	JCI6	JCI & 	J8I8I	J)>* 	J 	JCI6	J 	J1I1I *3 	J 	J@I	 *3 	J 	J@I	 *= 	J 	J@I	 *? 	J 	J@I	 CM 	J 	J 	JCI6	JCI	J;I>	J 	J8I8II	J 	J 	J6I6I	J 	J 	J 	J \\#?@ 	Z%%c(mY	K=Y	Z  ^^$H
 & 	 q!qRq!R'qqq!Rqqqqqqzqqqzqqqqqq!qqqRqqq+deodp)qqqqqqqq E!E!EE#EEEEEEEyEEEyEEEEEE!EEEEEE""w"$w
w$
2www$
wwwwwwywwwywww"www$www
www6_`i`s`s`u_v4wwwwwwww	Z 	Zs   X--X7c           	      F   |j                  dt        |             t        t        |            }|dz  }|j                  dd       d}|j	                  t        |      |dg      }d}||u}	|	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z   d|
iz  }t        t        j                  |            dx}	}|j                  dd       g t        j                  |dfd      5  t        d      5  t!        j"                  t$              5  |j'                  t        |      ||dg       ddd       ddd       ddd       t)              }d}||k(  }|st        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  }t        j                  dt)               d      dz   d|iz  }t        t        j                  |            dx}x}}t+        j,                  g d t        |      d!d!d"#      j.                  }||v }	|	st        j                  d$|	fd%||f      d&t        j                         v st        j                  |      rt        j                  |      nd&d't        j                         v st        j                  |      rt        j                  |      nd'd(z  }t        j                  d)|       d*z   d+|iz  }t        t        j                  |            d}	y# 1 sw Y   exY w# 1 sw Y   jxY w# 1 sw Y   oxY w),uf   stash 후 같은 파일 변경 → apply 시 conflict → 알림 + PreFlightCheckError + stash 잔존.r	   rH   zconflict.pyzversion = 1
r   r   zpreflight-task-6000-conflictNr   r   r  r   r!  rq   rr   zversion = 999
r   c                  &    j                  |       S r   r   r   s     r(   r   zmTestPreFlightCheckStashIsolationIntegration.test_integration_apply_conflict_triggers_notify.<locals>.<lambda>I  s    ^j^q^qrs^t r*   rY   r   z	task-6000r`   r   r   rc   r   rd   uA   _notify_chairman_stash_failure가 1회 호출되어야 하는데 r   ri   rj   r  TFr   rl   rt   r  r  rv   u*   실패 시 stash ref가 잔존해야 함: rx   ry   )r   r>   r   r"   r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rc   r<   r=   rP   )r   r   rC   r   r   conflict_filer  r  r   r   r   r   r   r   r   r   r  r   r   r   s                      @r(   /test_integration_apply_conflict_triggers_notifyz[TestPreFlightCheckStashIsolationIntegration.test_integration_apply_conflict_triggers_notify5  sI   +S];CM: !=0  7 C2	((X	M?S	 $Gy$GGGyGGGGGGyGGGyGGGGGG&GGGGGGG 	  !2W E \\#?Mtu 	b|$ b]]#67 b--c(mY	T_S`abb	b <   	BA  	B A%  	B  	B  	B A  	B  	B  	B  	B  	B  	Bs  	B  	B  	Bs  	B  	B  	B  	B  	B  	B<  	B  	B  	B<  	B  	B  	B   	B  	B  	BA  	B  	B  	B)jkno{k|j}  ~A  (B  	B  	B  	B  	B  	B  	B  	B  ^^$H
 & 	 J&aaayJaaaaaayaaayaaaaaaJaaaJaaaa*TU_T`(aaaaaaab bb b	b 	bs<   P P	:O<P	!P<PP		P	PP N)	r   r   r   r   r   r  r  r0  r4  r   r*   r(   r   r     sd    7*YD *Y\` *Y`3UD 3U\` 3Ur$x$ $xRV $xT$b $bX\ $br*   r   )'r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   osr<   systimepathlibr   typingr   unittest.mockr   r   r   r   r   environr   	WORKSPACEr>   pathinsertSCRIPTS
auto_merger   r   r  r)   r,   r@   rB   fixturer   r   r   r*   r(   <module>rF     sV  0  	  
    6 6   02GHI	y>!HHOOAs9~&
i
w<sxxHHOOAs7|$ :d S  \` sw   }A 0D 3 4 
jd 
jt 
j$eU eUZ	 t 	$d*: ;  ~b ~br*   