
    Xi_                     Z   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Z e
d      Z ee      ej                  vr"ej                  j                  d ee             dad Z ej$                  d      d        Z G d	 d
      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)u  
task-2431 회귀 테스트: post_merge_probe.py Scope-aware (단일 책임)

목적:
    익스텐션 PR이 server/tests 환경 결함으로 자동 revert되는 사고(task-2423/2429)를
    영구 차단한다. 본 task는 changed-path 기반 scope 판정과 scoped test 실행만
    책임진다 (baseline pre-flight는 별도 P1 task scope 외).

검증 시나리오:
    1. test_extension_pr_with_server_fail_does_not_revert — 핵심 회귀
    2. test_extension_pr_with_extension_fail_triggers_revert
    3. test_server_pr_with_server_fail_triggers_revert
    4. test_unmapped_changed_paths_runs_smoke_only
    5. test_no_changed_paths_fallback_to_smoke_not_full — full sweep 금지 회귀 가드
    6. test_run_tests_scoped_passes_rootdir_to_pytest — pytest 실제 인자 검증
    7. test_run_probe_does_not_have_baseline_check — baseline 코드 부재 회귀 가드
    8. test_resolve_test_scope_unions_multiple_areas — 단위
    9. test_changed_paths_returns_empty_on_git_failure — 단위
    N)Pathz/home/jay/workspacec                     t         t         S ddl} | j                  j                  dt        dz  dz        }||j
                  t        d      | j                  j                  |      }|j
                  j                  |       |a |S )u+  post_merge_probe 모듈을 한 번만 로드하여 캐시.

    fixture가 monkeypatch한 모듈 인스턴스를 테스트가 그대로 받도록
    프로세스 단위 캐시를 사용한다. monkeypatch는 fixture finalize 시
    자동 복원되므로 테스트 간 격리는 유지된다.
    Nr   post_merge_probescriptszpost_merge_probe.pyz+Could not load post_merge_probe module spec)	_PROBE_MODULE_CACHEimportlib.utilutilspec_from_file_location
_WORKSPACEloaderImportErrormodule_from_specexec_module)	importlibspecmods      @/home/jay/workspace/tests/scripts/test_post_merge_probe_scope.py_fresh_probe_moduler   +   s     &"">>11Y!66D |t{{*GHH
..
)
)$
/CKKC J    T)autousec              #      K   |dz  dz  }|dz  dz  }|j                  dd       |j                  dd       t               }| j                  |d|       | j                  |d|dz         | y	w)
uW   PROBE_MARK_DIR, AUDIT_LOG를 tmp_path 아래로 리다이렉트하여 테스트 격리.memoryeventsauditTparentsexist_okPROBE_MARK_DIR	AUDIT_LOGzauto-merge.logN)mkdirr   setattr)monkeypatchtmp_path
events_dir	audit_dirprobes        r   isolate_pathsr'   H   s      H$x/J8#g-ITD1OOD4O0!E/<{I8H,HI
s   A/A1c                   (    e Zd ZU dZeed<   d Zd Zy)_DummyPopenu8   subprocess.Popen 대체 — 호출 횟수/인자 기록.callsc                 B    t         j                  j                  |       y N)r)   r*   append)selfargs_kwargss      r   __init__z_DummyPopen.__init___   s      &r   c                      y)Nr    )r.   s    r   waitz_DummyPopen.waitb   s    r   N)__name__
__module____qualname____doc__list__annotations__r1   r4   r3   r   r   r)   r)   [   s    BK'r   r)   c                  $    g t         _        t         S )u/   DummyPopen 클래스를 초기화하고 반환.)r)   r*   r3   r   r   _make_popen_recorderr<   f   s    Kr   c                    t               }|dz  dz  }|j                  d       t               }| j                  t        d|       | j                  |dd        | j                  |dd	        | j                  |d
d        | j                  dd        |j                  dd|d      }|j                  }t        |      }d}||k(  }	|	s t        j                  d|	fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }
t        j                  d      dz   d|
iz  }t        t        j                   |            dx}x}x}	}|j"                  }d} ||      }d}||v }|st        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                  |      d z  }t        j                  d!|j#                  d             d"z   d#|iz  }t        t        j                   |            dx}x}x}x}}|j"                  }d$}i } |||      }|j"                  }d%} ||      }d&}||k(  }|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                  |      t        j                  |      t        j                  |      t        j                  |      d(	z  }t        j                  d)|j#                  d$i       j#                  d%             d*z   d+|iz  }t        t        j                   |            dx}x}x}x}x}x}x}x}}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                  d0|j#                  d,             d1z   d2|iz  }t        t        j                   |            dx}}y)3u2  changed_paths가 extension/ 파일만 포함하면 server/tests fail은 무시되어야 한다.

    _resolve_test_scope → mode="scoped", test_paths=["extension/__tests__/"]
    _run_tests_scoped는 extension/__tests__/만 실행하고 PASS 반환.
    auto_revert(Popen) 호출이 없어야 한다.
    	extension	__tests__Tr   Popen_changed_pathsc                 
    ddgS )Nextension/content.jsextension/popup.jsr3   project_path	merge_shas     r   <lambda>zDtest_extension_pr_with_server_fail_does_not_revert.<locals>.<lambda>   s    9OQe8f r   _resolve_test_scopec                     dgdfS Nextension/__tests__/scopedr3   changeds    r   rI   zDtest_extension_pr_with_server_fail_does_not_revert.<locals>.<lambda>       *@)A8(L r   _run_tests_scopedc                      y)N)Tzextension tests okr3   rG   
