
    iv                        d Z ddlZddlZddlZddlZddlZddlmZ ddlZdZ	e	ej                  vrej                  j                  de	       dej                  vrej                  j                  dd       dej                  vrej                  j                  dd       ddlmZ ddlmZ dd	lmZ dd
lmZmZmZ ej                  j/                  e	dd      Zej                  j/                  e	dd      Zh dZ ej6                         d        ZdedefdZdedefdZ  G d d      Z! G d d      Z" G d d      Z# G d d      Z$ G d d      Z% G d  d!      Z& G d" d#      Z' G d$ d%      Z( G d& d'      Z)y)(u  
test_quality_gates_integration.py — Dispatch Quality Gates 파이프라인 통합 테스트 (벨레스)

8개 시나리오로 개별 컴포넌트들이 함께 올바르게 동작하는지 검증.
- Scenario 1: "확인 불가" 보고서 → l1_smoketest FAIL
- Scenario 2: 수정 파일 심볼 역추적 → 미수정 파일 WARN
- Scenario 3: pytest FAIL → ci_preflight FAIL
- Scenario 4: goal_assertions 전부 PASS → GOAL-GATE PASS
- Scenario 5: goal_assertions 1개 FAIL → GOAL-GATE BLOCKED
- Scenario 6: 미해결 이슈 4개 → unresolved_gate BLOCK
- Scenario 7: gate-config mode=warn → WARN만 (block 없음)
- Scenario 8: 백틱 토큰 자동 추출 → affected_files 자동 주입
    N)Pathz/home/jay/workspacez/home/jay/workspace/scriptsz*/home/jay/workspace/teams/shared/verifiers)scan)verify)_auto_inject_affected_files)load_gate_configis_gate_enabledget_gate_modeconfigzgate-config.jsonscriptszci_preflight.sh>	   jqcatnpmnpxtsccurlgreppytestpython3c               #      K   t        t              j                  d      } d t        t              j                  | d       yw)uX   gate-config.json 내용을 백업하고, 테스트 종료 후 원본으로 복원한다.utf-8encodingN)r   GATE_CONFIG_PATH	read_text
write_text)original_contents    ;/home/jay/workspace/tests/test_quality_gates_integration.pygate_config_backupr   :   s?      ,-777I		%%&6%Is   AAtask_file_pathreturnc                 :   t        d      sdt        d      g dS t        d      }t        t        d      j	                  dt        t                          }t        | dd      5 }|j                         }ddd       t        j                  d	t        j                        }g }d
}|r|j                  d      j                         }|j                  d      D ]  }	t        j                  d|	      }
|
s|
j                  d      j                         }|s>|j                         d   }||vrVt        j                   |ddd      }|j#                  ||j$                  f       |j$                  dk7  sd} |r|dk(  }|rdnd||dS d||dS # 1 sw Y   xY w)u   
    finish-task.sh의 goal_assertions 게이트 로직을 Python으로 미러링.

    Returns:
        dict with keys:
            - "result": "PASS" | "FAIL" | "BLOCKED" | "DISABLED"
            - "gate_mode": "warn" | "fail"
            - "commands_run": list of (cmd, exit_code)
    goal_assertionsDISABLED)result	gate_modecommands_runallowed_commandsrr   r   Nz'## goal_assertions.*?\n(.*?)(?=\n##|\Z)F   
