
    wfiq                     x   d dl Zd dlmc mZ d dlZej                  j                  d d       d dl	Z	d dl
Z
d dlZd dlmZ d Zd Zd Zd Zd Zd	 Zd
 Zd Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z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'd# Z(d$ Z)d% Z*y)'    Nz /home/jay/workspace/teams/shared)scenario_runnerc                 z    t        | dd      5 }t        j                  ||d       d d d        y # 1 sw Y   y xY w)Nwutf-8encodingT)allow_unicode)openyamldumppathdatafs      A/home/jay/workspace/teams/shared/qc/tests/test_scenario_runner.py_write_yamlr      s5    	dC'	* /a		$./ / /   1:c                 z    t        | dd      5 }t        j                  ||d       d d d        y # 1 sw Y   y xY w)Nr   r   r   F)ensure_ascii)r
   jsonr   r   s      r   _write_jsonr      s5    	dC'	* /a		$./ / /r   c           	         dddgddddgdd	d
dg}| dz  }t        t        |      |       t        j                  t        |             }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}y)uH   subprocess 타입 시나리오가 모두 통과하면 gate='PASS' 반환zSC-TEST-001smokedummy.py
subprocess
echo hellohelloactionexpect_containsmustTu   echo hello 테스트idcategorytargettypestepspriorityautomatabledescriptionztest_pass.yamlscenarios_dirgatePASS==z%(py1)s == %(py4)spy1py4   gate가 PASS여야 하지만 
    반환됨
>assert %(py6)spy6N
r   strr   run_scenarios
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanation	tmp_pathscenario	yaml_fileresult@py_assert0@py_assert3@py_assert2@py_format5@py_format7s	            r   test_run_scenarios_all_passrL      s      !l  +'. 1	
H" ++II)**XGF&>_V_>V#___>V___>___V___'DVF^DTT^%________    c           	         dddgddddgdd	d
dg}| dz  }t        t        |      |       t        j                  t        |             }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}|d   }	t        |	      }d}
||
k\  }|st	        j
                  d|fd||
f      dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |	      t	        j                  |      t	        j                  |
      dz  }t	        j                  d      dz   d |iz  }t        t	        j                  |            dx}	x}x}}
y)!uS   must 시나리오 1개 FAIL → gate='FAIL'이고 failures에 1건 이상 기록됨zSC-TEST-002r   r   r   r   XYZZY_IMPOSSIBLE_STRING_12345r   r!   Tu4   절대 통과하지 않는 expect_contains 테스트r"   ztest_fail.yamlr+   r-   FAILr/   r1   r2       gate가 FAIL이어야 하지만 r6   r7   r8   Nfailures   >=z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} >= %(py7)slenpy0py2r4   py7u,   failures 목록에 1건 이상 있어야 함
>assert %(py9)spy9)r   r:   r   r;   r<   r=   r>   r?   r@   rA   rW   @py_builtinslocals_should_repr_global_name)rC   rD   rE   rF   rG   rH   rI   rJ   rK   @py_assert1@py_assert6@py_assert5@py_format8@py_format10s                 r   !test_must_fail_triggers_gate_failrf   6   sg     !l  +'F Q	
H" ++II)**XGF&>bVb>V#bbb>Vbbb>bbbVbbb'GvGWWa%bbbbbbbbj!W3!"WaW"a'WWW"aWWWWWW3WWW3WWW!WWW"WWWaWWW)WWWWWWWWrM   c                    | dz  }|j                          t        j                  dt        |      d      }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }t	        j                  d
|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}t        |j                  dd            }g }	d}||v }
|
}|
s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d|iz  }|	j                  |       |
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                  |       t	        j                  |	d      i z  }t	        j                  d|       dz   d |iz  }t        t	        j                  |            dx}x}	x}x}
x}}y)!u=   시나리오 0건 + level=3 → verify() 반환 status='FAIL'emptytest   task_idr,   levelstatusrP   r/   r1   r2   u=   level=3에서 시나리오 없으면 FAIL이어야 하지만 r6   r7   r8   Ndetails zLv.3u   시나리오 없음in)z%(py3)s in %(py5)sdetails_str)py3py5z%(py7)sr[   )z%(py10)s in %(py12)s)py10py12z%(py14)spy14rS   u>   details에 'Lv.3' 또는 '시나리오 없음' 포함 필요: z
>assert %(py17)spy17)mkdirr   verifyr:   r<   r=   r>   r?   r@   rA   getr^   r_   r`   append_format_boolop)rC   	empty_dirrF   rG   rH   rI   rJ   rK   rs   ra   @py_assert4@py_assert9@py_assert11@py_format6rd   @py_format13@py_format15@py_format16@py_format18s                      r    test_empty_scenarios_level3_failr   R   s   7"IOO##)nF (  Dv  Dv%  D  D  Dv  D  D  D  D  D  Dv  D  D  D)fgmnvgwfx  yC  (D  D  D  D  D  D  D  DfjjB/0KVV+V!6V!6+!EV VDUDUV+V VLUI 	V VOUvV V=U=U V VLUI V V VOUvVOUVDUDUV!6+V VLUI "7V VOUvV V=U=U ;FV VLUI ;FV V VOUvVOUVGU~V VDUDU	G}UV V VBUBUV V VrM   c                    | dz  }|j                          t        j                  dt        |      d      }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }t	        j                  d
|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}y)u=   시나리오 0건 + level=1 → verify() 반환 status='SKIP'rh   ri   rS   rk   rn   SKIPr/   r1   r2   u=   level=1에서 시나리오 없으면 SKIP이어야 하지만 r6   r7   r8   N)
rz   r   r{   r:   r<   r=   r>   r?   r@   rA   )rC   r   rF   rG   rH   rI   rJ   rK   s           r    test_empty_scenarios_level1_skipr   d   s    7"IOO##)nF (  Dv  Dv%  D  D  Dv  D  D  D  D  D  Dv  D  D  D)fgmnvgwfx  yC  (D  D  D  D  D  D  D  DrM   c           
      B   dddgddddgdd	d
ddddgddddgdd	ddg}| dz  }t        t        |      |       ddgi}| dz  }t        t        |      |       t        j                  t        |       t        |            }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|d    d      dz   d|	iz  }
t        t        j                  |
            dx}x}}y)uS   impact.json으로 시나리오 필터링 — affected에 없는 target은 제외됨zSC-FILTER-001r   za.pyr   zecho aar   r!   Tu   a.py 시나리오r"   zSC-FILTER-002zb.pyzecho bbu   b.py 시나리오 — 필터됨zfilter_test.yamlaffectedzimpact.json)r,   impact_filetotalrS   r/   r1   r2   u-   impact 필터 후 total=1이어야 하지만 r6   r7   r8   N)r   r:   r   r   r;   r<   r=   r>   r?   r@   rA   )rC   	scenariosrE   impact_datar   rF   rG   rH   rI   rJ   rK   s              r   test_impact_filteringr   r   s(    "h !)cBC.		
 "h !)cBC<		
I, --II	*x(K]*KK +.**(m$F
 '?lal?alll?alll?lllalll#PQWX_Q`Paak!llllllllrM   c           	         dddgddddgdd	d
