
    Ki>                        d Z ddlZddlmc mZ ddlZddlZddl	m
Z
 ddlmZmZ ddlZej                  j!                  d e e
e      j&                               ddlmZmZmZmZmZmZmZmZ  G d d      Z G d d	      Z G d
 d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z# G d d      Z$ G d d      Z% G d d      Z& G d d      Z' G d d      Z( G d d      Z) G d  d!      Z* G d" d#      Z+ G d$ d%      Z, G d& d'      Z- G d( d)      Z. G d* d+      Z/ G d, d-      Z0 G d. d/      Z1 G d0 d1      Z2 G d2 d3      Z3 G d4 d5      Z4y)6u  TDD 테스트 — image_router.py 이미지 생성 라우터 + fallback.

테스트 범위:
- ImageType enum 존재 및 값
- GenerationResult dataclass 필드
- route_image_type() 라우팅 로직 (한글/영문 모두)
- generate_image() 정상 경로
- generate_image() fallback 시나리오
- _extract_structured_json() JSON 추출 로직
- _render_json_to_png() Satori JSON 렌더링
- fallback 최대 2회 제한
- 로그 기록 (log.warning 포맷)
- 모든 외부 API는 unittest.mock으로 차단
    N)Path)	MagicMockpatch)GenerationResult	ImageType_extract_structured_json_generate_infographic_render_html_to_png_render_json_to_pnggenerate_imageroute_image_typec                   D    e Zd Zd	dZd	dZd	dZd	dZd	dZd	dZd	dZ	y)
