
    i`                    N   d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
mZ ddlZddlmZmZmZ  ee      j%                         j&                  d   dz  dz  d	z  d
z  Zg dZddZej.                  j1                  de      dd       Zd Zd Zd Zd Zd Zd Zd Z d Z!d Z"d Z#y)u   tests/poc/test_termination_classifier.py

Phase B 종료 분류 classifier 회귀 테스트.

⚠️ Dry-run only. 마커 파일 생성/dispatch 호출 검증 안 함 (classifier가 안 한다는 사실 자체를 테스트).
    )annotationsN)Path)TaskEvidenceTerminationClassificationclassify   memorypoctermination_classifierfixtures)ztask-2466.jsonztask-2481.jsonztask-2472+1.jsonztask-2483.jsonztask-2485.jsonc                   t         | 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}}|j                         5 }t        j                  |      cd d d        S # 1 sw Y   y xY w)Nzfixture not found: zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}path)py0py2py4)FIXTURES_DIRexists
@pytest_ar_format_assertmsg@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationopenjsonload)filenamer   @py_assert1@py_assert3@py_format5fs         </home/jay/workspace/tests/poc/test_termination_classifier.py_load_fixturer%   !   s    ("D;;6;=6=66/v666666646664666;666=666666	 yy|  s   C88Dfixture_namec           
        t        |       }|j                  d      }t        j                  j	                         }|j                         D ci c]  \  }}||v s|| }}}t        di |}t        |      }|j                  }	|	j                  }
|
|k(  }|s.t        j                  d|fd|
|f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  |  d| d|j                  j                   d	|j                    d
      dz   d|iz  }t#        t        j$                  |            dx}	x}
}yc c}}w )uQ   각 회귀 fixture의 expected_classification과 classifier 출력 일치 확인.expected_classification==)zQ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.classification
}.value
} == %(py6)sresultexpectedr   r   r   py6z: expected z, got z (rule=)z
>assert %(py8)spy8N )r%   popr   __dataclass_fields__keysitemsr   classificationvaluer   _call_reprcomparer   r   r   r   r   matched_ruler   r   )r&   datar,   
valid_keyskvevidence_kwargsevidencer+   r    r!   @py_assert5@py_format7@py_format9s                 r$   $test_classification_matches_expectedrC   (   s    &Dxx12H22779J(,

H1Zq!tHOH.o.HhF    && &(2   &(              !    '      +3    +3    .H:VF4I4I4O4O3P Q$$%Q	(      Is   GGc                 
   t        dddddd      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}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                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u   직접 입력 케이스: DONE.z	test-doneMERGED2026-05-06T10:00:00ZCLEANPASSr   )task_idpr_statepr_merged_atclose_lifecycle_stateessence_verdictforbidden_violationsr)   zP%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.DONE
}rr   r-   assert %(py8)sr0   N.donez3%(py2)s
{%(py2)s = %(py0)s.marker_file
} == %(py5)sr   r   py5assert %(py7)spy7)r   r   r6   r   DONEr   r8   r   r   r   r   r   r   marker_file
evrP   r    r@   r!   rA   rB   @py_assert4@py_format6@py_format8s
             r$   test_done_evidencer_   :   s3   	+%
B 	A=8===============1===1=========8===8=============#G#=G####=G######1###1###=###G#######    c                     t        d      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}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                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}y
)u&   모든 룰 미해당 시 UNCLASSIFIED.ztest-unknown)rI   r)   )zX%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.UNCLASSIFIED
}rP   r   r-   rQ   r0   Ng        )z2%(py2)s
{%(py2)s = %(py0)s.confidence
} == %(py5)srT   rV   rW   )r   r   r6   r   UNCLASSIFIEDr   r8   r   r   r   r   r   r   
confidencerZ   s
             r$   test_unclassified_when_no_matchrd   I   s   	n	-BAE8EEEEEEEEEEEEEEE1EEE1EEEEEEEEE8EEE8EEEEEEEEEEE<<3<3<311<3r`   c                    t        ddddddddd	      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u:   DONE이 DOGFOODING_PENDING보다 우선 (close clean 시).ztest-priorityrE   rF   rG   rH   r   T)	rI   rJ   rK   rL   rM   	ci_rollup	qc_resultrN   dogfooding_external_dependencyr)   rO   rP   r   r-   rQ   r0   N)r   r   r6   r   rX   r   r8   r   r   r   r   r   r   r[   rP   r    r@   r!   rA   rB   s          r$   "test_priority_done_over_dogfoodingrj   Q   s    	+%'+

B 	A=8===============1===1=========8===8===========r`   c           	     $   |j                  |        t        ddd      }t        |       | j                  } |       }t	        |      }g }||k(  }|s4t        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                  |      t        j                  |      t        j                  |      d	z  }t        j                  d
      dz   d|iz  }	t        t        j                  |	            dx}x}x}x}}y)u:   Classifier가 마커 파일 생성하지 않음을 보장.ztest-side-effectsrE   rG   )rI   rJ   rL   r)   )ze%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.iterdir
}()
})
} == %(py10)slisttmp_path)r   py1py3rU   rW   py10u7   classifier가 부수효과 발생시킴 (파일 생성)z
>assert %(py12)spy12N)chdirr   r   iterdirrl   r   r8   r   r   r   r   r   r   r   )
rm   monkeypatchr[   @py_assert2r\   @py_assert6@py_assert9@py_assert8@py_format11@py_format13s
             r$   test_dry_run_no_side_effectsr{   b   s     h	#%
B
 RL  d "d4"#drd#r)ddd#rdddddd4ddd4dddddddddddd ddd"ddd#dddrddd+ddddddddr`   c            	        t        ddddddd      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d}|j                  d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }t        t        j                  |            dx}x}}y)uO   MERGE_PENDING_DEPENDENCY 직접 케이스: PR OPEN + essence PASS + dependency.ztest-merge-pendingOPENrH   ztask-2472+2FAIL      )rI   rJ   rM   dependency_task_idrf   retry_count	retry_maxr)   )zd%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.MERGE_PENDING_DEPENDENCY
}rP   r   r-   rQ   r0   Nr   in)z%(py1)s in %(py4)s)rn   r   zassert %(py6)sr.   )r   r   r6   r   MERGE_PENDING_DEPENDENCYr   r8   r   r   r   r   r   r   followup_conditions)
r[   rP   r    r@   r!   rA   rB   @py_assert0ru   r"   s
             r$   $test_merge_pending_dependency_directr   t   s!   	$(
B 	AQ8QQQQQQQQQQQQQQQ1QQQ1QQQQQQQQQ8QQQ8QQQQQQQQQQQ4A11!44=44444=4444=44444444444r`   c                 ,   t        ddddddddd		      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}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                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d}
|j                  }|
|v }|st        j                  d|fd|
|f      t        j                  |
      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}
x}}d}
|j                  }|
|v }|st        j                  d|fd|
|f      t        j                  |
      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}
x}}y)uZ   MERGED_CLOSE_BLOCKED_EXTERNAL 직접 케이스: PR MERGED + close FAIL + external blocker.ztest-close-blockedrE   z2026-05-07T13:54:33ZrH   r~   externalr   F)	rI   rJ   rK   rf   rL   close_blocker_ownerrN   admin_override_usedbranch_protection_bypassr)   )zi%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.MERGED_CLOSE_BLOCKED_EXTERNAL
}rP   r   r-   rQ   r0   Nz.close-blocked-externalrS   rT   rV   rW   rR   r   )z8%(py1)s in %(py5)s
{%(py5)s = %(py3)s.preserve_markers
})rn   ro   rU   	.escalate)r   r   r6   r   MERGED_CLOSE_BLOCKED_EXTERNALr   r8   r   r   r   r   r   r   rY   preserve_markers)r[   rP   r    r@   r!   rA   rB   r\   r]   r^   r   ru   s               r$   )test_merged_close_blocked_external_directr      s$   	$+$&!!&

B 	AV8VVVVVVVVVVVVVVV1VVV1VVVVVVVVV8VVV8VVVVVVVVVVV==555=55555=555555515551555=55555555555(a(((7(((((7((((7((((((a(((a(((((((((((,!,,,;,,,,,;,,,,;,,,,,,!,,,!,,,,,,,,,,,r`   c            	     r   t        ddddddd      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}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                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }
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                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}x}}y)uY   DOGFOODING_PENDING 직접 케이스: PR MERGED + essence PASS + dogfooding 외부 의존.ztest-dogfoodingrE   rH   WARNNOT_RUNT)rI   rJ   rM   rf   rg   rL   rh   r)   )z^%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.DOGFOODING_PENDING
}rP   r   r-   rQ   r0   Nz.dogfooding-pendingrS   rT   rV   rW   r   )zY%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.followup_conditions
})
} == %(py8)slen)r   rn   ro   rU   r0   zassert %(py10)srp   )r   r   r6   r   DOGFOODING_PENDINGr   r8   r   r   r   r   r   r   rY   r   r   )r[   rP   r    r@   r!   rA   rB   r\   r]   r^   ru   @py_assert7rv   ry   s                 r$   test_dogfooding_pending_directr      s   	!''+
B 	AK8KKKKKKKKKKKKKKK1KKK1KKKKKKKKK8KKK8KKKKKKKKKKK==111=11111=111111111111111=11111111111$$*3$%**%****%******3***3******q***q***$***%**********r`   c                 v   t        dddd      } t        |       }|j                  }t        j                  }||k7  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}y)uH   PR OPEN이지만 essence PASS 없으면 MERGE_PENDING_DEPENDENCY 아님.ztest-open-no-passr}   Nz	task-9999)rI   rJ   rM   r   !=)zd%(py2)s
{%(py2)s = %(py0)s.classification
} != %(py6)s
{%(py6)s = %(py4)s.MERGE_PENDING_DEPENDENCY
}rP   r   r-   rQ   r0   )r   r   r6   r   r   r   r8   r   r   r   r   r   r   ri   s          r$   6test_merge_pending_open_no_essence_pass_not_classifiedr      s    	#&	
B 	AQ8QQQQQQQQQQQQQQQ1QQQ1QQQQQQQQQ8QQQ8QQQQQQQQQQQr`   c                 z   t        dddddd      } t        |       }|j                  }t        j                  }||k7  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)uP   close_blocker_owner == 'internal'이면 MERGED_CLOSE_BLOCKED_EXTERNAL 미해당.ztest-internal-blockerrE   z2026-05-07T00:00:00Zr~   internalr   )rI   rJ   rK   rL   r   rN   r   )zi%(py2)s
{%(py2)s = %(py0)s.classification
} != %(py6)s
{%(py6)s = %(py4)s.MERGED_CLOSE_BLOCKED_EXTERNAL
}rP   r   r-   rQ   r0   N)r   r   r6   r   r   r   r8   r   r   r   r   r   r   ri   s          r$   =test_merged_close_blocked_not_triggered_with_internal_blockerr      s    	'+$&
B 	AV8VVVVVVVVVVVVVVV1VVV1VVVVVVVVV8VVV8VVVVVVVVVVVr`   c                    t        dddd      } t        |       }|j                  }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}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                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u'   ESCALATED: retry 초과 + essence FAIL.ztest-escalated   r   r~   )rI   r   r   rM   r)   )zU%(py2)s
{%(py2)s = %(py0)s.classification
} == %(py6)s
{%(py6)s = %(py4)s.ESCALATED
}rP   r   r-   rQ   r0   Nr   rS   rT   rV   rW   )r   r   r6   r   	ESCALATEDr   r8   r   r   r   r   r   r   rY   rZ   s
             r$   test_escalated_ruler      s-   	 	
B 	AB8BBBBBBBBBBBBBBB1BBB1BBBBBBBBB8BBB8BBBBBBBBBBB=='K'=K''''=K''''''1'''1'''='''K'''''''r`   )r   strreturndict)r&   r   )$__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   pathlibr   pytest tools.poc.termination_classifierr   r   r   __file__resolveparentsr   FIXTURE_FILESr%   markparametrizerC   r_   rd   rj   r{   r   r   r   r   r   r   r1   r`   r$   <module>r      s    #       H~%%'//2X=EH``cmm 7 8"$>"e$5 -(+"	RW
(r`   