dg}| dz  }t        t        |      |       t        j                         }t	        j
                  t        |             }t        j                         |z
  }|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d      dz   d|	iz  }
t        t        j                  |
            dx}}y)u7   단순 시나리오 실행이 30초 이내에 완료됨zSC-TIMEOUT-001r   r   r   zecho timingtimingr   r!   Tu   실행 시간 측정용r"   ztimeout_test.yamlr+   duration_seconds   )<)z%(py0)s < %(py3)sdurationrY   rt   u   실행 시간이 30초 초과: z.2fu   초
>assert %(py5)sru   N)r   r:   time	monotonicr   r;   r|   r<   r=   r^   r_   r`   r>   r?   r@   rA   )rC   rD   rE   startrF   elapsedr   rI   ra   @py_format4r   s              r   test_execution_within_timeoutr      s     #!l !.8LM4		
H ..II)NNE**XGFnn&Gzz,g6HM8b=MMM8bMMMMMM8MMM8MMMbMMM;HS>MMMMMMMrM   c                        e Zd ZddZd Zd Zy)MockLocatorc                     || _         y N_visible)selfvisibles     r   __init__zMockLocator.__init__   	    rM   c                     | j                   S r   r   r   s    r   
is_visiblezMockLocator.is_visible       }}rM   c                 ,    | j                   rdddddS d S )Nd      i,  2   )xywidthheightr   r   s    r   bounding_boxzMockLocator.bounding_box   s    CG==SsSB?ZVZZrM   NT)__name__
__module____qualname__r   r   r    rM   r   r   r      s     [rM   r   c                   D    e Zd Zd Zd Zd Zd Zd Zd Zd Z	dd	Z
d
 Zy)MockPagec                      d| _         d | _        y NT)r   _screenshot_pathr   s    r   r   zMockPage.__init__   s     $rM   c                      y r   r   )r   _url_kwargss      r   gotozMockPage.goto       rM   c                      y r   r   )r   _states     r   wait_for_load_statezMockPage.wait_for_load_state   r   rM   c                      y r   r   )r   	_selector_values      r   fillzMockPage.fill   r   rM   c                      y r   r   r   r   s     r   clickzMockPage.click   r   rM   c                     | j                   S r   r   r   s     r   r   zMockPage.is_visible   r   rM   c                 ,    t        | j                        S r   )r   r   r   s     r   locatorzMockPage.locator   s    4==))rM   Nc                     || _         y r   )r   )r   r   s     r   
screenshotzMockPage.screenshot   s
     $rM   c                      y r   r   )r   _mss     r   wait_for_timeoutzMockPage.wait_for_timeout   r   rM   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   rM   r   r   r      s/    %*%rM   r   c                       e Zd Zd Zd Zd Zy)MockContextc                     || _         y r   _page)r   pages     r   r   zMockContext.__init__   s	    
rM   c                     | j                   S r   r   r   s    r   new_pagezMockContext.new_page   s    zzrM   c                      y r   r   r   s    r   closezMockContext.close   r   rM   N)r   r   r   r   r   r   r   rM   r   r   r      s    rM   r   c                       e Zd Zd Zd Zd Zy)MockBrowserc                     || _         y r   _context)r   contexts     r   r   zMockBrowser.__init__   r   rM   c                     | j                   S r   r   )r   kwargss     r   new_contextzMockBrowser.new_context   r   rM   c                      y r   r   r   s    r   r   zMockBrowser.close   r   rM   N)r   r   r   r   r   r   r   rM   r   r   r      s     rM   r   c                       e Zd Zd Zy)MockPlaywrightc                 >     t        dddfdi             | _        y )NChromiumr   launchc                     S r   r   )r   kwbrowsers     r   <lambda>z)MockPlaywright.__init__.<locals>.<lambda>   s    7 rM   )r&   chromium)r   r   s    `r   r   zMockPlaywright.__init__   s    TZh8R-STVrM   N)r   r   r   r   r   rM   r   r   r      s    WrM   r   c                 n    t               }| |_        t        |      }t        |      }t	        |      }||fS )u%   playwright mock 객체 생성 헬퍼.)r   r   r   r   r   )r   r   r   r   pws        r   _make_playwright_mockr      s7    :DDM$G'"G		 Bt8OrM   c           	         dddgdddiddigdd	d
dg}| dz  }t        t        |      |       t        j                  t        |             }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}y)u8   playwright 타입이지만 automatable=false → skippedz	SC-PW-001e2efrontend/app.tsx
playwrightr   navigate http://localhost:3000assert_visible h1r!   Fu&   playwright automatable=false 테스트r"   zpw_skip.yamlr+   skippedrS   r/   r1   r2   u   skipped=1이어야 하지만 r6   r7   r8   Nr-   r.   r5   r9   rB   s	            r   5test_playwright_scenario_skipped_when_not_automatabler    sF    )* ;<./  C	
H >)II)**XGF)``!````````````%B6)CTBUU_#````````&>_V_>V#___>V___>___V___'DVF^DTT^%________rM   c           	         dddgdddiddigdd	d
dg}| dz  }t        t        |      |       t        d	      \  }ddl}|j                  fd       }|j                  d|d       t        j                  t        |             }|d   }d}	||	k(  }
|
st        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }t        j                  d|d    d      dz   d|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	k(  }
|
st        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }t        j                  d |d    d      dz   d|iz  }t        t        j                  |            dx}x}
}	y)!u<   playwright 타입 시나리오가 정상 실행되면 passedz	SC-PW-002r   r   r   r   r   r  r!   Tu"   playwright 정상 실행 테스트r"   zpw_pass.yamlr   r   Nc               3      K     y wr   r   pw_mocks   r   mock_sync_playwrightz;test_playwright_scenario_pass.<locals>.mock_sync_playwright5          	"qc.scenario_runner.sync_playwrightFraisingr+   passedrS   r/   r1   r2      passed=1이어야 하지만 r6   r7   r8   r-   r.   r5   )r   r:   r   
contextlibcontextmanagersetattrr   r;   r<   r=   r>   r?   r@   rA   )rC   monkeypatchrD   rE   r   r  r	  rF   rG   rH   rI   rJ   rK   r  s                @r   test_playwright_scenario_passr    s    )* ;<./ ?	
H >)II))$7MGT  ,   **XGF(]q]q ]]]q]]]]]]q]]]$@AQ@RR\"]]]]]]]]&>_V_>V#___>V___>___V___'DVF^DTT^%________rM   c           	         dddgdddiddigdd	d
dg}| dz  }t        t        |      |       t        d      \  }ddl}|j                  fd       }|j                  d|d       t        j                  t        |             }|d   }d}	||	k(  }
|
st        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }t        j                  d|d    d      dz   d|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	k(  }
|
st        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }t        j                  d |d    d      dz   d|iz  }t        t        j                  |            dx}x}
}	|d!   }t        |      }	d}|	|k\  }|st        j                  d"|fd#|	|f      d$t        j                          v st        j"                  t              rt        j                  t              nd$t        j                  |      t        j                  |	      t        j                  |      d%z  }t        j                  d&      d'z   d(|iz  }t        t        j                  |            dx}x}	x}}|d!   d   }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}
}	y)+uJ   playwright assert 실패 시 failed + reason에 스크린샷 경로 포함z	SC-PW-003r   r   r   r   r   z$assert_visible .non-existent-elementr!   Tu"   playwright assert 실패 테스트r"   zpw_fail.yamlFr  r   Nc               3      K     y wr   r   r  s   r   r	  zQtest_playwright_scenario_assert_fail_returns_failed.<locals>.mock_sync_playwright]  r
  r  r  r  r+   failedrS   r/   r1   r2   u   failed=1이어야 하지만 r6   r7   r8   r-   rP   rQ   rR   rT   rV   rW   rX   u%   failures에 1건 이상 있어야 함r\   r]   r#   u2   실패 시나리오 ID가 SC-PW-003이어야 함: )r   r:   r   r  r  r  r   r;   r<   r=   r>   r?   r@   rA   rW   r^   r_   r`   )rC   r  rD   rE   r   r  r	  rF   rG   rH   rI   rJ   rK   ra   rb   rc   rd   re   failurer  s                      @r   3test_playwright_scenario_assert_fail_returns_failedr  E  s    )* ;<AB ?	
