
    wj&                        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 ddlmZmZmZmZ ddlmZ dZd	ed
ddZd Zd Zd Zd Zd Zd Zd Zd Z d Z!y)u  task-2554+2 §5 신규 fixture #2: PR #105 fresh medium 3건 §1~§2 충족 어셀션.

회장 §명시 (2026-05-12) Gemini fresh medium 3건 (false-positive 아님, §1~§2 직접 일치):
  1. ``_iter_rows`` O(N) full scan → §2 bounded/reverse scan 으로 해결
  2. ``RESULT_PENDING`` import 누락 → §1 import 추가
  3. ``http_post`` 직전 PENDING 미기록 → §1 fail-closed crash-safety 추가

본 fixture 는 위 3건이 모두 충족됨을 정적/동적 양쪽으로 어셀션.
    )annotationsN)Path)owner_trigger_auditowner_trigger_only)DEDUPE_SCAN_MAX_ROWSOwnerTriggerAuditRESULT_PENDINGRESULT_POSTED)OwnerTriggerOnly(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai   prheadc               x    dd||dddddddd}| d	z  }|j                  t        j                  |      d
       |S )Nz anu_v2.owner_trigger_decision.v1ztask-2554+2-testTFr   "POST_GEMINI_REVIEW_TRIGGER_COMMENTz/gemini review)schematask_idr   current_head