test_pathss     r   rI   zDtest_extension_pr_with_server_fail_does_not_revert.<locals>.<lambda>       r   
time.sleepc                      y r,   r3   ss    r   rI   zDtest_extension_pr_with_server_fail_does_not_revert.<locals>.<lambda>   rV   r   ztask-ext-testabc123r   delay==zK%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.calls
})
} == %(py8)slen
popen_mockpy0py1py3py5py8u]   auto_revert가 호출되지 않아야 함: server/tests 환경 fail은 extension PR과 무관
>assert %(py10)spy10Noutcome)
probe_passscoped_passinzI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} in %(py9)sresultrd   py2py4py6py9u(   probe 결과가 PASS여야 함, 실제: 
>assert %(py11)spy11scopemoderN   )z%(py14)s
{%(py14)s = %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s, %(py6)s)
}.get
}(%(py12)s)
} == %(py17)s)	rd   rs   rt   ru   rh   rj   py12py14py17u$   scope.mode='scoped' 기대, 실제: z
>assert %(py19)spy19baseline_checknot inz%(py1)s not in %(py3)sre   rf   :   baseline_check 키는 본 task에서 제거되어야 함: 
>assert %(py5)srg   r   r    r<   r!   
subprocess	run_prober*   ra   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationget)r"   r#   r&   	ext_testsrb   rq   @py_assert2@py_assert4@py_assert7@py_assert6@py_format9@py_format11@py_assert1@py_assert3@py_assert5@py_assert8@py_format10@py_format12@py_assert9@py_assert11@py_assert13@py_assert16@py_assert15@py_format18@py_format20@py_assert0@py_format4@py_format6s                               r   2test_extension_pr_with_server_fail_does_not_revertr   q   s$     !E ;&4IOODO!%'J
GZ8 /fh 4LN 2UW n5___h_JF 3  A  A%    A                             !    %&    	h      :: i :i  $A  $AA   $A                       !    %B    36::i3H2IJ      :: g r :gr" "&& v &v. ( .(:   .(                       "    #    '    (.    /    3;    /vzz'2/F/J/J6/R.ST        6)  6          $*    $*    EVZZP`EaDbc    r   c                 ^   t               }|dz  dz  }|j                  d       t               }| j                  t        d|       | j                  |dd        | j                  |dd	        | j                  |d
d        | j                  dd        |j                  dd|d      }|j                  }t        |      }d}||k(  }	|	s6t        j                  d|	fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }
t        j                  dt        |j                               dz   d|
iz  }t        t        j                   |            dx}x}x}	}|j"                  }d} ||      }d}||v }|st        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                  |      d!z  }t        j                  d"|j#                  d             d#z   d$|iz  }t        t        j                   |            dx}x}x}x}}y)%u   changed_paths=extension/, _run_tests_scoped는 extension/__tests__/에서 fail.

    baseline에서도 fail이 아닐 때 auto_revert가 1회 호출되어야 한다.
    r>   r?   Tr@   rA   rB   c                     dgS NrD   r3   rF   s     r   rI   zGtest_extension_pr_with_extension_fail_triggers_revert.<locals>.<lambda>   
    9O8P r   rJ   c                     dgdfS rL   r3   rO   s    r   rI   zGtest_extension_pr_with_extension_fail_triggers_revert.<locals>.<lambda>   rQ   r   rR   c                      y)N)Fz&FAILED 2 tests in extension/__tests__/r3   rT   s     r   rI   zGtest_extension_pr_with_extension_fail_triggers_revert.<locals>.<lambda>   rV   r   rW   c                      y r,   r3   rY   s    r   rI   zGtest_extension_pr_with_extension_fail_triggers_revert.<locals>.<lambda>   rV   r   ztask-ext-faildef456r   r\      r^   r`   ra   rb   rc   u?   auto_revert가 1회 호출되어야 함, 실제 호출 횟수: ri   rj   Nrk   )
probe_failscoped_failrn   rp   rq   rr   u+   probe 결과가 FAIL이어야 함, 실제: rw   rx   r   )r"   r#   r&   r   rb   rq   r   r   r   r   r   r   r   r   r   r   r   r   s                     r   5test_extension_pr_with_extension_fail_triggers_revertr      s   
  !E;&4IOODO!%'J
GZ8/PR4LN 2jl n5___h_JF 3  A  A%    A                             !    %&    J#jN^N^J_I`a      :: i :i  $A  $AA   $A                       !    %B    6fjj6K5LM     r   c                    t               }|dz  dz  }|j                  d       t               }| j                  t        d|       | j                  |dd        | j                  |dd	        | j                  |d
d        | j                  dd        |j                  dd|d      }|j                  }t        |      }d}||k(  }	|	s6t        j                  d|	fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }
t        j                  dt        |j                               dz   d|
iz  }t        t        j                   |            dx}x}x}	}y)u   changed_paths=server/, server/tests fail은 server PR에 대한 정당한 revert 사유다.

    auto_revert가 1회 호출되어야 한다.
    servertestsTr@   rA   rB   c                 
    ddgS )Nzserver/app.pyserver/routes.pyr3   rF   s     r   rI   zAtest_server_pr_with_server_fail_triggers_revert.<locals>.<lambda>   s    J\8] r   rJ   c                     dgdfS )Nzserver/tests/rN   r3   rO   s    r   rI   zAtest_server_pr_with_server_fail_triggers_revert.<locals>.<lambda>   s    /):H(E r   rR   c                      y)N)Fz5ModuleNotFoundError: No module named 'tests.conftest'r3   rT   s     r   rI   zAtest_server_pr_with_server_fail_triggers_revert.<locals>.<lambda>   rV   r   rW   c                      y r,   r3   rY   s    r   rI   zAtest_server_pr_with_server_fail_triggers_revert.<locals>.<lambda>   rV   r   ztask-server-failghi789r   r\   r   r^   r`   ra   rb   rc   uP   server PR의 server/tests fail은 revert를 트리거해야 함, 호출 횟수: ri   rj   N)r   r    r<   r!   r   r   r*   ra   r   r   r   r   r   r   r   r   r   )r"   r#   r&   server_testsrb   rq   r   r   r   r   r   r   s               r   /test_server_pr_with_server_fail_triggers_revertr      s   
  !Eh&0Lt$%'J
GZ8/]_4EG 2y{ n5__/81_MF 3  A  A%    A                             !    %&    [[^_i_o_o[pZqr     r   c                 l   t               |dz  dz  }|j                  d       | j                  dd        i fd}| j                  d|       | j                  d	d
        | j                  t        dt	                      | j                  dd        j                  dd|d       j                  }d} ||      }d}||k(  }|st        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                  |      dz  }	t        j                  dj                  d             dz   d|	iz  }
t        t        j                  |
            dx}x}x}x}}t!        ddg      }j                  }d} ||      }||k(  }|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                  |      dt        j                         v st        j                  |      rt        j                  |      ndd z  }t        j                  d!j                  d             d"z   d#|iz  }t        t        j                  |            dx}x}x}}y)$u   changed_paths가 docs/ README.md 등 SCOPE_MAP 미매치 경로일 때
    mode='smoke', test_paths=SMOKE_TEST_PATHS 가 반환되어야 한다.
    r   smokeTr@   rB   c                 
    ddgS )Nzdocs/README.mdzdocs/api.mdr3   rF   s     r   rI   z=test_unmapped_changed_paths_runs_smoke_only.<locals>.<lambda>   s    9I=8Y r   c                 @   t        di       }g }|j                         D ]+  \  }t        fd| D              s|j                  |       - |st        ddg      }dd<   |d<   |dfS dd<   t	        t        |            d<   t	        t        |            dfS )	N	SCOPE_MAPc              3   @   K   | ]  }|j                          y wr,   )
startswith).0pprefixs     r   	<genexpr>zUtest_unmapped_changed_paths_runs_smoke_only.<locals>._mock_resolve.<locals>.<genexpr>  s     ?A1<<'?s   SMOKE_TEST_PATHStests/smoke/r   rz   rU   rN   )getattritemsanyextendsortedset)changed_paths	scope_mapmatched	test_dirssmoke_pathsr   capturedr&   s        @r   _mock_resolvezBtest_unmapped_changed_paths_runs_smoke_only.<locals>._mock_resolve  s    E;3	!*!2 	*FI???y)	* !%);n=MNK&HV%0H\"))#!'G!5s7|$h//r   rJ   rR   c                      y)NTzsmoke okr3   rT   s     r   rI   z=test_unmapped_changed_paths_runs_smoke_only.<locals>.<lambda>  rV   r   rA   rW   c                      y r,   r3   rY   s    r   rI   z=test_unmapped_changed_paths_runs_smoke_only.<locals>.<lambda>  rV   r   z	task-docsjkl012r   r\   rz   r^   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sr   rr   u:   SCOPE_MAP 미매치 PR은 smoke 모드여야 함, 실제: rw   rx   Nr   r   rU   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py8)ssmoke_expected)rd   rs   rt   ru   rh   u:   smoke 모드의 test_paths는 SMOKE_TEST_PATHS여야 함: ri   rj   )r   r    r!   r   r<   r   r   r   r   r   r   r   r   r   r   r   r   )r"   r#   	smoke_dirr   r   r   r   r   r   r   r   r   r   r   r   r&   s                 @@r   +test_unmapped_changed_paths_runs_smoke_onlyr      s     !E 7"W,IOODO!/Y[ H0  4mD2KM
G-A-CDn5	OOK81O=<<  < 7 7*  7                           $+    EX\\RXEYDZ[      U$68HIN<<  <% %7   %                  %    &      *8    *8    EX\\R^E_D`a     r   c                 
   t               }| j                  |dd        i fd}| j                  |d|       | j                  t        dt                      | j                  dd        |j	                  dd	|d
      }|j                  di       j                  d      }d}||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}||k7  }|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   }