H >)II))%8MGT  ,   **XGF(]q]q ]]]q]]]]]]q]]]$@AQ@RR\"]]]]]]]]&>bVb>V#bbb>Vbbb>bbbVbbb'GvGWWa%bbbbbbbbj!P3!"PaP"a'PPP"aPPPPPP3PPP3PPP!PPP"PPPaPPP)PPPPPPPPZ #Gg'$-g;-'ggg;-ggg;ggg-ggg+]^e]f)ggggggggrM   c           	         dddgddddgdd	d
dg}| dz  }t        t        |      |       t        j                  dt        |       d      }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}t        |j                  dd            }	d}|	j                  }
 |
       }||v }|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  }t	        j                  d!|	       d"z   d#|iz  }t        t	        j                  |            dx}x}x}
}y)$u;   level=3에서 playwright 시나리오가 없으면 → WARNzSC-PW-GATE-001r   backend/api.pyr   echo okokr   r!   Tu#   playwright 없는 Lv.3 시나리오r"   zno_pw_level3.yamlztest-lv3rj   rk   rn   WARNr/   r1   r2   u;   level=3에서 playwright 없으면 WARN이어야 하지만 r6   r7   r8   Nro   rp   r   rq   )zD%(py1)s in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.lower
}()
}rs   )r3   rt   ru   r[   u'   details에 'playwright' 포함 필요: r\   r]   )r   r:   r   r{   r<   r=   r>   r?   r@   rA   r|   lowerr^   r_   r`   )rC   rD   rE   rF   rG   rH   rI   rJ   rK   rs   r   rb   rd   re   s                 r   &test_verify_level3_requires_playwrightr!  q  s   
 #'( !*tDE@		
H ..II)##(mF (  Bv  Bv%  B  B  Bv  B  B  B  B  B  Bv  B  B  B)deklteudv  wA  (B  B  B  B  B  B  B  BfjjB/0Kg;,,g,.g<..ggg<.ggg<gggggg;ggg;ggg,ggg.ggg2YZeYf0ggggggggrM   c                    | j                  dd       t        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  }d	d
|iz  }t        t        j                  |            dx}}t        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  }d	d
|iz  }t        t        j                  |            dx}}y)u;   환경변수 기반 플레이스홀더 치환 동작 확인SCENARIO_test_doc_idzdoc-123z(http://localhost:3000/docs/{test_doc_id}z"http://localhost:3000/docs/doc-123r/   z%(py0)s == %(py3)srF   r   zassert %(py5)sru   Nz{unknown_var}result2)setenvr   _resolve_placeholdersr<   r=   r^   r_   r`   r>   r@   rA   )r  rF   rI   ra   r   r   r%  s          r   test_resolve_placeholdersr(    s    -y9223]^F99699999699999996999699999999999 33ODG%%7o%%%%7o%%%%%%7%%%7%%%o%%%%%%%rM   c           	         dddgddddgdd	d
dg}| dz  }t        t        |      |       t        j                  dt        |       d      }|d   }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }t	        j                  d|d    d      dz   d|iz  }t        t	        j                  |            dx}x}}y)uK   level=2에서 playwright 시나리오가 없어도 → 기존 동작 유지zSC-PW-GATE-002r   r  r   r  r  r   r!   Tu#   playwright 없는 Lv.2 시나리오r"   zno_pw_level2.yamlztest-lv2   rk   rn   r.   r/   r1   r2   u;   level=2에서는 playwright 없어도 PASS여야 하지만 r6   r7   r8   N)
r   r:   r   r{   r<   r=   r>   r?   r@   rA   rB   s	            r   (test_verify_level2_skips_playwright_gater+    s#    #'( !*tDE@		
H ..II)##(mF (  Bv  Bv%  B  B  Bv  B  B  B  B  B  Bv  B  B  B)deklteudv  wA  (B  B  B  B  B  B  B  BrM   c                 	   ddl }g t        dd      D cg c]  }d|dddgd	d
digddd| d }}t        dd      D cg c]  }d|dddgddddgddd| d }}| dz  }t        t        |      ||z          t	        d      \  }|j
                  fd       }|j                  d|d       t        j                  }	d?dt        d t        d!t        ffd"}