z	`([^`]+)`r   T)shellcapture_outputtextfailBLOCKEDFAILPASS)r   r	   setr   getlistALLOWED_COMMANDSopenreadresearchSgroupstripsplit
subprocessrunappend
returncode)r   r%   allowed_cmdsfcontentmr&   	goal_failsection_textline	cmd_matchcmd
first_wordprocblockeds                  r   _run_goal_assertions_gaterN   F   s    ,-$=AR3Seghh/0I'(9:>>?QSWXhSijkL	ncG	4 &&( 			<grttLALIwwqz'') &&t, 	!D		,5I//!$**,CQJ->>44D doo 67!# 	'	!* v%#*i"(
 	
 9lSSO s   %FFreport_file_pathc                    t        d      sdddt        d      dS t        d      }t        d      j                  dd      }d}t        j
                  j                  |       rJt        | dd	      5 }|D ]-  }t        j                  d
|t        j                        s)|dz  }/ 	 ddd       ||kD  r|dk(  }|rdnd|||dS |dkD  rd|||dS d|||dS # 1 sw Y   1xY w)u  
    finish-task.sh의 unresolved_gate 로직을 Python으로 미러링.

    Returns:
        dict with keys:
            - "result": "PASS" | "WARN" | "BLOCKED" | "DISABLED"
            - "count": int
            - "max": int
            - "gate_mode": str
    unresolved_gater#   r   )r$   countmaxr%   max_in_scope_unresolved   r(   r   r   u)   범위 내 미해결|in.scope.*unresolvedr)   Nr.   r/   WARNr1   )r   r	   r   r3   ospathisfiler6   r8   r9   
IGNORECASE)rO   r%   max_unresolvedrR   rC   rH   rM   s          r   _run_unresolved_gater\      s    ,-$qWhIijj/0I%&78<<=VXYZNE	ww~~&'"C': 	a 99I4QSQ^Q^_QJE	
 ~v%#*i!"	
 	
 
 5V_``u^R[\\!	 	s   1+CCCc                   "    e Zd ZdZd Zd Zd Zy)%TestScenario1_L1SmoketestBlockPatternuS   BLOCK 패턴이 포함된 보고서는 l1_smoketest가 FAIL을 반환해야 한다.c                    |dz  dz  }|j                  d       d}|| dz  }|j                  dd	       t        |t        |            }|d
   dk(  s
J d|        t	        d |d   D              sJ d|d           y)u^   'UI 직접 확인 불가' 문자열이 포함된 보고서 파일이 있으면 status == FAIL.memoryreportsTparentsztest-scenario-1.mduM   # 작업 보고서

UI 직접 확인 불가

테스트 진행 불가 환경.
r   r   statusr0   uS   BLOCK 패턴 'UI 직접 확인 불가' 포함 시 FAIL 이어야 하지만 실제: c              3   $   K   | ]  }d |v  
 yw)u   BLOCK 패턴N ).0ds     r   	<genexpr>z\TestScenario1_L1SmoketestBlockPattern.test_ui_block_pattern_triggers_fail.<locals>.<genexpr>   s     B1>Q&Bs   detailsu'   details에 BLOCK 패턴 언급 없음: N)mkdirr   	l1_verifystrany)selftmp_pathreports_dirtask_idreport_filer$   s         r   #test_ui_block_pattern_triggers_failzITestScenario1_L1SmoketestBlockPattern.test_ui_block_pattern_triggers_fail   s     )I5$'#!wisO3` 	 	

 7CM2h6) 	
abhaij	
) By0ABB 	
5fY6G5HI	
B    c                     |dz  dz  }|j                  d       d}|| dz  }|j                  dd	       t        |t        |            }|d
   dk(  sJ dj	                  |d         }d|v s