|j                  }|
|k(  }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	t        j                  dd          dz   d |	iz  }t        t        j                  |            dx}
x}}y)!u   _changed_paths가 빈 list 반환(merge_sha 없음 또는 git diff 실패)할 때
    run_probe는 mode='smoke'로 fallback해야 한다. 'full' 모드는 절대 사용 금지.
    rB   c                     g S r,   r3   rF   s     r   rI   zBtest_no_changed_paths_fallback_to_smoke_not_full.<locals>.<lambda>/  s     r   c                 "    t        |      d<   y)NrU   r   r9   )rG   rU   r   s     r   _capture_scopedzItest_no_changed_paths_fallback_to_smoke_not_full.<locals>._capture_scoped4  s    !%j!1!r   rR   rA   rW   c                      y r,   r3   rY   s    r   rI   zBtest_no_changed_paths_fallback_to_smoke_not_full.<locals>.<lambda>:  rV   r   ztask-no-diffxyz999r   r\   ry   rz   r   r^   z%(py0)s == %(py3)srd   rf   uG   changed_paths 빈 경우 mode='smoke'여야 함 (full 금지), 실제: r   rg   Nfull)!=)z%(py0)s != %(py3)su"   full sweep은 절대 사용 금지rU   )z8%(py1)s == %(py5)s
{%(py5)s = %(py3)s.SMOKE_TEST_PATHS
}r&   )re   rf   rg   uD   smoke fallback 시 SMOKE_TEST_PATHS로 호출되어야 함, 실제: 
>assert %(py7)spy7)r   r!   r   r<   r   r   r   r   r   r   r   r   r   r   r   r   )r"   r#   r&   r   rq   rz   r   r   r   r   r   r   @py_format8r   s                @r   0test_no_changed_paths_fallback_to_smoke_not_fullr   '  sv     !E /:< H" 2OD
G-A-CDn5__^Xxq_IF::gr"&&v.D 47?  47                  RRVQYZ     ?46>???46??????4???4???6??????????L! U%;%; !%;;  !%;    "      &+    &+    &<    OxXdOeNfg     r   c                    ! t               }|dz  dz  }|j                  d       i ! G d d        !fd}| j                  t        d|       |j	                  |d	g      \  }}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                  dg       }|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}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}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                  d      }||d#z      }t!        |      }||k(  }|st        j                  d|fd$||f      t        j                  |      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(||d#z             d)z   d*|iz  }t        t        j                  |            dx}x}}g }d+}||v }|}|rd,}||v }|}|sqt        j                  d|fd-||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndd.z  }
d/d0|
iz  }|j#                  |       |rt        j                  dfd1|f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndd2z  }d3d4|iz  }|j#                  |       t        j$                  |d      i z  }t        j                  d5|       d6z   d7|iz  }t        t        j                  |            dx}x}x}x}x}}!d8   }|j                  }d9} ||      }||k(  }|s t        j                  d|fd:||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d&t        j                         v st        j                  |      rt        j                  |      nd&d;z  }t        j                  d<!d8   j                  d9             d=z   d>|iz  }t        t        j                  |            dx}x}x}x}}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   _run_tests_scoped가 subprocess.run 호출 시 --rootdir과 -p no:cacheprovider를
    전달해야 한다. (collection 부작용 차단 + scope 강제)
    r>   r?   Tr@   c                       e Zd ZdZdZdZy)@test_run_tests_scoped_passes_rootdir_to_pytest.<locals>._FakeRunr   z1 passed Nr5   r6   r7   