|j                  d#|
       t        j                  t        |       $      }|d%   }d&}||k(  }|st        j                  d'|fd(||f      t        j                  |      t        j                  |      d)z  }t        j                  d*|d%    d+      d,z   d-|iz  }t        t        j                   |            dx}x}}|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 cg c]	  }d0|v s| }}t#        |      }d}||k(  }|st        j                  d'|fd1||f      d2t%        j&                         v st        j(                  t"              rt        j                  t"              nd2d3t%        j&                         v st        j(                  |      rt        j                  |      nd3t        j                  |      t        j                  |      d4z  }t        j                  d5|       d6z   d7|iz  }t        t        j                   |            dx}x}}t        dd      D ch c]  }d|d
 }}t+        |      }||k(  }|s<t        j                  d'|fd8||f      d9t%        j&                         v st        j(                  t*              rt        j                  t*              nd9d3t%        j&                         v st        j(                  |      rt        j                  |      nd3t        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c c}w c c}w c c}w c c}w )@u   playwright 시나리오가 병렬이 아닌 순차 실행되는지 확인.
    3개 playwright + 2개 subprocess 시나리오 → subprocess는 병렬, playwright는 순차.r   NrS      z
SC-SEQ-PW-03dr   r   r   r   r   r!   Tu#   playwright 순차 실행 테스트 r"   rj   z
SC-SEQ-SP-r   r  r   r  r  r   u#   subprocess 병렬 실행 테스트 zseq_test.yamlr  c               3      K     y wr   r   r  s   r   r	  z?test_playwright_runs_sequentially.<locals>.mock_sync_playwright  r
  r  r  Fr  rD   storage_statereturnc                 >    j                  | d          | d   dddS )Nr#   r  rp   )r#   rn   reasonr}   )rD   r0  execution_orders     r   recording_run_playwrightzCtest_playwright_runs_sequentially.<locals>.recording_run_playwright  s&    x~.tnBGGrM   z+qc.scenario_runner._run_playwright_scenarior+   r     r/   r1   r2   u   passed=5여야 하지만 r6   r7   r8   r  assert %(py6)sPWz0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)srW   pw_idsrY   r3   rt   r8   u1   playwright 3개 실행 기록이어야 하지만 
