
    Si/                     ,   d Z ddlZddlZddlmZ ddlZej                  j                  d e ee	      j                  j                  j                               ddlmZmZ  G d d      Z G d d      Z G d	 d
      Z G d d      Z G d d      Zy)uV   utils/aux_llm_router.py 테스트 스위트

TDD: 테스트 먼저 작성 후 구현.
    N)Path)	AuxRouterProviderConfigc                   0    e Zd ZdZddZddZddZddZy)TestProviderConfigu"   ProviderConfig dataclass 테스트Nc                     t        ddd      }|j                  dk(  sJ |j                  dk(  sJ |j                  dk(  sJ y)u   필수 필드 생성 확인	anthropiczclaude-haiku-4-5ANTHROPIC_API_KEYnamemodelapi_key_envN)r   r   r   r   selfcfgs     P/home/jay/workspace/.worktrees/task-2117-dev1/utils/tests/test_aux_llm_router.pytest_required_fieldsz'TestProviderConfig.test_required_fields   sO    $+

 xx;&&&yy...."5555    c                 ^    t        ddd      }|j                  J |j                  dk(  sJ y)u#   base_url, priority 기본값 확인openaizgpt-4o-miniOPENAI_API_KEYr   Nr   r   base_urlpriorityr   s     r   test_optional_defaultsz)TestProviderConfig.test_optional_defaults%   s9    (

 ||###||q   r   c                 h    t        ddddd      }|j                  dk(  sJ |j                  dk(  sJ y)u#   base_url, priority 커스텀 설정deepseekzdeepseek-chatDEEPSEEK_API_KEYzhttps://api.deepseek.com/v1   )r   r   r   r   r   Nr   r   s     r   !test_custom_base_url_and_priorityz4TestProviderConfig.test_custom_base_url_and_priority/   sB    !*2
 ||<<<<||q   r   c                 B    t        ddd      }|j                  dk(  sJ y)uL   dataclass 기본 동작 확인 (frozen 여부와 무관하게 생성 가능)googlezgemini-2.5-flashGOOGLE_API_KEYr   N)r   r   r   s     r   2test_dataclass_is_mutable_or_frozen_does_not_crashzETestProviderConfig.test_dataclass_is_mutable_or_frozen_does_not_crash;   s)    $(

 xx8###r   returnN)__name__
__module____qualname____doc__r   r   r    r$    r   r   r   r      s    ,	6!
!$r   r   c                   (    e Zd ZdZddZddZddZy)TestAuxRouterInitu   AuxRouter 초기화 테스트Nc                     t               }|j                  D cg c]  }|j                   }}d|v sJ d|v sJ d|v sJ d|v sJ yc c}w )uD   기본 프로바이더 4개(anthropic/openai/google/deepseek) 등록r	   r   r"   r   N)r   	providersr   )r   routerpnamess       r   test_default_providers_loadedz/TestAuxRouterInit.test_default_providers_loadedM   sc    !'!1!12A22e###5   5   U"""	 3s   Ac                     t        ddd      g}t        |      }t        |j                        dk(  sJ |j                  d   j                  dk(  sJ y)	u'   커스텀 프로바이더 목록 주입zmy-providerzmy-modelMY_KEYr   r/      r   N)r   r   lenr/   r   )r   customr0   s      r   test_custom_providersz'TestAuxRouterInit.test_custom_providersV   s[     ZXV
 V,6##$)))"''=888r   c                 P    t        d      }t        |j                        dk\  sJ y)u2   providers=None이면 기본 프로바이더 사용Nr6      )r   r8   r/   )r   r0   s     r   !test_providers_none_uses_defaultsz3TestAuxRouterInit.test_providers_none_uses_defaults_   s%    T*6##$)))r   r%   )r'   r(   r)   r*   r3   r:   r=   r+   r   r   r-   r-   J   s    '#9*r   r-   c                   0    e Zd ZdZddZddZddZddZy)TestGetAvailableProvidersu#   get_available_providers() 테스트Nc                    |j                  dd       |j                  dd       t        ddd      t        d	d
d      g}t        |      }|j	                         }t        |      dk(  sJ |d   j                  dk(  sJ y)u;   API 키가 환경변수에 있는 프로바이더만 반환MY_KEY_Az	sk-test-aMY_KEY_BFraisingzhas-keym1r   zno-keym2r6   r7   r   N)setenvdelenvr   r   get_available_providersr8   r   r   monkeypatchr/   r0   	availables        r   (test_returns_only_providers_with_api_keyzBTestGetAvailableProviders.test_returns_only_providers_with_api_keym   s    :{3:u5 	:N*M
	 Y/224	9~"""|  I---r   c                     |j                  dd       |j                  dd       t        ddd      t        dd	d      g}t        |
      }|j                         g k(  sJ y)u&   모든 키 미설정 시 빈 리스트MY_KEY_XFrC   MY_KEY_Yp1rE   r   p2rF   r6   N)rH   r   r   rI   )r   rK   r/   r0   s       r   test_empty_when_no_keys_setz5TestGetAvailableProviders.test_empty_when_no_keys_set|   sl    :u5:u5 DjIDjI
	 Y/--/2555r   c                     |j                  dd       t        ddd      g}t        |      }|j                         }t	        |t
              sJ y)u   반환형이 list인지 확인MY_KEY_Cvalr1   mr   r6   N)rG   r   r   rI   
isinstancelist)r   rK   r/   r0   results        r   test_returns_list_typez0TestGetAvailableProviders.test_returns_list_type   sO    :u-3JG
	 Y///1&$'''r   c                     |j                  dd       t        ddd      g}t        |      }|j                         }t	        |      dk(  sJ y)	u/   빈 문자열 API 키는 미설정으로 간주MY_KEY_EMPTY z	empty-keyrW   r   r6   r   N)rG   r   r   rI   r8   rJ   s        r   .test_empty_string_key_not_considered_availablezHTestGetAvailableProviders.test_empty_string_key_not_considered_available   sQ    >2.3NS
	 Y/224	9~"""r   r%   )r'   r(   r)   r*   rM   rS   r[   r_   r+   r   r   r?   r?   j   s    -.
6(#r   r?   c                   p    e Zd ZdZdefdZddZddZddZddZ	dd	Z
dd
Z	 	 ddZddZddZddZy)	TestRouteu"   route() task_type 매핑 테스트r&   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               S )	u?   모든 기본 프로바이더 키를 설정한 AuxRouter 반환r
   zsk-ant-testr   sk-openai-testr#   google-testr   zdeepseek-test)rG   r   )r   rK   s     r   _router_with_all_keyszTestRoute._router_with_all_keys   sO    .>+-=>+];-?{r   Nc                 r    | j                  |      }|j                  d      }|J |j                  dk(  sJ y)u%   summarize → Haiku(anthropic) 우선	summarizeNr	   re   router   r   rK   r0   rZ   s       r   test_summarize_prefers_haikuz&TestRoute.test_summarize_prefers_haiku   s>    ++K8k*!!!{{k)))r   c                 r    | j                  |      }|j                  d      }|J |j                  dk(  sJ y)u$   analyze → Sonnet(anthropic) 우선analyzeNr	   rh   rj   s       r   test_analyze_prefers_sonnetz%TestRoute.test_analyze_prefers_sonnet   s>    ++K8i(!!!{{k)))r   c                 r    | j                  |      }|j                  d      }|J |j                  dk(  sJ y)u$   classify → Haiku(anthropic) 우선classifyNr	   rh   rj   s       r   test_classify_prefers_haikuz%TestRoute.test_classify_prefers_haiku   s>    ++K8j)!!!{{k)))r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J |j                  d
k(  sJ y	)u7   default → 첫 번째 사용 가능한 프로바이더r
   FrC   r   rc   r#   r   defaultNr   rH   rG   r   ri   r   rj   s       r   $test_default_returns_first_availablez.TestRoute.test_default_returns_first_available       .>+-=>+U;-u=i(!!!{{h&&&r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J y	)
u+   알 수 없는 task_type → default 동작r   sk-testr
   FrC   r#   r   completely_unknown_taskN)rG   rH   r   ri   rj   s       r   ,test_unknown_task_type_falls_back_to_defaultz6TestRoute.test_unknown_task_type_falls_back_to_default   sq    +Y7.>+U;-u=78!!!r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J y)	u6   사용 가능한 프로바이더 없으면 None 반환r
   FrC   r   r#   r   rg   N)rH   r   ri   rj   s       r   3test_route_returns_none_when_no_providers_availablez=TestRoute.test_route_returns_none_when_no_providers_available   sq    .>+U;+U;-u=k*~~r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J |j                  d
k(  sJ y	)u;   summarize: anthropic 없으면 openai(gpt-4o-mini) fallbackr
   FrC   r   rc   r#   r   rg   Nr   rt   rj   s       r   8test_summarize_fallback_to_openai_when_anthropic_missingzBTestRoute.test_summarize_fallback_to_openai_when_anthropic_missing   s     	.>+-=>+U;-u=k*!!!{{h&&&r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J |j                  d
k(  sJ y	)u,   analyze: anthropic 없으면 openai fallbackr
   FrC   r   rc   r#   r   rm   Nr   rt   rj   s       r   ,test_analyze_fallback_when_anthropic_missingz6TestRoute.test_analyze_fallback_when_anthropic_missing   rv   r   c                     |j                  dd       t               }|j                  d      }|J t        |t              sJ y)u*   route() 반환값이 ProviderConfig 타입r
   rx   rg   N)rG   r   ri   rX   r   rj   s       r   'test_route_returns_provider_config_typez1TestRoute.test_route_returns_provider_config_type   sD    .	:k*!!!&.111r   c                     |j                  dd       |j                  dd       |j                  dd       |j                  dd       t               }|j                  d      }|J |j                  d
