
    >i?                     2   d Z ddlZddlZddlZddlZddlZddlZddlmZm	Z	m
Z
 ddlmZ ddlmZ ddlZdZej"                  j%                  dej&                  j)                  edd	            Zej"                  j-                  e      Zeej0                  d<   ej2                  j5                  e       ej6                  Zej8                  Zej:                  Zej<                  Zej"                  j%                  d
ej&                  j)                  edd            Zej"                  j-                  e      Z ej2                  j5                  e         ejB                  d      d        Z" ejB                         defd       Z# ejB                         defd       Z$ G d d      Z% G d d      Z& G d d      Z' G d d      Z( ejB                         d        Z) G d d      Z* G d d      Z+ G d d       Z, G d! d"      Z- G d# d$      Z. G d% d&      Z/y)'u   
tests/dev7/test_done_semantics.py

옵션 D 시맨틱 핵심 동작 검증 (task-2374)
Phase 마커 3개 (work-done / merged / merge-failed) × 시나리오 4개 매트릭스

카마소츠(개발7팀 테스터) 작성
    N)datetime	timedeltatimezone)Path)	MagicMockz-/home/jay/workspace/.worktrees/task-2374-dev7auto_merge_wtscriptszauto_merge.pydone_watcherzdone-watcher.pyT)autousec                 j    | dz  dz  }|j                  dd       |j                  t        d|       |S )u8   auto_merge._STATE_DIR을 tmp_path 기반으로 redirect.memorystateTparentsexist_ok
_STATE_DIR)mkdirsetattr
auto_merge)tmp_pathmonkeypatch	state_dirs      O/home/jay/workspace/.worktrees/task-2374-dev7/tests/dev7/test_done_semantics.py_patch_state_dirr   7   s;     8#g-IOOD4O0
L)<    returnc                 <    | dz  dz  }|j                  dd       |S )Nr   eventsTr   )r   )r   ds     r   
events_dirr    @   s&    8h&AGGD4G(Hr   c                     | dz  dz  S )Nr   r    )r   s    r   r   r   G   s    h((r   c                       e Zd Zd Zd Zd Zy)TestCreateMergedMarkerAtomicc                 X    t        |dd      }||dz  k(  sJ |j                         sJ y)u?   _create_merged_marker가 .merged 파일을 atomic 생성한다.	task-9999abc123merge_commit_shatask-9999.mergedN)_create_merged_markerexistsselfr    r   results       r   test_creates_merged_filez5TestCreateMergedMarkerAtomic.test_creates_merged_fileQ   s3    &z;QYZ&88888}}r   c                     t        |dd       t        j                  |dz  j                  d            }|d   dk(  sJ |d   dk(  sJ d	|v sJ y
)u>   .merged 파일 내용에 task_id / merge_commit_sha가 있다.r&   r'   r(   r*   utf-8encodingtask_idr)   	merged_atN)r+   jsonloads	read_textr.   r    r   datas       r   !test_merged_file_has_correct_jsonz>TestCreateMergedMarkerAtomic.test_merged_file_has_correct_jsonX   sd    j+Qzz:(::EEwEWXI+---&'8333d"""r   c                     t        |dd       t        j                  dz  }|j                         sJ t	        j
                  |j                  d            }|d   dk(  sJ |d	   dk(  sJ y
)u.   state.json의 phase가 merged로 갱신된다.r&   r'   r(   task-9999.jsonr2   r3   phasemergedr)   N)r+   r   r   r,   r7   r8   r9   )r.   r    r   
state_filer   s        r   test_state_json_phase_mergedz9TestCreateMergedMarkerAtomic.test_state_json_phase_mergeda   ss    j+Q**-==
  """

:///ABW~)))'(H444r   N)__name__
__module____qualname__r0   r<   rB   r"   r   r   r$   r$   P   s    #5r   r$   c                       e Zd Zd Zy) TestCreateMergedMarkerIdempotentc                     |dz  }|j                  t        j                  ddd      d       t        |dd      }t        j                  |j                  d            }|j                  d	      du sJ ||k(  sJ y
)u9   이미 .merged가 있으면 no-op (기존 파일 유지).r*   r&   T)r5   originalr2   r3   new_shar(   rI   N)
write_textr7   dumpsr+   r8   r9   get)r.   r    r   r@   r/   r;   s         r   test_no_op_when_already_existsz?TestCreateMergedMarkerIdempotent.test_no_op_when_already_existsm   s    00$**$%OP[bc&z;QZ[zz&**G*<=xx
#t+++r   N)rC   rD   rE   rN   r"   r   r   rG   rG   l   s    	 r   rG   c                       e Zd Zd Zd Zd Zy)TestBumpRetryCounterc                 v    t        |d      }|dk(  sJ t        |d      }|dk(  sJ t        |d      }|dk(  sJ y)u6   retry_count가 0 → 1 → 2 → 3으로 증가한다.r&            N)_bump_retry_counter)r.   r    r   count1count2count3s         r   test_increments_from_zeroz.TestBumpRetryCounter.test_increments_from_zeroz   sJ    $Z={{$Z={{$Z={{r   c                     t        |d       |dz  }|j                         sJ |j                  d      j                         dk(  sJ y)u'   .retry_count 파일도 동기화된다.r&   ztask-9999.retry_countr2   r3   1N)rU   r,   r9   strip)r.   r    r   
retry_files       r   test_retry_count_file_syncedz1TestBumpRetryCounter.test_retry_count_file_synced   sN    J4"99
  """##W#5;;=DDDr   c                     t        |dd       t        j                  t        j                  dz  j                  d            }|d   dk(  sJ y)	u)   last_error가 state.json에 저장된다.r&   zmerge conflict)
last_errorr>   r2   r3   r`   N)rU   r7   r8   r   r   r9   r.   r    r   r   s       r   test_last_error_storedz+TestBumpRetryCounter.test_last_error_stored   sL    J@PQ

J114DDOOY`Oab\"&6666r   N)rC   rD   rE   rY   r^   rb   r"   r   r   rP   rP   y   s    	E7r   rP   c                       e Zd Zd Zd Zd Zy)TestCreateMergeFailedMarkerc                 Z    t        |ddd      }||dz  k(  sJ |j                         sJ y)u,   .merge-failed 파일이 atomic 생성된다.r&      zbranch conflictretry_countr`   task-9999.merge-failedN)_create_merge_failed_markerr,   r-   s       r   test_creates_merge_failed_filez:TestCreateMergeFailedMarker.test_creates_merge_failed_file   s:    ,?P
 &>>>>>}}r   c                     t        |ddd       t        j                  |dz  j                  d            }|d   dk(  sJ |d	   dk(  sJ d
|v sJ y)u.   .merge-failed JSON에 필수 필드가 있다.r&   rf   errrg   ri   r2   r3   r5   rh   	failed_atN)rj   r7   r8   r9   r:   s       r   test_merge_failed_json_contentz:TestCreateMergeFailedMarker.test_merge_failed_json_content   sg    #JW\]zz:(@@KKU\K]^I+---M"a'''d"""r   c                     t        |ddd       t        j                  t        j                  dz  j                  d            }|d   d	k(  sJ y
)u4   state.json의 phase가 merge-failed로 갱신된다.r&   rf   rm   rg   r>   r2   r3   r?   merge-failedN)rj   r7   r8   r   r   r9   ra   s       r   "test_state_json_phase_merge_failedz>TestCreateMergeFailedMarker.test_state_json_phase_merge_failed   sL    #JW\]