>assert %(py8)spy8)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)ssetexpected_pw_ids)rY   r3   rt   ru   u   playwright ID 불일치: z
>assert %(py7)sr[   rp   )r  ranger   r:   r   r  r  r   _run_playwright_scenariodictr;   r<   r=   r>   r?   r@   rA   rW   r^   r_   r`   r?  )rC   r  r  ipw_scenariossp_scenariosrE   _r	  original_run_playwrightr6  rF   rG   rH   rI   rJ   rK   sc_idr;  rc   r   @py_format9r@  r   rd   r5  r  s                            @@r   !test_playwright_runs_sequentiallyrL    s     "$O q!  qg&)* !ABC@D		
L 0 q!  qg&'( !*tDE@D		
L  ?*II| ;<&t4JGQ  <>R\ab .FFH4 H HT H EG_`**XGF (ZqZq ZZZqZZZZZZqZZZ$=fX>N=Oz"ZZZZZZZZ( q q    q      q        "1BDEMeBFBv;Y!Y;!YYY;!YYYYYY3YYY3YYYYYYvYYYvYYY;YYY!YYYPQWPXYYYYYYYY
 6;1a[AAc7+AOAv;O;/)OOO;/OOOOOO3OOO3OOOOOOvOOOvOOO;OOOOOO/OOO/OOOO-Fvh+OOOOOOO{R C Bs   SS#3	S(=S(;S-c           	      .   ddl }g dddgdddigd	d
ddg}| dz  }t        t        |      |       t        d
      \  }|j                  fd       }|j                  d|d       dt        ffd}ddl}|j                  d      }	||	_	        |j                  t        j                  d|	       t        j                  t        |             }
|
d   }d}||k(  }|st        j                   d|fd||f      t        j"                  |      t        j"                  |      dz  }t        j$                  d|
d    d      dz   d|iz  }t'        t        j(                  |            dx}x}}t+              }d}||k\  }|st        j                   d |fd!||f      d"t-        j.                         v st        j0                  t*              rt        j"                  t*              nd"d#t-        j.                         v st        j0                        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)(uF   playwright 시나리오 실행 전 check_and_refresh_ttl 호출 확인r   Nz
SC-TTL-001r   r   r   r   r   r!   Tu   TTL 체크 테스트r"   zttl_test.yamlr  c               3      K     y wr   r   r  s   r   r	  zEtest_ttl_check_called_before_playwright.<locals>.mock_sync_playwright  r
  r  r  Fr  r1  c                  (    j                  d       yNTFr4  argsr   ttl_call_logs     r   mock_check_and_refresh_ttlzKtest_ttl_check_called_before_playwright.<locals>.mock_check_and_refresh_ttl       D!rM   qc.auth.setup_authr+   r  rS   r/   r1   r2   r  r6   r7   r8   rT   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)srW   rS  r<  u8   check_and_refresh_ttl이 최소 1회 호출되어야 함r=  r>  )r  r   r:   r   r  r  booltypes