J d|        y)uD   details 필드에 매칭된 패턴 정보가 포함되어야 한다.r`   ra   Trb   ztest-scenario-1brd   u7   UI 직접 확인 불가 — 스크린샷 첨부 불가
r   r   re   r0    rk   u   UI 직접 확인 불가u*   details에 패턴이 포함되어야 함: N)rl   r   rm   rn   join)rp   rq   rr   rs   rt   r$   details_combineds          r   test_block_pattern_in_detailszCTestScenario1_L1SmoketestBlockPattern.test_block_pattern_in_details   s    )I5$'$!wisO3F 	 	

 7CM2h6)))88F9$56(,<< 	
89I8JK	
<rv   c                 B    t        dt        |            }|d   dk(  sJ y)uQ   보고서 파일이 없으면 SKIP을 반환해야 한다 (경계 조건 확인).znonexistent-task-9999re   SKIPN)rm   rn   rp   rq   r$   s      r    test_no_report_file_returns_skipzFTestScenario1_L1SmoketestBlockPattern.test_no_report_file_returns_skip   s&    2CMBh6)))rv   N)__name__
__module____qualname____doc__ru   r{   r   rg   rv   r   r^   r^      s    ]
,
(*rv   r^   c                   "    e Zd ZdZd Zd Zd Zy)TestScenario2_ImpactScannerWarnu_   수정된 A.py의 심볼을 B.py가 참조하고, B.py는 미수정이면 WARN이어야 한다.c                 b   |dz  }|dz  |j                  dd       j                  dd       t        t        |      dg      }|d   dk(  sJ d	|d           |d
   D cg c]  }|d   	 }}t        j                               t	        fd|D              s
J d|        yc c}w )uL   A.py 수정, B.py 미수정일 때 B.py에서 심볼 참조 감지 → WARN.A.pyB.pyul   def calculate_premium(base_rate, multiplier):
    """보험료 계산."""
    return base_rate * multiplier
r   r   zGfrom A import calculate_premium

monthly = calculate_premium(100, 1.5)
gate_resultrV   u?   미수정 파일 참조 감지 시 WARN 이어야 함. 실제: unmodified_referencesfilec              3   D   K   | ]  }|v xs t              |v   y w)N)rn   )rh   rfb_pyb_py_abss     r   rj   zXTestScenario2_ImpactScannerWarn.test_unmodified_reference_causes_warn.<locals>.<genexpr>   s%     I8r>4SY"_4Is    u/   B.py가 unmodified_references에 없음. refs: N)r   r   rn   resolvero   )rp   rq   a_pyr$   ref	ref_filesr   r   s         @@r   %test_unmodified_reference_causes_warnzETestScenario2_ImpactScannerWarn.test_unmodified_reference_causes_warn   s    & & 2 	 	 	
 	6 	 	
 c(mfX.m$. 	
MfUbNcMde	
. -33J,KLSS[L	Lt||~&IyII 	
=i[I	
I Ms   %B,c                     |dz  }|dz  }|j                  dd       |j                  dd       t        t        |      dg      }d|d   v sJ d	|d           y
)uR   scan() 결과의 symbols_checked에 추출된 심볼명이 포함되어야 한다.r   r   z'def calculate_premium(x):
    return x
r   r   z from A import calculate_premium
calculate_premiumsymbols_checkedu/   symbols_checked에 'calculate_premium' 없음: Nr   r   rn   )rp   rq   r   r   r$   s        r   +test_symbols_checked_contains_function_namezKTestScenario2_ImpactScannerWarn.test_symbols_checked_contains_function_name  s    & & 7 	 	
 	/ 	 	

 c(mfX."f->&?? 	
=fEV>W=XY	
?rv   c                     |dz  }|j                  dd       t        t        |      dg      }|d   dk(  sJ d|d           y)	u:   미수정 파일에 참조가 없으면 PASS여야 한다.r   z2def unique_isolated_function_xyz():
    return 42
r   r   r   r1   u-   참조 없으면 PASS 이어야 함. 실제: Nr   )rp   rq   r   r$   s       r   %test_no_unmodified_references_is_passzETestScenario2_ImpactScannerWarn.test_no_unmodified_references_is_pass  sa    & B 	 	

 c(mfX.m$. 	
;F=<Q;RS	
.rv   N)r   r   r   r   r   r   r   rg   rv   r   r   r      s    i
:
(
rv   r   c                   "    e Zd ZdZd Zd Zd Zy)TestScenario3_CIPreflightuw   항상 실패하는 pytest 테스트가 있는 디렉터리에 대해 ci_preflight.sh가 exit 1을 반환해야 한다.c                 l   |dz  j                  dd       |dz  j                  dd       t        j                  dt        t	        |      gddd	
      }|j
                  dk(  s.J d|j
                   d|j                   d|j                          d|j                  v sJ d|j                          y)uX   test_always_fail.py가 존재하는 프로젝트에서 exit code 1, overall=FAIL 확인.zrequirements.txtzpytest
r   r   ztest_always_fail.pyuJ   def test_always_fail():
    assert False, '항상 실패하는 테스트'
bashT<   r,   r-   timeoutr)   u3   pytest FAIL 시 exit code 1 이어야 함. 실제: 	
stdout: z	
stderr: zoverall=FAILu!   stdout에 'overall=FAIL' 없음: N)r   r>   r?   CI_PREFLIGHT_SCRIPTrn   rA   stdoutstderrr~   s      r   !test_failing_pytest_causes_exit_1z;TestScenario3_CIPreflight.test_failing_pytest_causes_exit_1+  s     
&	&22:2P	)	)55Z 	6 	

 (#h-8	
   A% 	
A&BSBSAT U}}oZ@	
% . 	
/?	
.rv   c                     |dz  j                  dd       t        j                  dt        t	        |      gddd      }|j
                  d	k(  s!J d
|j
                   d|j                          y)uu   테스트 파일이 없으면 pytest가 SKIP되고 overall=PASS 또는 No runners detected가 출력되어야 한다.zpyproject.tomlz[tool.pytest.ini_options]
r   r   r   Tr   r   r   u:   테스트 파일 없을 때 exit 0 이어야 함. 실제: r   N)r   r>   r?   r   rn   rA   r   r~   s      r   test_no_test_files_causes_passz8TestScenario3_CIPreflight.test_no_test_files_causes_passD  s     
$	$00) 	1 	
 (#h-8	
   A% 	
HIZIZH[ \}}o'	
%rv   c                     t        |dz        }t        j                  dt        |gddd      }|j                  dk(  sJ d|j                          y)	uX   존재하지 않는 디렉터리를 PROJECT_ROOT로 전달하면 exit 1이어야 한다.does_not_existr   T
   r   r)   uB   존재하지 않는 PROJECT_ROOT는 exit 1 이어야 함. 실제: N)rn   r>   r?   r   rA   )rp   rq   nonexistentr$   s       r   !test_invalid_project_root_exits_1z;TestScenario3_CIPreflight.test_invalid_project_root_exits_1Y  sc    (%556(+6	
   A% 	
PQWQbQbPcd	
%rv   N)r   r   r   r   r   r   r   rg   rv   r   r   r   (  s     B
2
*
rv   r   c                       e Zd ZdZd Zd Zy)#TestScenario4_GoalAssertionsAllPassuT   모든 goal_assertions 명령이 exit 0을 반환하면 GOAL-GATE PASS여야 한다.c                     |dz  }|j                  dt         dd       t        t        |            }|d   dk(  s
J d|        t	        |d	         d
k\  sJ d       |d	   D ]  \  }}|dk(  rJ d| d|         y)uD   grep으로 실제 존재하는 패턴을 검색 → exit 0 → PASS.task.mdz;# task-integration-4

## goal_assertions
- `grep -c "def " /scripts/impact_scanner.py`
r   r   r$   r1   u8   모든 assertion 통과 시 PASS 이어야 함. 실제: r&   r)   )   최소 1개 명령이 실행되어야 함r   u   명령 'u*   '의 exit code가 0이어야 함. 실제: Nr   WORKSPACE_ROOTrN   rn   len)rp   rq   	task_filer$   rJ   	exit_codes         r   *test_all_passing_assertions_result_in_passzNTestScenario4_GoalAssertionsAllPass.test_all_passing_assertions_result_in_passp  s    y(	##1"22OQ 	 	 	
 +3y>:h6) 	
FvhO	
) 6.)*a/\1\\/$^4 	iNC>hXcU2\]f\g#hh>	irv   c                     |dz  }|j                  dt         dd       t        t        |            }|d   dk(  s
J d|        t	        |d	         d
k(  sJ dt	        |d	                 y)u4   여러 assertion 명령이 모두 통과하면 PASS.r   z<# task-integration-4b

## goal_assertions
- `grep -c "def " zE/scripts/impact_scanner.py`
- `python3 -c "import sys; sys.exit(0)"`
r   r   r$   r1   u   모두 통과하면 PASS: r&      u,   2개 명령이 실행되어야 함. 실제: Nr   )rp   rq   r   r$   s       r   )test_multiple_passing_assertions_all_passzMTestScenario4_GoalAssertionsAllPass.test_multiple_passing_assertions_all_pass  s    y(	##1"2 3;<  	 	
 +3y>:h6)P-Gx+PP)6.)*a/ 	
:3vn?U;V:WX	
/rv   N)r   r   r   r   r   r   rg   rv   r   r   r   m  s    ^i(
rv   r   c                       e Zd ZdZd Zd Zy) TestScenario5_GoalAssertionsFailuS   mode=fail 상태에서 goal_assertions가 실패하면 BLOCKED 반환해야 한다.c                     |dz  }|j                  dt         dd       t        d      }|dk(  s
J d|        t        t	        |            }|d	   d
k(  s
J d|        y)uL   존재하지 않는 패턴을 grep → exit non-0 → mode=fail → BLOCKED.r   zQ# task-integration-5

## goal_assertions
- `grep -c "NONEXISTENT_PATTERN_xyz123" r   r   r   r"   r.   u3   이 시나리오는 mode=fail 전제. 실제 mode: r$   r/   u@   mode=fail + assertion 실패 → BLOCKED 이어야 함. 실제: N)r   r   r	   rN   rn   )rp   rq   r   moder$   s        r    test_failing_grep_causes_blockedzATestScenario5_GoalAssertionsFail.test_failing_grep_causes_blocked  s    y(	99G8HHeg 	 	 	
 ./v~ 	
