
    MOiG                        d Z ddlZddlmc mZ ddlZddlZddl	m
Z
 ddlmZ  e
e      j                         j                  j                  j                  Z ee      ej$                  vr"ej$                  j'                  d ee             ddlmZmZmZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$ dZ%dZ&	 	 	 	 	 	 	 dded	ed
ededede'dede(fdZ)d Z*d Z+d Z,d Z-d Z.d Z/d Z0d Z1d Z2d Z3d Z4y)u   task-2503 회귀 테스트 파일 2 — 회장 §6 10건 + 자기참조 1건.

회장 §6 회귀 테스트 (파일 2/3):
  - classify 9 룰 검증 (10건)
  - 자기참조 검증 (1건)
  - AUDIT_LOG_PATH monkeypatch로 실제 audit 파일 오염 방지
    N)Path)Any)ALLOWBLOCKLIMITED_PARALLELREQUIRE_CHAIR_OVERRIDEREASON_CHERRY_PICK_REQUESTEDREASON_DUPLICATE_FILEREASON_DUPLICATE_VERIFIERREASON_METADATA_MISSINGREASON_MISSING_DEPENDENCY&REASON_PARALLEL_SAFE_FALSE_DECLARATIONREASON_QUEUE_POSITION_MISSINGREASON_STALE_RECHECK_REQUIREDclassifyparse_topology_metadatarun_gatevalidate_metadataz# Test task

```yaml
expected_files:
  - "foo/bar.py"
risk_area: "verifier_layer"
dependency: "none"
parallel_policy: "serial_only"
merge_queue_position: 1
stale_recheck_required: true
cherry_pick_allowed: false
```
z# Parallel safe task

```yaml
expected_files:
  - "foo/unique_file.py"
risk_area: "content"
dependency: "none"
parallel_policy: "parallel_safe"
merge_queue_position: "n/a"
stale_recheck_required: false
cherry_pick_allowed: false
```
files	risk_area
dependencyparallel_policymerge_queue_positionstale_recheck_requiredcherry_pick_allowedreturnc                      | xs dg||||||dS )u   테스트용 완전한 metadata dict 생성 헬퍼.

    각 필드는 metadata schema가 union 타입(int|str, bool|str, list|str)을 허용하므로
    `Any`로 어노테이션. 실제 검증은 validate_metadata가 수행한다.
    z
foo/bar.pyexpected_filesr   r   r   r   r   r    )r   r   r   r   r   r   r   s          P/home/jay/workspace/tests/regression/test_merge_topology_gate_classifier_2503.py_make_metadatar"   C   s*      1L> * 4"82     c                     t        i g       } | j                  }|t        k(  }|s	t        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  }t        j                  d| j                         dz   d	|iz  }t        t        j                  |            d
x}}| j                  }t        |v }|s	t        j                  d|fdt        |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                  |      dz  }t        j                  d| j                         dz   d	|iz  }t        t        j                  |            d
x}}y
)uJ   룰 1: 빈 metadata → decision=BLOCK + METADATA_MISSING in reason_codes.active_tasks==z0%(py2)s
{%(py2)s = %(py0)s.decision
} == %(py4)sdecisionr   py0py2py4expected BLOCK, got 
>assert %(py6)spy6Ninz4%(py0)s in %(py4)s
{%(py4)s = %(py2)s.reason_codes
}r   zexpected METADATA_MISSING in )r   r*   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationreason_codesr   )r*   @py_assert1@py_assert3@py_format5@py_format7s        r!   test_metadata_missing_blocksrC   _   s   ,HQ%QQQQQQQQQ8QQQ8QQQQQQQQQQQQQQQQ)=h>O>O=P'QQQQQQQ&.&;&; "&;;   "&;      #    #      '/    '/    '<    ((=(='>?    r#   c                     t        ddg      } dddgdddd	}t        | |g
      }|j                  }|t        k(  }|s	t	        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  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        |v }|s	t	        j
                  d|fdt        |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                  |      dz  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}y)uQ   룰 2: 다른 active task와 expected_files 교집합 → BLOCK + DUPLICATE_FILE.zutils/shared.pyztests/test_foo.py)r   z	task-9001zother/file.pyotherserial_only   task_idr   r   r   r   r%   r'   r)   r*   r   r+   r/   r0   r1   Nr2   r4   r
   zexpected DUPLICATE_FILE in )r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r
   metadataactive_taskr*   r?   r@   rA   rB   s          r!   "test_duplicate_file_overlap_blocksrM   k   s   %68K$LMH ,o>( !K }=HQ%QQQQQQQQQ8QQQ8QQQQQQQQQQQQQQQQ)=h>O>O=P'QQQQQQQ$,$9$9  $99    $9      !    !      %-    %-    %:    &h&;&;%<=    r#   c                     t        dgd      } ddgdddd}t        | |g	      }|j                  }|t        k(  }|s	t	        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  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        |v }|s	t	        j
                  d|fdt        |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                  |      dz  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}y)uO   룰 4: 두 task 모두 risk_area=verifier_layer → BLOCK + DUPLICATE_VERIFIER.zverifier/new_check.pyverifier_layer)r   r   z	task-9002zverifier/other_check.pyrF   rG   rH   r%   r'   r)   r*   r   r+   r/   r0   r1   Nr2   r4   r   zexpected DUPLICATE_VERIFIER in )r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r   rJ   s          r!   3test_duplicate_verifier_risk_area_blocks_or_limitedrP      s   &'"H 45%( !K }=HQ%QQQQQQQQQ8QQQ8QQQQQQQQQQQQQQQQ)=h>O>O=P'QQQQQQQ(0(=(= $(==   $(=      %    %      )1    )1    )>    *(*?*?)@A    r#   c                     t        dgdg      } d }t        | g |      }|j                  }|t        k(  }|s	t	        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  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        |v }|s	t	        j
                  d|fdt        |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                  |      d