returncodestdoutstderrr3   r   r   _FakeRunr   Z  s    
r   r   c                 8    t        |       d<   |d<           S )Nr/   kwargsr   )r/   r   r   r   s     r   	_fake_runzAtest_run_tests_scoped_passes_rootdir_to_pytest.<locals>._fake_run_  s#    :#zr   runrM   isz%(py0)s is %(py3)sokr   u,   테스트가 PASS여야 함, 실제 출력: r   rg   Nr/   u%   subprocess.run이 호출되어야 함z
>assert %(py0)srd   r   pytestr^   )z%(py1)s == %(py4)s)re   rt   u    첫 인자는 pytest여야 함: z
>assert %(py6)sru   z	--rootdirrn   )z%(py1)s in %(py3)sr   u:   --rootdir 옵션이 pytest 인자에 포함되어야 함: r   )z0%(py1)s == %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}strr#   )re   rf   rt   ru   u)   --rootdir 값은 project_path여야 함: 
>assert %(py8)srh   z-pzno:cacheproviderz%(py3)s in %(py5)srf   rg   %(py7)sr   )z%(py10)s in %(py12)s)rj   r{   z%(py14)sr|   u3   -p no:cacheprovider 옵션이 포함되어야 함: z
>assert %(py17)sr}   r   cwd)zI%(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.get
}(%(py5)s)
} == %(py9)s)re   rf   rg   r   rv   u   cwd는 project_path여야 함: rw   rx   u:   scoped test path가 pytest 인자에 포함되어야 함: )r   r    r!   r   rR   r   r   r   r   r   r   r   r   r   r   indexr  r-   _format_boolop)"r"   r#   r&   
scoped_dirr   r  outr   r   r   r   r/   @py_format1r   r   @py_format5@py_format7rootdir_idxr   r   r   r   r   r   @py_format13@py_format15@py_format16r   r   r   r   r   r   r   s"                                   @@r   .test_run_tests_scoped_passes_rootdir_to_pytestr  N  sY     !E K'+5JT"H 

 
E95%%h1G0HIGBK2:KKK2KKKKKK2KKK2KKKKKKEcUKKKKKKK<<#D88888888848884888887IhI7hIII7hIII7IIIhIII"B4& IIIIIIII ;$  ;$                  ETFK     **[)Ka  CM  M1    M    !      %(    %(      )1    )1    %2    4Dq4I3JK    4 44< . .$6   44                   .$    /      37    37        >dVD      H !! % !%( (H4   (H        "    #(    )      -5    -5    *(8*<*@*@*G)HI      " !T)  !T    "      &*    &*    ETFK    r   c                 	   t               }|dz  dz  }|j                  dd       | j                  |d|       |dz  dz  }|j                  d       t               }| j                  t        d	|       | j                  |d
d        | j                  |dd        | j                  |dd        | j                  dd        d}|j                  |d|d      }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|j                  d             dz   d|
iz  }t        t        j                  |            dx}}	|| 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}x}}d$}	t#        ||	      }| }|st        j                  d%      d&z   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        t        j                  |            dx}	x}}|j$                  }	t'        |	      }d*}||k(  }|s7t        j                  d+|fd,||f      d-t        j                         v st        j                  t&              rt        j                  t&              nd-d.t        j                         v st        j                  |      rt        j                  |      nd.t        j                  |	      t        j                  |      t        j                  |      d/z  }t        j                  d0t'        |j$                         d1      d2z   d3|iz  }t        t        j                  |            dx}	x}x}}y)4u   probe FAIL 시에도 record["baseline_check"] 키는 절대 존재하면 안 된다.
    baseline pre-flight는 별도 task scope이며, 본 task는 단일 책임만 가진다.
    또한 .probe-baseline-fail 마커도 생성되면 안 된다.
    r   r   Tr   r   r>   r?   r@   rA   rB   c                     dgS r   r3   rF   s     r   rI   z=test_run_probe_does_not_have_baseline_check.<locals>.<lambda>  r   r   rJ   c                     dgdfS rL   r3   rO   s    r   rI   z=test_run_probe_does_not_have_baseline_check.<locals>.<lambda>  rQ   r   rR   c                      y)N)Fzfail in extensionr3   rT   s     r   rI   z=test_run_probe_does_not_have_baseline_check.<locals>.<lambda>  rV   r   rW   c                      y r,   r3   rY   s    r   rI   z=test_run_probe_does_not_have_baseline_check.<locals>.<lambda>  rV   r   ztask-no-baselinedeadbeefr   r\   r   r   r   rq   r   r   r   rg   Nz.probe-baseline-failuK   .probe-baseline-fail 마커는 본 task에서 절대 생성되면 안 됨: zG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}marker_filerd   rs   rt   _baseline_test_checkuK   _baseline_test_check 함수는 본 task에서 완전히 제거되어야 함z;