A$H	
~ +3y>:h9, 	
NvhW	
,rv   c                    |dz  }|j                  dt         dd       t        t        |            }t	        |d         dk\  sJ d       |d   D cg c]  \  }}|d	k7  s||f }}}t	        |      dk\  sJ d
|d           yc c}}w )u:   실패한 명령의 exit_code가 0이 아니어야 한다.r   zR# task-integration-5b

## goal_assertions
- `grep -c "NONEXISTENT_PATTERN_xyz123" r   r   r   r&   r)   r   r   u=   실패한 명령이 최소 1개 있어야 함. commands_run: Nr   )rp   rq   r   r$   rJ   ecfailing_cmdss          r   (test_failing_assertion_exit_code_nonzerozITestScenario5_GoalAssertionsFail.test_failing_assertion_exit_code_nonzero  s    y(	99G8HHeg 	 	 	
 +3y>:6.)*a/\1\\/171GSgc22QR7b	SS< A% 	
KFSaLbKcd	
% Ts   B#BN)r   r   r   r   r   r   rg   rv   r   r   r     s    ]
,
rv   r   c                   "    e Zd ZdZd Zd Zd Zy)!TestScenario6_UnresolvedGateBlockuJ   max_in_scope_unresolved=3인데 count=4이면 BLOCKED 반환해야 한다.c                 t   t        d      j                  dd      }|dk(  s
J d|        |dz  }|j                  dd       t        t              j                  d      }	 t        j                  |      }d	|d
   d   d<   t        t              j                  t        j                  |dd      d       t        t        |            }t        t              j                  |d       |d   dk(  s
J d|        |d   dk(  sJ d|d           y# t        t              j                  |d       w xY w)u>   '범위 내 미해결' 4줄 → count(4) > max(3) → BLOCKED.rQ   rT   rU   u.   이 시나리오는 max=3 전제. 실제 max: 	report.mdu   # 보고서

- 범위 내 미해결: 이슈 A
- 범위 내 미해결: 이슈 B
- 범위 내 미해결: 이슈 C
- 범위 내 미해결: 이슈 D
r   r   r.   gatesr   r   Findentensure_asciir$   r/   u?   count=4 > max=3 + mode=fail → BLOCKED 이어야 함. 실제: rR      u   count는 4여야 함. 실제: N)r   r3   r   r   r   r   jsonloadsdumpsr\   rn   )rp   rq   max_valrt   original_configconfig_datar$   s          r   .test_four_unresolved_exceed_max_causes_blockedzPTestScenario6_UnresolvedGateBlock.test_four_unresolved_exceed_max_causes_blocked  s_    ##4599:SUVW!|WMgYWW|,1
  	 	
 /0::G:L		Q**_5K>DK !23F;!"--

;quEPW .  *#k*:;F!"--o-Ph9, 	
MfXV	
, g!#W'EfWoEV%WW# !"--o-Ps   #A*D "D7c                     |dz  }|j                  dd       t        t        |            }|d   dk7  s
J d|        |d   d	k(  sJ y
)uP   '범위 내 미해결' 정확히 3줄 → count == max → WARN (초과 아님).r   up   # 보고서

- 범위 내 미해결: 이슈 A
- 범위 내 미해결: 이슈 B
- 범위 내 미해결: 이슈 C
r   r   r$   r/   uK   count=3 == max=3은 초과가 아님 → BLOCKED 아니어야 함. 실제: rR   rU   Nr   r\   rn   rp   rq   rt   r$   s       r   0test_three_unresolved_within_max_is_warn_or_passzRTestScenario6_UnresolvedGateBlock.test_three_unresolved_within_max_is_warn_or_pass  sv    ,1  	 	
 &c+&67 h9, 	
YZ`Yab	
, g!###rv   c                     |dz  }|j                  dd       t        t        |            }|d   dk(  s
J d|        |d   d	k(  sJ y
)u    미해결 이슈 없으면 PASS.r   u&   # 보고서