k(  sJ y	)u:   classify: anthropic 없으면 openai, 없으면 google 순r
   FrC   r   r#   rd   r   rp   Nr"   rt   rj   s       r   test_classify_fallback_chainz&TestRoute.test_classify_fallback_chain  s    .>+U;+];-u=j)!!!{{h&&&r   r%   )r'   r(   r)   r*   r   re   rk   rn   rq   ru   rz   r|   r~   r   r   r   r+   r   r   ra   ra      sK    ,I ***	'"'	'	'2	'r   ra   c                        e Zd ZdZddZddZy)TestCustomProviderRoutingu5   커스텀 프로바이더와 route() 통합 테스트Nc                     |j                  dd       t        ddd      g}t        |      }dD ](  }|j                  |      }|J |j                  dk(  r(J  y)	uW   커스텀 단일 프로바이더 - 모든 task_type에서 해당 프로바이더 반환
CUSTOM_KEYz	sk-customr9   zcustom-modelr   r6   )rg   rm   rp   rs   N)rG   r   r   ri   r   )r   rK   r9   r0   taskrZ   s         r   %test_custom_provider_single_availablez?TestCustomProviderRouting.test_custom_provider_single_available  sq    <5$(
 V,C 	+D\\$'F%%%;;(***	+r   c                 4   |j                  dd       |j                  dd       t        dddd      t        d	ddd
      g}t        |      }|j                         }t	        |      dk(  sJ |D cg c]  }|j
                   }}d|v sJ d	|v sJ yc c}w )uK   priority 높은 프로바이더가 get_available_providers 결과에 포함KEY_LOWval1KEY_HIGHval2lowrW   r7   )r   r   r   r   high
   r6      N)rG   r   r   rI   r8   r   )r   rK   r/   r0   rL   r1   r2   s          r   'test_priority_ordering_in_get_availablezATestCustomProviderRouting.test_priority_ordering_in_get_available%  s    9f-:v.SiRSTczTVW
	 Y/224	9~"""!*+A++~~ ,s   3Br%   )r'   r(   r)   r*   r   r   r+   r   r   r   r     s    ?+ r   r   )r*   ossyspathlibr   pytestpathinsertstr__file__parentutils.aux_llm_routerr   r   r   r-   r?   ra   r   r+   r   r   <module>r      s   
 
 
   3tH~,,33::; < :+$ +$f* *@0# 0#ph' h'`   r   