
    wfi)H                        d dl Z d dlZd dlZd dlZd dlZd dlmZmZ d dlm	Z	 d dl
Z
	 d dlmZ dedee   fdZdedee   fd	Zd
ee   dee   defdZdedeeef   fdZdedeeef   fdZdedeeef   fdZdedefdZd&dedeeef   fdZd'dededefdZd'dedefdZdedefdZdee   dee   fdZdedefdZd(dedededefdZ 	 	 	 	 d)d ededed!edz  d"e!defd#Z"d*d$Z#e$d%k(  r e#        yy# e$ r dZY w xY w)+    N)ThreadPoolExecutoras_completed)Any)sync_playwrightimpact_filereturnc                     | rt         j                  j                  |       sg S 	 t        | dd      5 }t	        j
                  |      }d d d        j                  dg       S # 1 sw Y   xY w# t        j                  t        f$ r g cY S w xY w)Nrutf-8encodingaffected)	ospathisfileopenjsonloadgetJSONDecodeErrorOSError)r   fdatas      6/home/jay/workspace/teams/shared/qc/scenario_runner.py_load_impactr      s    bggnn[9	+sW5 	 99Q<D	 xx
B''	  	    '* 	s(   A/ A#	A/ #A,(A/ /BBscenarios_dirc                    g }t        j                  |       D ]  \  }}}|D ]  }|j                  d      s|j                  d      s&t         j                  j	                  ||      }	 t        |dd      5 }t        j                  |      }d d d        t        t              r|j                  |       n!t        |t              r|j                  |         |S # 1 sw Y   RxY w# t        j                  t        f$ r Y w xY w)N.yaml.ymlr
   r   r   )r   walkendswithr   joinr   yaml	safe_load
isinstancelistextenddictappend	YAMLErrorr   )	r   	scenariosroot_filesfnamefpathr   r   s	            r   _load_scenariosr1      s    I''-0 a 	E>>'*5>>&3IGGLLu-E%w7 -1>>!,D-dD)$$T*d+$$T*	 - - NNG, s+   (C)6CAC)C&"C))DDtargetr   c                 d    |sy| D ](  }|j                  d      d   }|D ]  }||v s||v s  y * y)NT:r   F)split)r2   r   tt_baseas        r   _matches_affectedr9   1   sL     a 	A{a6k	
     stepc                 H   | j                  dd      }| j                  d      }| j                  dd      }	 |}|d|vr	d|vr|dz   }t        j                  |dddd	
      }|j                  }|Kd|v rG|j	                  dd      }|d   }|d   j                         }		 t        |	      }
|
|k7  r
dd|
 d| fS |}|r||vrdd| dfS d|fS # t        $ r d}
Y 0w xY w# t        j                  $ r Y yt        $ r&}dt        |      j                   d| fcY d }~S d }~ww xY w)Naction expect_statusexpect_containsz-w z--write-outz -w "\n__STATUS__%{http_code}"Tx   shellcapture_outputtexttimeout
__STATUS__   r   FzHTTP status z != expected 
expected '' not found in outputFztimeout after 120s: )r   
subprocessrunstdoutrsplitstripint
ValueErrorTimeoutExpired	Exceptiontype__name__)r;   r=   r?   r@   cmdresultoutputpartsbodystatus_code_strstatus_codees               r   _run_curl_stepr`   <   sj   XXh#FHH_-Mhh0"5O1$CM$<==44Y\]$)?MM,2E8D#Ahnn.O !/2 m+[M}oVVVFf<J&77LMMMV|     $$ +* 1a))*"QC0001sT   A'C C *C 9C 	C CC CC D!3D!;DD!D!c                 b   | j                  dd      }| j                  dd      }	 t        j                  |dddd      }|j                  |j                  z   }|r||vrdd| d	fS d|fS # t        j
                  $ r Y y
t        $ r&}dt        |      j                   d| fcY d }~S d }~ww xY w)Nr=   r>   r@   TrA   rB   FrI   rJ   rK   rL   )	r   rM   rN   rO   stderrrT   rU   rV   rW   )r;   r=   r@   rY   combinedr_   s         r   _run_subprocess_steprd   c   s    XXh#Fhh0"5O1d4d\_`==6==0h>J&77LMMMX~$$ +* 1a))*"QC0001s*   A A+ 'A+ +B. B.B)#B.)B.c                 j   | j                  dd      }	 t        j                  d| dddd      }|j                  dk7  r!dd	|j                   d
|j                  dd   fS d|j                  fS # t        j
                  $ r Y yt        $ r&}dt        |      j                   d