모든 이슈 해결됨.
r   r   r$   r1   u   미해결 없으면 PASS: rR   r   Nr   r   s       r   test_zero_unresolved_is_passz>TestScenario6_UnresolvedGateBlock.test_zero_unresolved_is_pass  sb    ,JU\]%c+&67h6)P-Gx+PP)g!###rv   N)r   r   r   r   r   r   r   rg   rv   r   r   r     s    T XD$&$rv   r   c                       e Zd ZdZej
                  j                  d      d        Zej
                  j                  d      d        Zy)TestScenario7_WarnModeNoBlockuk   goal_assertions mode=warn이면 assertion 실패 시 BLOCKED가 아닌 FAIL(경고)만 반환해야 한다.r   c                    t        j                  t        t              j	                  d            }d|d   d   d<   t        t              j                  t        j                  |dd	      d       |d
z  }|j                  dt         dd       t        t        |            }|d   dk7  s
J d|        |d   dk(  s
J d|        |d   dk(  sJ d|d           y)u   
        gate-config.json goal_assertions mode를 warn으로 변경 후
        failing assertion 실행 → BLOCKED가 아닌 FAIL (경고) 반환.
        gate_config_backup 픽스처가 테스트 후 원본 자동 복원 보장.
        r   r   warnr   r"   r   r   Fr   r   zQ# task-integration-7

## goal_assertions
- `grep -c "NONEXISTENT_PATTERN_xyz123" r   r$   r/   uC   mode=warn + assertion 실패 → BLOCKED 아니어야 함. 실제: r0   uM   mode=warn + assertion 실패 → FAIL 이어야 함 (블록 없음). 실제: r%   u)   gate_mode가 warn 이어야 함. 실제: N)
r   r   r   r   r   r   r   r   rN   rn   )rp   rq   r   r   r$   s        r   )test_warn_mode_failing_assertion_no_blockzGTestScenario7_WarnModeNoBlock.test_warn_mode_failing_assertion_no_block  s*    jj&6!7!A!A7!A!ST:@G./7))JJ{15AG 	* 	
 y(	99G8HHeg 	 	 	
 +3y>: h9, 	
QRXQYZ	
, h6) 	
[\b[cd	
) k"f, 	
7{8K7LM	
,rv   c                    t        j                  t        t              j	                  d            }d|d   d   d<   t        t              j                  t        j                  |dd	      d       t        d      }|dk(  s
J d
|        y)u   
        gate_config_backup 픽스처 동작 검증 — 테스트 후 원본 mode=fail 복원 확인.
        이 테스트는 gate_config_backup을 사용하여 설정을 변경 후 픽스처가 복원하는지 확인.
        r   r   r   r   r"   r   r   Fr   u/   변경 후 mode는 warn 이어야 함. 실제: N)r   r   r   r   r   r   r   r	   )rp   r   changed_modes      r   )test_gate_config_restored_after_scenario7zGTestScenario7_WarnModeNoBlock.test_gate_config_restored_after_scenario78  s     jj&6!7!A!A7!A!ST:@G./7))JJ{15AG 	* 	

 %%67v%g)XYeXf'gg%rv   N)	r   r   r   r   r   markusefixturesr   r   rg   rv   r   r   r     sN    u[[12 
 3 
D [[12h 3hrv   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)%TestScenario8_AutoInjectAffectedFilesuh   FeatureGate 백틱 토큰이 있는 task_desc에 affected_files 섹션이 자동 주입되어야 한다.c                 D    d}t        |t              }d|v s
J d|        y)ui   `FeatureGate` 토큰 → grep → 2개 이상 파일 → ## affected_files (auto-detected) 섹션 생성.uH   다음 클래스를 확인하세요: `FeatureGate` 관련 변경 사항.!## affected_files (auto-detected)u>   '## affected_files (auto-detected)' 섹션이 없음.
결과:
Nr   r   rp   	task_descr$   s      r   5test_backtick_token_triggers_affected_files_injectionz[TestScenario8_AutoInjectAffectedFiles.test_backtick_token_triggers_affected_files_injectionT  s2    ^	,YG2f< 	
NvhW	
<rv   c                 b   d}t        |t              }t        j                  d|t        j                        }|
J d|        |j                  d      j                         }|j                  d      D cg c]  }|j                  d      s| }}t        |      dk\  s
J d	|        yc c}w )
u{   FeatureGate는 tests/test_dispatch_auto_inject.py와 scripts/tests/test_impact_scanner.py에 존재 — 2개 이상 감지.u+   클래스 `FeatureGate`를 수정하세요.z5## affected_files \(auto-detected\)\n(.*?)(?=\n##|\Z)Nu   affected_files 섹션 없음:
r)   r*   z- r   u=   최소 2개 파일이 감지되어야 함. 감지된 파일:
)
r   r   r8   r9   r:   r;   r<   r=   
startswithr   )rp   r   r$   section_matchfiles_sectionrH   
file_liness          r    test_at_least_two_files_detectedzFTestScenario8_AutoInjectAffectedFiles.test_at_least_two_files_detected^  s    A	,YG 		DDD

 (T,KF8*TT(%++A.446'4':':4'@ZtDOOTXDYdZ
Z:!# 	
L]O\	
# [s   6B,B,c                 N    d}t        |t              }d|v sd|v s
J d|        yy)ur   알려진 참조 파일(test_dispatch_auto_inject.py, test_impact_scanner.py)이 목록에 포함되어야 한다.u+   클래스 `FeatureGate`를 분석하세요.ztest_dispatch_auto_inject.pyztest_impact_scanner.pyuI   알려진 FeatureGate 참조 파일이 affected_files에 없음.
결과:
Nr   r   s      r   !test_known_files_in_affected_listzGTestScenario8_AutoInjectAffectedFiles.test_known_files_in_affected_listr  sC    A	,YG-7;SW];] 	
YZ`Yab	
];]7rv   c                 P    d}t        |t              }d|vs
J d|        d|v sJ y)uR   이미 ## affected_files 섹션이 있으면 중복 주입하지 않아야 한다.uU   클래스 `FeatureGate`를 수정하세요.