z  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}y)uP   룰 6: dependency=['task-9999.merged'] 미머지 → BLOCK + MISSING_DEPENDENCY.zutils/new_feature.pytask-9999.merged)r   r   c                      ~ ddgfS )NFrR   r    _argss    r!   _dep_unmergedz6test_dependency_unmerged_blocks.<locals>._dep_unmerged   s    *+,,r#   r&   dependency_checkr'   r)   r*   r   r+   r/   r0   r1   Nr2   r4   r   zexpected MISSING_DEPENDENCY in )r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r   )rK   rV   r*   r?   r@   rA   rB   s          r!   test_dependency_unmerged_blocksrY      s   %&&'H- &H Q%QQQQQQQQQ8QQQ8QQQQQQQQQQQQQQQQ)=h>O>O=P'QQQQQQQ(0(=(= $(==   $(=      %    %      )1    )1    )>    *(*?*?)@A    r#   c                     t        dgdd      } ddgdddd	}t        | |g
      }|j                  }|t        k(  }|s	t	        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  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        |v }|s	t	        j
                  d|fdt        |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                  |      dz  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}y)u   룰 9: parallel_policy=parallel_safe인데 active task와 expected_files 교집합 → BLOCK + PARALLEL_SAFE_FALSE_DECLARATION.zutils/shared_module.pyparallel_safen/a)r   r   r   z	task-9003rE   rF      rH   r%   r'   r)   r*   r   r+   r/   r0   r1   Nr2   r4   r   z,expected PARALLEL_SAFE_FALSE_DECLARATION in )r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r   rJ   s          r!   +test_parallel_safe_false_declaration_blocksr^      s   '('"H 34( !K }=HQ%QQQQQQQQQ8QQQ8QQQQQQQQQQQQQQQQ)=h>O>O=P'QQQQQQQ5=5J5J 15JJ   15J      2    2      6>    6>    6K    7x7L7L6MN    r#   c                     t        dgd      } t        | g       }|j                  }|t        k(  }|s	t	        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  }t	        j                  d
|j                         dz   d|iz  }t        t	        j                  |            dx}}|j                  }t        |v }|s	t	        j
                  d|fdt        |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                  |      d	z  }t	        j                  d|j                         dz   d|iz  }t        t	        j                  |            dx}}y)u^   룰 7: cherry_pick_allowed='recovery_only' → REQUIRE_CHAIR_OVERRIDE + CHERRY_PICK_REQUESTED.zhotfix/recovery.pyrecovery_only)r   r   r%   r'   r)   r*   r   r+   %expected REQUIRE_CHAIR_OVERRIDE, got r0   r1   Nr2   r4   r	   z"expected CHERRY_PICK_REQUESTED in )r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r	   rK   r*   r?   r@   rA   rB   s         r!   >test_cherry_pick_allowed_recovery_only_requires_chair_overriderc      s   #$+H
 r2H  66    6                    !7    !7    00A0A/BC     ,4+@+@ '+@@   '+@      (    (      ,4    ,4    ,A    -X-B-B,CD    r#   c                 	   | dz  }|j                  d|       d}t        d|d      \  }}d}||u }|st        j                  d|fd||f      d	t	        j
                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }t        j                  d|       dz   d|iz  }	t        t        j                  |	            dx}}|j                  }|t        k(  }