| fcY d }~S d }~ww xY w)Nr=   r>   zpython3 -m pytest TrA   rB   r   Fzpytest exited with code rL   irK   )	r   rM   rN   
returncoderO   rT   rU   rV   rW   )r;   r=   rY   r_   s       r   _run_pytest_steprg   v   s    XXh#F	1"4VH =TZ^eisvw!4V5F5F4Gr&--X\X]J^I_```V]]""$$ +* 1a))*"QC0001s*   AA/ !A/ /B2B2B-'B2-B2rE   c                 \    ddl }d|j                  dt        fd}|j                  d||       S )ug   환경변수 기반 플레이스홀더 치환. {key} → os.environ[SCENARIO_key] 또는 원본 유지.r   Nmr   c                     | j                  d      }t        j                  j                  d|       xs | j                  d      S )NrH   	SCENARIO_r   )groupr   environr   )ri   keys     r   _replacez'_resolve_placeholders.<locals>._replace   s6    ggajzz~~	#/0>AGGAJ>r:   z	\{(\w+)\})reMatchstrsub)rE   rp   ro   s      r   _resolve_placeholdersrt      s1    ?BHH ? ? 66,$//r:   r=   c                    	 t        |      }|j                  d      }|r|d   nd}t        |      dkD  r|d   nd}|dk(  r| j                  ||dk(  rdnd       n|d	k(  r@|j	                  d
d      }t        |      dk(  r| j                  |d   |d          ndd| fS |dk(  r| j                  |       n|dk(  r'| j                  |      j                         sdd| dfS |dk(  r)| j                  |      j                         }|mdd| dfS |dk(  r'| j                  |      j                         rAdd| dfS |dk(  r-| j                  |j                         rt        |      nd       ndd| fS t        |t              r|dkD  r| j                  |       y# t        $ r&}dt        |      j                    d| fcY d}~S d}~ww xY w)u    개별 Playwright 액션 실행.rH   )maxsplitr   r>   navigatenetworkidler   )
wait_untilfill    Fu   fill 액션 형식 오류: clickassert_visibleu   assert_visible 실패: 'u   ' 가시적이지 않음assert_in_viewportNu   assert_in_viewport 실패: 'u   ' bounding box 없음assert_not_visibleu   assert_not_visible 실패: 'u   ' 가시적임waiti  u   알 수 없는 액션: )Tr>   rL   )rt   r5   lengotorP   rz   r}   locator
is_visiblebounding_boxwait_for_timeoutisdigitrR   r%   rU   rV   rW   )	pager=   r   r[   rX   argselector_and_valueboxr_   s	            r   _execute_playwright_actionr      s   &1&v.a(eAhRe*q.eAhb*IIct}7LmRXIYF]!$C!3%&!+		,Q/1CA1FG ;F8DDDG^JJsO$$<<$//1 8=UVVV((,,s#002C{ <SEAVWWW((||C ++- <SEPPPF]!!ckkm#c(F3C5999 dC TAX!!$' 1a))*"QC0001s=   B F  #AF  &-F  +F   8F  9&F   	G)G
G
Gscenariostorage_statec           	      j   | j                  dd      }| j                  dg       }t        j                  j                  t        j                  j	                  t
              d      }t        j                  |d       t        |dd	d
S 	 t               5 }|j                  j                  d      }i }|r$t        j                  j                  |      r||d<    |j                  di |}|j                         }	t        |      D ]  \  }
}|j                  dd      }|j                  dd      }t        |	||      \  }}|r=t        j                  j                  || d|
 d      }	 |	j                  |       |j#                          |j#                          |dd|
 d| |dc cddd       S  |j#                          |j#                          |ddd