## affected_files
- src/feature_gate.py
r   uK   이미 affected_files 있으면 auto-detected 섹션 추가 없어야 함:
z## affected_filesNr   r   s      r   &test_no_injection_when_already_presentzLTestScenario8_AutoInjectAffectedFiles.test_no_injection_when_already_present|  sM    & 	 -YG 3&@ 	
Z[aZbc	
@ #f,,,rv   c                 V    d}t        |t              }t        |t              sJ d       y)uh   COMMON_FILTER에 해당하는 토큰(예: `data`, `config`)은 grep 대상에서 제외되어야 한다.u-   다음 `data`와 `config`를 확인하세요.u#   반환값은 문자열이어야 함N)r   r   
isinstancern   r   s      r   &test_common_filter_tokens_not_injectedzLTestScenario8_AutoInjectAffectedFiles.test_common_filter_tokens_not_injected  s.     D	 -YG&#&M(MM&rv   N)	r   r   r   r   r   r   r   r   r   rg   rv   r   r   r   Q  s    r

(
-"	Nrv   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestGateConfigConsistencyu]   gate-config.json 설정이 기대값과 일치하는지 확인하는 회귀 방지 테스트.c                 F    g d}|D ]  }t        |      rJ d| d        y)u^   impact_scanner, ci_preflight, l1_smoketest, goal_assertions, unresolved_gate 모두 활성화.)impact_scannerci_preflightl1_smoketestr"   rQ   u   게이트 'u   '가 비활성화 상태N)r   )rp   required_gatesgates      r   test_all_required_gates_enabledz9TestGateConfigConsistency.test_all_required_gates_enabled  s9    
 # 	WD"4(VKv=U*VV(	Wrv   c                 8    t        d      }|dk(  s