queue_headcurrent_head_confirmedgemini_evidence_freshnudge_count_for_pr_headallowed_actioncomment_bodyallowedzdecision.jsonzutf-8)encoding)
write_textjsondumps)tmp_pathr   r   decisionps        O/home/jay/workspace/anu_v2/tests/test_owner_trigger_fresh_medium_3_2554plus1.py_write_decisionr%      sT    4%"&!&#$>(H 	?"ALLH%L8H    c                    t         j                  } d}t        | |      }|sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      dz  }t        t        j                  |            dx} x}}d} t        | k(  }|st        j                  d|fd	t        | f      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}} y)uc   ``_iter_rows_reverse`` 가 audit 모듈에 존재하고 max_rows 기본값이 DEDUPE_SCAN_MAX_ROWS._iter_rows_reversez\assert %(py7)s
{%(py7)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.OwnerTriggerAudit
}, %(py5)s)
}hasattrr   )py0py1py3py5py7Ni   ==z%(py0)s == %(py3)sr   r*   r,   assert %(py5)sr-   )r   r   r)   @py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprAssertionError_format_explanationr   _call_reprcompare)@py_assert2@py_assert4@py_assert6@py_format8@py_assert1@py_format4@py_format6s          r$   0test_iter_rows_reverse_method_exists_and_boundedrC   5   s    &88O:NO78:NOOOOOOOO7OOO7OOOOOO&OOO&OOO8OOO:NOOOOOOOOOO#&&3&&&&3&&&&&&&&&&&&3&&&&&&&r&   c                   t        |       }|j                          ddt        dddddddddddd	dddg}|D ]  }|j                  |        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   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}|
|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}|
|k(  }|slt        j                  d
|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y)u-   audit JSONL 끝 행부터 역순으로 yield.r      POSTEDFactionr   r   resulttoken_value_logged   (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb   (ccccccccccccccccccccccccccccccccccccccccr/   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenreversed_rowsr*   r+   r,   py6assert %(py8)spy8Nr   r   z%(py1)s == %(py4)sr+   py4assert %(py6)srS   )r   _ensure_parent_HEAD_Aappendlistr(   rP   r6   r;   r4   r5   r7   r8   r9   r:   )r!   auditrowsrrQ   r<   @py_assert5r=   @py_format7@py_format9@py_assert0@py_assert3@py_format5s                r$   *test_iter_rows_reverse_yields_newest_firstrg   <   s   h'E	7q'5	:7q(5	:7q(5	:D  Q1134M}""""""""""""3"""3""""""}"""}"""""""""""""D!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&D!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&D!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&r&   c           	        t        |       }|j                          t        d      D ]  }|j                  d|dz   |dddd         t	        |j                  d	            }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 	cg c]  }	|	d   	 }
}	g 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c c}	w )u0   max_rows 한정 시 그 이상은 yield 안 함.
   r   rE   040xrF   FrG   rM   )max_rowsr/   rO   rP   samplerR   rT   rU   Nr   )ri   	      rV   rW   rY   rS   )r   rZ   ranger\   r]   r(   rP   r6   r;   r4   r5   r7   r8   r9   r:   )r!   r^   irl   r<   ra   r=   rb   rc   r`   rd   re   rf   s                r$   (test_iter_rows_reverse_respects_max_rowsrq   R   s[   h'E	2Y :a%h"'
 	 %**A*67Fv;!;!;!33vv;!#$AdG$2$
2$
2222$
222$222
2222222$s   7Hc           	        t        |       }|j                          t        d      D ]  }|j                  dd|dddd        dd}|j                  }d} |||	      }d
}||u }|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t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}ddl}|j                  t        j                   j                        }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                  t        j                   j"                        }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)u>  ``_has_posted`` / ``_has_active_trigger`` 가 ``_iter_rows`` 가 아닌 reverse scan 을 사용.

    동작 어셀션: 작은 audit 에서도 결과가 같고, 파일이 거대해질 때 dedupe 비용이 일정.
    검증: 정적 (소스 코드 grep) + 동적 (큰 파일에서 마지막 1행이 dedupe 결정).
       r   i  rj   rF   FrG      r   Tis)zc%(py7)s
{%(py7)s = %(py2)s
{%(py2)s = %(py0)s._has_posted
}(pr=%(py4)s, head=%(py5)s)
} is %(py10)sr^   	last_head)r*   py2rX   r-   r.   py10zassert %(py12)spy12Nr   r(   )in)z%(py1)s in %(py3)ssource)r+   r,   z)_has_posted must use bounded reverse scanz
>assert %(py5)sr-   source_activez1_has_active_trigger must use bounded reverse scan)r   rZ   ro   r\   _has_postedr6   r;   r4   r5   r7   r8   r9   r:   inspect	getsourcer   _format_assertmsg_has_active_trigger)r!   r^   rp   rw   r@   re   r>   @py_assert9@py_assert8@py_format11@py_format13r   r|   rd   r<   rA   rB   r}   s                     r$   +test_dedupe_uses_bounded_scan_not_full_filer   d   s#    h'E	3Z :h"'
 	 t*I<<)4<<4<<<<4<<<<<<5<<<5<<<<<<<<<<<<)<<<)<<<4<<<<<<<<<<2DDPPQFV6)VVV6VVVVVVVVV6VVV6VVVV+VVVVVVV%%&9&K&K&_&_`Me=0eee=eeeeeeeee=eee=eeee2eeeeeeer&   c                    d} t        t        |       }|sddt        j                         v st	        j
                  t               rt	        j                  t               nddt        j                         v st	        j
                  t              rt	        j                  t              ndt	        j                  |       t	        j                  |      dz  }t        t	        j                  |            dx} }t        j                  }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                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}y)uP   ``RESULT_PENDING`` 가 owner_trigger_only 모듈에 import 되어 있어야 함.r	   z5assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}r)   r   )r*   r+   r,   r-   NPENDINGr/   )z6%(py2)s
{%(py2)s = %(py0)s.RESULT_PENDING
} == %(py5)s)r*   rx   r-   zassert %(py7)sr.   )r)   r   r4   r5   r6   r7   r8   r9   r:   r	   r;   )r<   r=   rB   r@   re   r?   s         r$   2test_result_pending_imported_in_owner_trigger_onlyr      s    '787%'78888888878887888888%888%888'78888888888,,9	9,	9999,	999999999999,999	9999999r&   c                    d} t         | k(  }|st        j                  d|fdt         | f      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}} g }t        j                   }|t         u }|}|st        j                   }|t         k(  }|}|st        j                  d	|fd
|t         f      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dz  }	dd|	iz  }
|j                  |
       |st        j                  dfdt         f      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dz  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}}y)u1   audit 모듈의 RESULT_PENDING 과 동일 상수.r   r/   r1   r	   r2   r3   r-   Nru   )z6%(py4)s
{%(py4)s = %(py2)s.RESULT_PENDING
} is %(py6)sr   )rx   rX   rS   z%(py8)srU   )z:%(py12)s
{%(py12)s = %(py10)s.RESULT_PENDING
} == %(py14)s)ry   rz   py14z%(py16)spy16rE   zassert %(py19)spy19)r	   r6   r;   r4   r5   r7   r8   r9   r:   r   r\   _format_boolop)r<   r@   rA   rB   re   ra   rd   @py_assert11@py_assert13rb   rc   @py_format15@py_format17@py_format18@py_format20s                  r$   7test_result_pending_constant_value_matches_audit_moduler      sz   &&>Y&&&&>Y&&&&&&>&&&>&&&Y&&&&&&&u,,u,>uBTBcBcuBcguBuuuuu,uuuuuuuuuuuu,uuuuuuuuuuuuuuuuBcguuuuuuuBTuuuBTuuuBcuuuuuuguuuuguuuuuuuuuuuuuuur&   c                L   t        |       }t        |       g fd}t        | |d       }|j                  |ddt              }|j
                  }|t        k(  }|st        j                  d|fd|t        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
dz  }dd|iz  }t        t        j                  |            dx}}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   }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   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}||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	}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   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}||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	}y) ub   http_post 호출 시점에 audit 에 PENDING 이 이미 기록되어 있어야 함 (crash-safety).c                \    j                  t        j                                      ddiS )Nstatus   )r\   r]   
_iter_rows)methodpathbodyheadersr^   recorded_at_http_posts       r$   	http_postz?test_pending_record_written_before_http_post.<locals>.http_post   s)    $$T%*:*:*<%=>#r&   c                      yNzowner-token r   r&   r$   <lambda>z>test_pending_record_written_before_http_post.<locals>.<lambda>       r&   workspace_rootr   token_providerr^   or`   decision_pathownerrepocurrent_head_actualr/   z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py4)srI   r
   r*   rx   rX   rY   rS   NrE   rO   rP   r   rR   rT   rU   r   snapshotr   rV   rW   r   r   rK   