cddd       S # t         $ r d}Y uw xY w# 1 sw Y   yxY w# t         $ r(}|dt%        |      j&                   d| d
cY d}~S d}~ww xY w)u#   Playwright E2E 시나리오 실행.idunknownstepsscreenshotsTexist_okNskippedzplaywright not installedr   statusreason)headlessr   r=   r>   r   z-stepz	-fail.png)r   failedzstep rL   )r   r   r   
screenshotpassed )r   r   r   r"   dirname__file__makedirsr   chromiumlaunchr   new_contextnew_page	enumerater   r   rU   closerV   rW   )r   r   sc_idr   screenshots_dirpbrowsercontext_kwargscontextr   ir;   r=   r   okr   screenshot_pathr_   s                     r   _run_playwright_scenarior      s    LLy)ELL"%Eggll277??8#<mLOKK$/y<VWW!W 	C!jj'''6GN!>2?/)g));N;G##%D$U+ 4(B/xx-7fdK
F&(ggll?ugUSTRUU^D_&`O-_= MMOMMO#"*$)!Bvh"7&5	 +	C 	C( MMOMMO8rB=	C 	C" % -*,-#	C 	C>  Wxd1g>N>N=OrRSQT;UVVWss   

H B.G5&G5*G$<.G5*	H 4&G5	H $G2/G51G22G55G>:H >H 	H2
H-'H2-H2storage_state_pathc                     t         j                  j                  t         j                  j                  t              dd      }	 t        j                  d|dd| gddd      }|j                  d	k(  S # t        $ r Y y
w xY w)uQ   storageState를 자동 갱신합니다. 성공 시 True, 실패 시 False 반환.authzsetup_auth.pypython3z	--refresh--outputT<   )rD   rE   rF   r   F)	r   r   r"   r   r   rM   rN   rf   rU   )r   setup_auth_pathrY   s      r   _auto_refresh_storage_stater      su    ggll277??8#<foVO	jBTU	
   A%% s   ,A1 1	A=<A=c                    | j                  dd      }| j                  dd      }| j                  dg       }| j                  dd      }|s|dd	d
S |dk(  rOt        j                  j                  t        j                  j	                  t
              dd      }t        | |      S |D ]B  }|dk(  rt        |      \  }}n"|dk(  rt        |      \  }}nt        |      \  }}|r<|d|d
c S  |ddd
S )Nr   r   rV   rM   r   automatableTr   zautomatable=falser   
playwrightr   zstorageState.json)r   curlpytestr   r   r>   )
r   r   r   r"   r   r   r   r`   rg   rd   )	r   r   sc_typer   r   r   r;   r   r   s	            r   _run_scenarior      s    LLy)Ell6<0GLL"%E,,}d3Ky<OPP ,RWW__X%>H[\'NN  	Gf'-JB )$/JB-d3JB8vFF	G 8r::r:   r+   c                     ddl m} | D cg c]"  }|j                  d      t        |d         $ }} ||      }|j	                         D cg c]  \  }}|dkD  s| c}}S c c}w c c}}w )u4   id 기준 중복 감지, 중복 id 리스트 반환.r   )Counterr   rH   )collectionsr   r   rr   items)r+   r   sids	id_countsr   counts          r   _detect_duplicatesr     sa    #,5Qqt9Pc!D'lQCQI&/oo&7EleU519EEE REs   A%A%A*A*c                    i }g }t         j                  j                  |       rt        j                  |       D ]  }|j	                         rBt        |j                        }|s+t        |      ||j                  <   |j                  |       U|j                  j                  d      sq	 t        |j                  dd      5 }ddl}|j                  |      }ddd       t        t              r|j                  |       n!t        |t              r|j!                  |        t%        |      }|rt'        |j)                               n
t        |      }	||	|d}
t+        d|	        t-        |j/                               D ]  \  }}t+        d	| d
| d        |rt+        ddj1                  |              |
S # 1 sw Y   xY w# t"        $ r Y w xY w)u   scenarios_dir 하위 프로젝트별 시나리오 카운트 및 중복 감지.

    반환값: {"projects": {"insuwiki": 10, "dashboard": 5}, "total": 15, "duplicates": ["SC-001"]}
    )r   r   r
   r   r   r   N)projectstotal
