
    (<i-                     8   d Z ddlZddlZddlmZmZmZ ddlZddlm	Z
 ej                  j                  d        Zej                  j                  d        Zej                  j                  d        Zej                  j                  d        Zej                  j                  d	        Zej                  j                  d
        Zej                  j                  d        Zej                  j                  d        Zej                  j                  d        Zy)u  
test_mcp_server.py

TDD RED 단계: mcp_server.py 구현 전 테스트 먼저 작성.

테스트 대상:
- search_knowledge tool: hybrid_search() 호출
- get_document tool: Supabase knowledge_documents 테이블 조회
- list_sources tool: source별 GROUP BY COUNT

테스트 전략:
- pytest.mark.anyio: FastMCP가 async 기반
- unittest.mock.patch: 외부 의존성(hybrid_search, Supabase) mock
- mcp.call_tool(name, arguments): MCP 프로토콜 레벨 tool 호출
    N)	AsyncMock	MagicMockpatch)mcpc                    K   ddddddddd	dd
dddg} t        d|       5 }t        j                  dddi       d{   }|\  }}|j                  ddd       t	        |      dkD  sJ |j                  dg       }t	        |      dk(  sJ |d   }d|v sJ d|v sJ d|v sJ d|v sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ 	 ddd       y7 # 1 sw Y   yxY ww)uZ   기본 검색: query만 제공하면 hybrid_search가 호출되고 결과가 반환된다.u   보험 약관 내용입니다.u   보험 약관 제1조insurance_docsgffffff?gq=
ףp?zdoc-001contenttitlesource
similaritycombined_scoredocument_idu+   보험료 납입에 관한 내용입니다.u   보험료 납입gףp=
?g333333?zdoc-002mcp_server.hybrid_searchreturn_valuesearch_knowledgequeryu   보험 약관N   r   limitsource_filterr   result   r
   r   r   r   )r   server	call_toolassert_called_once_withlenget)mock_search_resultsmock_hsr   content_blocks
structuredresult_list
first_items          O/home/jay/workspace/.worktrees/task-2057-dev2/services/tests/test_mcp_server.pytest_search_knowledge_basicr'       s    
 8,&"$	
 E'&"$	
& 
)8K	L 0PW''(:Wo<VWW &,"
 	''! 	( 	
 >"Q&&& !nnXr2;1$$$ !^
J&&&*$$$:%%%z))))$(HHHH'"&====(#'7777,'4///=0 0W0 0s-    C+CCBC	C+CC($C+c                  <  K   dddddddg} t        d| 	      5 }t        j                  d
ddd       d{   }|\  }}|j                  ddd       |j	                  dg       }t        |      dk(  sJ |d   d   dk(  sJ 	 ddd       y7 V# 1 sw Y   yxY ww)uU   source 필터: source 파라미터가 hybrid_search의 source_filter로 전달된다.u   필터된 결과u   필터 문서specific_sourceg?g)\(?zdoc-003r	   r   r   r   	   검색어)r   r   Nr   r   r      r   r   )r   r   r   r   r   r   r    r!   r   r"   r#   r$   s         r&   (test_search_knowledge_with_source_filterr-   W   s     
 *$'"$	
	 
)8K	L =PW''!->?
 

 &,"
 	''+ 	( 	
 !nnXr2;1$$$1~h'+<<<<#= =
= =s-   BBBAB	BBBBc                     K   g } t        d|       5 }t        j                  dddd       d{   }|j                  ddd       |\  }}|j	                  d	g       }|g k(  sJ 	 ddd       y7 @# 1 sw Y   yxY ww)
u?   limit 파라미터: limit 값이 hybrid_search에 전달된다.r   r   r   r*   
   )r   r   Nr   r   )r   r   r   r   r   r,   s         r&    test_search_knowledge_with_limitr0   y   s      	)8K	L !PW''!B/
 
 	'' 	( 	
 &,"
 nnXr2b   ! !
! !s,   A>A2A06A2'	A>0A22A;7A>c                  H  K   dddddddd} t               }| g|_        t               }||j                  j                  j                  j                  j
                  j                  j                  _        t        d	|      5  t        j                  d
ddi       d{   }|\  }}|j                  di       }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dddk(  sJ |j                  j                  d       ddd       y7 t# 1 sw Y   yxY ww)ud   문서 조회 성공: 존재하는 document_id로 조회하면 문서 상세 정보가 반환된다.zdoc-123u   보험 기본 약관u=   이 약관은 보험 계약의 기본 사항을 정합니다.standard_policy2024life_insurance)versioncategory)idr   r
   r   metadatamcp_server.supabase_clientget_documentr   Nr   r7   r   r
   r   r8   knowledge_documents)r   datatabler   selecteqexecuter   r   r   r   assert_called_with)mock_docmock_responsemock_supabaser   r"   r#   