>assert not %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr&   rd   re   rf   rg   r   r^   r`   ra   rb   rc   uL   baseline 게이트가 없으니 probe FAIL은 즉시 auto_revert 트리거: u   회ri   rj   )r   r    r!   r<   r   r   r   r   r   r   r   r   r   r   r   r   existsr  r*   ra   )r"   r#   r&   r$   r   rb   task_idrq   r   r   r   r   r  r   r   r   r   r   r  r   r   r   s                         r   +test_run_probe_does_not_have_baseline_checkr#    sw   
  !EH$x/JTD1/<;&4IOODO!%'J
GZ8/PR4LN 2UWn5 G__Wj(!_DF  6)  6          $*    $*    EVZZP`EaDbc     '*>??K!! !# ## #   VVaUbc             "    $      5 wu45 55 5   	V                       5    6       3  A  A%    A                             !    %&    WWZ[e[k[kWlVmmpq     r   c                  P   	 t               } | j                  }| j                  }ddg} |      \  }}j                  dg       }|j                  dg       }t        t        ||z               }d}	||	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}
}	t        |      }	|	|k(  }|sHt        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                  |	      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d| dt        |             dz   d|iz  }t#        t        j$                  |            dx}	} ||      \  }}||k(  }
|
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z   d|iz  }t#        t        j$                  |            d}
y# t        t        f$ r t        j                  d       Y w xY w)u   changed_paths에 extension/과 server/ 파일이 모두 있으면
    두 영역의 test_paths를 union하고 결정적으로 정렬해야 한다.
    u6   토르 구현 대기 — _resolve_test_scope 미존재rD   r   z
