
    xji%                     &   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mZ ddlZej                   j#                  d e e
e      j(                               ddlZddlmZmZ  G d d      Z G d d	      Z G d
 d      Z G d d      Zy)u  통합 테스트 — image_router.py + iptc_tagger.py 연동.

테스트 범위:
- _generate_gemini() 실제 API 호출 구조 (gcloud_auth + REST API mock)
- _generate_satori() subprocess 호출 구조 (node subprocess mock)
- _generate_hybrid() generate_hybrid 모듈 호출 구조 (playwright mock)
- _generate_gpt() OpenAI API 호출 구조 (openai mock)
- generate_image() + iptc_tagger 연동 (IPTC 태깅이 성공 시 호출되는지)
    N)Path)	MagicMockpatchcall)GenerationResultgenerate_imagec                   ,    e Zd ZdeddfdZdeddfdZy)TestGenerateGeminiIntegrationtmp_pathreturnNc                    t               }d|j                  _        t               }d }||j                  _        t        j                  d||d      5  dt        j                  v rt        j                  d= ddl	}ddl
}|j                  d|d	z        }ddd       |j                  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# 1 sw Y   xY w)uE   _generate_gemini() 호출 시 gcloud_auth.get_access_token 호출됨.
fake_tokenc                 &    |j                  d       y )Ns   fake_image_data)write_bytes)tokenpromptoutput_pathscenarios       G/home/jay/workspace/tools/ai-image-gen/test_image_router_integration.pyside_effect_create_filezmTestGenerateGeminiIntegration.test_gemini_calls_gcloud_auth_get_access_token.<locals>.side_effect_create_file$   s    ##$67    sys.modulesgcloud_authgemini_pro_generateimage_routerr   N   테스트 프롬프트test.pngTisz%(py0)s is %(py3)sresultpy0py3assert %(py5)spy5)r   get_access_tokenreturn_valuegenerate_image_via_gemini_apiside_effectr   dictsysmodules	importlibr   _generate_geminiassert_called_once
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)selfr   mock_gcloudmock_gemini_genr   r/   _irr"   @py_assert2@py_assert1@py_format4@py_format6s               r   .test_gemini_calls_gcloud_auth_get_access_tokenzLTestGenerateGeminiIntegration.test_gemini_calls_gcloud_auth_get_access_token   s   k4@$$1#+	8 E\55AZZ&#2(
  		[
 ,KK/&))*BHzDYZF		[ 	$$779v~vvv		[ 		[s   AEE%c                    t               }t        d      |j                  _        t               }t	        j
                  d||d      5  dt        j                  v rt        j                  d= ddl}ddl	}|j                  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)u$   gcloud_auth 예외 시 False 반환.u   인증 실패r   r   r   r   Nr   r   Fr   r!   r"   r#   r&   r'   )r   RuntimeErrorr(   r+   r   r,   r-   r.   r/   r   r0   r2   r3   r4   r5   r6   r7   r8   r9   )r:   r   r;   r<   r/   r=   r"   r>   r?   r@   rA   s              r   )test_gemini_returns_false_on_auth_failurezGTestGenerateGeminiIntegration.test_gemini_returns_false_on_auth_failure7   s    k3?3P$$0#+ZZ&#2(
  	[ ,KK/&))*BHzDYZF	[ vvvv	[ 	[s   AD77E )__name__
__module____qualname__r   rB   rE    r   r   r
   r
      s)    t PT 4$ 4 r   r
   c                   ,    e Zd ZdeddfdZdeddfdZy)TestGenerateSatoriIntegrationr   r   Nc                    |dz  fd}t        d      5 }||j                  _        t        d      j                  |_        t        j                  d      }ddd       j                  }|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}}|j                  j                  }|d
   r|d
   d
   n|d   j!                  dg       }	|	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}|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)uO   _generate_satori() 호출 시 subprocess.run이 'node'를 첫 인자로 호출.
