
    ci                     :   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 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  G d d      Z G d	 d
      Z G d d      Z  G d d      Z!y)uN   gemini_nb2_generate.py의 순수 함수 및 상수에 대한 단위 테스트.    N)Path)Any)	MagicMockpatch)GEMINI_API_BASEGEMINI_SCOPEMODEL_ID
OUTPUT_DIRRESULTS_JSON	SCENARIOS	log_errorc                   4    e Zd ZddZddZddZddZddZy)TestConstantsNc                 p   d}t         |k(  }|st        j                  d|fdt         |f      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}}y)	uB   모델 ID가 Nano Banana 2 (gemini-3.1-flash-image-preview)이다.zgemini-3.1-flash-image-preview==z%(py0)s == %(py3)sr	   py0py3assert %(py5)spy5N)	r	   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)self@py_assert2@py_assert1@py_format4@py_format6s        B/home/jay/workspace/tools/ai-image-gen/test_gemini_nb2_generate.pytest_model_id_is_nano_banana2z+TestConstants.test_model_id_is_nano_banana2   s^    ;;x;;;;;x;;;;;;;x;;;x;;;;;;;;;;;    c                    t         j                  }d} ||      }|sddt        j                         v st	        j
                  t               rt	        j                  t               ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }t        t	        j                  |            dx}x}}d}|t         v }|st	        j                  d|fd|t         f      t	        j                  |      dt        j                         v st	        j
                  t               rt	        j                  t               ndd	z  }d
d|iz  }t        t	        j                  |            dx}}y)u0   Gemini API 기본 URL이 올바른 형식이다.zhttps://zLassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}r   )r   py2py4py6Nz!generativelanguage.googleapis.cominz%(py1)s in %(py3)spy1r   r   r   )
r   
startswithr   r   r   r   r   r   r    r   )	r!   r#   @py_assert3@py_assert5@py_format7@py_assert0r"   r$   r%   s	            r&   !test_gemini_api_base_is_valid_urlz/TestConstants.test_gemini_api_base_is_valid_url!   s    ))5*5)*55555555555555)555*55555555552E2oEEEE2oEEE2EEEEEEoEEEoEEEEEEEr(   c                 n   d}|t         v }|st        j                  d|fd|t         f      t        j                  |      dt	        j
                         v st        j                  t               rt        j                  t               nddz  }dd|iz  }t        t        j                  |            dx}}y)	u2   Gemini API 스코프가 generative-language이다.zgenerative-languager-   r/   r   r0   r   r   N)	r   r   r   r   r   r   r   r   r    )r!   r6   r"   r$   r%   s        r&   (test_gemini_scope_is_generative_languagez6TestConstants.test_gemini_scope_is_generative_language&   s^    $4$4444$444$4444444444444444r(   c                 :   d}t        t              }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	)
u2   출력 디렉토리가 v2-gemini-nb2 폴더이다.zv2-gemini-nb2r-   )z0%(py1)s in %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}strr
   )r1   r   r+   r,   assert %(py8)spy8N)
r;   r
   r   r   r   r   r   r   r   r    )r!   r6   r4   r"   r5   @py_format9s         r&    test_output_dir_is_v2_gemini_nb2z.TestConstants.test_output_dir_is_v2_gemini_nb2*   s    1#j/1/1111/111111111#111#111111j111j111/1111111r(   c                    t         j                  }|t        k(  }|st        j                  d|fd|t        f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}y)	u,   results.json이 OUTPUT_DIR 하위에 있다.r   )z.%(py2)s
{%(py2)s = %(py0)s.parent
} == %(py4)sr   r
   r   r*   r+   zassert %(py6)sr,   N)r   parentr
   r   r   r   r   r   r   r   r    )r!   r#   r3   @py_format5r5   s        r&   "test_results_json_is_in_output_dirz0TestConstants.test_results_json_is_in_output_dir.   s    ""0"j0000"j000000|000|000"000000j000j0000000r(   returnN)__name__
