
    hi7                        d dl Zd dlmc mZ d dlZd dlZej                  j                  d ej                  j                  ej                  j                  e      d             d dlmZmZmZmZ d dlZddedededefdZ	 	 	 	 dd	ed
edededef
dZdedefdZddedefdZddededefdZ G d d      Z G d d      Z G d d      Zy)    Nz..)	AsyncMock	MagicMockcallpatchuser_idis_botusernamereturnc                 D    t               }| |_        ||_        ||_        |S N)r   idr   r	   )r   r   r	   users       G/home/jay/workspace/services/multimodel-bot/tests/test_thinking_mode.py
_make_userr      s#    ;DDGDKDMK    text	from_userchat_id	chat_typec                     t               }| |_        ||_        t               |_        ||j                  _        ||j                  _        ||_        |xs g |_        t               |_	        |S r   )
r   r   r   chatr   typereply_to_messageentitiesr   
reply_text)r   r   r   r   r   r   msgs          r   _make_messager      sY     +CCHCM{CHCHHKCHHM+C>rCL[CNJr   messagec                 X    t               }| |_        | |_        | j                  |_        |S r   )r   r   effective_messager   effective_user)r   updates     r   _make_updater#   ,   s+    [FFN&F#--FMr   bot_usernamec                 h    t               }t               |_        | |j                  _        i |_        |S r   )r   botr	   bot_data)r$   ctxs     r   _make_contextr)   4   s*    
+CkCG#CGGCLJr   bot_idc                     t               }| |_        ||_        t               }d|_        t	        |      |_        t	               |_        |S )uJ   send_message / edit_message_text를 AsyncMock으로 가진 봇 목 객체.90  return_value)r   r   r	   
message_idr   send_messageedit_message_text)r*   r	   r&   sent_msgs       r   _make_async_botr3   <   sB    
+CCFCL{HH h7C%KCJr   c                       e Zd ZdZej
                  j                  d        Zej
                  j                  d        Zej
                  j                  d        Z	y)TestThinkingMessageHelpersuD   bot_utils.send_thinking_message / replace_thinking_message 테스트c                   K   ddl m} t               }d} |||       d{   }|j                  j	                  |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}}y7 ȭw)u   send_thinking_message() 호출 시 '🤔 생각 중...' 텍스트로
        bot.send_message가 호출되고, message_id가 반환되어야 한다.r   )send_thinking_messaged   Nu   🤔 생각 중...)r   r   r,   ==z%(py0)s == %(py3)sresultpy0py3assert %(py5)spy5)	bot_utilsr7   r3   r0   assert_called_once_with
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)	selfr7   r&   r   r<   @py_assert2@py_assert1@py_format4@py_format6s	            r   test_send_thinking_messagez5TestThinkingMessageHelpers.test_send_thinking_messageW   s      	4,S'::00G[0\vvvv ;s   C,C*C	C,c                    K   ddl m} t               }d}d}d} |||||       d{    |j                  j	                  |||       |j
                  j                          y7 =w)u   replace_thinking_message()에 4096자 이하 텍스트를 전달하면
        bot.edit_message_text가 한 번 호출되어야 한다.r   replace_thinking_messager8   r,   u   짧은 응답입니다.N)r   r/   r   )rB   rT   r3   r1   rC   r0   assert_not_called)rL   rT   r&   r   r/   