ModuleTypecheck_and_refresh_ttlsetitemsysmodulesr   r;   r<   r=   r>   r?   r@   rA   rW   r^   r_   r`   )rC   r  r  rD   rE   rH  r	  rT  rY  mock_setup_authrF   rG   rH   rI   rJ   rK   rc   r   rK  r  rS  s                      @@r   'test_ttl_check_called_before_playwrightr`    s   !L )* !ABC1		
H ?*II)&t4JGQ  <>R\abt 
 &&';<O,FO)%9?K**XGF(]q]q ]]]q]]]]]]q]]]$@AQ@RR\"]]]]]]]]|]]!]]]]]]]]]3]]]3]]]]]]|]]]|]]]]]]]]]#]]]]]]]]rM   c           	         ddl }ddl}dddgdddigd	d
ddg}| dz  }t        t        |      |       t	        d
      \  }|j
                  fd       }|j                  d|d       dt        fd}	|j                  d      }
|	|
_	        |j                  t        j                  d|
       t        j                  t        |              |j                         }g }d}|j                   }||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t#        j&                  |      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&                  |      d z  }d!d"|iz  }|j/                  |       t#        j0                  |d#      i z  }t#        j2                  d$|j                         d%z   d&|iz  }t5        t#        j6                  |            dx}x}x}x}x}x}x}}y)'u*   TTL 만료 시 경고 로그 출력 확인r   Nz
SC-TTL-002r   r   r   r   r   r!   Tu   TTL 만료 경고 테스트r"   zttl_expire_test.yamlr  c               3      K     y wr   r   r  s   r   r	  zHtest_ttl_check_warning_logged_when_expired.<locals>.mock_sync_playwrightH  r
  r  r  Fr  r1  c                       yr   r   rR  r   s     r   mock_check_expiredzFtest_ttl_check_warning_logged_when_expired.<locals>.mock_check_expiredO      rM   rV  r+   TTLstorageStaterq   z+%(py3)s in %(py7)s
{%(py7)s = %(py5)s.out
}capturedrt   ru   r[   %(py9)sr]   z/%(py12)s in %(py16)s
{%(py16)s = %(py14)s.out
}rw   rx   py16%(py18)spy18rS   uF   TTL 만료 경고가 출력되어야 하지만 출력 없음. stdout: 
>assert %(py21)spy21r  rY  r   r:   r   r  r  rX  rZ  r[  r\  r]  r^  r   r;   
readouterroutr<   r=   r>   r^   r_   r`   r}   r~   r?   r@   rA   )rC   r  capsysr  rY  rD   rE   rH  r	  re  r_  rj  ra   rI   rb   r   rG   r   @py_assert15@py_assert13rd   re   @py_format17@py_format19@py_format20@py_format22r  s                             @r   *test_ttl_check_warning_logged_when_expiredr~  2  s    )* !ABC8		
H 11II)&t4JGQ  <>R\abt  &&';<O,>O)%9?K!!H>  "Haaaa!/a3;<<a!/<!?a aO`O`aa aW`W` 	a aZ`Z`a aH`H` a aW`W` a aW`W` a a aZ`Z`aZ`aO`O`a!/<a aW`W` "0a aZ`Z`a aH`H` 4<a aW`W` 4<a aW`W` 4@a a aZ`Z`aZ`aR`R`a aO`O`	OPXP\P\O_`a a aM`M`a a a arM   c           	      ~   ddl }g dddgdddd	gd
dddg}| dz  }t        t        |      |       dt        ffd}|j	                  d      }||_        |j                  t        j                  d|       t        j                  t        |             }|d   }d}	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	t!              }
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)!uF   playwright 시나리오 없으면 check_and_refresh_ttl 호출 안 됨r   Nz
SC-TTL-003r   r  r   r  r  r   r!   Tu#   subprocess only - TTL 호출 안됨r"   zno_pw_ttl.yamlr1  c                  (    j                  d       yrP  r4  rQ  s     r   rT  zJtest_ttl_not_called_without_playwright.<locals>.mock_check_and_refresh_ttls  rU  rM   rV  r+   r  rS   r/   r1   r2   r8  r8   r:  rW   rS  r<  u-   playwright 없으면 TTL 체크 호출 안됨r=  r>  )rY  r   r:   rX  rZ  r[  r\  r]  r^  r   r;   r<   r=   r>   r@   rA   rW   r^   r_   r`   r?   )rC   r  rY  rD   rE   rT  r_  rF   rG   rH   rI   rJ   rK   rc   r   rK  rS  s                   @r   &test_ttl_not_called_without_playwrightr  ^  s   !L '( !*tDE@		
H ++II)t  &&';<O,FO)%9?K**XGF( q q    q      q       |RR!RRRRRRRRR3RRR3RRRRRR|RRR|RRRRRRRRR#RRRRRRRRrM   c                 
   | dz  }|j                          t        dd      D cg c]  }d|dddgdd	d