doc_results          r&   test_get_document_foundrF      sP     'R# &4DEH KM"MKM`mM$$++88;;HHPP]	+]	; F''	8RSS%+"
^^Hb1
$9,,,'"&<<<<)$(gggg(#'8888*%VIY)ZZZZ 	../DEF FSF Fs1   BD"DD A+D	D"DDD"c                    K   t               } g | _        t               }| |j                  j                  j                  j                  j
                  j                  j                  _        t        d|      5  t        j                  dddi       d{   }|\  }}|j                  dd      }|8dt        |      j                         v sdt        |      j                         v sJ ddd       y7 ^# 1 sw Y   yxY ww)	u`   문서 조회 실패: 존재하지 않는 document_id로 조회하면 None 또는 에러 반환.r9   r:   r   znonexistent-docNr   errorz	not found)r   r<   r=   r   r>   r?   r@   r   r   r   r   strlower)rC   rD   r   r"   r#   rE   s         r&   test_get_document_not_foundrK      s      KMMKM`mM$$++88;;HHPP]	+]	; r''HY8Z[[%+"
  ^^Hd3
!WJ0E0E0G%G;Z]^hZiZoZoZqKqqqr r[r rs1   A5D 7C4C2AC4)	D 2C44C=9D c                  2  K   t               } dddddddddg| _        t               }| |j                  j                  j                  j                  j
                  _        t        d|      5  t        j                  d	i        d
{   }|\  }}|j                  dg       }t        |      dk(  sJ |d   }d|v sJ d|v sJ |D cg c]  }|d   	 }}d|v sJ t        d |D              }	|	d   dk(  sJ 	 d
d
d
       y
7 zc c}w # 1 sw Y   y
xY ww)uP   소스 목록 조회: knowledge_documents에서 source별 COUNT를 반환한다.r   *   )r   document_countlegal_texts   faq   r9   list_sourcesNr      r   r   rN   c              3   2   K   | ]  }|d    dk(  s|  yw)r   r   N ).0ss     r&   	<genexpr>z$test_list_sources.<locals>.<genexpr>   s     [a1X;JZ;Z[s   )r   r<   r=   r   r>   r@   r   r   r   r   r   next)
rC   rD   r   r"   r#   sources_listfirst_sourcerX   source_namesinsurance_sources
             r&   test_list_sourcesr_      sA     KM#r: B7A.M KMP]M$$++88@@M	+]	; 8'';;%+"
!~~h3< A%%% $A<'''</// .::(::<///[<[[ 01R777%8 8; ;8 8sC   A-D/D	D
A D
D$D;	DDDDDc                  t  K   t               } g | _        t               }| |j                  j                  j                  j                  j
                  _        t        d|      5  t        j                  di        d{   }|\  }}|j                  dg       }|g k(  sJ 	 ddd       y7 ,# 1 sw Y   yxY ww)uU   빈 결과 조회: knowledge_documents가 비어있으면 빈 목록을 반환한다.r9   rS   Nr   )
r   r<   r=   r   r>   r@   r   r   r   r   )rC   rD   r   r"   r#   r[   s         r&   test_list_sources_emptyra      s      KMMKMP]M$$++88@@M	+]	; "'';;%+"
!~~h3r!!!" ";" "s0   A!B8#B,=B*>"B,!	B8*B,,B51B8c                  z  K   t        dt        d            5  	 t        j                  dddi       d{   } | \  }}|j	                  dd      }|t        |      d	kD  sJ ddd       y7 6# t        $ r4}dt        |      v sd
t        |      j                         v sJ Y d}~Cd}~ww xY w# 1 sw Y   yxY ww)u_   에러 핸들링: hybrid_search가 예외를 던지면 적절한 에러 응답을 반환한다.r   zDB connection failed)side_effectr   r   u	   테스트Nr   r   rH   )r   	Exceptionr   r   r   r   rI   rJ   )r   r"   r#   result_dataes        r&   $test_search_knowledge_error_handlingrg   
  s      
"45
 Q	Q!++,>+@VWWF)/&NJ %..48K*c..AA.EEEQ Q X  	Q)SV3w#a&,,.7PPP	Q!Q QsO   B;B/A/A--A/$	B;-A//	B,8*B'"B/'B,,B//B84B;c                     K   t        j                          d{   } | D cg c]  }|j                   }}d|v s
J d|        d|v s
J d|        d|v s
J d|        t        |       dk(  sJ d	t        |        d
|        y7 lc c}w w)u[   Tool 등록 확인: 3개의 tool이 모두 FastMCP 서버에 등록되어 있어야 한다.Nr   u?   search_knowledge tool이 등록되지 않음. 등록된 tools: r:   u;   get_document tool이 등록되지 않음. 등록된 tools: rS   u;   list_sources tool이 등록되지 않음. 등록된 tools: rT   u   예상 tool 수: 3, 실제: z	. tools: )r   
list_toolsnamer   )toolstool
tool_namess      r&   test_tool_registrationrn   '  s      ##%%E(-.$)).J. +{/noynz-{{+Z's+fgqfr)ss'Z's+fgqfr)ss' u:?\:3u:,iPZ|\\? &.s    BBBB	AB	B)__doc__jsonsysunittest.mockr   r   r   pytest
mcp_serverr   r   markanyior'   r-   r0   rF   rK   r_   ra   rg   rn   rV       r&   <module>rx      s:     
 5 5  % 30 30l = =B ! !6 F FB r r0 8 8D " ", Q Q8 ] ]rw   