short_texts         r   )test_replace_thinking_with_short_responsezDTestThinkingMessageHelpers.test_replace_thinking_with_short_responsee   sr      	7
.
&sGZLLL55! 	6 	

 	**, 	Ms   %A'A%>A'c                 B  K   ddl m} t               }d}d}ddz  } |||||       d{    |j                  }|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                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}|j                  j                  j                  }|d   }||k(  }|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   }||k(  }|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   }t        |      }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}	}|j                   }|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                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}|j                   j"                  D ]  }|j                  }|d   }||k(  }|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   }t        |      }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7 w)u   replace_thinking_message()에 4096자 초과 텍스트를 전달하면
        첫 청크는 edit_message_text로, 나머지 청크는 send_message로 전송해야 한다.r   rS   r8   r,   u   가i  N   r9   )zY%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.edit_message_text
}.call_count
} == %(py7)sr&   )r>   py2py4py7zassert %(py9)spy9r   )z%(py1)s == %(py3)s)py1r?   r@   rA   r/   r   i   )<=)z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} <= %(py7)slen)>=)zT%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.send_message
}.call_count
} >= %(py7)s)rB   rT   r3   r1   
call_countrD   rE   rF   rG   rH   rI   rJ   rK   	call_argskwargsr`   r0   call_args_list)rL   rT   r&   r   r/   	long_textrN   @py_assert3@py_assert6@py_assert5@py_format8@py_format10first_call_kwargs@py_assert0rM   rO   rP   	send_callsent_kwargss                      r   (test_replace_thinking_with_long_responsezCTestThinkingMessageHelpers.test_replace_thinking_with_long_responsey   s     	7
DL	&sGZKKK $$4$//414/14444/1444444s444s444$444/4441444444411;;BB +6+w6666+w666+666666w666w6666666 .<.*<<<<.*<<<.<<<<<<*<<<*<<<<<<<$V,5s,-55-5555-555555s555s555,555-5555555555 /**/a/*a////*a//////s///s//////*///a///////))88 	4I#**Ky)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4"6*33*+3t3+t3333+t33333333333333*333+333t3333333	4 	Ls   (XXW2XN)
__name__
__module____qualname____doc__pytestmarkasynciorQ   rW   rp    r   r   r5   r5   T   s]    N[[  [[- -& [[4 4r   r5   c                       e Zd ZdZd Zej                  j                  d        Zej                  j                  d        Z	ej                  j                  d        Z
y)TestSequentialResponseu(   멀티봇 순차 응답 로직 테스트c                     ddl m}  |       S )Nr   DiscussionManager)discussion_managerr}   )rL   r}   s     r   _make_discussion_managerz/TestSequentialResponse._make_discussion_manager   s    8 ""r   c                 b
  K   ddl m} | j                         }d}|j                  |d       |j                  d   }|j                  d   }|j                  d   }|j
                  }d}d}	 ||||	|	      }
d
}|
|u }|skt        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t        j                  |      t        j                  |	      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}x}	x}
x}}|j
                  }d}d}	 ||||	|	      }
d}|
|u }|skt        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t        j                  |      t        j                  |	      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}x}	x}
x}}|j
                  }d}d}	 ||||	|	      }
d}|
|u }|skt        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t        j                  |      t        j                  |	      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}x}	x}
x}}yw)u   유저 메시지 수신 시 첫 번째 봇(gemini_view_bot)의 current_turn이
        설정되고, 나머지 봇은 should_bot_respond()에서 False를 반환해야 한다.r   r|         안녕rY      someuserFr$   sender_usernamesender_is_botr   Tis)z%(py10)s
{%(py10)s = %(py2)s
{%(py2)s = %(py0)s.should_bot_respond
}(bot_username=%(py3)s, sender_username=%(py5)s, sender_is_bot=%(py7)s, chat_id=%(py8)s)
} is %(py13)sdm	first_botr   )r>   rZ   r?   rA   r\   py8py10py13zassert %(py15)spy15N
second_bot	third_bot)r~   r}   r   on_user_messageBOT_USERNAMESshould_bot_respondrD   rE   rF   rG   rH   rI   rJ   rK   )rL   r}   r   r   r   r   r   rN   @py_assert4rh   @py_assert9@py_assert12@py_assert11@py_format14@py_format16s                  r   )test_user_message_triggers_first_bot_onlyz@TestSequentialResponse.test_user_message_triggers_first_bot_only   s     	9**,
7H-%33A6	&44Q7
%33A6	 !!	
 !+	
 $		
!& *#		
 	
 	
 	
 	
 	
 	
 
6	
 	
  	
 	
 
	 	
 	
 
	 "	
 	
 
6	
 	
  '	
 	
 
	 '	
 	
 
	 !+	
 	
 
		 $		
 	
 
6	
 	
   	
 	
 
	  	
 	
 
		
 	
 
	 	
 	
 	
 	
 	
 	
 	
 !!	
 !+	
 $		
!' *#		
 	
 	
 	
 	
 	
 	
 
6	
 	
  	
 	
 
	 	
 	
 
	 "	
 	
 
6	
 	
  (	
 	
 
	 (	
 	
 
	 !+	
 	
 
		 $		
 	
 
6	
 	
   	
 	
 
	  	
 	
 
		
 	
 
	 	
 	
 	
 	
 	
 	
 	
 !!	
 !+	
 $		
!& *#		
 	
 	
 	
 	
 	
 	
 
6	
 	
  	
 	
 
	 	
 	
 
	 "	
 	
 
6	
 	
  '	
 	
 
	 '	
 	
 
	 !+	
 	
 
		 $		
 	
 
6	
 	
   	
 	
 
	  	
 	
 
		
 	
 
	 	
 	
 	
 	
 	
 	
 	
s   T-T/c                   K   ddl m} | j                         }d}|j                  |d       |j                  d   }|j                  d   }|j                  |      }||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}|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	t        j                         v st        j                  |      rt        j                  |      nd	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t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}
x}x}}yw)uO   첫 번째 봇이 응답하면 두 번째 봇의 턴으로 넘어가야 한다.r   r|      r   rY   r9   z%(py0)s == %(py2)snext_botr   r>   rZ   assert %(py4)sr[   NTr   r   )z%(py9)s
{%(py9)s = %(py2)s
{%(py2)s = %(py0)s.should_bot_respond
}(bot_username=%(py3)s, sender_username=%(py4)s, sender_is_bot=%(py6)s, chat_id=%(py7)s)
} is %(py12)sr   r   r   )r>   rZ   r?   r[   py6r\   r]   py12zassert %(py14)spy14)r~   r}   r   r   r   on_bot_responserD   rE   rF   rG   rH   rI   rJ   rK   r   )rL   r}   r   r   r   r   r   rN   @py_format3@py_format5ri   @py_assert8r   @py_assert10@py_format13@py_format15s                   r   test_first_bot_triggers_secondz5TestSequentialResponse.test_first_bot_triggers_second   s     	9**,
7H-%33A6	&44Q7
 %%i0:%%%%x:%%%%%%x%%%x%%%%%%:%%%:%%%%%%% !!	
 #		
!' )"		
 	
 	
 	
 	
 	
 	
 
6	
 	
  	
 	
 
	 	
 	
 
	 "	
 	
 
6	
 	
  (	
 	
 
	 (	
 	
 
6	
 	
  !*	
 	
 
	 !*	
 	
 
		 #		
 	
 
6	
 	
   	
 	
 
	  	
 	
 
		
 	
 
	 	
 	
 	
 	
 	
 	
 	
s   K/K1c                 *
  K   ddl m} | j                         }d}|j                  |d       |j                  d   }|j                  d   }|j                  d   }|g}|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}
}	||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}
|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}
}	||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}
|j                  |       |||g}	||	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                  |      }||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w)u   세 봇이 순서대로 모두 응답하는 흐름이 정상 동작해야 한다.

        유저 메시지 → gemini → codex → claude → gemini(wrap-around) 순서.r   r|      r   rY   r   N)is not)z%(py0)s is not %(py3)snext_br=   r@   rA   r9   r   bot_br   r   r[   next_cbot_cr;   response_order	next_wrapbot_a)r~   r}   r   r   r   r   rD   rE   rF   rG   rH   rI   rJ   rK   append)rL   r}   r   r   r   r   r   r   r   rM   rN   rO   rP   r   r   r   r   s                    r   &test_response_order_includes_all_threez=TestSequentialResponse.test_response_order_includes_all_three   s    
 	9**,
7H-!//2!//2!//2  ##E*!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!vvvf%##E*!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!vvvf% #(!66~!66666~!6666666~666~666!66666666 &&u-	E!!!!yE!!!!!!y!!!y!!!!!!E!!!E!!!!!!!s   TTN)rq   rr   rs   rt   r   ru   rv   rw   r   r   r   rx   r   r   rz   rz      se    2#
 [[*
 *
X [[
 
6 [[!" !"r   rz   c                   n    e Zd ZdZdededefdZd Zd Zd Ze	j                  j                  d	        Zy
)TestContextPromptuT   봇이 생성하는 프롬프트에 맥락이 올바르게 포함되는지 테스트previous_bot_nameprevious_messager
   c                     d| d| dS )uh   main_bot.py의 trigger_next_bot_response에서 사용하는
        프롬프트 포맷을 재현한다.u   [그룹 토론 중] u   의 발언: uH   

위 내용에 대해 당신의 의견을 간결하게 말해주세요.rx   )rL   r   r   s      r   _make_discussion_promptz)TestContextPrompt._make_discussion_prompt  s'     ##4"5\BRAS TU U	
r   c                    d}d}| j                  ||      }||v }|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
)uQ   프롬프트에 이전 대화 내용(previous_message)이 포함되어야 한다.gemini_view_botu.   인공지능은 인류에 도움이 됩니다.inz%(py0)s in %(py2)sprevious_msgpromptr   r   r[   N	r   rD   rE   rF   rG   rH   rI   rJ   rK   )rL   previous_botr   r   rN   r   r   s          r   'test_context_includes_previous_messagesz9TestContextPrompt.test_context_includes_previous_messages)  s    (G--lLIv%%%%|v%%%%%%|%%%|%%%%%%v%%%v%%%%%%%r   c                 h   d}d}| j                  ||      }||v }|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
}||v }|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
)u\   두 번째 봇의 프롬프트에 첫 번째 봇의 응답 내용이 포함되어야 한다.r   u<   저는 이 문제에 대해 긍정적으로 생각합니다.r   r   first_bot_namer   r   r   r[   Nfirst_bot_responser   )rL   r   r   r   rN   r   r   s          r   'test_second_bot_sees_first_bot_responsez9TestContextPrompt.test_second_bot_sees_first_bot_response2  s    *[--n>PQ ''''~''''''~'''~''''''''''''''''!V++++!V++++++!+++!++++++V+++V+++++++r   c                    ddl m} |j                  D ]  }d}| j                  ||      }||v }|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)u^   각 봇의 페르소나(이름)가 프롬프트의 발언자 표기에 포함되어야 한다.r   r|   u   테스트 발언입니다.r   r   r$   r   r   r   r[   N)r~   r}   r   r   rD   rE   rF   rG   rH   rI   rJ   rK   )rL   r}   r$   some_messager   rN   r   r   s           r   test_persona_included_in_promptz1TestContextPrompt.test_persona_included_in_prompt=  s    8-;; 	*L7L11,MF  6))))<6))))))<)))<))))))6)))6)))))))	*r   c                   K   g d}d/dt         dt         dt         ffd}t               }||j                  _        ddd	d
}t	        j
                  d|      5  t	        d      5 }t	        d|      5  t	        d      5 }t	        d|      5  t	        dt              5  d|j                  _        d|j                  _        d|j                  j                  _
        d|j                  _        t               }t        t        d            |j                  _        t               |j                  _        t        d      |_        t        |t        d      f      |_        ddlm}  |dddd        d{    ddd       ddd       ddd       ddd       ddd       ddd       t'              }	d!}
|	|
k(  }|st)        j*                  d"|fd#|	|
f      d$t-        j.                         v st)        j0                  t&              rt)        j2                  t&              nd$d%t-        j.                         v st)        j0                        rt)        j2                        nd%t)        j2                  |	      t)        j2                  |
      d&z  }d'd(|iz  }t5        t)        j6                  |            dx}	x}}
d   }||k(  }|st)        j*                  d"|fd)||f      dt-        j.                         v st)        j0                  |      rt)        j2                  |      ndd*t-        j.                         v st)        j0                  |      rt)        j2                  |      nd*d+z  }d,d-|iz  }t5        t)        j6                  |            d}|j                  j9                  ddd.       y7 8# 1 sw Y   8xY w# 1 sw Y   =xY w# 1 sw Y   BxY w# 1 sw Y   GxY w# 1 sw Y   LxY w# 1 sw Y   QxY ww)0u   trigger_next_bot_response()가 memory.format_context()를 통해
        맥락 기반 프롬프트를 엔진 호출에 사용해야 한다.uE   코덱스의 관점에서 이전 대화를 참고하여 답변하라.gpt-5.1-codex-minir   modelr
   c                 0   K   j                  |        yw)N   모의 응답)r   )r   r   captured_promptss     r   fake_call_codexz_TestContextPrompt.test_trigger_next_bot_response_builds_correct_prompt.<locals>.fake_call_codexS  s     ##F+"s   zfake-gemini-tokenzfake-codex-tokenzfake-claude-token)GEMINI_BOT_TOKENCODEX_BOT_TOKENCLAUDE_BOT_TOKENz
os.environzmain_bot.dmzmain_bot.memoryzmain_bot.bot_appszmain_bot.call_codex)newzasyncio.sleep)new_callableTNdiverge  )r/   r-   r   r   )trigger_next_bot_responsecodex_view_boti,  u%   이전 봇의 발언 내용입니다.r   )next_bot_usernamer   r   r   rY   r9   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr`   r   )r>   r^   r?   r   zassert %(py8)sr   r   expected_context_promptr   r   r[   )phase)r   )strr   format_contextr.   r   dictr   is_discussion_activer   get_current_phasevalueget_codex_modelr&   r0   r1   __contains____getitem__main_botr   r`   rD   rE   rF   rG   rH   rI   rJ   rK   rC   )rL   r   r   mock_memory	env_patchmock_dmmock_bot_appsmock_appr   rM   ri   r   @py_format7@py_format9r   rN   r   r   r   s                     @r   4test_trigger_next_bot_response_builds_correct_promptzFTestContextPrompt.test_trigger_next_bot_response_builds_correct_promptH  s     ')"i	## 	#c 	#UX 	#  k2I""/ !41 3
	 JJ|Y/	- 	$+#[1	 %&		 +8'_=	 /	:	 9=G((537G##0;DG%%2283GG##0 {H(1yTW?X(YHLL%-6[HLL*)2)EM&(1)apJq?r(sM%:+"2!H"3	  +	 	 	 	 	 	8 #$))$))))$))))))s)))s))))))#)))#)))$))))))))))!!$00000v0000000v000v000000000000000000""::3@PXa:b+	 	 	 	 	 	 	 	 	 	 	 	s   AP O3$O&1O=O	N?CN27N/8N2<N?O	OO&O3$HP /N22N<7N??O	O	OOO#O&&O0	+O33O=8P N)rq   rr   rs   rt   r   r   r   r   r   ru   rv   rw   r   rx   r   r   r   r     sU    ^



 

 
	

&	,	* [[6c 6cr   r   )Ftestuser)r8   groupNN)mybot)r   r   )builtinsrF   _pytest.assertion.rewrite	assertionrewriterD   ossyspathinsertjoindirname__file__unittest.mockr   r   r   r   ru   intboolr   r   r   r#   r)   r3   r5   rz   r   rx   r   r   <module>r     s     	 
 277<< 94@ A ; ;  T S R[  
  	 () 	  ) 	C 	 	9 	0?4 ?4Ts" s"xec ecr   