TestImageTypeEnumNc                    d}t        t        |      }|sddt        j                         v st	        j
                  t               rt	        j                  t               nddt        j                         v st	        j
                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d x}}y )NPHOTOREALISTIC5assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr   py0py1py3py5	r   r   @py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprAssertionError_format_explanationself@py_assert2@py_assert4@py_format6s       ;/home/jay/workspace/tools/ai-image-gen/test_image_router.pytest_has_photorealisticz)TestImageTypeEnum.test_has_photorealistic*   s}    "23wy"233333333w333w333333y333y333"23333333333    c                    d}t        t        |      }|sddt        j                         v st	        j
                  t               rt	        j                  t               nddt        j                         v st	        j
                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d x}}y )NCARDNEWSr   r   r   r   r   r!   s       r&   test_has_cardnewsz#TestImageTypeEnum.test_has_cardnews-   s{    ",-wy*--------w---w------y---y---*----------r(   c                    d}t        t        |      }|sddt        j                         v st	        j
                  t               rt	        j                  t               nddt        j                         v st	        j
                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d x}}y )NHYBRIDr   r   r   r   r   r!   s       r&   test_has_hybridz!TestImageTypeEnum.test_has_hybrid0   s{    "*+wy(++++++++w+++w++++++y+++y+++(++++++++++r(   c           	         t        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                  t               rt        j                  t               nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}x}}y )
N   ==)zN%(py6)s
{%(py6)s = %(py0)s(%(py4)s
{%(py4)s = %(py1)s(%(py2)s)
})
} == %(py9)slenlistr   )r   r   py2py4py6py9assert %(py11)spy11)r4   r   r3   r   _call_reprcomparer   r   r   r   r   r    )r"   @py_assert3@py_assert5@py_assert8@py_assert7@py_format10@py_format12s          r&   test_three_membersz$TestImageTypeEnum.test_three_members3   s    	?(s?#(q(#q((((#q((((((s(((s((((((4(((4((((((	(((	(((?(((#(((q(((((((r(   c                    t         j                  }|j                  }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  }dd|iz  }t        t        j                  |            d x}x}x}}y )Nphotorealisticr1   )zQ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.PHOTOREALISTIC
}.value
} == %(py7)sr   r   r5   r6   py7assert %(py9)sr8   )r   r   valuer   r;   r   r   r   r   r   r    r"   @py_assert1r<   @py_assert6r=   @py_format8r@   s          r&   test_photorealistic_valuez+TestImageTypeEnum.test_photorealistic_value6   s    ''A'--A1AA-1AAAAA-1AAAAAAAyAAAyAAA'AAA-AAA1AAAAAAAAr(   c                    t         j                  }|j                  }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  }dd|iz  }t        t        j                  |            d x}x}x}}y )Ncardnewsr1   )zK%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.CARDNEWS
}.value
} == %(py7)sr   rE   rG   r8   )r   r*   rH   r   r;   r   r   r   r   r   r    rI   s          r&   test_cardnews_valuez%TestImageTypeEnum.test_cardnews_value9   s    !!5!''5:5':5555':555555y555y555!555'555:5555555r(   c                    t         j                  }|j                  }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  }dd|iz  }t        t        j                  |            d x}x}x}}y )Nhybridr1   )zI%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.HYBRID
}.value
} == %(py7)sr   rE   rG   r8   )r   r-   rH   r   r;   r   r   r   r   r   r    rI   s          r&   test_hybrid_valuez#TestImageTypeEnum.test_hybrid_value<   s    1%%11%1111%111111y111y111111%1111111111r(   returnN)
__name__
__module____qualname__r'   r+   r.   rB   rM   rP   rS    r(   r&   r   r   )   s&    4.,)B62r(   r   c                   D    e Zd Zd	dZd	dZd	dZd	dZd	dZd	dZd	dZ	y)
TestGenerationResultDataclassNc           	         t        dt        d      dddd d      }|j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )NT/tmp/test.pnggeminiF         ?success
image_pathmethod_usedfallback_usedattemptserror_messageelapsed_secondsisz/%(py2)s
{%(py2)s = %(py0)s.success
} is %(py5)srr   r5   r   assert %(py7)srF   )r   r   rb   r   r;   r   r   r   r   r   r    r"   rl   rJ   r$   r<   r%   rL   s          r&   test_success_fieldz0TestGenerationResultDataclass.test_success_fieldF   s    O, 
 yy D yD    yD      q   q   y   D       r(   c           	          t        d      }t        d|dddd d      }|j                  }||k(  }|st        j                  d|fd	||f      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  }dd|iz  }t        t        j                  |            d x}}y )Nr]   Tr^   Fr_   r`   ra   r1   )z2%(py2)s
{%(py2)s = %(py0)s.image_path
} == %(py4)srl   pr   r5   r6   assert %(py6)sr7   )r   r   rc   r   r;   r   r   r   r   r   r    )r"   rr   rl   rJ   r<   @py_format5@py_format7s          r&   test_image_path_fieldz3TestGenerationResultDataclass.test_image_path_fieldR   s    ! 
 || |q    |q      q   q   |      q   q       r(   c           	         t        dd ddddd      }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )NFsatoriT      타임아웃g      ?ra   r1   z3%(py2)s
{%(py2)s = %(py0)s.method_used
} == %(py5)srl   rm   rn   rF   )
r   rd   r   r;   r   r   r   r   r   r    ro   s          r&   test_method_used_fieldz4TestGenerationResultDataclass.test_method_used_field_   s     (
 }}((}((((}((((((q(((q(((}((((((((((r(   c           	         t        dd ddddd      }|j                  }d}||u }|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )NFry   Trz   u   에러g?ra   ri   z5%(py2)s
{%(py2)s = %(py0)s.fallback_used
} is %(py5)srl   rm   rn   rF   )
r   re   r   r;   r   r   r   r   r   r    ro   s          r&   test_fallback_used_fieldz6TestGenerationResultDataclass.test_fallback_used_fieldk   s     "
 &$&$&&&&$&&&&&&q&&&q&&&&&&$&&&&&&&r(   c           	         t        dt        d      dddd d      }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )NT
/tmp/x.pngr^   Fr_          @ra   r1   z0%(py2)s
{%(py2)s = %(py0)s.attempts
} == %(py5)srl   rm   rn   rF   )r   r   rf   r   r;   r   r   r   r   r   r    ro   s          r&   test_attempts_fieldz1TestGenerationResultDataclass.test_attempts_fieldw   s    L) 
 zzQzQzQqqzQr(   c           	         t        dt        d      dddd d      }|j                  }d }||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )NTr   r^   Fr_   r   ra   ri   z5%(py2)s
{%(py2)s = %(py0)s.error_message
} is %(py5)srl   rm   rn   rF   )r   r   rg   r   r;   r   r   r   r   r   r    ro   s          r&   $test_error_message_none_when_successzBTestGenerationResultDataclass.test_error_message_none_when_success   s    L) 
 &$&$&&&&$&&&&&&q&&&q&&&&&&$&&&&&&&r(   c           	         t        dt        d      dddd d      }|j                  }t        j                  }d} ||      }||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}x}}y )NTr   ry   Fr_   g333333?ra   r1   )zs%(py2)s
{%(py2)s = %(py0)s.elapsed_seconds
} == %(py10)s
{%(py10)s = %(py6)s
{%(py6)s = %(py4)s.approx
}(%(py8)s)
}rl   pytest)r   r5   r6   r7   py8py10zassert %(py12)spy12)r   r   rh   r   approxr   r;   r   r   r   r   r   r    )	r"   rl   rJ   r=   r?   @py_assert9r<   @py_format11@py_format13s	            r&   test_elapsed_seconds_fieldz8TestGenerationResultDataclass.test_elapsed_seconds_field   s    L) 
   6FMM6#6M#$66 $66666 $6666666q666q666 666666F666F666M666#666$666666666r(   rT   )
rV   rW   rX   rp   rw   r}   r   r   r   r   rY   r(   r&   r[   r[   E   s%    
!!
)
'

'
7r(   r[   c                   <    e Zd ZddZddZddZddZddZddZy)	TestRouteImageTypeEnglishNc                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	NrD   r1   zT%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py8)s
{%(py8)s = %(py6)s.PHOTOREALISTIC
}r   r   r   r5   r6   r7   r   assert %(py10)sr   r   r   r   r   r;   r   r   r   r   r   r    r"   rJ   r<   r?   r=   @py_format9r   s          r&   test_photorealistic_keywordz5TestRouteImageTypeEnglish.test_photorealistic_keyword        0M 01MY5M5MM15MMMMM15MMMMMMMMMMMMM 0MMM1MMMMMMYMMMYMMM5MMMMMMMMr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	NrO   r1   zN%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py8)s
{%(py8)s = %(py6)s.CARDNEWS
}r   r   r   r   r   r   r   r*   r   r;   r   r   r   r   r   r    r   s          r&   test_cardnews_keywordz/TestRouteImageTypeEnglish.test_cardnews_keyword        *A
+Ay/A/AA+/AAAAA+/AAAAAAAAAAAAA
AAA+AAAAAAyAAAyAAA/AAAAAAAAr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	NrR   r1   zL%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py8)s
{%(py8)s = %(py6)s.HYBRID
}r   r   r   r   r   r   r   r-   r   r;   r   r   r   r   r   r    r   s          r&   test_hybrid_keywordz-TestRouteImageTypeEnglish.test_hybrid_keyword        (=)=Y-=-==)-=====)-================)======Y===Y===-========r(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nr   r1   r   r   r   r   r   r   r   r   s          r&   $test_case_insensitive_photorealisticz>TestRouteImageTypeEnglish.test_case_insensitive_photorealistic   r   r(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	NCardNewsr1   r   r   r   r   r   r   r   r   s          r&   test_case_insensitive_cardnewsz8TestRouteImageTypeEnglish.test_case_insensitive_cardnews   r   r(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nr-   r1   r   r   r   r   r   r   r   r   s          r&   test_case_insensitive_hybridz6TestRouteImageTypeEnglish.test_case_insensitive_hybrid   r   r(   rT   )	rV   rW   rX   r   r   r   r   r   r   rY   r(   r&   r   r      s$    NB>NB>r(   r   c                   D    e Zd Zd	dZd	dZd	dZd	dZd	dZd	dZd	dZ	y)
TestRouteImageTypeKoreanNc                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nu   광고r1   r   r   r   r   r   r   r   r   s          r&   #test_gwanggo_maps_to_photorealisticz<TestRouteImageTypeKorean.test_gwanggo_maps_to_photorealistic        (E)EY-E-EE)-EEEEE)-EEEEEEEEEEEEEEEE)EEEEEEYEEEYEEE-EEEEEEEEr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nu   포토r1   r   r   r   r   r   r   r   r   s          r&   !test_photo_maps_to_photorealisticz:TestRouteImageTypeKorean.test_photo_maps_to_photorealistic   r   r(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	N   카드뉴스r1   r   r   r   r   r   r   r   r   s          r&   %test_cardnews_korean_maps_to_cardnewsz>TestRouteImageTypeKorean.test_cardnews_korean_maps_to_cardnews   s     .E/E93E3EE/3EEEEE/3EEEEEEEEEEEEEEEE/EEEEEE9EEE9EEE3EEEEEEEEr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	N   배너r1   r   r   r   r   r   r   r   r   s          r&   test_banner_maps_to_cardnewsz5TestRouteImageTypeKorean.test_banner_maps_to_cardnews   s     (?)?Y-?-??)-?????)-????????????????)??????Y???Y???-????????r(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nu   인포그래픽r1   r   r   r   r   r   r   r   r   s          r&   !test_infographic_maps_to_cardnewsz:TestRouteImageTypeKorean.test_infographic_maps_to_cardnews   s     1H 12Hi6H6HH26HHHHH26HHHHHHHHHHHHH 1HHH2HHHHHHiHHHiHHH6HHHHHHHHr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	N   한글+사진r1   r   r   r   r   r   r   r   r   s          r&    test_korean_photo_maps_to_hybridz9TestRouteImageTypeKorean.test_korean_photo_maps_to_hybrid   s     /D0DI4D4DD04DDDDD04DDDDDDDDDDDDDDDD0DDDDDDIDDDIDDD4DDDDDDDDr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}}y )	Nu   텍스트오버레이r1   r   r   r   r   r   r   r   r   s          r&    test_text_overlay_maps_to_hybridz9TestRouteImageTypeKorean.test_text_overlay_maps_to_hybrid   s     7L 78LI<L<LL8<LLLLL8<LLLLLLLLLLLLL 7LLL8LLLLLLILLLILLL<LLLLLLLLr(   rT   )
rV   rW   rX   r   r   r   r   r   r   r   rY   r(   r&   r   r      s,    FFF@IEMr(   r   c                       e Zd ZddZddZy)TestRouteImageTypeUnknownNc                 z    t        j                  t        d      5  t        d       d d d        y # 1 sw Y   y xY w)Nu   알 수 없는 용도)matchu   알수없는용도xyzr   raises
ValueErrorr   r"   s    r&   'test_unknown_purpose_raises_value_errorzATestRouteImageTypeUnknown.test_unknown_purpose_raises_value_error   s/    ]]:-DE 	645	6 	6 	6s   1:c                 v    t        j                  t              5  t        d       d d d        y # 1 sw Y   y xY w)N r   r   s    r&   $test_empty_string_raises_value_errorz>TestRouteImageTypeUnknown.test_empty_string_raises_value_error   s+    ]]:& 	!R 	! 	! 	!s   /8rT   )rV   rW   rX   r   r   rY   r(   r&   r   r      s    6!r(   r   c                   |    e Zd ZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
y)&TestGenerateImagePhotorealisticSuccesstmp_pathrU   Nc                    t        dd      5  t        ddd|      }d d d        t        t              }|sd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
dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d }y # 1 sw Y   +xY w)Nimage_router._generate_geminiTreturn_valuerD   u   테스트 프롬프트u   테스트브랜드purposepromptbrand
output_dir5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceresultr   r   r   r5   r6   )r   r   r   r   r   r   r   r   r   r   r    )r"   r   r   r<   ru   s        r&   test_returns_generation_resultzETestGenerateImagePhotorealisticSuccess.test_returns_generation_result   s    2F 	#(/*#	F	 &"233333333z333z333333&333&333333"2333"23333333333	 	s   EEc                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   	   테스트	   브랜드r   ri   rk   r   rm   rn   rF   r   r   rb   r   r;   r   r   r   r   r   r    r"   r   r   rJ   r$   r<   r%   rL   s           r&   test_success_is_truez;TestGenerateImagePhotorealisticSuccess.test_success_is_true   s    2F 	#("!#	F	 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%	 	   C..C7c                    t        dd      5  t        ddd|      }d d d        j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   r   r   r   r^   r1   r|   r   rm   rn   rF   r   r   rd   r   r;   r   r   r   r   r   r    r   s           r&   test_method_used_is_geminizATestGenerateImagePhotorealisticSuccess.test_method_used_is_gemini   s    2F 	#("!#	F	 !!-X-!X----!X------v---v---!---X-------	 	   C//C8c                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   r   r   r   Fri   r   r   rm   rn   rF   r   r   re   r   r;   r   r   r   r   r   r    r   s           r&   test_fallback_not_usedz=TestGenerateImagePhotorealisticSuccess.test_fallback_not_used  s    2F 	#("!#	F	 ##,u,#u,,,,#u,,,,,,v,,,v,,,#,,,u,,,,,,,	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   r   r   r   r_   r1   r   r   rm   rn   rF   r   r   rf   r   r;   r   r   r   r   r   r    r   s           r&   test_attempts_is_onez;TestGenerateImagePhotorealisticSuccess.test_attempts_is_one  s    2F 	#("!#	F	 #!#!####!######v###v######!#######	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d }||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   r   r   r   ri   r   r   rm   rn   rF   r   r   rg   r   r;   r   r   r   r   r   r    r   s           r&   test_error_message_is_nonezATestGenerateImagePhotorealisticSuccess.test_error_message_is_none  s    2F 	#("!#	F	 ##+t+#t++++#t++++++v+++v+++#+++t+++++++	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||k\  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   rD   r   r   r   g        >=)z7%(py2)s
{%(py2)s = %(py0)s.elapsed_seconds
} >= %(py5)sr   rm   rn   rF   )r   r   rh   r   r;   r   r   r   r   r   r    r   s           r&   test_elapsed_seconds_positivezDTestGenerateImagePhotorealisticSuccess.test_elapsed_seconds_positive"  s    2F 	#("!#	F	 %%,,%,,,,%,,,,,,v,,,v,,,%,,,,,,,,,,	 	r   )rV   rW   rX   r   r   r   r   r   r   r   r   rY   r(   r&   r   r      s    4t 4 4&T &d &.4 .D .-t - -$T $d $,4 ,D ,-d -t -r(   r   c                   ,    e Zd ZdeddfdZdeddfdZy) TestGenerateImageCardnewsSuccessr   rU   Nc                    t        dd      5  t        ddd|      }d d d        j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nimage_router._generate_satoriTr   r   u   카드뉴스 프롬프트r   r   ry   r1   r|   r   rm   rn   rF   r   r   s           r&   test_method_used_is_satoriz;TestGenerateImageCardnewsSuccess.test_method_used_is_satori3  s    2F 	#&2!#	F	 !!-X-!X----!X------v---v---!---X-------	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Tr   r   u   배너 프롬프트r   r   ri   rk   r   rm   rn   rF   r   r   s           r&   test_success_true_satoriz9TestGenerateImageCardnewsSuccess.test_success_true_satori=  s    2F 	# ,!#	F	 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%	 	r   )rV   rW   rX   r   r   r   rY   r(   r&   r   r   2  (    .4 .D .& &$ &r(   r   c                   ,    e Zd ZdeddfdZdeddfdZy)TestGenerateImageHybridSuccessr   rU   Nc                    t        dd      5  t        ddd|      }d d d        j                  }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nimage_router._generate_hybridTr   rR   u   하이브리드 프롬프트r   r   r1   r|   r   rm   rn   rF   r   r   s           r&   test_method_used_is_hybridz9TestGenerateImageHybridSuccess.test_method_used_is_hybridN  s    2F 	# 5!#	F	 !!-X-!X----!X------v---v---!---X-------	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr  Tr   r   u   하이브리드r   r   ri   rk   r   rm   rn   rF   r   r   s           r&   test_success_true_hybridz7TestGenerateImageHybridSuccess.test_success_true_hybridX  s    2F 	#'(!#	F	 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%	 	r   )rV   rW   rX   r   r  r  rY   r(   r&   r  r  M  r   r(   r  c                   P    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZy)	TestGenerateImageGeminiFailureu@   Gemini 실패 시 fallback 없이 에러 반환 (GPT 제거됨).r   rU   Nc                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Fr   rD   r   r   r   ri   rk   r   rm   rn   rF   r   r   s           r&   !test_gemini_failure_returns_errorz@TestGenerateImageGeminiFailure.test_gemini_failure_returns_errork  s    2G 	#("!#	F	 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d }||u}|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Fr   rD   r   r   r   is notz9%(py2)s
{%(py2)s = %(py0)s.error_message
} is not %(py5)sr   rm   rn   rF   r   r   s           r&   %test_gemini_failure_error_message_setzDTestGenerateImageGeminiFailure.test_gemini_failure_error_message_setu  s    2G 	#("!#	F	 ##/4/#4////#4//////v///v///#///4///////	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Fr   rD   r   r   r   ri   r   r   rm   rn   rF   r   r   s           r&   test_gemini_failure_no_fallbackz>TestGenerateImageGeminiFailure.test_gemini_failure_no_fallback  s    2G 	#("!#	F	 ##,u,#u,,,,#u,,,,,,v,,,v,,,#,,,u,,,,,,,	 	r   c                    t        dd      5  t        ddd|      }d d d        j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Fr   rD   r   r   r   r_   r1   r   r   rm   rn   rF   r   r   s           r&    test_gemini_failure_attempts_onez?TestGenerateImageGeminiFailure.test_gemini_failure_attempts_one  s    2G 	#("!#	F	 #!#!####!######v###v######!#######	 	r   )	rV   rW   rX   __doc__r   r
  r  r  r  rY   r(   r&   r  r  h  sQ    J'$ '4 '0d 0t 0- - -$ $$ $r(   r  c                   ,    e Zd ZdeddfdZdeddfdZy)TestGenerateImageSatoriFailurer   rU   Nc                    t        dd      5  t        ddd|      }d d d        j                  }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w)Nr   Fr   r   r   r   r   ri   rk   r   rm   rn   rF   r   r   s           r&   !test_satori_failure_returns_errorz@TestGenerateImageSatoriFailure.test_satori_failure_returns_error  s    2G 	#&"!#	F	 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&	 	r   c                 \   t        dd      5  t        ddd|      }d d d        j                  }d }||u}|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|j                  }t        |      }d}	||	kD  }
|
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  }dd|iz  }t        t        j                  |            d x}x}x}
}	y # 1 sw Y   xY w)Nr   Fr   r   r   r   r   r  r  r   rm   rn   rF   r   >)zR%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.error_message
})
} > %(py8)sr3   )r   r   r   r   r   r   r   )r   r   rg   r   r;   r   r   r   r   r   r    r3   )r"   r   r   rJ   r$   r<   r%   rL   r#   r?   rK   r   r   s                r&   %test_satori_failure_has_error_messagezDTestGenerateImageSatoriFailure.test_satori_failure_has_error_message  sT   2G 	#&"!#	F	 ##/4/#4////#4//////v///v///#///4///////'',s'(,1,(1,,,,(1,,,,,,s,,,s,,,,,,6,,,6,,,',,,(,,,1,,,,,,,	 	s   H!!H+)rV   rW   rX   r   r  r  rY   r(   r&   r  r    s(    '$ '4 '	-d 	-t 	-r(   r  c                   d    e Zd ZdeddfdZdeddfdZdeddfdZdedej                  ddfdZ	y)	TestGenerateImageHybridFallbackr   rU   Nc                 8   t        dd      5  t        dd      5  t        ddd|	      }d d d        d d d        j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w# 1 sw Y   xY w)Nr  Fr   r   TrR   r   r   r   ri   rk   r   rm   rn   rF   r   r   s           r&   (test_hybrid_failure_falls_back_to_geminizHTestGenerateImageHybridFallback.test_hybrid_failure_falls_back_to_gemini  s    1F		1E		 $ "!#	F			 		 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%		 		 		 		!   DDDD		DDc                 :   t        dd      5  t        dd      5  t        ddd|	      }d d d        d d d        j                  }d
}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w# 1 sw Y   xY w)Nr  Fr   r   TrR   r   r   r   r^   r1   r|   r   rm   rn   rF   r   r   s           r&   *test_hybrid_fallback_method_used_is_geminizJTestGenerateImageHybridFallback.test_hybrid_fallback_method_used_is_gemini  s    1F		1E		 $ "!#	F			 		 !!-X-!X----!X------v---v---!---X-------		 		 		 		!   DDDD	
DDc                 8   t        dd      5  t        dd      5  t        ddd|	      }d d d        d d d        j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y # 1 sw Y   xY w# 1 sw Y   xY w)Nr  Fr   r   TrR   r   r   r   ri   r   r   rm   rn   rF   r   r   s           r&   !test_hybrid_fallback_used_is_truezATestGenerateImageHybridFallback.test_hybrid_fallback_used_is_true  s    1F		1E		 $ "!#	F			 		 ##+t+#t++++#t++++++v+++v+++#+++t+++++++		 		 		 		r!  caplogc           	         t        dd      5  t        dd      5  |j                  t        j                  d      5  t	        dd	d
|       d d d        d d d        d d d        |j
                  D cg c]  }d|j                  v s| }}t        |      }d}||k\  }|st        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  }dd|iz  }	t        t        j                  |	            d x}x}}y # 1 sw Y   SxY w# 1 sw Y   XxY w# 1 sw Y   ]xY wc c}w )Nr  Fr   r   Timage_routerloggerrR   r   r   r   
[FALLBACK]r_   r   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)sr3   fallback_logsr   r   r   r7   assert %(py8)sr   )r   at_levelloggingWARNINGr   recordsmessager3   r   r;   r   r   r   r   r   r    
r"   r   r'  rl   r.  r#   r=   r$   rv   r   s
             r&   test_hybrid_fallback_loggedz;TestGenerateImageHybridFallback.test_hybrid_fallback_logged  s<   1F
	1E
	 OOGOONOC
	
  "!#	
	 
	 
	 %+NNPqlaii6OPP=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&&&&=&&&=&&&!&&&Q&&&&&&&
	 
	 
	 
	 
	 
	 QsE   F9"F,FF,F94GGF)$F,,F6	1F99G)
rV   rW   rX   r   r   r#  r&  r   LogCaptureFixturer7  rY   r(   r&   r  r    s\    & &$ &.4 .D .,$ ,4 ,'D '&BZBZ '_c 'r(   r  c                   <    e Zd ZdeddfdZdeddfdZdeddfdZy)TestFallbackMaxAttemptsr   rU   Nc                    t        dd      5  t        ddd|      }ddd       j                  }d}||u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)u9   Gemini 실패 → 에러 반환 (GPT fallback 제거됨).r   Fr   rD   r   r   r   Nri   rk   r   rm   rn   rF   r_   r1   r   r   r   rb   r   r;   r   r   r   r   r   r    rf   r   s           r&   -test_gemini_failure_returns_error_no_fallbackzETestFallbackMaxAttempts.test_gemini_failure_returns_error_no_fallback  s   2G 	#("!#	F	 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&#!#!####!######v###v######!#######	 	s   F66G c                    t        dd      5  t        dd      5  t        ddd|      }d	d	d	       d	d	d	       j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u9   Hybrid + Gemini fallback 모두 실패 시 에러 반환.r  Fr   r   rR   r   r   r   Nri   rk   r   rm   rn   rF   rz   r1   r   r<  r   s           r&   #test_hybrid_both_fail_returns_errorz;TestFallbackMaxAttempts.test_hybrid_both_fail_returns_error  s8    1F		1F		 $ "!#	F			 		 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&#!#!####!######v###v######!#######		 		 		 		!   GGGG	GG#c                    t        dd      5  t        dd      5  t        ddd|      }d	d	d	       d	d	d	       j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u>   Infographic + Satori fallback 모두 실패 시 에러 반환."image_router._generate_infographicFr   r   infographicr   r   r   Nri   rk   r   rm   rn   rF   rz   r1   r   r<  r   s           r&   (test_infographic_both_fail_returns_errorz@TestFallbackMaxAttempts.test_infographic_both_fail_returns_error  s8    6UK		1F		 $%"!#	F			 		 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&#!#!####!######v###v######!#######		 		 		 		r@  )rV   rW   rX   r   r=  r?  rD  rY   r(   r&   r:  r:    s;    
$d 
$t 
$$D $T $$ $$ $r(   r:  c                   T    e Zd ZdeddfdZdedej                  ddfdZdeddfdZy)"TestGenerateImageExceptionHandlingr   rU   Nc                    t        dt        d            5  t        ddd|      }ddd       j                  }d	}||u }|st	        j
                  d
|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|j                  }d}||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y# 1 sw Y   xY w)uD   Gemini가 예외를 발생시키면 에러 반환 (fallback 없음).r   u   API 연결 실패side_effectrD   r   r   r   NFri   rk   r   rm   rn   rF   r  r  r   RuntimeErrorr   rb   r   r;   r   r   r   r   r   r    rg   r   s           r&   *test_gemini_raises_exception_returns_errorzMTestGenerateImageExceptionHandling.test_gemini_raises_exception_returns_error$  s$   +$%89
 		 $("!#	F			 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&##/4/#4////#4//////v///v///#///4///////		 		   F>>Gr'  c                    t        dt        d            5  |j                  t        j                  d      5  t        ddd|	       d
d
d
       d
d
d
       |j                  D cg c]"  }|j                  t        j                  k(  s!|$ }}t        |      }d}||k\  }|st        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  }dd|iz  }	t        t        j                   |	            d
x}x}}y
# 1 sw Y   ZxY w# 1 sw Y   _xY wc c}w )u0   예외 발생 시 경고 로그가 기록된다.r   r{   rH  r)  r*  rD   r   r   r   Nr_   r   r-  r3   r.  r/  r0  r   )r   TimeoutErrorr1  r2  r3  r   r4  levelnor3   r   r;   r   r   r   r   r   r    r6  s
             r&   test_gemini_exception_loggedz?TestGenerateImageExceptionHandling.test_gemini_exception_logged3  s*    /(8	
 OOGOONOC	 ("!#		 	 %+NNSqaii7??6RSS=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&&&&=&&&=&&&!&&&Q&&&&&&&	 	 	 	 Ts.   "F.F!	F.'"F;
F;!F+	&F..F8c                    t        dt        d            5  t        ddd|      }ddd       j                  }d	}||u }|st	        j
                  d
|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|j                  }d}||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y# 1 sw Y   xY w)u9   Satori 예외 발생 시 에러 반환 (fallback 없음).r   u   HTML 파싱 오류rH  r   r   r   r   NFri   rk   r   rm   rn   rF   r  r  rJ  r   s           r&    test_satori_raises_returns_errorzCTestGenerateImageExceptionHandling.test_satori_raises_returns_errorE  s$   +$%9:
 		 $&"!#	F			 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&##/4/#4////#4//////v///v///#///4///////		 		rM  )	rV   rW   rX   r   rL  r   r8  rQ  rS  rY   r(   r&   rF  rF  #  sI    04 0D 0'T '6C[C[ '`d '$0 0$ 0r(   rF  c                   \    e Zd Zdedej
                  ddfdZdedej
                  ddfdZy)TestGenerateImageLoggingr   r'  rU   Nc                 n   t        dd      5  |j                  t        j                  d      5  t	        ddd|	       d
