
    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
Z
ddlZddlZej                  j                  dd       ddlZde
j                   d<   ddlmZmZmZ ddlmZ dd	lmZ d
ZdddZddZ G d d      Z G d d      Z G d d      Zy)u   Tests for v3.6 Harness — JSONL schema validation and logging correctness.

chair_authorization_id=CHAIR-AUTH-TASK-2703-V36-HARNESS-MVP-260528
    )annotationsNz/home/jay/workspace1ANU_V36_HARNESS_TEST_MODE)validate_recordREQUIRED_FIELDSVALID_DECISIONS)log_decision)evaluatez$/tmp/v36_harness_decision_test.jsonlc                Z   g }t         j                  j                  t              s|S t	        t        d      5 }|D ]:  }|j                         }|s	 |j                  t        j                  |             < 	 ddd       ||  d S # t        j                  $ r Y aw xY w# 1 sw Y   (xY w)z$Read last n records from test JSONL.zutf-8)encodingN)
ospathexists_TEST_JSONLopenstripappendjsonloadsJSONDecodeError)nrecordsflines       ;/home/jay/workspace/tests/harness/test_v36_harness_jsonl.py_read_jsonl_tailr      s    G77>>+&	kG	,  	D::<DNN4::d#34		 A23< ++  s/   B!$B7B!BB!BB!!B*c                 `    dt        j                          dddddd}|j                  |        |S )Nz2026-05-28T12:00:00+00:00DENYpattern.forbidden_tool_or_shellztest reasonzgit push origin main	task-2703)ts	timestampdecisionmatched_rulereasoncommand_or_tooltask_id)timeupdate)	overridesbases     r   _make_valid_recordr,   ,   s7    )YY[91D 	KK	K    c                  N    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)TestSchemaValidationc                   t               }t        |      \  }}|s~t        j                  d|       dz   ddt	        j
                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            d x}}y )NExpected valid, errors: 
>assert %(py0)spy0valid==)z%(py0)s == %(py3)serrors)r3   py3zassert %(py5)spy5)r,   r   
@pytest_ar_format_assertmsg@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation_call_reprcompare)	selfrecordr4   r7   @py_format1@py_assert2@py_assert1@py_format4@py_format6s	            r   test_valid_deny_record_passesz2TestSchemaValidation.test_valid_deny_record_passes?   s    #%'/v9909999999u999u99999v|vvvr-   c                <   t        ddd      }t        |      \  }}|s~t        j                  d|       dz   ddt	        j
                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            y )	NHOLD_FOR_CHAIR!pattern.mtime_speculation_as_factu   회장 승인 필요r#   r$   r%   r1   r2   r3   r4   
r,   r   r:   r;   r<   r=   r>   r?   r@   rA   rC   rD   r4   r7   rE   s        r   test_valid_hold_record_passesz2TestSchemaValidation.test_valid_hold_record_passesE   sh    #%<)

 (/v9909999999u999u99999ur-   c                <   t        dd d       }t        |      \  }}|s~t        j                  d|       dz   ddt	        j
                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            y )NALLOWrN   z"Expected valid for ALLOW, errors: r2   r3   r4   rO   rP   s        r   test_valid_allow_record_passesz3TestSchemaValidation.test_valid_allow_record_passesN   sd    #W4PTU'/v CC:6(CCCCCCCuCCCuCCCCCur-   c                   t         D ]m  t               }|= t        |      \  }}| }|s~t        j                  d      dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            d }fd|D        }t        |      }|st        j                  d      dz   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}}p y )
Nz%Should be invalid when missing field z
>assert not %(py0)sr3   r4   c              3  &   K   | ]  }|v  
 y w)N ).0efields     r   	<genexpr>zITestSchemaValidation.test_missing_required_field_fails.<locals>.<genexpr>\   s     2auz2s   zError should mention z.
>assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr3   py2py4)r   r,   r   r:   r;   r<   r=   r>   r?   r@   rA   r\   )	rC   rD   r4   r7   rG   @py_format2@py_assert3@py_format5rZ   s	           @r   !test_missing_required_field_failsz6TestSchemaValidation.test_missing_required_field_failsV   s    $ 	VE')Fu+F3ME69O9OO EeYOOOOOOOuOOOuOOOOOO262U322U2UU6KE94UUUUUUU3UUU3UUU2UUU2UUUUUU	Vr-   c                n   t        d      }t        |      \  }}| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d }d |D        }t        |      }|sd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}}y )
NINVALID_DECISIONr#   assert not %(py0)sr3   r4   c              3  h   K   | ]*  }d |j                         v xs d|j                         v  , yw)r#   invalidN)lowerrX   rY   s     r   r[   zCTestSchemaValidation.test_invalid_decision_fails.<locals>.<genexpr>b   s.     U:*Di1779.DDUs   02,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}r\   r]   
r,   r   r<   r=   r:   r>   r?   r@   rA   r\   rC   rD   r4   r7   rG   r`   ra   rb   s           r   test_invalid_decision_failsz0TestSchemaValidation.test_invalid_decision_fails^   s    #-?@'/vyy55UfUUsUUUUUUUUUsUUUsUUUUUUUUUUUUUUr-   c                p   t        dd       }t        |      \  }}| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d }d |D        }t        |      }|sd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}}y )
Nr   )r#   r$   rg   r3   r4   c              3  $   K   | ]  }d |v  
 yw)r$   NrW   rk   s     r   r[   zLTestSchemaValidation.test_deny_without_matched_rule_fails.<locals>.<genexpr>h   s     71>Q&7   rl   r\   r]   rm   rn   s           r   $test_deny_without_matched_rule_failsz9TestSchemaValidation.test_deny_without_matched_rule_failsd   s    #V$G'/vyy55777s777777777s777s77777777777777r-   c                p   t        dd       }t        |      \  }}| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d }d |D        }t        |      }|sd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}}y )
NrL   )r#   r%   rg   r3   r4   c              3  $   K   | ]  }d |v  
 yw)r%   NrW   rk   s     r   r[   zFTestSchemaValidation.test_hold_without_reason_fails.<locals>.<genexpr>n   s     1Q8q=1rr   rl   r\   r]   rm   rn   s           r   test_hold_without_reason_failsz3TestSchemaValidation.test_hold_without_reason_failsj   s    #-=dK'/vyy551&11s111111111s111s11111111111111r-   c                   t        d      \  }}| }|sedddt        j                         v st        j                  |      rt        j
                  |      ndiz  }t        t        j                  |            d }|sedddt        j                         v st        j                  |      rt        j
                  |      ndiz  }t        t        j                  |            y )Nz
not a dictrg   r3   r4   assert %(py0)sr7   )r   r<   r=   r:   r>   r?   r@   rA   )rC   r4   r7   rG   r`   rE   s         r   test_non_dict_input_failsz.TestSchemaValidation.test_non_dict_input_failsp   sk    '5vyy55vvvr-   c                   t        d      }t        |      \  }}| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            y )Ni90  )r!   rg   r3   r4   rx   r7   	r,   r   r<   r=   r:   r>   r?   r@   rA   rC   rD   r4   r7   rG   r`   rE   s          r   test_ts_must_be_stringz+TestSchemaValidation.test_ts_must_be_stringu   su    #u-'/vyy55vvvr-   c                   t        d      }t        |      \  }}| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            y )Nznot-a-number)r"   rg   r3   r4   rx   r7   r{   r|   s          r   test_timestamp_must_be_numericz3TestSchemaValidation.test_timestamp_must_be_numeric{   su    #n='/vyy55vvvr-   c                   t         D ]  }t        |      }|dv rd|d<   d|d<   n|dk(  r
d |d<   d |d<   t        |      \  }}|r>t        j                  d|d	|       d
z   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |             y )Nrf   )r   rL   r   r$   testr%   rS   z	Decision z should be valid; errors=r2   r3   r4   )r   r,   r   r:   r;   r<   r=   r>   r?   r@   rA   )rC   decrD   r4   r7   rE   s         r   test_all_valid_decision_valuesz3TestSchemaValidation.test_all_valid_decision_values   s    " 		OC'5F00)J~&#)x )-~&#'x +F3ME6NNIcW,EfXNNNNNNN5NNN5NNNNN		Or-   N)__name__
__module____qualname__rJ   rQ   rT   rc   ro   rs   rv   ry   r}   r   r   rW   r-   r   r/   r/   >   s=    :DVV82

Or-   r/   c                  B    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)
TestLoggerc                    t         j                  j                  t              rt        j                  t               t         j
                  j                  dd       y)"Clear test JSONL before each test.V36_HARNESS_LOG_ALLOWN)r   r   r   r   unlinkenvironpoprC   s    r   setup_methodzTestLogger.setup_method   s1    77>>+&IIk"