|
s	t        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  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}}
|j                  } |       }
|
st        j                  d      dz   dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      dz  }t        t        j                  |            dx}}
|j                         j                         D cg c](  }|j!                         st#        j$                  |      * }}|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   }|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 }|st        j                  d|fd$||
f      t        j                  |      t        j                  |
      d!z  }t        j                  d%|j'                  d#             dz   d|iz  }t        t        j                  |            dx}x}}
yc c}w )&uT   run_gate(task_id, desc, override=True) 시 audit jsonl에 override_used=true 기록.merge-topology-gate.jsonl(utils.merge_topology_gate.AUDIT_LOG_PATHz# Override test task

```yaml
expected_files:
  - "hotfix/override_test.py"
risk_area: "governance"
dependency: "none"
parallel_policy: "serial_only"
merge_queue_position: 1
stale_recheck_required: false
cherry_pick_allowed: recovery_only
```
ztest-override-001T)rI   	task_descoverrideis)z%(py0)s is %(py3)sallowedr,   py3zexpected allowed=True, got 
>assert %(py5)spy5Nr'   r)   r*   r   r+   ra   r0   r1   zaudit jsonl must be createdC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
audit_filez+audit file must contain at least one record
>assert %(py0)sr,   recordsrI   )z%(py1)s == %(py4)s)py1r.   zassert %(py6)soverride_used)z%(py1)s is %(py4)sz$expected override_used=true but got )setattrr   r5   r6   r7   r8   r9   r:   r;   r<   r=   r*   r   exists	read_text
splitlinesstripjsonloadsget)tmp_pathmonkeypatchrq   rg   r*   rk   @py_assert2r?   @py_format4@py_format6r@   rA   rB   liners   @py_format1last_record@py_assert0s                     r!   )test_override_used_audit_record_generatedr      sy   77J 2I !#Hg C7d?CCC7dCCCCCC7CCC7CCCdCCC9'CCCCCCC  66    6                    !7    !7    00A0A/BC     ==== =======:===:============ -7,@,@,B,M,M,O`DSWS]S]S_tzz$`G`AAAAAAAAA7AAA7AAAAA"+Ky!8%88!%88888!%8888!888%88888888' 4 '4/  '4    (    ,0    /{/O.PQ      as   *S Sc                    |dz  }| j                  d|       t        dgdddd      }t        |g 	      }|j                  }|t        k(  }|st        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  }t        j                  d|j                   d|j                         dz   d|iz  }t        t        j                  |            dx}}y)u?   metadata 정상 + active task 없음 + parallel_safe → ALLOW.re   rf   zutils/isolated_new_module.pycontentnoner[   r\   )r   r   r   r   r   r%   r'   r)   r*   r   r+   z1expected ALLOW for clean parallel_safe task, got  reason_codes=r0   r1   N)rw   r"   r   r*   r   r5   r6   r7   r8   r9   r:   r;   r>   r<   r=   )	r   r   rq   rK   r*   r?   r@   rA   rB   s	            r!    test_normal_parallel_safe_allowsr     sJ    77J2
 -.'"H r2H %                       !&    !&    <H<M<M;N O --.	0    r#   c                     dgddddddd} t        | g       }|j                  }|t        k(  }|s	t        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  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}}|j                  }t        |v }|s	t        j                  d|fdt        |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                  |      dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}}y)uh   룰 8: parallel_policy=limited_parallel + merge_queue_position='n/a' → BLOCK + QUEUE_POSITION_MISSING.zutils/limited_task.pydispatch_layerr   limited_parallelr\   Fr   r%   r'   r)   r*   r   r+   zAexpected BLOCK for limited_parallel with n/a queue_position, got r0   r1   Nr2   r4   r   z#expected QUEUE_POSITION_MISSING in )r   r*   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r   rb   s         r!   8test_limited_parallel_with_missing_queue_position_blocksr   2  s    33%- %"'$H r2H %                       !&    !&    LHL]L]K^_     -5,A,A (,AA   (,A      )    )      -5    -5    -B    .h.C.C-DE    r#   c            	      "   t        dgd      } t        | g       }|j                  }|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                  |      t	        j                  |      t	        j                  |      t	        j                  |      d	z  }t	        j                  d