extension/zserver/rN   r^   r   rz   r   u$   mode는 'scoped'여야 함, 실제: r   rg   N)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)sr   rU   expected_unionr   u'   test_paths union이 잘못됨. 기대: u
   , 실제: r   r   )z%(py0)s == %(py2)stest_paths2)rd   rs   u@   동일 입력에 대해 test_paths 순서가 일관되어야 함z
>assert %(py4)srt   )r   rJ   r   r   AttributeErrorr  skipr   r   r   r   r   r   r   r   r   r   r   r   )r&   _resolver   rP   rU   rz   expected_extensionexpected_serverr%  r   r   r   r   r   r   r&  _@py_format3r  s                      r   -test_resolve_test_scope_unions_multiple_areasr.    s   N#%,,OO	 &'9:G(J"|R8mmIr2OC 2_ DEFNJ48JJJ48JJJJJJ4JJJ4JJJ8JJJCD6JJJJJJJ* /                                 "0    "0    2.1AFS]L^K_`     g&NK$hhh:hhhhhh:hhh:hhhhhhhhhhhhh&hhhhhhh! ( NLMNs   "M= =$N%$N%c                 t   	 t               }|j                  } G d d      | j                  t        dfd        t        d      d      }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}}y# t        t        f$ r t	        j
                  d       Y w xY w)u^   git diff 명령이 rc != 0으로 종료하면 _changed_paths는 [] 반환 (예외 안 던짐).u1   토르 구현 대기 — _changed_paths 미존재c                       e Zd ZdZdZdZy)Ctest_changed_paths_returns_empty_on_git_failure.<locals>._FailedRunr   r   zfatal: not a git repositoryNr   r3   r   r   