.5r-   c                n   t        d      }t        |       t        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}}	y )Nr   rf      r5   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slentailr3   py1r8   py6assert %(py8)spy8r   r#   z%(py1)s == %(py4)sr   r_   assert %(py6)sr   r,   r	   r   r   r:   rB   r<   r=   r>   r?   r@   rA   rC   rD   r   rF   @py_assert5@py_assert4@py_format7@py_format9@py_assert0ra   rb   s              r   test_deny_record_is_loggedz%TestLogger.test_deny_record_is_logged   s    #V4V"4yAyA~yAss44yAAwz",f,"f,,,,"f,,,",,,f,,,,,,,r-   c                x   t        ddd      }t        |       t        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}}	y )NrL   rM   u	   테스트rN   r   r5   r   r   r   r   r   r   r   r#   r   r   r   r   r%   r   r   s              r   test_hold_record_is_loggedz%TestLogger.test_hold_record_is_logged   sd   #%<

 	V"4yAyA~yAss44yAAwz"6&66"&66666"&6666"666&66666666Awx /K/ K//// K/// ///K///////r-   c                   t        dd d       }t        |       t        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}}y )NrS   rN      r   r5   r   r   r   r   z7ALLOW must not be logged unless V36_HARNESS_LOG_ALLOW=1z
>assert %(py8)sr   )r,   r	   r   r   r:   rB   r<   r=   r>   r?   r;   r@   rA   )rC   rD   r   rF   r   r   r   r   s           r    test_allow_not_logged_by_defaultz+TestLogger.test_allow_not_logged_by_default   s    #W4PTUV"4yXAXyA~XXXyAXXXXXXsXXXsXXXXXX4XXX4XXXyXXXAXXXXXXXXXXXr-   c                $   dt         j                  d<   	 t        dd d       }t        |       t	        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}}	t         j                  j                  dd        y # t         j                  j                  dd        w xY w)Nr   r   rS   rN   r   r5   r   r   r   r   r   r   r   r#   r   r   r   r   )r   r   r,   r	   r   r   r:   rB   r<   r=   r>   r?   r@   rA   r   r   s              r   test_allow_logged_when_env_setz)TestLogger.test_allow_logged_when_env_set   s?   .1

*+	:'tTXYF #A&Dt9!!9>!!!9!!!!!!3!!!3!!!!!!t!!!t!!!9!!!!!!!!!!7:&1'1&'1111&'111&111'1111111JJNN2D9BJJNN2D9s   F7G- -"Hc                <   t        d      }|j                  dd        t        |       t        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   }|j                  }d} ||      }	d}
|	|
k(  }|st        j                  d|fd|	|
f      t        j                  |      t        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}x}x}x}	x}}
y )Nr   rf   chair_authorization_idr   r5   r   r   r   r   r   r   r   z+CHAIR-AUTH-TASK-2703-V36-HARNESS-MVP-260528)zJ%(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.get
}(%(py5)s)
} == %(py10)s)r   r8   r9   py7py10zassert %(py12)spy12)r,   r   r	   r   r   r:   rB   r<   r=   r>   r?   r@   rA   get)rC   rD   r   rF   r   r   r   r   r   @py_assert6@py_assert9@py_assert8@py_format11@py_format13s                 r   $test_chair_authorization_id_injectedz/TestLogger.test_chair_authorization_id_injected   sL   #V4

+T2V"4yAyA~yAss44yAAwew{{e3e{34e8ee48eeeee48eeeeweee{eee3eee4eee8eeeeeeeeer-   c                   t        d      D ]  }t        t        dd|               t        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}}y)z+Multiple log_decision calls append records.   r   zcmd-)r#   r&   
   r5   r   r   r   r   r   r   N)ranger	   r,   r   r   r:   rB   r<   r=   r>   r?   r@   rA   )rC   ir   rF   r   r   r   r   s           r   test_jsonl_is_append_onlyz$TestLogger.test_jsonl_is_append_only   s    q 	ZA+VtTUSVZXY	Z#4yAyA~yAss44yAr-   c                    dddg ddig}|D ]  }	 t        |        y# t        $ r%}t        j                  d|d|        Y d}~;d}~ww xY w)z#Logger must not raise on any input.Nstring{   circularzlog_decision raised on input z: )r	   	Exceptionpytestfail)rC   