output.pngc                 `    | r| d   dk(  rj                  d       t               }d|_        |S )Nr   nodes   fake_png_data)r   r   
returncode)cmdargskwargsmock_resultr   s       r   fake_subprocess_runz\TestGenerateSatoriIntegration.test_satori_calls_node_subprocess.<locals>.fake_subprocess_runU   s5    s1v'''(89#+K%&K"r   image_router.subprocess
subprocessu   카드뉴스 프롬프트NzCassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.run
}.called
}mock_subprocess)r$   py2py4r      rR   rO   )==)z%(py1)s == %(py4)s)py1rZ   zassert %(py6)spy6Tr   r!   r"   r#   r&   r'   )r   runr+   
__import__CalledProcessErrorr   _generate_satoricalledr4   r5   r2   r6   r7   r8   r9   	call_argsgetr3   )r:   r   rU   rX   r"   r?   @py_assert3@py_format5rd   rQ   @py_assert0r>   @py_format7r@   rA   r   s                  @r   !test_satori_calls_node_subprocessz?TestGenerateSatoriIntegration.test_satori_calls_node_subprocessQ   s   -	 ,- 	].AO+1;L1I1\1\O.!223NP[\F	] "")")))))))))))))))"))))))))))#''11	!*1il1o9Q<3C3CFB3O1vvvvv~vvv	] 	]s   AI**I4c                     |dz  }d }t        d      5 }||j                  _        t        d      j                  |_        t        j                  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)u0   subprocess.run returncode != 0 시 False 반환.rM   c                 6    t               }d|_        d|_        |S )Nr[   u   node 실행 오류)r   rP   stderr)rQ   rR   rS   rT   s       r   fake_subprocess_run_failzmTestGenerateSatoriIntegration.test_satori_returns_false_on_subprocess_error.<locals>.fake_subprocess_run_failm   s    #+K%&K"!5Kr   rV   rW   u   실패 프롬프트NFr   r!   r"   r#   r&   r'   )r   r_   r+   r`   ra   r   rb   r2   r3   r4   r5   r6   r7   r8   r9   )
r:   r   r   rn   rX   r"   r>   r?   r@   rA   s
             r   -test_satori_returns_false_on_subprocess_errorzKTestGenerateSatoriIntegration.test_satori_returns_false_on_subprocess_errori   s    -	 ,- 	W.FO+1;L1I1\1\O.!223H+VF	W
 vvvv	W 	Ws   ADD)rF   rG   rH   r   rj   ro   rI   r   r   rK   rK   P   s(    $ 4 0d t r   rK   c                       e Zd ZdeddfdZy)TestGenerateHybridIntegrationr   r   Nc                 h   t               }t        d      |j                  _        t	        j
                  dd|i      5  dt        j                  v rt        j                  d= ddl}|j                  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)uA   bg_A.jpg가 없으면 False 반환 (generate_hybrid 모듈 mock).u5   배경 이미지를 찾을 수 없습니다: bg_A.jpgr   generate_hybridr   r   Nu   하이브리드 프롬프트rM   Fr   r!   r"   r#   r&   r'   )r   FileNotFoundErrorrs   r+   r   r,   r-   r.   r   _generate_hybridr2   r3   r4   r5   r6   r7   r8   r9   )	r:   r   mock_gen_hybridr=   r"   r>   r?   r@   rA   s	            r   )test_hybrid_returns_false_when_bg_missingzGTestGenerateHybridIntegration.test_hybrid_returns_false_when_bg_missing   s    #+6GC7
''3 ZZ(9?'KL 	c,KK/&))*H(UaJabF		c vvvv	c 	cs   =D((D1)rF   rG   rH   r   rw   rI   r   r   rq   rq      s    $ 4 r   rq   c                   ,    e Zd ZdeddfdZdeddfdZy)TestIptcTaggingIntegrationr   r   Nc                    t               }|dz  }||j                  _        d }t        d|      5  t        j                  dd|i      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                  j                          y# 1 sw Y   xY w# 1 sw Y   xY w)u   generate_image()에서 성공 후 iptc_tagger.tag_image가 호출되는지 확인.

        image_router.py 내부에서 'import iptc_tagger as _tagger' (lazy import) 방식 사용.
        sys.modules 패치로 해당 import를 가로챈다.
        zbrand_gemini.pngc                 p    ddl m} |j                  ddd      }|j                  t	        |      d       y	
Nr   )ImageRGB)
   r   )r   r      )colorPNG)formatTPILr}   newsavestrr   r   r}   imgs       r   fake_generate_geminizlTestIptcTaggingIntegration.test_iptc_tagger_called_after_successful_generation.<locals>.fake_generate_gemini   s1    !))E8;)?CHHS%eH4r   image_router._generate_geminir+   r   iptc_taggerphotorealisticr   brandpurposer   r   
output_dirNTr   z/%(py2)s
{%(py2)s = %(py0)s.success
} is %(py5)sr"   r$   rY   r'   assert %(py7)spy7)r   	tag_imager)   r   r,   r   successr2   r3   r4   r5   r6   r7   r8   r9   r1   )r:   r   	mock_iptc
output_pngr   r"   r?   @py_assert4rf   rA   @py_format8s              r   3test_iptc_tagger_called_after_successful_generationzNTestIptcTaggingIntegration.test_iptc_tagger_called_after_successful_generation   s    K	 22
+5	(	 1?ST		JJ}}i&@A		 $(/#	F			 		 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%..0		 		 		 		s#   E
EEE	EE!c                    t               }t        d      |j                  _        d }t	        d|      5  t	        j
                  dd|i      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   IPTC 태깅 실패해도 GenerationResult.success = True.

        image_router.py가 iptc_tagger 예외를 try/except로 무시하고 success를 반환해야 한다.
        u   IPTC 태깅 실패c                 p    ddl m} |j                  ddd      }|j                  t	        |      d       y	r|   r   r   s       r   r   ziTestIptcTaggingIntegration.test_iptc_tagging_failure_does_not_affect_result.<locals>.fake_generate_gemini   s1    !))E8;)?CHHS%eH4r   r   r   r   r   r   u   태깅 실패 테스트r   r   NTr   r   r"   r   r   r   )r   rD   r   r+   r   r,   r   r   r2   r3   r4   r5   r6   r7   r8   r9   )
r:   r   r   r   r"   r?   r   rf   rA   r   s
             r   0test_iptc_tagging_failure_does_not_affect_resultzKTestIptcTaggingIntegration.test_iptc_tagging_failure_does_not_affect_result   s     K	*67K*L	'	 1?ST		JJ}}i&@A		 $(0#	F			 		 ~~%%~%%%%~%%%%%%v%%%v%%%~%%%%%%%%%%		 		 		 		s#   ED6E6D?	;EE)rF   rG   rH   r   r   r   rI   r   r   ry   ry      s-    11	1B&&	&r   ry   )__doc__builtinsr4   _pytest.assertion.rewrite	assertionrewriter2   r-   jsonpathlibr   unittest.mockr   r   r   pytestpathinsertr   __file__parentr   r   r   r
   rK   rq   ry   rI   r   r   <module>r      ss     
   0 0  3tH~,,- .  9, ,h( (` .=& =&r   