|j                  j                  d             dz   d|iz  }	t        t	        j                  |	            dx}x}x}x}x}}g }|j                  }t        |v}|}
|s(|j                  }t         t"        t$        t&        f}||v }|}
|
st	        j
                  d|fdt        |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                  |      dz  }dd|iz  }|j)                  |       |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  }|j)                  |       t	        j*                  |d      i z  }t	        j                  d      dz   d|iz  }t        t	        j                  |            dx}
x}x}x}x}x}}y)uU   stale_recheck_required=True인 metadata로 classify 호출 시 metadata에 보존됨.zutils/stale_check.pyT)r   r   r%   r   ri   )zh%(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.metadata
}.get
}(%(py6)s)
} is %(py11)sr*   )r,   r-   r.   r1   py8py11zIstale_recheck_required=True must be preserved in decision.metadata, got: z
>assert %(py13)spy13N)not in)z8%(py2)s not in %(py6)s
{%(py6)s = %(py4)s.reason_codes
}r   )r-   r.   r1   z%(py8)sr   r2   )z4%(py12)s
{%(py12)s = %(py10)s.decision
} in %(py15)s)py10py12py15z%(py17)spy17r]   zEstale_recheck_required alone should not introduce unexpected decisionz
>assert %(py20)spy20)r"   r   rK   r~   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r   r*   r   r   r   r   append_format_boolop)rK   r*   r?   r@   @py_assert5@py_assert7@py_assert10@py_assert9@py_format12@py_format14r   @py_assert11@py_assert14@py_assert13rB   @py_format9@py_format16@py_format18@py_format19@py_format21s                       r!   &test_stale_recheck_required_invocationr   K  s   %&#H
 r2H     !9  !9: d :dB   :d                  !    ":    ;    ?C   !!%%&>?@	B     
O0E0E O(0EE OIZIZ O!7_ OIZ _ J O O=N=NO(0E O OHNO O6N6N ) O OENY ) O OHNO O6N6N 19 O OENY 19 O OENY 1F O O OHNOHNO=N=NOIZ _ O OHNO O6N6N JR O OENY JR O OENY J[ O OENY_ O O OHNOHNO@NO O=N=NNO O O;N;NO O O Or#   c                     t         dz  dz  d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                  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                  |            t        |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}}d }t        |g |      }|j                  }|t         k7  }|st        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  }t        j                  d|j                   d|j"                         dz   d|iz  }t        t        j                  |            dx}}y) u   자기참조: task-2503.md 파일 자체를 파싱→validate→classify (active_tasks=[]) 했을 때
    errors=[] AND decision != BLOCK.memorytasksztask-2503.mdztask-2503.md not found: rp   task_md_pathr+   Nzutf-8)encodingz?parse_topology_metadata must extract metadata from task-2503.mdrr   r,   rK   r'   )z%(py0)s == %(py3)serrorsrl   z?task-2503.md metadata must pass validate_metadata, got errors: rn   ro   c                      ~ dg fS )NTr    rT   s    r!   _dep_mergedzMtest_self_reference_task_2503_passes_metadata_extraction.<locals>._dep_mergedv  s    bzr#   rW   )!=)z0%(py2)s
{%(py2)s = %(py0)s.decision
} != %(py4)sr*   r   z:task-2503.md self-reference must NOT result in BLOCK, got r   r0   r1   )	WORKSPACErx   r5   r;   r7   r8   r9   r:   r<   r=   ry   r   r   r6   r   r*   r   r>   )r   r?   r@   rA   rg   rK   r   r   r   r   r   r   r*   rB   s                 r!   8test_self_reference_task_2503_passes_metadata_extractionr   c  s    x''1NBLK K KK$<\N"KKKKKKK<KKK<KKKKKK KKKKKK&&&8I 'y1HVVVVVVVVV8VVV8VVVVV x(F 6R<  6R                  J&R    
 $H  %                       !&    !&     !0E0E/F	H    r#   )Nr   r   rF   r]   FF)5__doc__builtinsr7   _pytest.assertion.rewrite	assertionrewriter5   r|   syspathlibr   typingr   __file__resolveparentr   strpathinsertutils.merge_topology_gater   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   TASK_DESC_TEMPLATETASK_DESC_PARALLEL_SAFEbooldictr"   rC   rM   rP   rY   r^   rc   r   r   r   r   r   r    r#   r!   <module>r      s2     
   N""$++2299	y>!HHOOAs9~&    (    ( !#($  	
  !  
8,464&.h62O0 r#   