__module____qualname__r'   r7   r9   r?   rD    r(   r&   r   r      s    <F
521r(   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)	TestScenariosNc                 <   t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	)
u7   SCENARIOS dict에 3개의 시나리오가 존재한다.   r   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   r   r1   r   r,   r<   r=   N)
rO   r   r   r   r   r   r   r   r   r    )r!   r"   r4   @py_assert4r5   r>   s         r&   test_has_three_scenariosz&TestScenarios.test_has_three_scenarios9   s    9~""~""""~""""""s"""s""""""9"""9"""~""""""""""r(   c           	         t         j                  } |       }t        |      }h 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t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}x}x}}y	)
u#   SCENARIOS의 키는 A, B, C 이다.>   ABCr   )zb%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.keys
}()
})
} == %(py10)ssetr   )r   r1   r   r   py7py10zassert %(py12)spy12N)r   keysrW   r   r   r   r   r   r   r   r    )r!   r"   rQ   @py_assert6@py_assert9@py_assert8@py_format11@py_format13s           r&   test_scenario_keys_are_a_b_cz*TestScenarios.test_scenario_keys_are_a_b_c=   s    >>7>#7s#$77$7777$777777s777s77777797779777>777#777$77777777777r(   c                 
   t        j                         D ]k  \  }}t        |t              }|s%t	        j
                  d| d      dz   dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }t        t	        j                  |            d}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                  |      dz  }t	        j
                  d| d      dz   d|iz  }	t        t	        j                  |	            dx}x}}n y)u@   모든 시나리오 값은 비어 있지 않은 문자열이다.u   시나리오 u   의 값이 str이 아닙니다.z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancevaluer;   )r   r1   r*   r+   Nr   )>)z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)srO   rP   u   의 값이 비어 있습니다.z
>assert %(py8)sr=   )r   itemsrc   r;   r   _format_assertmsgr   r   r   r   r   r    rO   r   )
r!   keyrd   r3   rC   r"   r4   rQ   r5   r>   s
             r&   %test_all_values_are_non_empty_stringsz3TestScenarios.test_all_values_are_non_empty_stringsA   s[   #//+ 	XJCeS)_)__]3%?^+_______:___:______e___e______S___S___)______u:WW:>WWW:WWWWWW3WWW3WWWWWWuWWWuWWW:WWWWWW]3%7V#WWWWWWWW	Xr(   c                    g }d}t         d   }|j                  } |       }||v }|}|sd}t         d   }	||	v }
|
}|s&t        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }|j                  |       |s_t        j                  d
fd		f      t        j                  |      t        j                  |	      d
z  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}x}x}
}	y)u@   시나리오 A에 리크루팅 관련 키워드가 포함된다.
recruitingrT   u	   커리어r-   zF%(py3)s in %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s.lower
}()
}r   r,   r=   rY   %(py12)srZ   z%(py15)s in %(py18)spy15py18%(py20)spy20   assert %(py23)spy23N	r   lowerr   r   r   append_format_boolopr   r    r!   r#   r"   r4   @py_assert7r]   rQ   r6   @py_assert14@py_assert17@py_assert16r_   r`   @py_format19@py_format21@py_format22@py_format24s                    r&   ,test_scenario_a_contains_recruiting_keywordsz:TestScenarios.test_scenario_a_contains_recruiting_keywordsG   s    V|Vy~V~33V35V|55VV	RUV9VVVVV|5VVV|VVV~VVV3VVV5VVVVVVVVVVVVVVVVVVVVVVVVVVVVr(   c                    g }d}t         d   }|j                  } |       }||v }|}|sd}t         d   }	||	v }
|
}|s&t        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }|j                  |       |s_t        j                  d
fd		f      t        j                  |      t        j                  |	      d
z  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}x}x}
}	y)u=   시나리오 B에 브랜딩 관련 키워드가 포함된다.brandingrU   u   보험r-   rl   rm   rn   rZ   ro   rp   rs   rt   ru   rv   rw   Nrx   r|   s                    r&   *test_scenario_b_contains_branding_keywordsz8TestScenarios.test_scenario_b_contains_branding_keywordsK   s    QzQYs^Q^11Q13Qz33QxQ9S>Qx>7QQQQQz3QQQzQQQ^QQQ1QQQ3QQQQQQQx>QQQxQQQ>QQQQQQQQQQQQQQQr(   c                    g }d}t         d   }|j                  } |       }||v }|}|sd}t         d   }	||	v }
|
}|s&t        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }|j                  |       |s_t        j                  d
fd		f      t        j                  |      t        j                  |	      d
z  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}x}x}
}	y)u@   시나리오 C에 동기부여 관련 키워드가 포함된다.motivationalrV   u   기회r-   rl   rm   rn   rZ   ro   rp   rs   rt   ru   rv   rw   Nrx   r|   s                    r&   .test_scenario_c_contains_motivational_keywordsz<TestScenarios.test_scenario_c_contains_motivational_keywordsO   s    U~U3U!5!5U!5!7U~!77U8UyQT~U8~;UUUUU~!7UUU~UUUUUU!5UUU!7UUUUUUU8~UUU8UUU~UUUUUUUUUUUUUUUr(   rE   )	rG   rH   rI   rR   ra   ri   r   r   r   rJ   r(   r&   rL   rL   8   s$    #8XWRVr(   rL   c                   \    e Zd ZdededdfdZdededdfdZdededdfdZdededdfdZy)	TestLogErrortmp_pathmonkeypatchrF   Nc                    ddl }|dz  }|j                  |d|       |j                  d       |j                  } |       }|sddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)	u:   log_error 호출 시 에러 로그 파일이 생성된다.r   N
errors.log
ERRORS_LOGu   테스트 에러 메시지zAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
errors_logrA   )gemini_nb2_generatesetattrr   existsr   r   r   r   r   r   r    )r!   r   r   modr   r#   r3   rC   s           r&   test_log_error_creates_filez(TestLogError.test_log_error_creates_fileZ   s    ),
Cz:23  " """"""""z"""z""" """"""""""r(   c                    ddl }|dz  }|j                  |d|       |j                  d       |j                  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)u@   log_error 호출 시 메시지가 로그 파일에 기록된다.r   Nr   r   u   에러 내용: 테스트utf-8encodingr-   r/   contentr0   r   r   r   r   r   	read_textr   r   r   r   r   r   r   r    
r!   r   r   r   r   r   r6   r"   r$   r%   s
             r&   test_log_error_writes_messagez*TestLogError.test_log_error_writes_messagee   s    ),