J114DDOOY`OabW~///r   N)rC   rD   rE   rk   ro   rr   r"   r   r   rd   rd      s    #0r   rd   c                     | dz  dz  }| dz  dz  }|j                  dd       |j                  dd       |j                  t        d|       |j                  t        d|       ||fS )uI   done-watcher의 EVENTS_DIR / STATE_DIR을 tmp_path 기반으로 redirect.r   r   r   Tr   
EVENTS_DIR	STATE_DIR)r   r   r
   )r   r   r   r   s       r   patched_dw_dirsrv      sp      8+Fx')E
LLL-	KKtK,lF;k595=r   c                       e Zd Zd Zd Zy)$TestReconcileMetaTaskImmediateMergedc                 *   |\  }}ddddd}|dz  j                  t        j                  |      d       |d	z  }|j                  t        j                  d
di      d       t        j	                  |      }|dk(  sJ |dz  j                         sJ y)uI   kind=meta + merge_required=False → .merged 즉시 생성 (system-done).z	task-9001z	work-donemetaF)r5   r?   kindmerge_requiredztask-9001.jsonr2   r3   ztask-9001.work-doner5   system-doneztask-9001.mergedNrK   r7   rL   r
   reconcile_work_doner,   r.   rv   r   r   r   
state_datawdr/   s           r   )test_meta_kind_creates_merged_immediatelyzNTestReconcileMetaTaskImmediateMerged.test_meta_kind_creates_merged_immediately   s    ' # #	

 
!	!--djj.Dw-W ++
djj)[!9:WM11"5&&&++33555r   c                 (   |\  }}dddd}|dz  j                  t        j                  |      d       |dz  }|j                  t        j                  d	di      d       t        j	                  |      }|d
k(  sJ |dz  j                         sJ y)u;   merge_required=False (kind=code) → .merged 즉시 생성.z	task-9002codeF)r5   r{   r|   ztask-9002.jsonr2   r3   ztask-9002.work-doner5   r}   ztask-9002.mergedNr~   r   s           r   4test_merge_required_false_creates_merged_immediatelyzYTestReconcileMetaTaskImmediateMerged.test_merge_required_false_creates_merged_immediately   s    ' ##


 
!	!--djj.Dw-W++
djj)[!9:WM11"5&&&++33555r   N)rC   rD   rE   r   r   r"   r   r   rx   rx      s    6,6r   rx   c                       e Zd Zd Zy)TestReconcilePollingNoMergedYetc                   	 |\  }}ddddd}|dz  j                  t        j                  |      d       |d	z  }|j                  t        j                  ddd
      d       t               	d	_        t        j                  ddd      	_        d	_        |j                  t        d	fd       t        j                  |      }|dk(  sJ |dz  j                         rJ y)uG   gh pr view mergedAt=null → polling 상태 유지 (.merged 미생성).z	task-9003r   Tz$https://github.com/test/repo/pull/42r5   r{   r|   pr_urlztask-9003.jsonr2   r3   ztask-9003.work-doner5   r   r   NmergedAtmergeCommit runc                      S Nr"   akwmock_results     r   <lambda>zYTestReconcilePollingNoMergedYet.test_gh_pr_no_merged_at_returns_polling.<locals>.<lambda>        r   pollingztask-9003.merged)rK   r7   rL   r   
returncodestdoutstderrr   
subprocessr
   r   r,   )
r.   rv   r   r   r   r   r   r   r/   r   s
            @r   'test_gh_pr_no_merged_at_returns_pollingzGTestReconcilePollingNoMergedYet.test_gh_pr_no_merged_at_returns_polling   s    ' #"<	

 
!	!--djj.Dw-W++
djj[Dj!klw~  k!"!ZZT$(OPJ/KL11"5"""//779999r   N)rC   rD   rE   r   r"   r   r   r   r      s    :r   r   c                       e Zd Zd Zy)TestReconcileMergedObservedc                 2   |\  }}ddddd}|dz  j                  t        j                  |      d       |d	z  }|j                  t        j                  ddd
      d       t               d_        t        j                  dddid      _        d_        |j                  t        dfd       t        j                  |      }|dk(  sJ |dz  }	|	j                         sJ t        j                  |	j                  d            }
|
d   dk(  sJ y)u4   gh pr view mergedAt!=null → atomic .merged 생성.z	task-9004r   Tz$https://github.com/test/repo/pull/43r   ztask-9004.jsonr2   r3   ztask-9004.work-doner   r   z2026-05-02T10:00:00Zoiddeadbeef123r   r   r   c                      S r   r"   r   s     r   r   zXTestReconcileMergedObserved.test_gh_pr_merged_at_creates_merged_marker.<locals>.<lambda>  r   r   r@   ztask-9004.mergedr)   N)rK   r7   rL   r   r   r   r   r   r   r
   r   r,   r8   r9   )r.   rv   r   r   r   r   r   r   r/   merged_filer;   r   s              @r   *test_gh_pr_merged_at_creates_merged_markerzFTestReconcileMergedObserved.test_gh_pr_merged_at_creates_merged_marker	  s(   ' #"<	

 
!	!--djj.Dw-W++
djj[Dj!klw~k!"!ZZ.!=1)
   J/KL11"5!!!11!!###zz+///AB&'=888r   N)rC   rD   rE   r   r"   r   r   r   r     s    9r   r   c                       e Zd Zd Zy)!TestReconcile30MinTimeoutToFailedc                    |\  }}dddddd}|dz  j                  t        j                  |      d	       |d
z  }|j                  t        j                  ddd      d	       t        j                         dz
  }t	        j
                  |||f       t               d_        t        j                  ddd      _        d_	        |j                  t        dfd       t        j                  |      }	|	dk(  sJ |dz  }
|
j                         sJ t        j                  |
j!                  d	            }d|j#                  dd      j%                         v sJ y)uC   work-done mtime 31분 전 + mergedAt null → .merge-failed 생성.z	task-9005r   Tz$https://github.com/test/repo/pull/44r   )r5   r{   r|   r   rh   ztask-9005.jsonr2   r3   ztask-9005.work-doner   iD  Nr   r   r   c                      S r   r"   r   s     r   r   zgTestReconcile30MinTimeoutToFailed.test_work_done_mtime_31min_ago_creates_merge_failed.<locals>.<lambda>D  r   r   rq   ztask-9005.merge-failedtimeoutr`   )rK   r7   rL   timeosutimer   r   r   r   r   r   r
   r   r,   r8   r9   rM   lower)r.   rv   r   r   r   r   r   r   	past_timer/   failed_filer;   r   s               @r   3test_work_done_mtime_31min_ago_creates_merge_failedzUTestReconcile30MinTimeoutToFailed.test_work_done_mtime_31min_ago_creates_merge_failed+  sS   ' #"<

 