bad_inputsbadrY   s       r   test_logger_never_raisesz#TestLogger.test_logger_never_raises   sg    Hc2
D/AB
 	JCJS!	J  J;C7"QCHIIJs   	AAAc                P   t        d      }t        |       t        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 ]  }	|	|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      dz   d|iz  }t        t	        j                  |            d}
 y)z6DENY records must contain all 5 required JSONL fields.r   rf   r   r5   r   r   r   r   r   r   Nr   )r!   r#   r$   r&   r'   r"   )in)z%(py0)s in %(py2)srZ   logged)r3   r^   zField z missing from JSONL recordz
>assert %(py4)sr_   )r,   r	   r   r   r:   rB   r<   r=   r>   r?   r@   rA   r;   )rC   rD   r   rF   r   r   r   r   r   rZ   rG   @py_format3rb   s                r   %test_5_required_fields_in_deny_recordz0TestLogger.test_5_required_fields_in_deny_record   s?   #V4V"4yAyA~yAss44yAab 	QEF?PPP5FPPPPPP5PPP5PPPPPPFPPPFPPPPfUI5O$PPPPPPP	Qr-   N)r   r   r   r   r   r   r   r   r   r   r   r   rW   r-   r   r   r      s3    6-
0Y	:fJQr-   r   c                  $    e Zd Zd Zd Zd Zd Zy)TestGuardLoggerPipelinec                ~    t         j                  j                  t              rt        j                  t               yy)r   N)r   r   r   r   r   r   s    r   r   z$TestGuardLoggerPipeline.setup_method   s#    77>>+&IIk" 'r-   c                "   t        dddiddi      }|d   }d}||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}y)z6evaluate returns all required Decision fields on DENY.Bashcommandzgit push origin main --forcer'   r    r#   r   r5   r   r   r   r   Nr$   is notz%(py1)s is not %(py4)sr%   r
   r:   rB   r?   r@   rA   rC   dr   ra   rF   rb   r   s          r   test_deny_decision_fieldsz1TestGuardLoggerPipeline.test_deny_decision_fields   s
   Vi)GH9VaJbc}&&}&&&&}&&&}&&&&&&&&&& ,, ,,,, ,,, ,,,,,,,,,,{&$&{$&&&&{$&&&{&&&$&&&&&&&r-   c                (   d}t        dd|dddi      }|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t	        t        j
                  |            dx}x}}y)z@evaluate returns all required Decision fields on HOLD_FOR_CHAIR.u9   Task completed (mtime) — timestamp analysis shows done.Writez/tmp/test-task.done)	file_pathcontentr'   r    r#   rL   r5   r   r   r   r   Nr$   r   r   r%   r   )rC   r   r   r   ra   rF   rb   r   s           r   test_hold_decision_fieldsz1TestGuardLoggerPipeline.test_hold_decision_fields   s   M/GD$

 }0 00} 00000} 0000}000 00000000 ,, ,,,, ,,, ,,,,,,,,,,{&$&{$&&&&{$&&&{&&&$&&&&&&&r-   c                "   t        dddiddi      }|d   }d}||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t	        t        j
                  |            dx}x}}y)z7evaluate returns None matched_rule and reason on ALLOW.r   r   z
git statusr'   r    r#   rS   r5   r   r   r   r   Nr$   )is)z%(py1)s is %(py4)sr%   r   r   s          r   test_allow_decision_fieldsz2TestGuardLoggerPipeline.test_allow_decision_fields   s   Vi6K8PQ}''}''''}'''}'''''''''' (D( D(((( D((( (((D((((((({"d"{d""""{d"""{"""d"""""""r-   N)r   r   r   r   r   r   r   rW   r-   r   r   r      s    #
'
'#r-   r   )r   )r   intreturnz
list[dict])r   dict)__doc__
__future__r   builtinsr<   _pytest.assertion.rewrite	assertionrewriter:   r   r   sysr(   r   insertr   r   scripts.harness.v36.schemar   r   r   scripts.harness.v36.loggerr	   scripts.harness.v36.guardr
   r   r   r,   r/   r   r   rW   r-   r   <module>r      s   
 #    	 
  ( )  +.

& ' X X 3 . 5 $MO MOhMQ MQh# #r-   