d
d
       d
d
d
       |j
                  D cg c]"  }|j                  t        j                  k(  s!|$ }}t        |      }d}||k\  }|st        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  }dd|iz  }	t        t        j                  |	            d
x}x}}y
# 1 sw Y   ZxY w# 1 sw Y   _xY wc c}w )u5   생성 시작 시 INFO 레벨 로그가 기록된다.r   Tr   r)  r*  rD   r   r   r   Nr_   r   r-  r3   	info_logsr/  r0  r   )r   r1  r2  INFOr   r4  rP  r3   r   r;   r   r   r   r   r   r    )
r"   r   r'  rl   rW  r#   r=   r$   rv   r   s
             r&   test_info_log_on_startz/TestGenerateImageLogging.test_info_log_on_start[  s    1E		OOGLLO@		 ("!#				 		 !'L1!))w||2KQL	L9~""~""""~""""""s"""s""""""9"""9"""~""""""""""		 		 		 		 Ms.   "F%F F%"F2F2F"	F%%F/c           	         t        dd      5  t        dd      5  |j                  t        j                  d      5  t	        dd	d
|       ddd       ddd       ddd       |j
                  D cg c]1  }d|j                  v s|j                  t        j                  k(  s0|3 }}t        |      }d}||k\  }|st        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  }dd|iz  }	t        t        j                   |	            dx}x}}y# 1 sw Y   qxY w# 1 sw Y   vxY w# 1 sw Y   {xY wc c}w )u2   fallback 로그는 WARNING 레벨이어야 한다.r  Fr   r   Tr)  r*  rR   r   r   r   Nr,  r_   r   r-  r3   r.  r/  r0  r   )r   r1  r2  r3  r   r4  r5  rP  r3   r   r;   r   r   r   r   r   r    r6  s
             r&   #test_warning_log_level_for_fallbackz<TestGenerateImageLogging.test_warning_log_level_for_fallbackj  sS    1F
	1E
	 OOGOONOC
	
  "!#	
	 
	 
	 %+NNqqlaii6OTUT]T]ahapapTpqq=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&&&&=&&&=&&&!&&&Q&&&&&&&
	 
	 
	 
	 
	 
	 rsK   G"G
F=G
G4G$G$&G$=GG

G	GG!)rV   rW   rX   r   r   r8  rY  r[  rY   r(   r&   rU  rU  Z  sD    #t #V=U=U #Z^ #'D '&JbJb 'gk 'r(   rU  c                       e Zd ZddZddZy)TestImageTypeInfographicNc                    d}t        t        |      }|sddt        j                         v st	        j
                  t               rt	        j                  t               nddt        j                         v st	        j
                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      dz  }t        t	        j                  |            dx}}y)u0   ImageType에 INFOGRAPHIC 멤버가 존재한다.INFOGRAPHICr   r   r   r   Nr   r!   s       r&   test_has_infographicz-TestImageTypeInfographic.test_has_infographic  s{    "/0wy-00000000w000w000000y000y000-0000000000r(   c                    t         j                  }|j                  }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  }dd|iz  }t        t        j                  |            dx}x}x}}y)	u4   INFOGRAPHIC의 값은 'infographic' 문자열이다.rC  r1   )zN%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.INFOGRAPHIC
}.value
} == %(py7)sr   rE   rG   r8   N)r   r_  rH   r   r;   r   r   r   r   r   r    rI   s          r&   test_infographic_valuez/TestImageTypeInfographic.test_infographic_value  s    $$;$**;m;*m;;;;*m;;;;;;y;;;y;;;$;;;*;;;m;;;;;;;r(   rT   )rV   rW   rX   r`  rb  rY   r(   r&   r]  r]    s    1<r(   r]  c                   4    e Zd ZddZddZddZddZddZy)TestRouteImageTypeInfographicNc                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}}y	)
u=   'infographic' 키워드는 INFOGRAPHIC으로 라우팅된다.rC  r1   zQ%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py8)s
{%(py8)s = %(py6)s.INFOGRAPHIC
}r   r   r   r   r   Nr   r   r_  r   r;   r   r   r   r   r   r    r   s          r&   test_infographic_keywordz6TestRouteImageTypeInfographic.test_infographic_keyword  s     -G.G)2G2GG.2GGGGG.2G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G2GGGGGGGGr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}}y	)
uB   'comparison_table' 키워드는 INFOGRAPHIC으로 라우팅된다.comparison_tabler1   rf  r   r   r   r   r   Nrg  r   s          r&   test_comparison_table_keywordz;TestRouteImageTypeInfographic.test_comparison_table_keyword  s     2L 23Ly7L7LL37LLLLL37LLLLLLLLLLLLL 2LLL3LLLLLLyLLLyLLL7LLLLLLLLr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}}y	)
u;   'checklist' 키워드는 INFOGRAPHIC으로 라우팅된다.	checklistr1   rf  r   r   r   r   r   Nrg  r   s          r&   test_checklist_keywordz4TestRouteImageTypeInfographic.test_checklist_keyword  s     +E,E	0E0EE,0EEEEE,0EEEEEEEEEEEEEEEE,EEEEEE	EEE	EEE0EEEEEEEEr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}}y	)
u>   'process_flow' 키워드는 INFOGRAPHIC으로 라우팅된다.process_flowr1   rf  r   r   r   r   r   Nrg  r   s          r&   test_process_flow_keywordz7TestRouteImageTypeInfographic.test_process_flow_keyword  s     .H/H93H3HH/3HHHHH/3HHHHHHHHHHHHHHHH/HHHHHH9HHH9HHH3HHHHHHHHr(   c                    d}t        |      }t        j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}}y	)
u7   'chart' 키워드는 INFOGRAPHIC으로 라우팅된다.chartr1   rf  r   r   r   r   r   Nrg  r   s          r&   test_chart_keywordz0TestRouteImageTypeInfographic.test_chart_keyword  s     'A(AI,A,AA(,AAAAA(,AAAAAAAAAAAAAAAA(AAAAAAIAAAIAAA,AAAAAAAAr(   rT   )rV   rW   rX   rh  rk  rn  rq  rt  rY   r(   r&   rd  rd    s     HMFIBr(   rd  c                   ,    e Zd ZdeddfdZdeddfdZy)#TestGenerateImageInfographicSuccessr   rU   Nc                    t        dd      5  t        ddd|      }ddd       j                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)uE   인포그래픽 생성 성공 시 method_used는 'infographic'이다.rB  Tr   rC     인포그래픽 프롬프트r   r   Nr1   r|   r   rm   rn   rF   r   r   s           r&   test_method_used_is_infographiczCTestGenerateImageInfographicSuccess.test_method_used_is_infographic  s    7dK 	#%5!#	F	 !!2]2!]2222!]222222v222v222!222]2222222	 	r   c                    t        dd      5  t        ddd|      }ddd       j                  }d}||u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)uC   comparison_table 목적으로 생성 성공 시 success는 True다.rB  Tr   rj  u   비교표 프롬프트r   r   Nri   rk   r   rm   rn   rF   r   r   s           r&   test_success_true_infographiczATestGenerateImageInfographicSuccess.test_success_true_infographic  s    7dK 	#*/!#	F	 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%	 	r   )rV   rW   rX   r   ry  r{  rY   r(   r&   rv  rv    s(    	3 	3 	3	&d 	&t 	&r(   rv  c                   L    e Zd ZdeddfdZdeddfdZdeddfdZdeddfdZy)$TestGenerateImageInfographicFallbackr   rU   Nc                    t        dd      5  t        dd      5  t        ddd|	      }d
d
d
       d
d
d
       j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}y
# 1 sw Y   xY w# 1 sw Y   xY w)uD   _generate_infographic 실패 시 satori fallback으로 성공한다.rB  Fr   r   TrC  rx  r   r   Nri   rk   r   rm   rn   rF   r   )r   r   rb   r   r;   r   r   r   r   r   r    re   r   s           r&   -test_infographic_failure_falls_back_to_satorizRTestGenerateImageInfographicFallback.test_infographic_failure_falls_back_to_satori  s=    6UK		1E		 $%5!#	F			 		 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%##+t+#t++++#t++++++v+++v+++#+++t+++++++		 		 		 		s!   GGGG	GG"c                 :   t        dd      5  t        dd      5  t        ddd|	      }d
d
d
       d
d
d
       j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}y
# 1 sw Y   xY w# 1 sw Y   xY w)u?   infographic → satori fallback 시 method_used는 'satori'다.rB  Fr   r   TrC  rx  r   r   Nry   r1   r|   r   rm   rn   rF   r   r   s           r&   /test_infographic_fallback_method_used_is_satorizTTestGenerateImageInfographicFallback.test_infographic_fallback_method_used_is_satori  s     6UK		1E		 $%5!#	F			 		 !!-X-!X----!X------v---v---!---X-------		 		 		 		r$  c                 8   t        dd      5  t        dd      5  t        ddd|      }d	d	d	       d	d	d	       j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u>   infographic과 satori 모두 실패하면 success는 False다.rB  Fr   r   rC  rx  r   r   Nri   rk   r   rm   rn   rF   r   r   s           r&   rD  zMTestGenerateImageInfographicFallback.test_infographic_both_fail_returns_error  s     6UK		1F		 $%5!#	F			 		 ~~&&~&&&&~&&&&&&v&&&v&&&~&&&&&&&&&&		 		 		 		r!  c                    |dz  }t        dd      5  t        d|      }ddd       d}|u }|st        j                  d|fd||f      d	t	        j
                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)uK   _extract_structured_json이 None 반환 시 _generate_infographic은 False.out.pngz%image_router._extract_structured_jsonNr   u   [infographic] 설명Fri   z%(py0)s is %(py3)sr   r   r   assert %(py5)sr   )
r   r	   r   r;   r   r   r   r   r   r    )r"   r   output_filer   r#   rJ   @py_format4r%   s           r&   %test_infographic_fails_when_json_nonezJTestGenerateImageInfographicFallback.test_infographic_fails_when_json_none  s    *:N 	P*+A;OF	Pvvvv	P 	Ps   CC)rV   rW   rX   r   r  r  rD  r  rY   r(   r&   r}  r}    sO    ,d ,t ,. .QU .' '$ 'd t r(   r}  c                   <    e Zd ZddZddZddZddZddZddZy)	TestExtractStructuredJsonNc                    d}t               }||_        d|_        t        d|      5  t	        dd      }ddd       t        t              }|sd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
dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)uF   _extract_structured_json은 유효한 JSON에서 dict를 반환한다.u:   {"type": "infographic", "title": "테스트", "items": []}r   subprocess.runr      설명rC  Nr   r   r   dictr   typer1   z%(py1)s == %(py4)sr   r6   rt   r7   )r   stdout
returncoder   r   r   r  r   r   r   r   r   r   r    r;   )	r"   json_responsemock_resultr   r<   ru   @py_assert0r#   rv   s	            r&   test_returns_dict_on_valid_jsonz9TestExtractStructuredJson.test_returns_dict_on_valid_json  s$   Tk*!"#+> 	G-hFF	G&$''''''''z'''z''''''&'''&''''''$'''$''''''''''f~..~....~...~..........	G 	Gs   GG(c                 l   t               }d|_        d|_        t        d|      5 }t	        dd       ddd       j                          |j                  }|d   r|d   d   n|d   j                  d	g       }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  }dd|iz  }	t        t        j                   |	            dx}}y# 1 sw Y   xY w)u<   subprocess.run 호출 시 claude CLI 경로가 포함된다.0{"type": "checklist", "title": "t", "items": []}r   r  r   r  rm  Nr_   argsz/home/jay/.local/bin/claudeinz%(py1)s in %(py3)scmd_strr   r   r  r   )r   r  r  r   r   assert_called_once	call_argsgetstrr   r;   r   r   r   r   r   r    )
r"   r  mock_runr  cmdr  r  r#   r  r%   s
             r&   test_calls_claude_cliz/TestExtractStructuredJson.test_calls_claude_cli  s    kO!"#+> 	<($X{;	<##%&&	!*1il1o9Q<3C3CFB3Oc(,7,7777,777,7777777777777777	< 	<s   D**D3c                    t               }d|_        d|_        t        d|      5  t        d      5  t	        dd      }ddd       ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)u4   stdout이 빈 문자열일 때 None을 반환한다.r   r   r  r   
time.sleepr  rC  Nri   r  r   r  r  r   r   r  r  r   r   r   r;   r   r   r   r   r   r    r"   r  r   r#   rJ   r  r%   s          r&   test_empty_result_returns_nonez8TestExtractStructuredJson.test_empty_result_returns_none  s    k!"#+> 	Gl@S 	G-hFF	G 	Gv~vvv	G 	G 	G 	G!   DC5D5C>	:DD
c                    t               }d|_        d|_        t        d|      5  t        d      5  t	        dd      }ddd       ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)uF   stdout이 JSON이 아닌 일반 텍스트일 때 None을 반환한다.u3   이것은 JSON이 아닌 일반 텍스트입니다.r   r  r   r  r  rC  Nri   r  r   r  r  r   r  r  s          r&   !test_non_json_result_returns_nonez;TestExtractStructuredJson.test_non_json_result_returns_none"  s    kR!"#+> 	Gl@S 	G-hFF	G 	Gv~vvv	G 	G 	G 	Gr  c                 D   ddddgdgd}t               }d|_        d|_        t        d	|
      5  t	        dd      }ddd       |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  }dd|iz  }t        t        j                  |            d}y# 1 sw Y   xY w)u3   유효한 JSON 응답이 올바르게 파싱된다.rj  u   비교Ax)labelfeaturesr  titleitemsu]   {"type": "comparison_table", "title": "비교", "items": [{"label": "A", "features": ["x"]}]}r   r  r   r  Nr1   )z%(py0)s == %(py2)sr   expected)r   r5   zassert %(py4)sr6   r  )r"   r  r  r   rJ   @py_format3ru   s          r&    test_valid_json_parsed_correctlyz:TestExtractStructuredJson.test_valid_json_parsed_correctly+  s    .WZilhmMnLopkk 	 "##+> 	L-h8JKF	L!!!!v!!!!!!v!!!v!!!!!!!!!!!!!!!!	L 	Ls   DDc                    t               }d|_        d|_        t        d|      5 }t	        dd       ddd       j
                  }|d   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  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   xY w)uB   _extract_structured_json 프롬프트에 img_type이 포함된다.r  r   r  r   r  rm  Nrz   r  r  
prompt_argr  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  r%   s
             r&   test_prompt_contains_img_typez7TestExtractStructuredJson.test_prompt_contains_img_type7  s    kO!"#+> 	<($X{;	<&&	l1oV
({j(((({j((({((((((j(((j(((((((	< 	<   C::DrT   )	rV   rW   rX   r  r  r  r  r  r  rY   r(   r&   r  r     s     	/8
"
)r(   r  c                   D    e Zd Zd	dZd	dZd	dZd	dZd	dZd	dZd	dZ	y)
TestExtractStructuredJsonRetryNc                    t               }d|_        d|_        t               }d|_        d|_        t        d||g      5 }t        d      5  t	        dd      }d	d	d	       d	d	d	       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  }dd|iz  }t        t        j                  |            d	x}}j                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	# 1 sw Y   xY w# 1 sw Y   xY w)u,   빈 응답 후 재시도하여 성공한다.r   r   7   {"type": "infographic", "title": "성공", "items": []}r  rH  r  r  rC  Nu   성공r  r1   )z%(py0)s == %(py3)sr   r  r  r   rz   z2%(py2)s
{%(py2)s = %(py0)s.call_count
} == %(py5)sr  rm   rn   rF   r   r  r  r   r   r   r;   r   r   r   r   r   r    
call_count)r"   
mock_emptymock_successr  r   r#   rJ   r  r%   r$   r<   rL   s               r&   test_retries_on_empty_responsez=TestExtractStructuredJsonRetry.test_retries_on_empty_responseJ  s_   [

 !
 {W"##*l1KL 	GPXZ_`lZm 	G-hFF	G 	G"/(RPPvPPPPPvPPPPPPPvPPPvPPPPPPPPPPP""'a'"a''''"a''''''x'''x'''"'''a'''''''	G 	G 	G 	Gs$    G)GG)G&	!G))G3c                    t               }d|_        d|_        t               }d|_        d|_        t        d||g      5 }t        d      5  t	        dd      }d	d	d	       d	d	d	       d	}|u}|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}j                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	# 1 sw Y   xY w# 1 sw Y   xY w)u7   JSON이 아닌 응답 후 재시도하여 성공한다.u   일반 텍스트입니다r   u5   {"type": "checklist", "title": "성공", "items": []}r  rH  r  r  rm  Nr  z%(py0)s is not %(py3)sr   r  r  r   rz   r1   r  r  rm   rn   rF   r  )r"   	mock_textr  r  r   r#   rJ   r  r%   r$   r<   rL   s               r&   !test_retries_on_non_json_responsez@TestExtractStructuredJsonRetry.test_retries_on_non_json_responseW  sT   K	6	 	 {U"##)\1JK 	ExY^_kYl 	E-hDF	E 	E!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!""'a'"a''''"a''''''x'''x'''"'''a'''''''	E 	E 	E 	Es$    G$GG$G!	G$$G.c                    t               }d|_        d|_        d|_        t               }d|_        d|_        t	        d||g      5 }t	        d      5  t        d	d
      }ddd       ddd       d}|u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}j                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y# 1 sw Y   xY w# 1 sw Y   xY w)u2   비정상 종료 후 재시도하여 성공한다.r   errorr_   r  r   r  rH  r  r  rC  Nr  r  r   r  r  r   rz   r1   r  r  rm   rn   rF   )r   r  stderrr  r   r   r   r;   r   r   r   r   r   r    r  )r"   	mock_failr  r  r   r#   rJ   r  r%   r$   r<   rL   s               r&   #test_retries_on_nonzero_return_codezBTestExtractStructuredJsonRetry.test_retries_on_nonzero_return_coded  s\   K		"	 	 {W"##)\1JK 	GxY^_kYl 	G-hFF	G 	G!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!""'a'"a''''"a''''''x'''x'''"'''a'''''''	G 	G 	G 	Gs$   G+G G+G(	#G++G5c                    t               }d|_        d|_        t        d|      5 }t        d      5  t	        dd      }ddd       ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}j                  }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }
t        t        j                  |
            dx}x}	}y# 1 sw Y   xY w# 1 sw Y   xY w)u2   3회 모두 빈 응답이면 None을 반환한다.r   r   r  r   r  r  rC  Nri   r  r   r  r  r      r1   r  r  rm   rn   rF   r  )r"   r  r  r   r#   rJ   r  r%   r$   r<   rL   s              r&   #test_returns_none_after_max_retrieszBTestExtractStructuredJsonRetry.test_returns_none_after_max_retriesr  s6   [

 !
#*= 	G5Q]K^ 	G-hFF	G 	Gv~vvv""'a'"a''''"a''''''x'''x'''"'''a'''''''	G 	G 	G 	Gs!   G
F=G
=G	G

Gc                     t               }d|_        d|_        t        d|      5  t        d      5 }t	        dd       ddd       ddd       j                          y# 1 sw Y   "xY w# 1 sw Y   &xY w)	u*   첫 시도 성공 시 sleep 호출 없다.r  r   r  r   r  r  rC  N)r   r  r  r   r   assert_not_called)r"   r  
mock_sleeps      r&   test_no_sleep_on_first_successz=TestExtractStructuredJsonRetry.test_no_sleep_on_first_success|  sn     {W"##,? 	>|AT 	>Xb$X}=	> 	>$$&	> 	> 	> 	>s!   A,A A, A)	%A,,A5c                 &   t               }d|_        d|_        t               }d|_        d|_        t        d||g      5  t        d      5 }t	        dd       d	d	d	       d	d	d	       j                  d
       y	# 1 sw Y   #xY w# 1 sw Y   'xY w)u,   재시도 시 time.sleep(1)이 호출된다.r   r   r  r  rH  r  r  rC  Nr_   )r   r  r  r   r   assert_called_once_with)r"   r  r  r  s       r&   !test_sleep_called_between_retriesz@TestExtractStructuredJsonRetry.test_sleep_called_between_retries  s    [

 !
 {W"##*l1KL 	>eT`Na 	>eo$X}=	> 	>**1-	> 	> 	> 	>s$    BA;B;B	 BBc                    t               }d|_        d|_        t               }d|_        d|_        t        d||||g      5 }t        d      5  t	        dd      }d	d	d	       d	d	d	       d	}|u}|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}j                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	# 1 sw Y   xY w# 1 sw Y   xY w)uJ   JSON 파싱 에러 시 피드백 포함 4번째 재시도가 실행된다.u+   {"type": "infographic", "title": 잘못된}r   r  r  rH  r  r  rC  Nr  r  r   r  r  r   r0   r1   r  r  rm   rn   rF   r  )r"   mock_invalidr  r  r   r#   rJ   r  r%   r$   r<   rL   s               r&   -test_json_parse_error_triggers_feedback_retryzLTestExtractStructuredJsonRetry.test_json_parse_error_triggers_feedback_retry  s]    {K"# {W"#"|\[g0hi	Gmu,	G .hFF		G 	G
 "!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!""'a'"a''''"a''''''x'''x'''"'''a'''''''	G 	G 	G 	Gs$   G&GG&G#	G&&G0rT   )
rV   rW   rX   r  r  r  r  r  r  r  rY   r(   r&   r  r  I  s%    (((('
.(r(   r  c                   L    e Zd ZdeddfdZdeddfdZdeddfdZdeddfdZy)TestRenderHtmlToPngr   rU   Nc                    |dz  dt         ddffd}t               }||j                  _        t               }||j                  _        t               }t               }||j                  j                  _        t        |      |_        t        d      |_	        t        d|      5  t        d	      }ddd       d
}|u }	|	st        j                  d|	fd||f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      dz  }
dd|
iz  }t%        t        j&                  |            dx}	}y# 1 sw Y   xY w)u5   Playwright 렌더링 성공 시 True를 반환한다.r  kwargsrU   Nc                  p    t        | j                  d            }t        |      j                  d       y Npath   fake png datar  r  r   write_bytesr  rr   r  s     r&   fake_screenshotzITestRenderHtmlToPng.test_returns_true_on_success.<locals>.fake_screenshot  +    FJJv{34AG 01r(   r   F#playwright.sync_api.sync_playwright<div>test</div>Tri   r  r   r  r  r   )objectr   
screenshotrI  new_pager   chromiumlaunch	__enter____exit__r   r
   r   r;   r   r   r   r   r   r    )r"   r   r  	mock_pagemock_browsermock_context_managermock_playwrightr   r#   rJ   r  r%   r  s               @r&   test_returns_true_on_successz0TestRenderHtmlToPng.test_returns_true_on_success  s   *	2f 	2 	2 K	+:	( {-6*({#+7C  ''4)2)P&(1u(E%8G[\ 	I():KHF	Iv~vvv	I 	Is   )E$$E-c                    t               }t        t        d            |_        t        d      |_        t	        d|      5  t        d|dz        }ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)uE   Playwright 렌더링 실패(예외 발생) 시 False를 반환한다.u   Playwright 초기화 실패rH  Fr   r  r  r  Nri   r  r   r  r  r   )r   rK  r  r  r   r
   r   r;   r   r   r   r   r   r    )r"   r   r  r   r#   rJ   r  r%   s           r&   test_returns_false_on_failurez1TestRenderHtmlToPng.test_returns_false_on_failure  s    ({)2|Li?j)k&(1u(E%8G[\ 	R():Hy<PQF	Rvvvv	R 	Rs   DD
c                    |dz  dt         ddffd}t               }||j                  _        t               }||j                  _        t               }t               }||j                  j                  _        t        |      |_        t        d      |_	        t        d|      5  t        d	       ddd       |j                  j                  d
   }|j                  }d}	 ||	      }
d}|
|u }|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  }dd|iz  }t)        t        j*                  |            dx}x}	x}
x}}y# 1 sw Y   !xY w)u7   screenshot() 호출 시 full_page=True가 전달된다.r  r  rU   Nc                  p    t        | j                  d            }t        |      j                  d       y r  r  r  s     r&   r  zWTestRenderHtmlToPng.test_screenshot_called_with_full_page_true.<locals>.fake_screenshot  r  r(   r   Fr  r  r_   	full_pageTri   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)scall_kwargs)r   r5   r6   r7   r8   r9   r:   )r  r   r  rI  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  r  rJ   r<   r=   r>   r?   r@   rA   r  s                  @r&   *test_screenshot_called_with_full_page_truez>TestRenderHtmlToPng.test_screenshot_called_with_full_page_true  sk   *	2f 	2 	2 K	+:	( {-6*({#+7C  ''4)2)P&(1u(E%8G[\ 	@ 1;?	@  **44Q73{3{+3t3+t3333+t333333{333{333333{333+333t33333333	@ 	@s   )GGc                    |dz  g dt         ddffd}t               }||j                  _        dt        dt        ddffd}t               }||j
                  _        t               }t               }||j                  j                  _        t        |	      |_	        t        d
	      |_
        t        d|	      5  t        d|      5  t        d       ddd       ddd       t              }d}	||	kD  }
|
st        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  }dd|iz  }t)        t        j*                  |            dx}x}
}	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  }dd|iz  }t)        t        j*                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)uF   생성된 HTML body CSS에 min-height가 사용된다 (height 대신).r  r  rU   Nc                  p    t        | j                  d            }t        |      j                  d       y r  r  r  s     r&   r  zJTestRenderHtmlToPng.test_body_css_uses_min_height.<locals>.fake_screenshot  r  r(   content_c                 (    j                  |        y )N)append)r  r  captured_htmls     r&   capture_write_textzMTestRenderHtmlToPng.test_body_css_uses_min_height.<locals>.capture_write_text  s      )r(   r   Fr  zpathlib.Path.write_textrH  r  r   r  )z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)sr3   r
  r/  r0  r   z
min-heightr  r  html_contentr  r  r   )r  r   r  rI  r  r  r   r  r  r  r  r   r
   r3   r   r;   r   r   r   r   r   r    )r"   r   r  r  r  r  r  r  r#   r=   r$   rv   r   r  r  r  r%   r
  r  s                    @@r&   test_body_css_uses_min_heightz1TestRenderHtmlToPng.test_body_css_uses_min_height  s   *#%	2f 	2 	2 K	+:	(	* 	*# 	*$ 	* !{-6*({#+7C  ''4)2)P&(1u(E% 7FZ[	@+9KL	@   1;?		@ 	@ =!%A%!A%%%%!A%%%%%%s%%%s%%%%%%=%%%=%%%!%%%A%%%%%%%$Q'+||++++||+++|++++++|+++|+++++++	@ 	@ 	@ 	@s$    J3J&J3&J0	+J33J=)rV   rW   rX   r   r  r  r  r  rY   r(   r&   r  r    sN    T d ,d t 44 4D 42,d ,t ,r(   r  c                   \    e Zd ZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZy)	TestRenderJsonToPngr   rU   Nc                    |dz  dt         dt         dt        ffd}t        d|      5  t        dd	d
gd      }ddd       d}|u }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}}y# 1 sw Y   xY w)u6   Satori JSON 렌더링 성공 시 True를 반환한다.r  r  r  rU   c                  L    j                  d       t               }d|_        |S Nr  r   r  r   r  r  r  mockr  s      r&   fake_runzBTestRenderJsonToPng.test_returns_true_on_success.<locals>.fake_run  %    ##$45;DDOKr(   r  rH  rm  tar  NTri   r  r   r  r  r   )r  r   r   r   r   r;   r   r   r   r   r   r    )	r"   r   r  r   r#   rJ   r  r%   r  s	           @r&   r  z0TestRenderJsonToPng.test_returns_true_on_success	  s    *	F 	f 	 	 #: 	k(+WZV[)\^ijF	kv~vvv	k 	ks   C,,C5c                    t               }d|_        d|_        t        d|      5  t	        ddg d|dz        }d	d	d	       d
}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	# 1 sw Y   xY w)u6   subprocess 비정상 종료 시 False를 반환한다.r_   r  r  r   rC  r  r  r  NFri   r  r   r  r  r   )r   r  r  r   r   r   r;   r   r   r   r   r   r    )r"   r   r  r   r#   rJ   r  r%   s           r&   "test_returns_false_on_nonzero_exitz6TestRenderJsonToPng.test_returns_false_on_nonzero_exit  s    k!"$#+> 	s(-#XZ)[]ehq]qrF	svvvv	s 	ss   C((C1c                   	 |dz  	dt         dt         dt        f	fd}t        d|      5 }t        dd	g d
	       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  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u;   subprocess.run 호출 시 --json 플래그가 포함된다.r  r  r  rU   c                  L    j                  d       t               }d|_        |S r  r  r  s      r&   r  zFTestRenderJsonToPng.test_calls_satori_with_json_flag.<locals>.fake_run$  r  r(   r  rH  rs  r  r  Nr   --jsonr  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#   r  r%   r  s
            @r&    test_calls_satori_with_json_flagz4TestRenderJsonToPng.test_calls_satori_with_json_flag   s    *	F 	f 	 	 #: 	[h3 LkZ	[&&q)!,	$x9$$$$x9$$$x$$$$$$9$$$9$$$$$$$	[ 	[s   C==Dc                 .   |dz  ddddgd}dt         dt         d	t        ffd
}t        d|      5 }t        |       ddd       j                  d   d   }|j                  d      dz   }ddl}|j                  ||         }|d   }	d}
|	|
k(  }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
|d   }	ddg}
|	|
k(  }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y# 1 sw Y   KxY w)u<   JSON 데이터가 올바르게 직렬화되어 전달된다.r  rm  r   u   항목1u   항목2r  r  r  rU   c                  L    j                  d       t               }d|_        |S r  r  r  s      r&   r  zITestRenderJsonToPng.test_json_data_serialized_correctly.<locals>.fake_run4  r  r(   r  rH  Nr   r  r_   r  r1   r  r  rt   r7   r  )r  r   r   r   r  indexjsonloadsr   r;   r   r   r    )r"   r   	test_datar  r  r  json_arg_idxr#  parsedr  r<   r#   ru   rv   r  s                 @r&   #test_json_data_serialized_correctlyz7TestRenderJsonToPng.test_json_data_serialized_correctly/  sI   *(;)U^I_`		F 	f 	 	 #: 	8h	;7	8&&q)!,	 x014Il34g-+-+----+------+-------g89i"88"88888"8888888"88888888	8 	8s   F

Fc                    t        dt        d            5  t        ddi|dz        }ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u(   예외 발생 시 False를 반환한다.r  znode not foundrH  r  rC  r  NFri   r  r   r  r  r   )r   rK  r   r   r;   r   r   r   r   r   r    )r"   r   r   r#   rJ   r  r%   s          r&   test_returns_false_on_exceptionz3TestRenderJsonToPng.test_returns_false_on_exceptionD  s    #>N1OP 	X(&-)@(YBVWF	Xvvvv	X 	Xs   CC )	rV   rW   rX   r   r  r  r  r(  r*  rY   r(   r&   r  r    sa    T d 4 D % %$ %9D 9T 9*  r(   r  c                   $    e Zd ZddZddZddZy)TestExtractStructuredJsonSchemaNc                    t               }d|_        d|_        t        d|      5 }t	        dd       ddd       j
                  }|d   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  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   xY w)uT   _extract_structured_json 프롬프트에 'JSON 스키마' 키워드가 포함된다.2{"type": "infographic", "title": "t", "items": []}r   r  r   r  rC  Nrz   u   JSON 스키마r  r  r  r  r  r   r  r  s
             r&   (test_prompt_contains_json_schema_keywordzHTestExtractStructuredJsonSchema.test_prompt_contains_json_schema_keywordQ  s    kQ!"#+> 	>($X}=	>&&	l1oV
-:----:---------:---:-------	> 	>r  c                    t               }d|_        d|_        t        d|      5 }t	        dd       ddd       j
                  }|d   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  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   xY w)uK   _extract_structured_json 프롬프트에 'items' 키워드가 포함된다.r.  r   r  r   r  rC  Nrz   r  r  r  r  r  r  r   r  r  s
             r&   "test_prompt_contains_items_keywordzBTestExtractStructuredJsonSchema.test_prompt_contains_items_keyword]  s    kQ!"#+> 	>($X}=	>&&	l1oV
$w*$$$$w*$$$w$$$$$$*$$$*$$$$$$$	> 	>r  c                    t               }d|_        d|_        t        d|      5 }t	        dd       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  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u9   _extract_structured_json은 haiku 모델을 사용한다.r.  r   r  r   r  rC  Nhaikur  r  r  r  r  r   r  )	r"   r  r  r  r  r  r#   r  r%   s	            r&   test_prompt_uses_haiku_modelz<TestExtractStructuredJsonSchema.test_prompt_uses_haiku_modeli  s    kQ!"#+> 	>($X}=	>&&	l1ow#~w#w##		> 	>s   C55C>rT   )rV   rW   rX   r/  r1  r4  rY   r(   r&   r,  r,  P  s    
.
%	r(   r,  c                       e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZy)TestValidateImageQualityu1   _validate_image_quality() QC 게이트 테스트.r   rU   Nc                 t   ddl m}  ||dz        \  }}d}||u }|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}}d}	|d   }
|	|
v }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)u&   존재하지 않는 파일 → 실패.r   _validate_image_qualityzno_such.pngFri   r  passedr  r  r   Nu   존재하지 않습니다r  z%(py1)s in %(py4)sr  rt   r7   )
r)  r9  r   r;   r   r   r   r   r   r    )r"   r   r9  r:  warningsr#   rJ   r  r%   r  r<   ru   rv   s                r&   test_nonexistent_file_failsz4TestValidateImageQuality.test_nonexistent_file_failsx  s    828m3KLvvvv*9hqk9*k9999*k999*999k9999999r(   c                    ddl m} |dz  }|j                  d        ||      \  }}d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }	t        t        j                  |	            dx}}d}
|d   }|
|v }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y)u5   15KB 미만 파일 → 실패 (빈 이미지 의심).r   r8  ztiny.pngsd                                                                                                       Fri   r  r:  r  r  r   Nu   최소 15KBr  r;  r  rt   r7   )r)  r9  r  r   r;   r   r   r   r   r   r    )r"   r   r9  
small_filer:  r<  r#   rJ   r  r%   r  r<   ru   rv   s                 r&   test_tiny_file_failsz-TestValidateImageQuality.test_tiny_file_fails  s    8
*
}-2:>vvvv++}++++}+++}++++++++++r(   c                 l   ddl m} 	 ddl}ddlm} j                  j                  j                  ddd|j                  	            }|d
z  }|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  }dd|iz  }t'        t        j(                  |            dx}
}	y# t
        $ r t        j                  d       Y  w xY w)u-   정상 크기+해상도 이미지 → 통과.r   r8  NImage   PIL/numpy 미설치2      i  i  r  dtypez	valid.pngTri   r  r:  r  r  r   )r)  r9  numpyPILrC  ImportErrorr   skip	fromarrayrandomrandintuint8saver   r;   r   r   r   r   r   r    )r"   r   r9  nprC  imgoutr:  r<  r#   rJ   r  r%   s                r&   test_valid_image_passesz0TestValidateImageQuality.test_valid_image_passes  s    8	/! oobii//Cbhh/WX$237v~vvv  	/KK-.	/s   
D D32D3c                    ddl m} 	 ddl}ddlm} j                  j                  j                  ddd|j                  	            }|d
z  }|j                  |        ||      \  }}d |D        }	t        |	      }
|
sddt        j                         v st!        j"                  t              rt!        j$                  t              ndt!        j$                  |	      t!        j$                  |
      dz  }t'        t!        j(                  |            dx}	}
d |D        }	t        |	      }
|
sddt        j                         v st!        j"                  t              rt!        j$                  t              ndt!        j$                  |	      t!        j$                  |
      dz  }t'        t!        j(                  |            dx}	}
y# t
        $ r t        j                  d       Y w xY w)u-   해상도가 최소 기준 미달 → 경고.r   r8  NrB  rD  rE  rF  )rF  i  r  rH  z	small.pngc              3   $   K   | ]  }d |v  
 yw)u   최소 800pxNrY   .0ws     r&   	<genexpr>zGTestValidateImageQuality.test_small_resolution_warns.<locals>.<genexpr>       91>Q&9   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyrs   c              3   $   K   | ]  }d |v  
 yw)u   최소 400pxNrY   rY  s     r&   r\  zGTestValidateImageQuality.test_small_resolution_warns.<locals>.<genexpr>  r]  r^  )r)  r9  rJ  rK  rC  rL  r   rM  rN  rO  rP  rQ  rR  r`  r   r   r   r   r   r   r    )r"   r   r9  rS  rC  rT  rU  r:  r<  rJ   r<   ru   s               r&   test_small_resolution_warnsz4TestValidateImageQuality.test_small_resolution_warns  s<   8	/! oobii//Cbhh/WX$237999s999999999s999s99999999999999999s999999999s999s99999999999999  	/KK-.	/s   
G
 
G,+G,c                    ddl m} 	 ddl}ddlm} j                  j                  ddd|j                        }j                  |      }|d	z  }|j                  |        ||      \  }}	d
 |	D        }
t        |
      }|sddt        j                         v st!        j"                  t              rt!        j$                  t              ndt!        j$                  |
      t!        j$                  |      dz  }t'        t!        j(                  |            dx}
}y# t
        $ r t        j                  d       Y ,w xY w)u&   매우 어두운 이미지 → 경고.r   r8  NrB  rD  
   rG  rH  zdark.pngc              3   $   K   | ]  }d |v  
 yw)u   너무 어둡습니다NrY   rY  s     r&   r\  zFTestValidateImageQuality.test_very_dark_image_warns.<locals>.<genexpr>  s     CQ+q0Cr^  r_  r`  rs   r)  r9  rJ  rK  rC  rL  r   rM  rO  rP  rQ  rN  rR  r`  r   r   r   r   r   r   r    r"   r   r9  rS  rC  arrrT  rU  r:  r<  rJ   r<   ru   s                r&   test_very_dark_image_warnsz3TestValidateImageQuality.test_very_dark_image_warns  s    8	/!
 ii2}BHHEooc"#237C(CCsCCCCCCCCCsCCCsCCCCCCCCCCCCCC  	/KK-.	/   
D D?>D?c                    ddl m} 	 ddl}ddlm} j                  j                  ddd|j                  	      }j                  |      }|d
z  }|j                  |        ||      \  }}	d |	D        }
t        |
      }|sddt        j                         v st!        j"                  t              rt!        j$                  t              ndt!        j$                  |
      t!        j$                  |      dz  }t'        t!        j(                  |            dx}
}y# t
        $ r t        j                  d       Y ,w xY w)u:   매우 밝은 이미지 → 경고 (빈 이미지 의심).r   r8  NrB  rD        rG  rH  z
bright.pngc              3   $   K   | ]  }d |v  
 yw)u   너무 밝습니다NrY   rY  s     r&   r\  zHTestValidateImageQuality.test_very_bright_image_warns.<locals>.<genexpr>  s     @!(A-@r^  r_  r`  rs   rf  rg  s                r&   test_very_bright_image_warnsz5TestValidateImageQuality.test_very_bright_image_warns  s    8	/!
 iiS-rxxHooc"%237@x@@s@@@@@@@@@s@@@s@@@@@@@@@@@@@@  	/KK-.	/rj  c                 <   ddl m} 	 ddl}ddlm} j                  dd|j                        }|j                  |j                  t              |j                  j                  dd	|j                        z   dd
      j                  |j                        }j!                  |      }|dz  }|j#                  |        ||      \  }}	d |	D        }
t%        |
      }|sddt'        j(                         v st+        j,                  t$              rt+        j.                  t$              ndt+        j.                  |
      t+        j.                  |      dz  }t1        t+        j2                  |            dx}
}y# t
        $ r t        j                  d       Y w xY w)u3   색상이 매우 단조로운 이미지 → 경고.r   r8  NrB  rD  rG     rH  r     zmonotone.pngc              3   $   K   | ]  }d |v  
 yw)u   단조롭습니다NrY   rY  s     r&   r\  zJTestValidateImageQuality.test_low_color_diversity_warns.<locals>.<genexpr>  s     ?'1,?r^  r_  r`  rs   )r)  r9  rJ  rK  rC  rL  r   rM  fullrQ  clipastypeintrO  rP  shaperN  rR  r`  r   r   r   r   r   r   r    rg  s                r&   test_low_color_diversity_warnsz7TestValidateImageQuality.test_low_color_diversity_warns  s!   8	/!
 ggmSg9ggcjjo		(9(9!Q		(JJAsSZZ[][c[cdooc"'237?h??s?????????s???s??????????????  	/KK-.	/s   
E9 9FF)rV   rW   rX   r  r   r=  r@  rV  rb  ri  ro  ry  rY   r(   r&   r6  r6  u  s    ;:D :T :,T ,d ,   :D :T :"D4 DD D$AT Ad A$@t @ @r(   r6  )5r  builtinsr   _pytest.assertion.rewrite	assertionrewriter   r2  syspathlibr   unittest.mockr   r   r   r  insertr  __file__parentr)  r   r   r   r	   r
   r   r   r   r   r[   r   r   r   r   r   r  r  r  r  r:  rF  rU  r]  rd  rv  r}  r  r  r  r  r,  r6  rY   r(   r&   <module>r     s`     
  *  3tH~,,- .	 	 	 2 28T7 T7x> >2M M8! !E- E-Z& &6& &6)$ )$b- -85' 5'z)$ )$b/0 /0n' 'L< <B B6& &:1 1rA) A)RV( V(|Y, Y,B@ @P" "Jk@ k@r(   