J d|        y)u6   goal_assertions 기본 mode는 'fail'이어야 한다.r"   r.   u4   goal_assertions mode는 fail 이어야 함. 실제: Nr	   rp   r   s     r   !test_goal_assertions_mode_is_failz;TestGateConfigConsistency.test_goal_assertions_mode_is_fail  s(    ./v~\!UVZU[\\~rv   c                 8    t        d      }|dk(  s
J d|        y)u3   l1_smoketest 기본 mode는 'fail'이어야 한다.r  r.   u1   l1_smoketest mode는 fail 이어야 함. 실제: Nr  r  s     r   test_l1_smoketest_mode_is_failz8TestGateConfigConsistency.test_l1_smoketest_mode_is_fail  s'    ^,v~Y!RSWRXYY~rv   c                 V    t        d      }|j                  d      dk(  s
J d|        y)u@   unresolved_gate의 max_in_scope_unresolved는 3이어야 한다.rQ   rT   rU   u3   max_in_scope_unresolved는 3이어야 함. 실제: N)r   r3   )rp   cfgs     r   !test_unresolved_gate_max_is_threez;TestGateConfigConsistency.test_unresolved_gate_max_is_three  s6    01ww01Q6 	
A#G	
6rv   c                 z    t        d      }t        |j                  dg             }h d}||z
  }|r
J d|        y)uO   goal_assertions allowed_commands에 핵심 명령어가 포함되어야 한다.r"   r'   >   r   r   r   r   u#   allowed_commands에 없는 명령: N)r   r2   r3   )rp   r  allowedexpectedmissings        r   /test_goal_assertions_allowed_commands_whitelistzITestGateConfigConsistency.test_goal_assertions_allowed_commands_whitelist  sI    01cgg0"568W$KA'KK{7rv   N)	r   r   r   r   r  r  r
  r  r  rg   rv   r   r   r     s"    g
W]
Z

Lrv   r   )*r   r   rW   r8   r>   syspathlibr   r   r   rX   insertr   r   l1_smoketest_checkr   rm   dispatchr   utils.gate_config_loaderr   r   r	   ry   r   r   r5   fixturer   rn   dictrN   r\   r^   r   r   r   r   r   r   r   r   rg   rv   r   <module>r     s    	 	  
  
 '!HHOOA~& 0HHOOA45/sxx?HHOOACD
   2 0 U U
 77<<:LM ggll>9>OP Z  J J7Tc 7Td 7Tt#]3 #]4 #]T0* 0*n@
 @
N>
 >
J'
 '
\)
 )
`@$ @$N5h 5h~EN ENX&L &Lrv   