!	!--djj.Dw-W++
djj[Dj!klw~ IIK7+	
i+,  k!"!ZZT$(OPJ/KL11"5'''77!!###zz+///ABDHH\26<<>>>>r   N)rC   rD   rE   r   r"   r   r   r   r   *  s    !?r   r   c                       e Zd Zd Zy) TestFinishTaskCreatesDoneSymlinkc           	      2   |dz  dz  }|j                  dd       |dz  }ddt        j                  t        t	        d	                  j                  d
      d}|j                  t        j                  |      d       |dz  }|j                  |       |j                         sJ |j                         sJ |j                         |j                         k(  sJ t        j                  |j                  d            }|d   dk(  sJ |d   dk(  sJ y)ub   .work-done 생성 후 .done이 symlink로 .work-done을 가리키는 케이스를 시뮬레이트.r   r   Tr   ztask-9999.work-doner&   	work_done	   )hoursz%Y-%m-%dT%H:%M:%S+09:00)r5   status
created_atr2   r3   ztask-9999.doner5   r   N)r   r   nowr   r   strftimerK   r7   rL   
symlink_tor,   
is_symlinkresolver8   r9   )r.   r   r   r   payload	done_link	done_datas          r   %test_done_symlink_points_to_work_donezFTestFinishTaskCreatesDoneSymlink.test_done_symlink_points_to_work_doneT  s    H$x/TD1 22	"!",,x	0B'CDMMNgh

 	TZZ07C --	Y' !!!##%%%  "i&7&7&9999 JJy22G2DE	#{222"k111r   N)rC   rD   rE   r   r"   r   r   r   r   S  s    2r   r   c                       e Zd Zd Zd Zy)TestAtomicWriteNoPartialFilec                 d   ddl }|dz  }ddd}|j                  t        |j                        d|j                   dd	      \  }}t        |      }	 t        j                  |d
d      5 }t        j                  ||dd       ddd       |j                         rJ t        j                  ||       |j                         sJ |j                         rJ t        j                  |j                  d            }	|	|k(  sJ y# 1 sw Y   xY w# t        $ r |j                  d        w xY w)uC   tempfile + rename 패턴이 partial file을 노출하지 않는다.r   Nzatomic_test.jsonvalue*   )keynumber.z.tmp)dirprefixsuffixwr2   r3   FrS   )ensure_asciiindentT)
missing_ok)tempfilemkstempstrparentnamer   r   fdopenr7   dumpr,   rename	Exceptionunlinkr8   r9   )
r.   r   r   targetr;   fdtmp_path_strtmp_filefloadeds
             r   )test_atomic_write_no_partial_file_exposedzFTestAtomicWriteNoPartialFile.test_atomic_write_no_partial_file_exposedv  s+   .."- $++FMM"v{{m1% , 
L
 %
	2sW5 A		$a@A }}&&IIlF+ }}??$$$F,,g,>?~~A A  	OOtO,	s$   D +D0D DD D/c                     |dz  }ddd}t         j                  ||       |j                         sJ t        j                  |j                  d            }|d   dk(  sJ |d   dk(  sJ y	)
uC   auto_merge._atomic_write_json이 올바르게 JSON을 기록한다.ztest_output.jsonr&   r@   )r5   r?   r2   r3   r5   r?   N)r   _atomic_write_jsonr,   r7   r8   r9   )r.   r   r   r   r   s        r   !test_auto_merge_atomic_write_jsonz>TestAtomicWriteNoPartialFile.test_auto_merge_atomic_write_json  sv    ..)H=%%fg6}}F,,g,>?i K///g(***r   N)rC   rD   rE   r   r   r"   r   r   r   r   u  s    B
+r   r   )0__doc__importlib.util	importlibr7   r   r   sysr   r   r   r   pathlibr   unittest.mockr   pytestWORKTREEutilspec_from_file_locationpathjoin_am_specmodule_from_specr   modulesloaderexec_moduler+   rj   rU   _update_state_json_dw_specr
   fixturer   r    r   r$   rG   rP   rd   rv   rx   r   r   r   r   r   r"   r   r   <module>r      s	     	  
  2 2  #  ; >>11GGLL9o6 ^^,,X6
)O    J '"88 (DD  44 22  >>11GGLL9&78 ~~..x8   L )   D   )4 ) )5 58
  
 7 780 0>  (6 (6V: ::9 9D"? "?R2 2D,+ ,+r   