igddd| d }}t        t        |dz        |       | dz  }|j                          t        dd      D cg c]  }d|dddgdd	d
igddd| d }}t        t        |dz        |       t	        j
                  t        |             }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}}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}}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}}|d   d   }d}||k(  }|st        j                  d$|fd%||f      t        j                  |      t        j                  |      d&z  }t        j                  d'|d   j                  d             d(z   d)|iz  }t        t        j                  |            dx}x}}|d   d   }d*}||k(  }|st        j                  d$|fd%||f      t        j                  |      t        j                  |      d&z  }t        j                  d+|d   j                  d             d(z   d)|iz  }t        t        j                  |            dx}x}}|d    }d,}||k(  }|st        j                  d$|fd%||f      t        j                  |      t        j                  |      d&z  }t        j                  d-|d           d(z   d)|iz  }t        t        j                  |            dx}x}}yc c}w c c}w ).u4   프로젝트별 시나리오 카운트 정상 동작insuwikirS   r-  zSC-INSUWIKI-r.  r   zinsuwiki/main.pyr   r   r  r!   Tu   insuwiki 시나리오 r"   zinsuwiki.yaml	dashboardrj   zSC-DASH-zdashboard/main.pyu   dashboard 시나리오 zdashboard.yamlr+   projectsrq   z%(py1)s in %(py3)srF   r3   rt   u)   결과에 'projects' 키가 있어야 함r   ru   Nr   u&   결과에 'total' 키가 있어야 함
duplicatesu+   결과에 'duplicates' 키가 있어야 함r/   r1   r2   u(   insuwiki 카운트=3이어야 하지만 r7   r8   r*  u)   dashboard 카운트=2이어야 하지만 r7  u   total=5이어야 하지만 )rz   rB  r   r:   r   
show_statsr<   r=   r>   r^   r_   r`   r?   r@   rA   r|   )rC   insuwiki_dirrE  insuwiki_scenariosdashboard_dirdashboard_scenariosrF   rG   rI   r   r   rH   rJ   rK   s                 r   !test_show_stats_counts_by_projectr    s    j(L q!  !3()* +,3A37		
  L?235GH {*M q!  QsG$*+ +,4QC8		
  M$4457JK''c(mDFL:LLL:LLL:LLLLLLLLLLLLL!LLLLLLLF7fFFF7fFFF7FFFFFFfFFFfFFFFFFFFFFFP<6!PPP<6PPP<PPPPPP6PPP6PPPP#PPPPPPP*j)Q)Q.)Q)Q2Z[abl[m[q[qr|[}Z~0z;'Y+,Y'1,YGXGXY'1Y YOXy 	(Y YOXy ,-Y YGXGX	26*3E3I3I+3V2WXY Y YEXEXY Y '?PaP?aPPP?aPPP?PPPaPPP#>vg>O!PPPPPPPPU$s   S>>Tc                    ddg dddg dddg dddg dddg dg}t        j                  |      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndd	z  }t        j                  d
|       dz   d|iz  }t        t        j                  |            dx}}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}}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   중복 ID 감지z
SC-DUP-001r   r#   r&   r'   z
SC-DUP-002z
SC-DUP-003rq   r  r  r  u.   SC-DUP-001이 중복 목록에 있어야 함: r   ru   Nu.   SC-DUP-003이 중복 목록에 있어야 함: )not in)z%(py1)s not in %(py3)su    SC-DUP-002는 중복이 아님: )r   _detect_duplicatesr<   r=   r>   r^   r_   r`   r?   r@   rA   )rC   r   r  rG   rI   r   r   s          r   "test_detect_duplicates_finds_dupesr    s    \B?\B?\B?\B?\B?I !33I>Jd<:%ddd<:ddd<dddddd:ddd:dddd)WXbWc'dddddddd<:%ddd<:ddd<dddddd:ddd:dddd)WXbWc'dddddddZ<z)ZZZ<zZZZ<ZZZZZZzZZZzZZZZ-Mj\+ZZZZZZZrM   c                    ddg dddg dddg dg}t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }t        j                  d
|       dz   d|iz  }t        t        j                  |            dx}}y)u   중복 없으면 빈 리스트zSC-UNIQ-001r   r  zSC-UNIQ-002zSC-UNIQ-003r/   r$  r  r   u/   중복 없으면 빈 리스트여야 하지만 r   ru   N)r   r  r<   r=   r^   r_   r`   r>   r?   r@   rA   )rC   r   r  rI   ra   r   r   s          r   test_detect_duplicates_no_dupesr    s     lR@lR@lR@I !33I>J[:[[[:[[[[[[:[[[:[[[[[[Nzl[[[[[[[rM   c           	      :   ddl }ddl}dddgdddigd	d
ddg}| dz  }t        t        |      |       t	        d
      \  }|j
                  fd       }|j                  d|d       dt        fd}|j                  d      }	||	_	        |j                  t        j                  d|	       g d#dt        dt        ffd}
|j                  d|
       t        j                  t        |              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!        j0                  |            dx}x}}y)$uK   TTL 만료 시 _auto_refresh_storage_state 함수가 호출되는지 확인r   NzSC-AUTOREFRESH-001r   r   r   r   r   r!   Tu%   자동 갱신 호출 확인 테스트r"   zauto_refresh_test.yamlr  c               3      K     y wr   r   r  s   r   r	  zGtest_auto_refresh_called_when_ttl_expired.<locals>.mock_sync_playwright  r
  r  r  Fr  r1  c                       yr   r   rd  s     r   re  zEtest_auto_refresh_called_when_ttl_expired.<locals>.mock_check_expired  rf  rM   rV  storage_state_pathc                 (    j                  d       yr   r4  )r  auto_refresh_call_logs    r   mock_auto_refreshzDtest_auto_refresh_called_when_ttl_expired.<locals>.mock_auto_refresh  s    $$T*rM   .qc.scenario_runner._auto_refresh_storage_stater+   rS   rT   rW  rW   r  r<  uM   _auto_refresh_storage_state가 TTL 만료 시 최소 1회 호출되어야 함r=  r>  rA  )r  rY  r   r:   r   r  r  rX  rZ  r[  r\  r]  r^  r   r;   rW   r<   r=   r^   r_   r`   r>   r?   r@   rA   )rC   r  r  rY  rD   rE   rH  r	  re  r_  r  rI   rc   r   rK   rK  r  r  s                   @@r   )test_auto_refresh_called_when_ttl_expiredr    s    ')* !ABCB		