duplicatesu-   [scenario_runner] 시나리오 통계: total=z  rL   u   개u*   [scenario_runner] 중복 시나리오 ID: z, )r   r   isdirscandiris_dirr1   r   namer'   r!   r   r#   r$   r%   r&   r(   r)   rU   r   sumvaluesprintsortedr   r"   )r   r   all_scenariosentryproject_scenariosr   _yamlr   r   r   rY   projr   s                r   
show_statsr     s   
  "H "M 
ww}}]#ZZ. 	E||~$3EJJ$?!$+./@+AHUZZ(!(():;$$%67
ejj#@ 2A,$q12 "$-%,,T2#D$/%,,T2!	( $M2J&.C!"C4FE"U*MF 
9%
ABhnn./ 'e4&5'%&':499Z;P:QRSM/2 2 ! s+   ,GG AG G		G	GGoutput_filec           	      8   t        |      }t        |       }|D cg c]!  }t        |j                  dg       |      s |# }}|D cg c]  }|j                  d      dk(  s| }}|D cg c]  }|j                  d      dk7  s| }}t	        j                         }	t        |      }
d}d}d}g }|r3	 ddlm}  |       }|r#t               }|rt        d       nt        d       t        d	      5 }|D ci c]  }|j                  t        |      | }}t        |      D ]M  }|j                         }|d
   dk(  r|dz  }!|d
   dk(  r|dz  }/|dz  }|j!                  |d   |d   d       O 	 d d d        |D ]H  }t        |      }|d
   dk(  r|dz  }|d
   dk(  r|dz  }*|dz  }|j!                  |d   |d   d       J |D ch c](  }|j                  d      dk(  s|j                  d      * c}t#        fd|D              }|rdnd}|
|||t%        t	        j                         |	z
  d      ||d}|r	 t'        j(                  t&        j*                  j-                  t&        j*                  j/                  |            d       t1        |dd      5 }t3        j4                  ||dd       d d d        |S |S c c}w c c}w c c}w # t        $ r Y w xY wc c}w # 1 sw Y   zxY wc c}w # 1 sw Y   |S xY w# t6        $ r Y |S w xY w)Nr2   rV   r   r   )check_and_refresh_ttlu4   [scenario_runner] storageState 자동 갱신 완료.um   [scenario_runner] storageState 자동 갱신 실패. 수동으로 setup_auth.py --refresh를 실행하세요.   )max_workersr   r   rH   r   r   r   )r   r   prioritymustc              3   ,   K   | ]  }|d    v   yw)r   Nr   ).0r   must_idss     r   	<genexpr>z run_scenarios.<locals>.<genexpr>  s     <aag)<s   FAILPASSr|   )r   r   r   r   duration_secondsfailuresgateTr   wr   r   Fensure_asciiindent)r   r1   r9   r   timer   qc.auth.setup_authr   r   r   ImportErrorr   submitr   r   rY   r)   anyroundr   r   r   r   abspathr   r   dumpr   )r   r   r   r   r   r   filteredplaywright_scenariosparallel_scenariosstartr   r   r   r   r   r   needs_refreshsuccessexecutor