_FailedRunr1    s    
.r   r2  r   c                              S r,   r3   )akwr2  s     r   rI   zAtest_changed_paths_returns_empty_on_git_failure.<locals>.<lambda>  s	    JL r   z/tmp/nonexistentbadshar^   r   rq   r   u?   git diff 실패 시 빈 리스트를 반환해야 함, 실제: r   rg   N)r   rB   r   r'  r  r(  r!   r   r   r   r   r   r   r   r   r   r   r   )	r"   r&   rB   rq   r   r   r   r   r2  s	           @r   /test_changed_paths_returns_empty_on_git_failurer7    s   I#%--/ /
 
E+HID!34h?F 6R<  6R                  J&T     ( IGHIs   D $D76D7c                 
   t               }| dz  dz  j                  d       | dz  dz  j                  d       | dz  dz  dz  j                  d       | dz  dz  d	z  j                  d
       |j                  ddg      \  }}d}||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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}}|j                  | |      \  }}	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|	dd        dz   d|iz  }t        t	        j                  |            dx}}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%d        dz   d|iz  }t        t	        j                  |            dx}
}g }d&}||	v }|}
|sd'}|	j                  } |       }||v }|}
|
st	        j
                  d(|fd)||	f      t	        j                  |      d"t        j                         v st	        j                  |	      rt	        j                  |	      nd"d*z  }d+d,|iz  }|j                  |       |st	        j
                  d(fd-f      t	        j                  |      d"t        j                         v st	        j                  |	      rt	        j                  |	      nd"t	        j                        t	        j                  |      d.z  }d/d0|iz  }|j                  |       t	        j                   |d1      i z  }t	        j                  d2|	d%d        d3z   d4|iz  }t        t	        j                  |            dx}