final_rowsrF   )r%   r   r   trigger_gemini_reviewr[   r   r
   r6   r;   r4   r5   r7   r8   r9   r:   rP   r]   r   )r!   r   r   modrI   r@   re   rf   rb   r<   ra   r=   rc   r   rd   r   r^   r   s                   @@r$   ,test_pending_record_written_before_http_postr      s   #H-Mh'E.0
 ,	C &&#3Sg ' F ==)=M))))=M))))))6)))6)))=))))))M)))M)))))))$%**%****%******3***3******$***$***%**********$Q'Hx=A=A=A33xx=AA;x -I- I---- I--- ---I-------A;t###################e&&()Jz?a?a?a33zz?aa="/i/"i////"i///"///i///////a=".h."h...."h..."...h.......r&   c                Z   t        |       }t        |       } G d dt              fd}t        | |d |      }t	        j
                        5  |j                  |ddt               d	d	d	       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   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)u]   http_post 직후 process crash 시뮬레이션 — PENDING 만 audit 에 남아있어야 함.c                      e Zd Zy)Ktest_pending_record_persists_after_simulated_crash.<locals>._SimulatedCrashN)__name__
__module____qualname__r   r&   r$   _SimulatedCrashr      s    r&   r   c                     d      )Nz crash after http_post invocationr   )r   r   r   r   r   s       r$   r   zEtest_pending_record_persists_after_simulated_crash.<locals>.http_post   s    @AAr&   c                      yr   r   r   r&   r$   r   zDtest_pending_record_persists_after_simulated_crash.<locals>.<lambda>   r   r&   r   r   r`   r   NrE   )>=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)srP   r_   rR   rT   rU   r   rI   r   r/   rV   rW   rY   rS   )r%   r   
SystemExitr   pytestraisesr   r[   r]   r   rP   r6   r;   r4   r5   r7   r8   r9   r:   )r!   r   r^   r   r   r_   r<   ra   r=   rb   rc   rd   re   rf   r   s                 @r$   2test_pending_record_persists_after_simulated_crashr      sy   #H-Mh'E* B ,	C 
	' 
!!'sRY 	" 	


   "#D t99>933tt978)	)	))))	))))))	)))))))
 
s   H  H*c                	   t        |       }t        |       }ddifd}t        | |d |      }t        j                  t
              5  |j                  |ddt               d	d	d	       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   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}||k(  }|slt        j                  d|fd||f      t        j                   |      t        j                   |      dz  }dd|iz  }	t#        t        j$                  |	            d	x}x}}|j                  |ddt              }|j&                  }|t(        k(  }|st        j                  d|fd|t(        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dz  }dd|iz  }	t#        t        j$                  |	            d	x}}t        |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}}|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   sxY w)!uK   PENDING → FAILED 후 retry 가 DEDUPED 가 아닌 새 trigger 로 진행.callsr   c                L    dxx   dz  cc<   d   dk(  rt        d      ddiS )Nr   rE   ztransient network errorr   r   )RuntimeError)r   r   r   r   states       r$   r   z8test_pending_then_failed_allows_retry.<locals>.http_post   s3    g!>Q899#r&   c                      yr   r   r   r&   r$   r   z7test_pending_then_failed_allows_retry.<locals>.<lambda>   r   r&   r   r   r`   r   NrK   r/   rO   rP   after_firstrR   rT   rU   rI   r   rV   rW   rY   rS   rE   FAILEDr   r2r
   r   rF   )r%   r   r   r   r   r   r   r[   r]   r   rP   r6   r;   r4   r5   r7   r8   r9   r:   r   r
   )r!   r   r^   r   r   r   r<   ra   r=   rb   rc   rd   re   rf   r   r@   finalr   s                    @r$   %test_pending_then_failed_allows_retryr      s)   #H-Mh'EaLE ,	C 
|	$ 
!!'sRY 	" 	


 u'')*K{ q q    q      3   3      {   {      q       q>(#0y0#y0000#y000#000y0000000q>(#/x/#x////#x///#///x///////		"	"#3Sg 
# 
B 99%9%%%%9%%%%%%2%%%2%%%9%%%%%%%%%%%%%%%%!!#$E9X*(*(****(******(*******9X+)+)++++)++++++)+++++++!
 
s   
SS)r!   r   r   intr   strreturnr   )"__doc__
__future__r   builtinsr4   _pytest.assertion.rewrite	assertionrewriter6   r   pathlibr   r   anu_v2r   r   anu_v2.owner_trigger_auditr   r   r	   r
   anu_v2.owner_trigger_onlyr   r[   r%   rC   rg   rq   r   r   r   r   r   r   r   r&   r$   <module>r      ss    #      :  7  25' ,'',3$f@:v/D*@",r&   