Cz:01&&&8)4)W4444)W444)444444W444W4444444r(   c                 8   ddl }|dz  }|j                  |d|       |j                  d       |j                  d       |j                  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}}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)u:   log_error를 두 번 호출하면 두 줄이 기록된다.r   Nr   r   u   첫 번째 에러u   두 번째 에러r   r   r-   r/   r   r0   r   r   r   r   s
             r&   test_log_error_appendsz#TestLogError.test_log_error_appendsq   s   ),
Cz:)*)*&&&8"-"g----"g---"------g---g-------"-"g----"g---"------g---g-------r(   c                 x   ddl }|dz  }|j                  |d|       |j                  d       |j                  d      }g }d}||v }|}	|sd	}
|
|v }|}	|	sXt	        j
                  d
|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }|j                  |       |st	        j
                  d
fd
|f      t	        j                  |
      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }|j                  |       t	        j                  |d      i z  }dd|iz  }t        t	        j                  |            dx}	x}x}x}x}
}y)u4   log_error 로그에 타임스탬프가 포함된다.r   Nr   r   u   타임스탬프 확인r   r   z[2026-z[202r-   )z%(py3)s in %(py5)sr   )r   r   z%(py7)srX   )z%(py10)s in %(py12)s)rY   rZ   z%(py14)spy14ru   zassert %(py17)spy17)r   r   r   r   r   r   r   r   r   r   rz   r{   r   r    )r!   r   r   r   r   r   r#   r"   rQ   r6   r]   @py_assert11r%   @py_format8r`   @py_format15@py_format16@py_format18s                     r&   !test_log_error_includes_timestampz.TestLogError.test_log_error_includes_timestamp   s   ),
Cz:./&&&87x7x7"7f7f&77777x7777x777777777777777777f777f77777777777777777777777r(   )	rG   rH   rI   r   r   r   r   r   r   rJ   r(   r&   r   r   Y   sl    	#D 	#s 	#t 	#
5d 
5 
5QU 
5.t .# .$ .8$ 8S 8UY 8r(   r   c                       e Zd ZddZddZy)TestGetGcloudAccessTokenNc                    ddl }t               }d|_        t        j                  |dddd      5  t        dt        d            5  t        d	|
      5 }|j                         }ddd       ddd       ddd       d}|k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}j                          y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)uS   gcloud_auth.get_access_token()이 gcloud CLI fallback으로 토큰을 반환한다.r   Nzfake-token-12345
_token_cachetokenexpirygoogle.auth.default
   ADC 없음side_effectsubprocess.runreturn_valuezfake-token-12345r   r   r   r   r   r   )gcloud_authr   stdoutr   object	Exceptionget_access_tokenr   r   r   r   r   r   r   r    assert_called_once)	r!   r   mock_resultmock_runr   r"   r#   r$   r%   s	            r&   test_returns_token_on_successz6TestGetGcloudAccessToken.test_returns_token_on_success   s    k1\\+~QU7VW 	;,)L:QR ;++F ;('88:E;;	;
 +*u*****u*******u***u***********##%	; ;; ;	; 	;s;   EED5&E.E5D>:EE
	EEc           	         ddl }t               }d|_        t        j                  |dddd      5  t        dt        d            5  t        d	|
      5  t        j                  t        d      5  |j                          ddd       ddd       ddd       ddd       y# 1 sw Y   "xY w# 1 sw Y   &xY w# 1 sw Y   *xY w# 1 sw Y   yxY w)uP   gcloud CLI fallback에서 빈 토큰 반환 시 RuntimeError를 발생시킨다.r   Nz   
r   r   r   r   r   r   r   u
   빈 토큰)match)
r   r   r   r   r   r   pytestraisesRuntimeErrorr   )r!   r   r   s      r&   test_raises_on_empty_tokenz3TestGetGcloudAccessToken.test_raises_on_empty_token   s    k$\\+~QU7VW 	7,)L:QR 7++F 7|<H 7#446777	7 	77 77 77 7	7 	7sS   CB;B/1B#	B/
B;C#B,(B//B84B;;C	 CCrE   )rG   rH   rI   r   r   rJ   r(   r&   r   r      s    &7r(   r   )"__doc__builtinsr   _pytest.assertion.rewrite	assertionrewriter   jsonsyspathlibr   typingr   unittest.mockr   r   r   pathinsertr;   __file__rB   r   r   r   r	   r
   r   r   r   r   rL   r   r   rJ   r(   r&   <module>r      sz    T    
   *  3tH~,,- .  1 18V VB18 18r7 7r(   