future_mapfutureresfailed_mustr   rY   r   r   s                             @r   run_scenariosr  K  s9   K(H#M2M(]a,=aeeHb>QS[,\]H] (0Q!155=L3PAQQ%-Ov,1N!OOIIKEMEFFGH 	@13M57PQ H 
	* 
LhDVWqhoomQ7:W
W":. 	LF--/C8}(!X)+1!s4yCM JK	L
L " HAx=H$aKF]i'qLGaKFOO3t9HFGH &.Mz1Bf1LdMH<8<<K 6fD !$))+"5q9F 	KK(DEPTUk39 CQ		&!%BC
 M6M[ ^ RO.  		
 X
L 
L0 N"C
 M  	M	s   !K	K	K"K,KK62K 4K-9K(AK-K:(K:AL #K?=L 	K%$K%(K--K7?L	L 	L 	LLtask_idcheck_fileslevelc                    |sddgdS t         j                  j                  |      s|dk\  r
dd| dgdS dd| gdS t        |      }t	        |      dk(  r|dk\  rdd	gdS dd
gdS |}|sX|rVt         j                  j                  |d|  d      }	 t        |dd      5 }t        j                  d|i|       d d d        |}t        ||      }	||k7  r5t         j                  j                  |      r	 t        j                  |       |	d   dk(  r4|	j                  dg       D cg c]  }|d    d|d     }
}d|
xs dgdS |	d   }|	d   }|dk\  r3|D cg c]  }|j                  d      dk(  s| }}|sd| d| ddgdS d | d| d!|  d"gdS # 1 sw Y   xY w# t        $ r Y w xY w# t        $ r Y w xY wc c}w c c}w )#NSKIPu   scenarios_dir 미지정)r   details   r   u"   시나리오 디렉토리 없음: u(    — Lv.3+ 작업은 시나리오 필수r   u8   시나리오 없음: Lv.3+ 작업은 시나리오 필수u>   시나리오 0건: Lv.1-2 작업은 시나리오 생략 허용z.impact-z.jsonr   r   r   r   )r   r   r   r   rL   r   z	gate=FAILr   r   rV   r   WARN/u    시나리오 통과uO   Lv.3+ 작업이지만 playwright 타입 시나리오 없음 (E2E 검증 권장)r   u    시나리오 통과 (task: ))r   r   r   r1   r   r"   r   r   r   r   r  r   remover   )r  r   r   r  r  r   effective_impact
tmp_impactr   rY   r
  r   r   r   pw_scenarioss                  r   verifyr    sW     .G-HII77=='A: @Owxy  !0RS`Ra.b-cdd#M2M
=QA:$2l1mnn .n-opp #;WW\\-8G9E1JK
	j#8 8A		:{3Q78) =6FGF ;&277>>:J+K	II&' f~8>

:r8RS1agYb8.SS W-EFF7OEHF z#0RaAEE&M\4QRR haw&:;e  VHAeW<XY`Xaab*c)deeE8 8  		  		 T SsN   F& #F<
F& 8F5 )GG	6G	F#F& &	F21F25	G Gc                     t        j                  d      } | j                  ddd       | j                  ddd	
       | j                  ddd
       | j                  ddd       | j                         }|j                  r8t        |j                        }t        t        j                  |dd             y t        |j                  |j                  |j                        }t        t        j                  |dd             y )Nu   시나리오 YAML 실행기)descriptionz--scenarios-dirTu   시나리오 YAML 디렉토리)requiredhelpz--impactr>   u   impact.json 파일 경로)defaultr  r   u   결과 JSON 파일 경로z--stats
store_trueu3   프로젝트별 통계 및 중복 감지 후 종료)r=   r  )r   Fr|   r   )r   r   r   )argparseArgumentParseradd_argument
parse_argsstatsr   r   r   r   dumpsr  impactrZ   )parserargsrY   s      r   mainr#    s    $$1NOF
)D?_`

B5PQ

B5PQ
	,=rsDzz$*<*<=djjeA>?((KKKKF
 
$**V%
:;r:   __main__)N)r>   )r>   r>   )r>   r>   Nr   )r   N)%r  r   r   rM   r   concurrent.futuresr   r   typingr   r#   playwright.sync_apir   r   rr   r&   r   r(   r1   boolr9   tupler`   rd   rg   rt   r   r   r   r   r   r   r  rR   r  r#  rW   r   r:   r   <module>r*     s'     	   ?  3
c d3i 3 4: &d3i 49  $1 $1%c	"2 $1N1t 1dCi(8 1&14 1E$)$4 10 0 0(1S (1dCi@P (1V+Wt +WC +W +W\C  ;D ;T ;:F$t* Fc F*c *d *ZQ Q3 Q# QW[ Ql #@f@f@f @f 	@f
 @f 
@fF<* zF Q  Os   C: :DD