H 33II)&t4JGQ  <>R\abt  &&';<O,>O)%9?K )+c 4  HJ[\!!H>$%{{%*{{{%{{{{{{3{{{3{{{{{{${{{${{{%{{{{{{,{{{{{{{{rM   c           	         ddl }ddl}dddgdddigd	d
ddg}| dz  }t        t        |      |       t	        d
      \  }|j
                  fd       }|j                  d|d       dt        fd}	|j                  d      }
|	|
_	        |j                  t        j                  d|
       d1dt        dt        fd}|j                  d|       t        j                  t        |             }|j                         }g }d}|j                   }||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t#        j&                  |      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&                  |      d#z  }d$d%|iz  }|j/                  |       t#        j0                  |d&      i z  }t#        j2                  d'|j                         d(z   d)|iz  }t5        t#        j6                  |            dx}x}x}x}x}x}x}}|d*   }d&}||k(  }|st#        j$                  d+|fd,||f      t#        j&                  |      t#        j&                  |      d-z  }t#        j2                  d.|d*          d/z   d0|iz  }t5        t#        j6                  |            dx}x}}y)2uo   자동 갱신 실패 시 경고 로그가 출력되고 playwright 시나리오가 계속 실행되는지 확인r   NzSC-AUTOREFRESH-002r   r   r   r   r   r!   Tu%   자동 갱신 실패 경고 테스트r"   zauto_refresh_fail_test.yamlr  c               3      K     y wr   r   r  s   r   r	  zDtest_auto_refresh_failure_logs_warning.<locals>.mock_sync_playwright  r
  r  r  Fr  r1  c                       yr   r   rd  s     r   re  zBtest_auto_refresh_failure_logs_warning.<locals>.mock_check_expired&  rf  rM   rV  r  c                      y)NFr   )r  s    r   mock_auto_refresh_failzFtest_auto_refresh_failure_logs_warning.<locals>.mock_auto_refresh_fail.  s    rM   r  r+   u   실패u   수동rq   ri  rj  rk  rl  r]   rm  rn  rp  rq  rS   uA   자동 갱신 실패 경고가 출력되어야 하지만 stdout: rr  rs  r   r/   r1   r2   u5   시나리오 1개가 실행되어야 하지만 total=r7   r8   rA  rt  ) rC   r  rw  r  rY  rD   rE   rH  r	  re  r_  r  rF   rj  ra   rI   rb   r   rG   r   rx  ry  rd   re   rz  r{  r|  r}  rH   rJ   rK   r  s                                   @r   &test_auto_refresh_failure_logs_warningr  	  s~    ')* !ABCB		
H 88II)&t4JGQ  <>R\abt  &&';<O,>O)%9?K3   HJ`a**XGF  "H\\LL\L \$,\08\$,$<\ \J[J[\L\ \R[R[ 	\ \U[U[\ \C[C[ \ \R[R[ \ \R[R[ !\ \ \U[U[\U[\J[J[\$,\ \R[R[ %-\ \U[U[\ \C[C[ 19\ \R[R[ 19\ \R[R[ 1=\ \ \U[U[\U[\M[^\ \J[J[	J8<<JZ[\ \ \H[H[\ \ \ \ '?jaj?ajjj?ajjj?jjjajjj#XY_`gYhXi!jjjjjjjjrM   r   )+builtinsr^   _pytest.assertion.rewrite	assertionrewriter<   r]  r   insertr   r   r   qcr   r   r   rL   rf   r   r   r   r   r   r   r   r   r   r   r  r  r  r!  r(  r+  rL  r`  r~  r  r  r  r  r  r  r   rM   r   <module>r     s      
 5 6    
/
/`6X8V$D$mNN8[ [ <  W W
`4%`P)hXh:&B<EPV-^`)aX SL/Qd["
\ /|d2krM   