x}x}x}x}x}x}}y)5u  통합형: 실제 _resolve_test_scope + _run_tests_scoped 동작 검증.

    - 임시 repo에 server/tests/ (의도적 fail 테스트) + extension/__tests__/ (PASS) 배치
    - extension/ 변경에 대해 _resolve_test_scope가 server/tests/를 포함하지 않음
    - _run_tests_scoped가 실제 pytest를 호출했을 때 server 영역 fail이 결과에 포함되지 않음
    r>   r?   Tr@   r   r   z
test_ok.pyz%def test_extension_ok(): assert True
ztest_broken.pyzOimport nonexistent_module_xyz_abc  # noqa
def test_server_fail(): assert False
rD   rE   rN   r^   r   rz   r   u#   scope mode 기대 'scoped', 실제 r   rg   NrM   rU   u.   server/tests/는 절대 포함되면 안 됨: r   r  r  u=   extension 테스트만 실행되었으니 PASS여야 함: ok=z	, output=itest_brokenr   r   outputr   uI   server/tests/ 의 broken 테스트가 출력에 나타나면 scope leak: itest_okpassedrn   r  r  r  r   )zJ%(py10)s in %(py16)s
{%(py16)s = %(py14)s
{%(py14)s = %(py12)s.lower
}()
})rj   r{   r|   py16z%(py18)spy18r   u/   extension 테스트가 실행되었어야 함: z
>assert %(py21)spy21)r   r    
write_textrJ   r   r   r   r   r   r   r   r   r   rR   lowerr-   r  )r#   r&   rU   rz   r   r   r   r   r  r:  r   r   r   r   r   r   r   @py_format17@py_format19r   @py_format22s                        r   9test_integration_extension_pr_scope_excludes_server_testsrE    s     !E k)000>7"))$)7 k)L8DD0 7"%55AA	1 00	!56J K48KKK48KKKKKK4KKK4KKK8KKKB4(KKKKKKK01 :11  :1              2    9G    
 ((:>JB 2:  2                  Ht9U[\`\aUbTcd      =&  =          !'    !'    TTZ[_[`TaSbc    9 9 ( fll ln (n"<   9                   (n    #+      /5    /5    /;    /=        :&-I      r   c                  \   t               } | j                  }|D ]  }t        |z  }|j                  } |       }|st	        j
                  d| 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}}t        |j                  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)u   SMOKE_TEST_PATHS가 가리키는 디렉토리가 실제 workspace에 존재해야 함.

    이 디렉토리가 없으면 unmapped PR이 _run_tests_scoped의
    "no scoped test target — skipped" 분기로 무검증 PASS 처리됨.
    u9   SMOKE_TEST_PATHS가 가리키는 디렉토리가 부재: u+    — unmapped PR이 무검증 통과 위험zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_dir
}()
}r   r  Nz	test_*.pyr   )>=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)sra   py_tests)rd   re   rf   ru   u:   smoke 디렉토리에 최소 1개의 test_*.py가 필요: r  rh   )r   r   r   is_dirr   r   r   r   r   r   r   r   r9   globra   r   )r&   r   spr   r   r   r  rH  r   r   r   r  r   s                r   (test_smoke_directory_exists_in_workspacerL  .  s     !E((K 

B{{ 	
{} 	
} 	
  Hv N9 :	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
	  	
 	
 
	  	
 	
 	
 	
 	

 		+./8} 	
 	
}! 	
 	
 	
} 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		 !" 	
 	
  IO	
 	
 	
 	
 	
 	


r   )!r8   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   syspathlibr   r  r   r  pathinsertr   r   fixturer'   r)   r<   r   r   r   r   r   r  r#  r.  r7  rE  rL  r3   r   r   <module>rV     s   (    
  
 '(
z?#(("HHOOAs:'  :  $ .lNH-jN1r-ji@:,j
r   