
    hi,s                    ,   d dl Zd dlmc mZ d dlZd dl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mZ e	j"                  j%                  d ej"                  j'                  ej"                  j)                  e      d             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 G d d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z# G d d      Z$ G d d      Z% G d d       Z& G d! d"      Z' G d# d$      Z( G d% d&      Z) G d' d(      Z* G d) d*      Z+ G d+ d,      Z, G d- d.      Z- G d/ d0      Z. G d1 d2      Z/ G d3 d4      Z0 G d5 d6      Z1 G d7 d8      Z2 G d9 d:      Z3 G d; d<      Z4 G d= d>      Z5 G d? d@      Z6 G dA dB      Z7 G dC dD      Z8 G dE dF      Z9 G dG dH      Z: G dI dJ      Z; G dK dL      Z< G dM dN      Z= G dO dP      Z> G dQ dR      Z? G dS dT      Z@y)U    N)datetime)Path)	AsyncMockMockpatchz..)ChatMessageConversationMemoryc                       e Zd ZdZd Zy)TestAddAndGetMessagesuM   test_add_and_get_messages - 메시지 추가 후 get_context로 조회 가능c                 F
   t               }d}|j                  |ddd       |j                  |ddd       |j                  |      }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                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   }	|	j                  }d}||k(  }|st	        j
                  d
|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||k(  }|st	        j
                  d
|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||u }|st	        j
                  d|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||k(  }|st	        j
                  d
|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||k(  }|st	        j
                  d
|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||u }|st	        j
                  d|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}y )N      제이회장님   안녕하세요Fis_bot	   잼민이u   안녕하세요!T   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenmessagespy0py1py3py6assert %(py8)spy8r   )z.%(py3)s
{%(py3)s = %(py1)s.sender
} == %(py6)sr   r   r   z,%(py3)s
{%(py3)s = %(py1)s.text
} == %(py6)sis)z.%(py3)s
{%(py3)s = %(py1)s.is_bot
} is %(py6)s)r	   add_messageget_contextr   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationsendertextr   )
selfmemchat_idr   @py_assert2@py_assert5@py_assert4@py_format7@py_format9@py_assert0s
             M/home/jay/workspace/services/multimodel-bot/tests/test_conversation_memory.pytest_add_and_get_messagesz/TestAddAndGetMessages.test_add_and_get_messages   sJ    "!24EeT.@N??7+8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{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{4{4#44#44444#4444{444444#44444444{*{!!*U*!U****!U***{***!***U*******{0{!!0[0![0000![000{000!000[0000000{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{){!!)T)!T))))!T))){)))!)))T)))))))    N)__name__
__module____qualname____doc__r:    r;   r9   r   r      s
    W*r;   r   c                       e Zd ZdZd Zy)TestRingBufferEvictionuY   test_ring_buffer_eviction - max_messages(20) 초과 시 오래된 메시지 자동 제거c                 >   t        d      }d}t        d      D ]  }|j                  |dd| d        |j                  |d	      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d
|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d
|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}y )N   max_messagesr      r   
   메시지 Fr   limitr   r   r   r   r   r   r   r      메시지 5r!   r    u   메시지 24r	   ranger$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r1   r2   ir   r3   r4   r5   r6   r7   r8   s              r9   test_ring_buffer_evictionz0TestRingBufferEviction.test_ring_buffer_eviction&   s    b1r 	XAOOG%6*QC8HQVOW	X ??7"?58}""}""""}""""""s"""s""""""8"""8"""}""""""""""{0{0=0=0000=000{000000=0000000|2|  2N2 N2222 N222|222 222N2222222r;   N)r<   r=   r>   r?   rP   r@   r;   r9   rB   rB   #   s
    c3r;   rB   c                       e Zd ZdZd Zy)TestMultiChatIsolationuK   test_multi_chat_isolation - 서로 다른 chat_id의 메시지가 격리됨c                 	   t               }d}d}|j                  |ddd       |j                  |ddd	       |j                  |      }|j                  |      }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                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}|d   }|j                  }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}x}}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                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}|d   }|j                  }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}x}}|d   }|j                  }|d   }|j                  }||k7  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }
dd|
iz  }t        t	        j                  |            d x}x}x}x}}y )Nd      r      채팅A 메시지Fr   r      채팅B 메시지Tr   r   r   r   msgs_ar   r   r   r   r!   r    msgs_b!=)zF%(py3)s
{%(py3)s = %(py1)s.text
} != %(py8)s
{%(py8)s = %(py6)s.text
}r   r   r   r   assert %(py10)spy10r	   r$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r1   chat_achat_brX   rY   r3   r4   r5   r6   r7   r8   @py_assert7@py_format11s                 r9   test_multi_chat_isolationz0TestMultiChatIsolation.test_multi_chat_isolation7   s    " 13FuU-@N((6{a{a{ass66{aay4y~~4!44~!44444~!4444y444~444!444444446{a{a{ass66{aay4y~~4!44~!44444~!4444y444~444!44444444 ay/y~~///~////~///y///~//////////////r;   N)r<   r=   r>   r?   rd   r@   r;   r9   rR   rR   4   s
    U0r;   rR   c                       e Zd ZdZd Zd Zy)TestGetContextWithLimituI   test_get_context_with_limit - limit 파라미터로 최근 N개만 조회c                 :   t               }d}t        d      D ]  }|j                  |dd| d        |j                  |d      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d	|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d	|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}y )N   
   r   rH   Fr      rI   r   r   r   recent_5r   r   r   r   rK   r!   r    rL      메시지 9rM   )r0   r1   r2   rO   rk   r3   r4   r5   r6   r7   r8   s              r9   test_get_context_with_limitz3TestGetContextWithLimit.test_get_context_with_limitO   s    "r 	XAOOG%6*QC8HQVOW	X ??7!?48}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{0{0=0=0000=000{000000=0000000|1|  1M1 M1111 M111|111 111M1111111r;   c                    t               }d}|j                  |ddd       |j                  |ddd       |j                  |d	
      }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                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}y )N   r      하나Fr   r      둘TrT   rI   r   r   r   r   r   r   r   r   )r	   r$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   )	r0   r1   r2   r   r3   r4   r5   r6   r7   s	            r9   )test_get_context_limit_larger_than_storedzATestGetContextWithLimit.test_get_context_limit_larger_than_stored[   s     "!2HUKeDA??7#?68}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!r;   N)r<   r=   r>   r?   rm   rr   r@   r;   r9   rf   rf   L   s    S
2"r;   rf   c                       e Zd ZdZd Zy)TestFormatContextBasicuH   test_format_context_basic - format_context가 올바른 문자열 생성c                    t               }d}|j                  |ddd       |j                  |ddd       |j                  |d	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}}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}}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 )Nrj   r   u*   데이터 기반 접근이 중요합니다Tr   r   u   그래서 결론이 뭐야?Fgemini_view_botu   현재 질문입니다   [이전 대화]inz%(py1)s in %(py3)sresultr   r   assert %(py5)spy5u5   잼민이: 데이터 기반 접근이 중요합니다u,   제이회장님: 그래서 결론이 뭐야?u   [현재 질문/발언]u'   제이회장님: 현재 질문입니다r	   r$   format_contextr&   r'   r+   r(   r)   r*   r,   r-   r0   r1   r2   r{   r8   r3   @py_format4@py_format6s           r9   test_format_context_basicz0TestFormatContextBasic.test_format_context_basici   s    ".Zcgh!24QZ_`##G->@XY * F**** F*** ******F***F*******FPF&PPPPF&PPPFPPPPPP&PPP&PPPPPPP=G=GGGG=GGG=GGGGGGGGGGGGGGGG'1'61111'6111'1111116111611111118B8FBBBB8FBBB8BBBBBBFBBBFBBBBBBBr;   N)r<   r=   r>   r?   r   r@   r;   r9   rt   rt   f   s    RCr;   rt   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestFormatContextWithPersonauc   test_format_context_with_persona - format_context에 봇 이름과 페르소나 프롬프트 포함c                    t               }d}|j                  |d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 )N   rv   u   테스트 질문u   너는 잼민이다rx   rz   r{   r|   r}   r~   U   이전 발언들을 참고하되 중복되지 않는 새로운 관점을 제시해라
r	   r   r&   r'   r+   r(   r)   r*   r,   r-   r   s           r9   "test_format_context_gemini_personaz?TestFormatContextWithPersona.test_format_context_gemini_persona|   s     "##G->@RS$.$....$...$................fpfjpppppfjppppfppppppjppppjppppppppr;   c                    t               }d}|j                  |d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 )N   codex_view_botu   코드 질문u   너는 코덱스다rx   rz   r{   r|   r}   r~   r   r   r   s           r9   !test_format_context_codex_personaz>TestFormatContextWithPersona.test_format_context_codex_persona   s     "##G-=O$.$....$...$................fpfjpppppfjppppfppppppjppppjppppppppr;   c                    t               }d}|j                  |d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 )N   claude_view_botu   균형 잡힌 분석u   너는 클로디다rx   rz   r{   r|   r}   r~   r   r   r   s           r9   "test_format_context_claude_personaz?TestFormatContextWithPersona.test_format_context_claude_persona   s     "##G->@VW$.$....$...$................fpfjpppppfjppppfppppppjppppjppppppppr;   c                    t               }d}|j                  |d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=   알 수 없는 봇 이름은 bot_username을 그대로 사용	   unknown_bot   질문r   rx   rz   r{   r|   r}   r~   Nr   r   s           r9   test_format_context_unknown_botz<TestFormatContextWithPersona.test_format_context_unknown_bot   s     "##G]HEfpfjpppppfjppppfppppppjppppjppppppppr;   N)r<   r=   r>   r?   r   r   r   r   r@   r;   r9   r   r   y   s    mqqqqr;   r   c                   "    e Zd ZdZd Zd Zd Zy)TestEmptyContextu`   test_empty_context - 빈 채팅의 format_context는 빈 문자열이 아닌 최소 프롬프트c                    t               }d}|j                  |d      }d}||k7  }|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}}t        |      }d
}||kD  }	|	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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            d x}x}	}y )Nri   rv    rZ   )z%(py0)s != %(py3)sr{   r   r   r}   r~   r   >z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)sr   r   r   r   )r	   r   r&   r'   r(   r)   r*   r+   r,   r-   r   )r0   r1   r2   r{   r3   @py_assert1r   r   r4   r5   r6   r7   s               r9   #test_empty_context_not_empty_stringz4TestEmptyContext.test_empty_context_not_empty_string   s    "##G->?v|vvv6{Q{Q{Qss66{Qr;   c                    t               }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 )
N   rv   r   rx   rz   r{   r|   r}   r~   r   r   s           r9   #test_empty_context_contains_personaz4TestEmptyContext.test_empty_context_contains_persona   s     "##G->?fpfjpppppfjppppfppppppjppppjppppppppr;   c                    t               }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>   메시지가 없으면 [이전 대화] 섹션이 없어야 함   rv   rw   not inz%(py1)s not in %(py3)sr{   r|   r}   r~   Nr   r   s           r9   2test_empty_context_no_previous_conversation_headerzCTestEmptyContext.test_empty_context_no_previous_conversation_header   s~     "##G->? . .... ... ................r;   N)r<   r=   r>   r?   r   r   r   r@   r;   r9   r   r      s    jq/r;   r   c                       e Zd ZdZd Zd Zy)TestMessageOrderinguB   test_message_ordering - 메시지가 시간 순서대로 반환됨c                    t               }d}|j                  |ddd       |j                  |ddd       |j                  |d	d
d       |j                  |      }|d   }|j                  }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}x}}|d   }|j                  }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}x}}|d   }|j                  }d
}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}x}}y )N   r   u
   첫 번째Fr   r   u
   두 번째T	   코덱스u
   세 번째r   r   r!   r    r   r   r   r   )	r	   r$   r%   r/   r&   r'   r+   r,   r-   )
r0   r1   r2   r   r8   r3   r4   r5   r6   r7   s
             r9   test_message_orderingz)TestMessageOrdering.test_message_ordering   s    "!2LOl4Hl4H??7+{/{/</<////<///{//////<///////{/{/</<////<///{//////<///////{/{/</<////<///{//////<///////r;   c                    t               }d}|j                  |ddd       |j                  |ddd       |j                  |d	d
d       |j                  |      }t        t	        |      dz
        D ]  }||   }|j
                  }||dz      }|j
                  }||k  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            d x}x}x}	x}} y )N   r   AFr   r   BTr   Cr   )<=)zP%(py3)s
{%(py3)s = %(py1)s.timestamp
} <= %(py8)s
{%(py8)s = %(py6)s.timestamp
}r\   r]   r^   )r	   r$   r%   rN   r   	timestampr&   r'   r+   r,   r-   )r0   r1   r2   r   rO   r8   r3   r4   rb   r5   r7   rc   s               r9   test_message_timestamps_orderedz3TestMessageOrdering.test_message_timestamps_ordered   s    "!2CFc$?c$???7+s8}q() 	FAA;E;((EHQUOEO,E,EE(,EEEEE(,EEEE;EEE(EEEOEEE,EEEEEEEEE	Fr;   N)r<   r=   r>   r?   r   r   r@   r;   r9   r   r      s    L0Fr;   r   c                       e Zd ZdZd Zd Zy)TestDefaultMaxMessagesu8   test_default_max_messages - 기본값이 50임을 확인c                    t               }|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                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )N2   r   z5%(py2)s
{%(py2)s = %(py0)s._max_messages
} == %(py5)sr1   r   py2r~   assert %(py7)spy7
r	   _max_messagesr&   r'   r(   r)   r*   r+   r,   r-   r0   r1   r   r5   @py_assert3r   @py_format8s          r9   test_default_max_messagesz0TestDefaultMaxMessages.test_default_max_messages   s|     "  &B& B&&&& B&&&&&&s&&&s&&& &&&B&&&&&&&r;   c                    t               }d}t        d      D ]  }|j                  |dd| d        |j                  |d      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}y )N   7   r   rH   Fr   rT   rI   r   r   r   r   all_messagesr   r   r   r	   rN   r$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   )
r0   r1   r2   rO   r   r3   r4   r5   r6   r7   s
             r9   "test_default_max_messages_enforcedz9TestDefaultMaxMessages.test_default_max_messages_enforced   s     "r 	XAOOG%6*QC8HQVOW	X wc:< &B& B&&&& B&&&&&&s&&&s&&&&&&<&&&<&&& &&&B&&&&&&&r;   N)r<   r=   r>   r?   r   r   r@   r;   r9   r   r      s    B''r;   r   c                   "    e Zd ZdZd Zd Zd Zy)TestCustomMaxMessagesu?   test_custom_max_messages - 커스텀 max_messages 동작 확인c                 >   t        d      }d}t        d      D ]  }|j                  |dd| d        |j                  |d	
      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}
x}x}}y )Nrj   rE      ri   r   rH   Fr   rT   rI   r   r   r   r   r   r   r   r   rK   r!   r    rL   rl   rM   )r0   r1   r2   rO   r   r3   r4   r5   r6   r7   r8   s              r9   test_custom_max_messages_5z0TestCustomMaxMessages.test_custom_max_messages_5   s    a0r 	XAOOG%6*QC8HQVOW	X wc:< %A% A%%%% A%%%%%%s%%%s%%%%%%<%%%<%%% %%%A%%%%%%%A4##4}4#}4444#}444444#444}4444444B5$$55$5555$555555$5555555555r;   c                    t        d      }d}|j                  |ddd       |j                  |dd	d
       |j                  |ddd
       |j                  |ddd       |j                  |d      }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                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   }	|	j                  }d	}||k(  }|st	        j
                  d|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}|d   }	|	j                  }d}||k(  }|st	        j
                  d|fd||f      t	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}	x}x}}y )Nrh   rE      r   rp   Fr   r   rq   Tr   u   셋u   넷rT   rI   r   r   r   r   r   r   r   r   r!   r    rL   r_   )
r0   r1   r2   r   r3   r4   r5   r6   r7   r8   s
             r9   test_custom_max_messages_3z0TestCustomMaxMessages.test_custom_max_messages_3   s    a0!2HUKeDAeDA!2E%Hwc:< %A% A%%%% A%%%%%%s%%%s%%%%%%<%%%<%%% %%%A%%%%%%%A,##,u,#u,,,,#u,,,,,,#,,,u,,,,,,,B-$$--$----$------$----------r;   c                    t        d      }|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                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )	Nr   rE   r   r   r1   r   r   r   r   r   s          r9   )test_custom_max_messages_stored_correctlyz?TestCustomMaxMessages.test_custom_max_messages_stored_correctly  s~     a0  %A% A%%%% A%%%%%%s%%%s%%% %%%A%%%%%%%r;   N)r<   r=   r>   r?   r   r   r   r@   r;   r9   r   r      s    I
6.&r;   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestPersistenceu$   JSONL 파일 저장/로드 테스트c                 0   t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|j                  } |       }|st        j                  d	|       d
z   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)uH   add_message 호출 시 날짜별 JSONL 파일이 생성되어야 한다.storage_basei  r      테스트 메시지Fr   %Y-%m-%d.jsonlu(   JSONL 파일이 생성되어야 한다: C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
jsonl_pathr   r   py4N)r	   strr$   r   nowstrftimeexistsr&   _format_assertmsgr(   r)   r*   r+   r,   r-   	r0   tmp_pathr1   r2   todayr   r   r   @py_format5s	            r9   #test_add_message_creates_jsonl_filez3TestPersistence.test_add_message_creates_jsonl_file  s     c(m<!24IRWX''
35' 00
  [ "["[[&Nzl$[[[[[[[z[[[z[[[ [[["[[[[[[r;   c                 X   t        t        |            }d}|j                  |ddd       |j                  |ddd	       t        j                         j                  d
      }|| dz  }|j                  d      j                         j                         }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t!        t        j"                  |            dx}x}	}y)uK   add_message 호출 시 JSONL 파일에 메시지가 추가되어야 한다.r   i  r   r   Tr   r   u   반갑습니다Fr   r   utf-8encodingr   r   r   r   linesr   r   r   N)r	   r   r$   r   r   r   	read_textstrip
splitlinesr   r&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   r2   r   r   r   r3   r4   r5   r6   r7   s               r9   #test_add_message_appends_jsonl_linez3TestPersistence.test_add_message_appends_jsonl_line%  s    c(m<.?M!24EeT''
35' 00
$$g$6<<>IIK5zQzQzQss55zQr;   c                    t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }|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   }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   }d}	||	u }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}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}||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:   JSONL 각 라인이 올바른 JSON 형식이어야 한다.r   i  r   u   데이터 분석 중Tr   r   r   r   r   r.   r   z%(py1)s == %(py4)sr   r   assert %(py6)sr   Nr/   r   r"   z%(py1)s is %(py4)sr2   z%(py1)s == %(py3)sr|   r}   r~   r   rx   rz   data)r	   r   r$   r   r   r   r   r   jsonloadsr&   r'   r+   r,   r-   r(   r)   r*   )r0   r   r1   r2   r   r   liner   r8   r   r3   r   r6   r   r   s                  r9   test_jsonl_line_formatz&TestPersistence.test_jsonl_line_format2  s    c(m<.DTR''
35' 00
##W#5;;=zz$H~,,~,,,,~,,,~,,,,,,,,,,F|555|55555|5555|55555555555H~%%~%%%%~%%%~%%%%%%%%%%I)'))))')))))))))')))')))))))"{d""""{d"""{""""""d"""d"""""""r;   c                 8   t        d      }d}|j                  |ddd       t        j                         j	                  d      }t        d	      | d
z  }|j                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d   }|j                   }d}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}y)uR   storage_base=None이면 파일을 생성하지 않는다 (인메모리만 동작).Nr   i  r   u   파일 없음Fr   r   z$/home/jay/workspace/memory/groupchatr   r   r   r   r   r   r   r   r   r   r!   r    )r	   r$   r   r   r   r   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   r1   r2   r   default_pathr   r3   r4   r5   r6   r7   r8   s                r9   &test_storage_base_none_no_file_createdz6TestPersistence.test_storage_base_none_no_file_createdD  sX    d3!2OER ''
3BCvFVV ??7+8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{2{2?2?2222?222{222222?2222222r;   c                    t        t        |            }d}d}|j                  |ddd       |j                  |dd	d
       t        j                         j                  d      }|| dz  }|j                  d      j                         j                         }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                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }dd|iz  }t!        t        j"                  |            dx}x}
}	|D ch c]  }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}||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c c}w )!uD   여러 chat_id의 메시지가 같은 날짜 파일에 저장된다.r   i  i  r   u   채팅AFr   r   u   채팅BTr   r   r   r   r   r   r   r   r   r   r   r   Nr2   rx   )z%(py0)s in %(py2)sr`   chat_ids_in_filer   r   assert %(py4)sr   ra   )r	   r   r$   r   r   r   r   r   r   r   r&   r'   r(   r)   r*   r+   r,   r-   r   r   )r0   r   r1   r`   ra   r   r   r   r3   r4   r5   r6   r7   lr  r   @py_format3r   s                     r9   #test_multiple_chat_ids_in_same_filez3TestPersistence.test_multiple_chat_ids_in_same_fileT  s    c(m< 19UKYtD''
35' 00
$$g$6<<>IIK5zQzQzQss55zQ>CDDJJqM)4DD)))))v)))))))v)))v)))))))))))))))))))))))v)))))))v)))v)))))))))))))))))) Es   1M/N)	r<   r=   r>   r?   r   r   r  r  r  r@   r;   r9   r   r     s    .	\#$3 *r;   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestLoadTodayu    load_today() 메서드 테스트c                    t        t        |            }d}|j                  |ddd       |j                  |ddd	       t        t        |            }|j                  |       |j	                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }|j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}|d   }|j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}y)uF   오늘 JSONL 파일에서 해당 chat_id의 메시지를 로드한다.r   i  r   u   첫 번째 메시지Fr   r   u   두 번째 메시지Tr   r   r   r   r   r   r   r   Nr   r!   r    r   r	   r   r$   
load_todayr%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   mem1r2   mem2r   r3   r4   r5   r6   r7   r8   s               r9   !test_load_today_restores_messagesz/TestLoadToday.test_load_today_restores_messagesj  s    "s8}="35KTYZ+/EdS "s8}= ##G,8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{9{9#99#99999#9999{999999#99999999{9{9#99#99999#9999{999999#99999999r;   c                    t        t        |            }d}|j                  |       |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}}y	)
u7   파일이 없으면 빈 deque로 시작한다 (정상).r   i  r   z%(py0)s == %(py3)sr   r   r}   r~   N)r	   r   r  r%   r&   r'   r(   r)   r*   r+   r,   r-   )	r0   r   r1   r2   r   r3   r   r   r   s	            r9   #test_load_today_no_file_empty_dequez1TestLoadToday.test_load_today_no_file_empty_deque{  s     c(m< 	w??7+x2~x2xx2r;   c                 \   t        t        |            }d}d}|j                  |ddd       |j                  |dd	d
       t        t        |            }|j                  |       |j	                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d   }|j                  }d}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}y)u7   로드 시 해당 chat_id의 메시지만 가져온다.r   i  i  r   rV   Fr   r   rW   Tr   r   r   r   r   r   r   r   Nr   r!   r    r  )r0   r   r  r`   ra   r  r   r3   r4   r5   r6   r7   r8   s                r9   "test_load_today_filters_by_chat_idz0TestLoadToday.test_load_today_filters_by_chat_id  sm   !s8}=!24GPUV.A$O!s8}=##F+8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{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6r;   c                 Z   t        t        |      d      }d}t        d      D ]  }|j                  |dd| d        t        t        |      	      }|j	                  |       |j                  |d
      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d   }|j                  }d}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}y)u5   load_today는 최근 50개 메시지만 로드한다.rU   )r   rF   i  <   r   rH   Fr   r   rT   rI   r   r   r   r   r   r   r   r   NrL   u   메시지 59r!   r    )r	   r   rN   r$   r  r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   r  r2   rO   r  r   r3   r4   r5   r6   r7   r8   s                r9   test_load_today_limits_to_50z*TestLoadToday.test_load_today_limits_to_50  sp   !s8}3Or 	YAW&7:aS9IRWX	Y "s8}= ##G3#78}""}""""}""""""s"""s""""""8"""8"""}""""""""""|2|  2N2 N2222 N222|222 222N2222222r;   c                    t        t        |            }d}|j                  |ddd       t        t        |            }|j                  |       |j	                  |      d   }|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                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|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                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|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                   }t#        |t$              }|sd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                  |      dt        j                         v st        j                  t$              rt        j                  t$              ndt        j                  |      dz  }t        t        j                  |            dx}}y)uB   로드된 메시지의 모든 필드가 올바르게 복원된다.r   i  r   u   코드 리뷰 완료Tr   r   r   )z.%(py2)s
{%(py2)s = %(py0)s.sender
} == %(py5)smsgr   r   r   N)z,%(py2)s
{%(py2)s = %(py0)s.text
} == %(py5)sr"   )z.%(py2)s
{%(py2)s = %(py0)s.is_bot
} is %(py5)szTassert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.timestamp
}, %(py4)s)
}
isinstancer   r   r   r   r   r   )r	   r   r$   r  r%   r.   r&   r'   r(   r)   r*   r+   r,   r-   r/   r   r   r  r   )r0   r   r  r2   r  r  r   r5   r   r   r   r3   r4   r6   s                 r9   'test_load_today_message_fields_restoredz5TestLoadToday.test_load_today_message_fields_restored  sC   !s8}=+/EdS!s8}= w'*zz([(z[((((z[((((((s(((s(((z((([(((((((xx111x11111x1111111s111s111x11111111111zz!T!zT!!!!zT!!!!!!s!!!s!!!z!!!T!!!!!!!--2z-22222222z222z222222#222#222-2222222222222222222r;   N)	r<   r=   r>   r?   r  r  r  r  r!  r@   r;   r9   r  r  g  s    *:"7 3 3r;   r  c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestSummaryGenerationu   요약 생성 mock 테스트c           
      L   t        t        |            }d}|j                  |ddd       |j                  |ddd	       |j                  }|j                  }d
} |||      }d}||k(  }	|	s*t        j                  d|	fd||f      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                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}x}x}	}y)u/   chat_id별 메시지 카운트가 추적된다.r   iq  r   u
   메시지1Fr   r   u
   메시지2Tr   r   r   )zw%(py9)s
{%(py9)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._message_count
}.get
}(%(py5)s, %(py7)s)
} == %(py12)sr1   r2   )r   r   r   r~   r   py9py12zassert %(py14)spy14N)r	   r   r$   _message_countgetr&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   r2   r   r   @py_assert6@py_assert8@py_assert11@py_assert10@py_format13@py_format15s               r9   #test_message_count_tracked_per_chatz9TestSummaryGeneration.test_message_count_tracked_per_chat  s    c(m<!2LOl4H!!6!%%6q6%gq16Q61Q66661Q666666s666s666!666%666666g666g666q6661666Q66666666r;   c                    t        t        |            }d}t        j                  |d      5 }t	        d      D ]  }|j                  |dd| d        |j                  }|syd	d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }t        t        j                  |            d}ddd       y# 1 sw Y   yxY w)u7   50개 메시지가 쌓이면 요약이 트리거된다.r   ir  _generate_summaryr   r   rH   Fr   *assert %(py2)s
{%(py2)s = %(py0)s.called
}mock_summaryr  N)r	   r   r   objectrN   r$   calledr(   r)   r&   r*   r+   r,   r-   )r0   r   r1   r2   r4  rO   r   r  s           r9   %test_summary_triggered_at_50_messagesz;TestSummaryGeneration.test_summary_triggered_at_50_messages  s     c(m<\\#23 	'|2Y \):j<LUZ[\  &&&&&&&&&<&&&<&&&&&&&&&&	' 	' 	's   B2C))C2c                     t        t        |            dt        d      D ]  }j                  dd| d        fd}t	        j
                   |              y	)
u1   _generate_summary가 call_claude를 호출한다.r   is  rj   r   u   내용 Fr   c                     K   t        dt              5 } d| _        	j                         d {    | j                  }|syddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }| j                  d   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 d d        y 7 N# 1 sw Y   y xY ww)Nconversation_memory.call_claudenew_callableu   요약 결과입니다r3  mock_clauder  r   uB   아래 대화의 핵심 논점과 결론을 3~5줄로 요약해줘rx   rz   	call_argsr|   r}   r~   )r   r   return_valuer2  r6  r(   r)   r&   r*   r+   r,   r-   r>  r'   
r=  r   r  r>  r8   r3   r   r   r2   r1   s
           r9   runzJTestSummaryGeneration.test_generate_summary_calls_call_claude.<locals>.run  s    8yQ iU`+C(++G444"))))))))){))){))))))))))'11!4Q7	[h[_hhhhh[_hhhh[hhhhhh_hhhh_hhhhhhhhi i4i is-   FFE?EF6	F?FFFNr	   r   rN   r$   asynciorA  r0   r   rO   rA  r2   r1   s       @@r9   'test_generate_summary_calls_call_claudez=TestSummaryGeneration.test_generate_summary_calls_call_claude  s\     c(m<q 	UAOOG%6'!eOT	U	i 	CEr;   c                     t        t                    dj                  ddd       j                  ddd       fd	}t        j                   |              y
)uL   _generate_summary가 summaries/ 디렉토리에 JSON 파일을 저장한다.r   it  r      AI 기술 동향Tr   r   u   코드 품질 향상c                  L  K   t        dt              5 } d| _        j                         d {    d d d        dz  }|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}}t        |j                  d            }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}x}}y 7 # 1 sw Y   xY ww)Nr:  r;  u"   핵심 논점: AI와 코드 품질	summariesAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}summaries_dirr   *.jsonr   r   r   r   
json_filesr   r   r   )r   r   r?  r2  r   r(   r)   r&   r*   r+   r,   r-   listglobr   r'   )r=  rK  r   r   r   rM  r3   r4   r5   r6   r7   r2   r1   r   s              r9   rA  zHTestSummaryGeneration.test_generate_summary_saves_json_file.<locals>.run  sV    8yQ 5U`+O(++G4445 %{2M '')'))))))))=)))=)))'))))))))))m00:;Jz?'a'?a''''?a''''''3'''3''''''z'''z'''?'''a''''''' 55 5,   H$HHHGH$HH!H$Nr	   r   r$   rC  rA  r0   r   rA  r2   r1   s    ` @@r9   %test_generate_summary_saves_json_filez;TestSummaryGeneration.test_generate_summary_saves_json_file  sU     c(m<.@N.DTR	( 	CEr;   c                     t        t                    dj                  ddd       j                  ddd	       fd
}t        j                   |              y)u>   저장된 요약 JSON 파일이 올바른 구조를 가진다.r   iu  r   u   주제 토론Tr   r   u   질문입니다Fc                  	  K   t        dt              5 } d| _        j                         d {    d d d        dz  }t	        |j                  d            d   }t        j                  |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}}d}|d   }||v }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                   |
            d x}x}}d}|d   }||v }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                   |
            d x}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}}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 7 # 1 sw Y   xY ww)Nr:  r;  u   요약 텍스트rI  rL  r   r   r   r   rx   rz   r   r|   r}   r~   message_rangefromz%(py1)s in %(py4)sr   r   r   tosummary
key_topicsparticipants)r   r   r?  r2  rN  rO  r   r   r   r&   r'   r+   r(   r)   r*   r,   r-   )r=  rK  	json_filer   r8   r3   r   r   r   r   r6   r2   r1   r   s              r9   rA  zGTestSummaryGeneration.test_generate_summary_json_structure.<locals>.run  s    8yQ 5U`+=(++G4445 %{2M]//9:1=I::i1171CDD&;$&&&&;$&&&;&&&&&&$&&&$&&&&&&&"*?d****?d***?******d***d*******2T/2262222262222622222222222040040000040000400000000000$9$$$$9$$$9$$$$$$$$$$$$$$$$'<4''''<4'''<''''''4'''4'''''''!)>T))))>T)))>))))))T)))T))))))) 55 5s,   SS R=S RS=S  S
SNrQ  rR  s    ` @@r9   $test_generate_summary_json_structurez:TestSummaryGeneration.test_generate_summary_json_structure  sU     c(m<odK!24EeT	*" 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u=   요약 생성 실패 시 예외를 발생시키지 않는다.r   iv  r   	   메시지Tr   c                     K   t        dt              5 } t        d      | _        j	                         d {    d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  
   API 오류)r   r   	Exceptionside_effectr2  r=  r2   r1   s    r9   rA  zOTestSummaryGeneration.test_generate_summary_failure_does_not_raise.<locals>.run  sU     8yQ 5U`*3L*A'++G4445 5 55 5s+   A%A
AA
	AA

AANrQ  rR  s      @@r9   ,test_generate_summary_failure_does_not_raisezBTestSummaryGeneration.test_generate_summary_failure_does_not_raise  s?     c(m<k$G	5 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u4   요약 생성마다 summary_counter가 증가한다.r   iw  r   u   주제 논의Tr   c                  $  K   t        dt              5 } d| _        	j                         d {    	j                  j                  d      }	j                         d {    	j                  j                  d      }d}||z   }||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	t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}}d d d        y 7 M7 # 1 sw Y   y xY ww)Nr:  r;  u   요약1r   r   r   z%(py0)s == (%(py2)s + %(py4)s)count2count1r   r   r   )r   r   r?  r2  _summary_counterr)  r&   r'   r(   r)   r*   r+   r,   r-   )
r=  rk  rj  r   r4   r   r   r   r2   r1   s
           r9   rA  zBTestSummaryGeneration.test_summary_counter_increments.<locals>.run&  s    8yQ ,U`+4(++G444--11'1=++G444--11'1=*++!+v++++v++++++v+++v++++++++++++!+++++++, ,44	, ,s?   FFE>4F&F'DF5	F>FFF	FNrQ  rR  s      @@r9   test_summary_counter_incrementsz5TestSummaryGeneration.test_summary_counter_increments  sA     c(m< 	odK	, 	CEr;   N)r<   r=   r>   r?   r0  r7  rE  rS  r^  rf  rm  r@   r;   r9   r#  r#    s(    &7
'$(6r;   r#  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestFormatContextWithSummariesu&   요약 포함 format_context 테스트c                 H   t        t        |            }d}|dz  }|j                  d       t        j                         j                  d      }|| dz  }t        j                         j                         dd	d
dddgddgd}|j                  t        j                  |d      d       |j                  |ddd       |j                  |d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 )!uC   요약이 있을 때 [이전 대화 요약] 섹션이 포함된다.r   iY  rI  Tparentsr   	_001.jsonr   r   rW  rY  u$   AI와 코드 품질에 대한 논의AIu   코드 품질r   r   r   rV  rZ  r[  r\  Fensure_asciir   r      최근 메시지r   rv      현재 질문   [이전 대화 요약]rx   rz   r{   r|   r}   r~   Nr	   r   mkdirr   r   r   	isoformat
write_textr   dumpsr$   r   r&   r'   r+   r(   r)   r*   r,   r-   r0   r   r1   r2   rK  r   summary_filesummary_datar{   r8   r3   r   r   s                r9   ,test_format_context_includes_summary_sectionzKTestFormatContextWithSummaries.test_format_context_includes_summary_section5  s    c(m< !;.D)''
3$%	'::!113&'r2=1(+6
 	

<e LW^_.@N##G->P'1'61111'6111'1111116111611111115?5????5???5????????????????r;   c                    t        t        |            }d}|j                  |ddd       |j                  |d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=   요약이 없을 때 [이전 대화 요약] 섹션이 없다.r   iZ  r   r`  Tr   rv   r   r{  r   r   r{   r|   r}   r~   N)r	   r   r$   r   r&   r'   r+   r(   r)   r*   r,   r-   )	r0   r   r1   r2   r{   r8   r3   r   r   s	            r9   8test_format_context_no_summary_section_when_no_summarieszWTestFormatContextWithSummaries.test_format_context_no_summary_section_when_no_summariesN  s     c(m<k$G##G->I'5'v5555'v555'555555v555v5555555r;   c                    t        t        |            }d}|dz  }|j                  d       t        j                         j                  d      }|| dz  }t        j                         j                         dd	d
ddgdgd}|j                  t        j                  |d      d       |j                  |ddd       |j                  |d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<   요약이 있을 때 [최근 대화] 섹션도 포함된다.r   i[  rI  Trq  r   rs  r   r   rt  u   테스트 요약	   테스트r   rv  Frw  r   r   r   ry  r   rv   rz  u   [최근 대화]rx   rz   r{   r|   r}   r~   Nr|  r  s                r9   /test_format_context_recent_conversation_sectionzNTestFormatContextWithSummaries.test_format_context_recent_conversation_sectionX  s-    c(m< ;.D)''
3$%	'::!113&'r2)&-./
 	

<e LW^_.@N##G->P * F**** F*** ******F***F*******r;   c                    t        t        |            }|j                         }t        |t              }|sd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}y)u2   get_recent_summaries가 리스트를 반환한다.r   5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r  r{   rN  r   r   r   r   N)r	   r   get_recent_summariesr  rN  r(   r)   r&   r*   r+   r,   r-   )r0   r   r1   r{   r   r   s         r9   &test_get_recent_summaries_returns_listzETestFormatContextWithSummaries.test_get_recent_summaries_returns_listo  s     c(m<))+&$''''''''z'''z''''''&'''&''''''$'''$''''''''''r;   c                    t        t        |            }|dz  }|j                  d       t        j                         j                  d      }t        dd      D ]u  }|| d|d	d
z  }t        j                         j                         |dz
  dz  dz   |dz  dd| d| gdgd}|j                  t        j                  |d      d       w |j                  d      }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"                  |      rt        j$                  |      ndt        j$                  |	      t        j$                  |
      dz  }dd|iz  }t'        t        j(                  |            dx}	x}}
y)u=   get_recent_summaries가 오늘 요약 파일들을 읽는다.r   rI  Trq  r   r   ro   _03d.jsonr   rt     요약    주제r   rv  Frw  r   r   rh   rI   r   r   r   r   r   r   Nr	   r   r}  r   r   r   rN   r~  r  r   r  r  r   r&   r'   r(   r)   r*   r+   r,   r-   r0   r   r1   rK  r   rO   r  r  rI  r3   r4   r5   r6   r7   s                 r9   +test_get_recent_summaries_reads_today_fileszJTestFormatContextWithSummaries.test_get_recent_summaries_reads_today_filesv  s    c(m< ;.D)''
3q! 		dA(eWAaWE+BBL%\\^557+,q5B,*:!b&!I$QC=!'s|n!2 3L ##DJJ|%$P[b#c		d ,,1,5	9~""~""""~""""""s"""s""""""9"""9"""~""""""""""r;   c                    t        t        |            }|dz  }|j                  d       t        j                         j                  d      }t        dd      D ]d  }|| d|d	d
z  }t        j                         j                         dddd| g g d}|j                  t        j                  |d      d       f |j                  d      }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"                  |      rt        j$                  |      ndt        j$                  |	      t        j$                  |
      dz  }dd|iz  }t'        t        j(                  |            dx}	x}}
y)u;   get_recent_summaries의 limit 파라미터가 동작한다.r   rI  Trq  r   r   r   r  r  r  r   rt  r  rv  Frw  r   r   r   rI   r   r   r   r   r   r   Nr  r  s                 r9   test_get_recent_summaries_limitz>TestFormatContextWithSummaries.test_get_recent_summaries_limit  sc    c(m< ;.D)''
3q! 		dA(eWAaWE+BBL%\\^557*+2!6$QC=  "L ##DJJ|%$P[b#c		d ,,1,5	9~""~""""~""""""s"""s""""""9"""9"""~""""""""""r;   N)
r<   r=   r>   r?   r  r  r  r  r  r  r@   r;   r9   ro  ro  2  s$    0@26+.(#,#r;   ro  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestGenerateInsightu   insight 생성 mock 테스트c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u.   generate_insight가 문자열을 반환한다.r   iA  r   u   오늘 논의 내용Fr   c                     K   t        dt              5 } d| _        
j                  	       d {   }t	        |t
              }|sd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  }|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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}d d d        y 7 ># 1 sw Y   y xY ww)Nr:  r;  u4   핵심 논점: ...
결론: ...
액션 아이템: ...r  r  r{   r   r  r   r   r   r   r   r   r   )r   r   r?  generate_insightr  r   r(   r)   r&   r*   r+   r,   r-   r   r'   )r=  r{   r   r   r3   r4   r5   r6   r7   r2   r1   s            r9   rA  zETestGenerateInsight.test_generate_insight_returns_string.<locals>.run  s`    8yQ 'U`+c("33G<<!&#........z...z......&...&......#...#..........6{&Q&{Q&&&{Q&&&&&&s&&&s&&&&&&6&&&6&&&{&&&Q&&&&&&&	' '<' 's-   I>I2I/H4I2&	I>/I22I;7I>NrQ  rR  s      @@r9   $test_generate_insight_returns_stringz8TestGenerateInsight.test_generate_insight_returns_string  sB     c(m<!24JSXY	' 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	uI   generate_insight가 insights/ 디렉토리에 .md 파일을 저장한다.r   iB  r   u   주요 논의Tr   c                  L  K   t        dt              5 } d| _        j                         d {    d d d        dz  }|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}}t        |j                  d            }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}x}}y 7 # 1 sw Y   xY ww)Nr:  r;  u   인사이트 내용insightsrJ  insights_dirr   *.mdr   r   r   r   md_filesr   r   r   )r   r   r?  r  r   r(   r)   r&   r*   r+   r,   r-   rN  rO  r   r'   )r=  r  r   r   r   r  r3   r4   r5   r6   r7   r2   r1   r   s              r9   rA  zDTestGenerateInsight.test_generate_insight_saves_md_file.<locals>.run  sV    8yQ 4U`+@(**73334 $j0L&&(&((((((((<(((<(((&((((((((((L--f56Hx=%A%=A%%%%=A%%%%%%3%%%3%%%%%%x%%%x%%%=%%%A%%%%%%% 44 4rP  NrQ  rR  s    ` @@r9   #test_generate_insight_saves_md_filez7TestGenerateInsight.test_generate_insight_saves_md_file  s?     c(m<odK	& 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	uE   generate_insight 파일명이 YYYY-MM-DD_insight_NNN.md 형식이다.r   iC  r   u   코드 리뷰Tr   c                    K   t        dt              5 } d| _        j                         d {    d d d        	dz  }t	        |j                  d            }t        j                         j                  d      fd|D        }t        |      }|sdd	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      d
z  }t        t        j                   |            d x}}y 7 # 1 sw Y   xY ww)Nr:  r;  u   인사이트r  r  r   c              3   Z   K   | ]"  }|j                   j                   d        $ yw)	_insight_N)name
startswith).0fr   s     r9   	<genexpr>z\TestGenerateInsight.test_generate_insight_md_filename_format.<locals>.run.<locals>.<genexpr>  s'     P!qvv((E7))<=Ps   (+,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr   )r   r   r?  r  rN  rO  r   r   r   r  r(   r)   r&   r*   r+   r,   r-   )
r=  r  r  r   r   r   r   r2   r1   r   s
         @r9   rA  zITestGenerateInsight.test_generate_insight_md_filename_format.<locals>.run  s     8yQ 4U`+9(**73334 $j0LL--f56HLLN++J7EPxPP3PPPPPPPPP3PPP3PPPPPPPPPPPPPP 44 4s,   ED6D3D6C<E3D66E ;ENrQ  rR  s    ` @@r9   (test_generate_insight_md_filename_formatz<TestGenerateInsight.test_generate_insight_md_filename_format  s@     c(m<odK	Q 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uc   generate_insight가 call_claude를 핵심 논점/결론/액션 아이템 요청으로 호출한다.r   iD  r      논의 내용Fr   c                  Z  K   t        dt              5 } d| _        j                         d {    | j                  }|syddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }| j                  d   d   j                         }fdd	D        }t        |      }|sd
dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            d x}}d d d        y 7 j# 1 sw Y   y xY ww)Nr:  r;  u   핵심 논점과 결론r3  r=  r  r   c              3   &   K   | ]  }|v  
 y wNr@   )r  keywordr>  s     r9   r  zoTestGenerateInsight.test_generate_insight_calls_call_claude_with_summary_prompt.<locals>.run.<locals>.<genexpr>  s     yG7i/ys   )u   핵심 논점u   결론u   액션 아이템   요약r  r  r   )r   r   r?  r  r6  r(   r)   r&   r*   r+   r,   r-   r>  lowerr  )	r=  r   r  prompt_lowerr   r   r>  r2   r1   s	         @r9   rA  z\TestGenerateInsight.test_generate_insight_calls_call_claude_with_summary_prompt.<locals>.run  s    8yQ zU`+D(**7333"))))))))){))){))))))))))'11!4Q7	(0y?xyysyyyyyyyyysyyysyyyyyyyyyyyyyyz z3z zs-   F+FFE F	F+FF($F+NrQ  rR  s      @@r9   ;test_generate_insight_calls_call_claude_with_summary_promptzOTestGenerateInsight.test_generate_insight_calls_call_claude_with_summary_prompt  sA     c(m<!2OER	z 	CEr;   N)r<   r=   r>   r?   r  r  r  r  r@   r;   r9   r  r    s    ' &&r;   r  c                   "    e Zd ZdZd Zd Zd Zy)TestGracefulDegradationu7   파일 저장 실패 시 graceful degradation 테스트c                     t        t        |            }d}t        dt        d            5  |j	                  |ddd	       d
d
d
       y
# 1 sw Y   y
xY w)u:   파일 저장 실패 시 예외가 발생하지 않는다.r   i)#  builtins.open   디스크 꽉 참rd  r   u   파일 실패 테스트Fr   N)r	   r   r   OSErrorr$   )r0   r   r1   r2   s       r9   &test_file_write_failure_does_not_raisez>TestGracefulDegradation.test_file_write_failure_does_not_raise  sU     c(m<?8K0LM 	aOOG%68QZ_O`	a 	a 	as   AAc                 :   t        t        |            }d}t        dt        d            5  |j	                  |ddd	       d
d
d
       |j                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}|d   }
|
j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}
x}x}}y
# 1 sw Y   xY w)u9   파일 저장 실패 시 인메모리에는 저장된다.r   i*#  r  r  r  r   u   메모리 저장 확인Fr   Nr   r   r   r   r   r   r   r   r   r!   r    )r	   r   r   r  r$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   r1   r2   r   r3   r4   r5   r6   r7   r8   s              r9   .test_file_write_failure_still_stores_in_memoryzFTestGracefulDegradation.test_file_write_failure_still_stores_in_memory  s\    c(m<?8K0LM 	aOOG%68QZ_O`	a ??7+8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{<{<#<<#<<<<<#<<<<{<<<<<<#<<<<<<<<	a 	as   HHc                    t        j                         j                  d      }|| dz  }t        j                  ddt        j                         j                         dddd	      }d
}|j                  | d| dd       t        t        |            }|j                  d       |j                  d      }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                   |      rt        j"                  |      ndt        j"                  |      t        j"                  |	      dz  }dd|iz  }t%        t        j&                  |            dx}x}
}	|d   }|j(                  }d}	||	k(  }
|
st        j                  d|
fd||	f      t        j"                  |      t        j"                  |      t        j"                  |	      dz  }dd|iz  }t%        t        j&                  |            dx}x}x}
}	y)u0   load_today 시 손상된 라인은 건너뛴다.r   r   r   u   정상 메시지Ti+#  )r.   r/   r   r   r2   Frw  u   { 손상된 JSON }
r   r   r   r   r   r   r   r   r   r   r   Nr   r!   r    )r   r   r   r   r  r~  r  r	   r   r  r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   r   r   
valid_linecorrupt_liner1   r   r3   r4   r5   r6   r7   r8   s                 r9   $test_load_today_corrupt_line_skippedz<TestGracefulDegradation.test_load_today_corrupt_line_skipped  s   ''
35' 00
 ZZ%*%\\^557 	

 ,B|nB?'R c(m<t??4(8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{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5r;   N)r<   r=   r>   r?   r  r  r  r@   r;   r9   r  r    s    Aa
=6r;   r  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestInactivityTimeru1   30분 무활동 자동 요약 트리거 테스트c                 B   t        t        |            }d}|j                  |d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
t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}}|j                  |   }t        |t              }|sd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t	        j                  |      dz  }	t        t	        j                  |	            dx}}y)u6   add_message 호출 시 _last_activity가 갱신된다.r   i'  r   r  Fr   rx   )z6%(py0)s in %(py4)s
{%(py4)s = %(py2)s._last_activity
}r2   r1   r   r   r   Nz5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}r  r   r   r   r   r~   )r	   r   r$   _last_activityr&   r'   r(   r)   r*   r+   r,   r-   r  r   )
r0   r   r1   r2   r   r   r   r6   r5   r   s
             r9   )test_last_activity_updated_on_add_messagez=TestInactivityTimer.test_last_activity_updated_on_add_message&  s1    c(m<!2KN,,,w,,,,,w,,,,,,,w,,,w,,,,,,#,,,#,,,,,,,,,,,,,W5@z5x@@@@@@@@z@@@z@@@5@@@@@@x@@@x@@@@@@@@@@r;   c                 p   t               }d}t        ||      }|sd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        t	        j                  |            dx}}|j                  }t        |t              }|sd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                  |      d	t        j                         v st	        j
                  t              rt	        j                  t              nd	t	        j                  |      d
z  }t        t	        j                  |            dx}}y)u2   _inactivity_tasks 딕셔너리가 초기화된다._inactivity_tasks5assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr1   r   r   r   r~   Nz\assert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s._inactivity_tasks
}, %(py4)s)
}r  dictr   )r	   r  r(   r)   r&   r*   r+   r,   r-   r  r  r  r0   r1   r3   r5   r   r4   r6   s          r9   !test_inactivity_tasks_dict_existsz5TestInactivityTimer.test_inactivity_tasks_dict_exists.  s%    "/0ws/00000000w000w000000s000s000/0000000000//6z/66666666z666z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666r;   c                     t        t        |            dt        d      D ]  }j                  dd| d        fd}t	        j
                   |              y	)
u(   무활동 시 요약이 트리거된다.r   i'  r   r   rH   Fr   c                    K   t        j                  d      5 } t        dt              5  j                         d {    | j	                         d d d        d d d        y 7 &# 1 sw Y   xY w# 1 sw Y   y xY wwN_schedule_summary!conversation_memory.asyncio.sleepr;  )r   r5  r   _check_inactivityassert_called_once_withr4  r2   r1   s    r9   rA  z\TestInactivityTimer.test_check_inactivity_triggers_summary_when_no_new_messages.<locals>.run<  s     c#67 B<>YW B//888 88ABB B 9B BB BsE   BA6A*A(A*A6	B(A**A3	/A66A?;BNrB  rD  s       @@r9   ;test_check_inactivity_triggers_summary_when_no_new_messageszOTestInactivityTimer.test_check_inactivity_triggers_summary_when_no_new_messages4  s^     c(m<q 	XAOOG%6*QC8HQVOW	X	B 	CEr;   c                     t        t        |            dt        d      D ]  }j                  dd| d        fd}t	        j
                   |              y	)
u?   새 메시지가 있으면 요약을 트리거하지 않는다.r   i'  r   r   rH   Fr   c                  
  K   t        j                  d      5 } fd}t        d|      5  j                         d {    | j                          d d d        d d d        y 7 %# 1 sw Y   xY w# 1 sw Y   y xY ww)Nr  c                 P   K   t        j                         j                  <   y wr  )r   r   r  )secondsr2   r1   s    r9   
fake_sleepziTestInactivityTimer.test_check_inactivity_skips_when_new_message_arrived.<locals>.run.<locals>.fake_sleepO  s     2:,,.C&&w/s   #&r  r  )r   r5  r  assert_not_called)r4  r  r2   r1   s     r9   rA  zUTestInactivityTimer.test_check_inactivity_skips_when_new_message_arrived.<locals>.runL  s     c#67 5<A >JW 5//888 22455 5 95 55 5sE   BA7A+A)A+A7 	B)A++A4	0A77B <BNrB  rD  s       @@r9   4test_check_inactivity_skips_when_new_message_arrivedzHTestInactivityTimer.test_check_inactivity_skips_when_new_message_arrivedE  s]     c(m<q 	XAOOG%6*QC8HQVOW	X		5 	CEr;   c                     t        t        |            dj                  ddd       j                  ddd	       fd
}t        j                   |              y)uC   메시지가 5개 미만이면 요약을 트리거하지 않는다.r   i'  r   rp   Fr   r   rq   Tc                    K   t        j                  d      5 } t        dt              5  j                         d {    | j	                          d d d        d d d        y 7 %# 1 sw Y   xY w# 1 sw Y   y xY wwr  )r   r5  r   r  r  r  s    r9   rA  zNTestInactivityTimer.test_check_inactivity_skips_when_few_messages.<locals>.run`  su     c#67 5<>YW 5//888 22455 585 55 5sE   BA5A)A'A)A5	B'A))A2	.A55A>:BNrQ  rR  s      @@r9   -test_check_inactivity_skips_when_few_messageszATestInactivityTimer.test_check_inactivity_skips_when_few_messagesY  sT     c(m<!2HUKeDA	5 	CEr;   c                     t        t        |            }d}t               }d|j                  _        ||j
                  |<   |j                  |ddd       |j                  j                          y)u9   새 메시지가 오면 기존 타이머가 취소된다.r   i'  Fr   u   새 메시지r   N)	r	   r   r   doner?  r  r$   cancelassert_called_once)r0   r   r1   r2   	mock_tasks        r9   ,test_existing_timer_cancelled_on_new_messagez@TestInactivityTimer.test_existing_timer_cancelled_on_new_messageh  sa     c(m< F	&+	#)2g&!2OER++-r;   N)
r<   r=   r>   r?   r  r  r  r  r  r  r@   r;   r9   r  r  #  s$    ;A7"(.r;   r  c                   "    e Zd ZdZd Zd Zd Zy)TestKeyTopicsExtractionu"   key_topics 자동 추출 테스트c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u@   call_claude가 JSON 응답을 주면 key_topics가 채워진다.r   i*  r   rG  Tr   c                    K   t        j                  dg ddd      } t        dt              5 }| |_        j                         d {    d d d        dz  }t        |j                  d	            }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}t        j"                  |d   j%                  d            }	|	d   }
g d}|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}
x}}y 7 # 1 sw Y   xY ww)Nu!   AI 기술 동향에 대한 논의)ru  u   기술u   동향rZ  r[  Frw  r:  r;  rI  rL  r   r   r   r   rM  r   r   r   r   r   r   r[  r   r   r   r   )r   r  r   r   r?  r2  rN  rO  r   r&   r'   r(   r)   r*   r+   r,   r-   r   r   )json_responser=  rK  rM  r3   r4   r5   r6   r7   r   r8   r   r   r2   r1   r   s                r9   rA  zQTestKeyTopicsExtraction.test_key_topics_populated_from_json_response.<locals>.run  s     JJB"< #M 8yQ 5U`+8(++G4445 %{2Mm00:;Jz?'a'?a''''?a''''''3'''3''''''z'''z'''?'''a'''''''::jm55w5GHD%C)CC%)CCCCC%)CCCC%CCC)CCCCCCCC 55 5s/   .IH7H4H7G"I4H77I<INrQ  rR  s    ` @@r9   ,test_key_topics_populated_from_json_responsezDTestKeyTopicsExtraction.test_key_topics_populated_from_json_responsey  sA     c(m<.@N	D& 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	uI   call_claude가 일반 텍스트를 주면 key_topics는 빈 배열이다.r   i*  r   u   일반 대화Tr   c                  0  K   t        dt              5 } d| _        
j                  	       d {    d d d        dz  }t	        |j                  d            }t        j                  |d   j                  d            }|d	   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|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}}y 7 U# 1 sw Y   UxY ww)Nr:  r;  u    그냥 요약 텍스트입니다rI  rL  r   r   r   r[  r   r   r   r   r   rZ  r   r   r?  r2  rN  rO  r   r   r   r&   r'   r+   r,   r-   r=  rK  rM  r   r8   r   r3   r   r6   r2   r1   r   s            r9   rA  zOTestKeyTopicsExtraction.test_key_topics_empty_on_non_json_response.<locals>.run  s0    8yQ 5U`+M(++G4445 %{2Mm00:;J::jm55w5GHD%++%++++%+++%++++++++++	?H&HH?&HHHHH?&HHHH?HHH&HHHHHHHH 55 5s,   FF	FF	EFF		FFNrQ  rR  s    ` @@r9   *test_key_topics_empty_on_non_json_responsezBTestKeyTopicsExtraction.test_key_topics_empty_on_non_json_response  s@     c(m<odK		I 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uA   _generate_summary의 프롬프트가 JSON 형식을 요청한다.r   i*  r   u   토론 내용Fr   c                    K   t        dt              5 } d| _        j                         d {    | j                  d   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}}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}}d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  u/   {"summary": "요약", "key_topics": ["주제"]}r   r[  u	   키워드rx   z%(py3)s in %(py5)sr>  r   r~   %(py7)sr   z%(py10)s in %(py12)sr^   r&  %(py14)sr'  r   assert %(py17)spy17JSONr   )r   r   r?  r2  r>  r&   r'   r+   r(   r)   r*   append_format_boolopr,   r-   r=  r>  r   r3   r5   r8   @py_assert9r,  r   r   r.  r/  @py_format16@py_format18r2   r1   s                 r9   rA  zOTestKeyTopicsExtraction.test_generate_summary_prompt_requests_json.<locals>.run  s    8yQ BU`+\(++G444'11!4Q7	L|L|y0LKLK94LLLLL|yLLL|LLLLLLyLLLyLLLLLLLK9LLLKLLLLLL9LLL9LLLLLLLLLLLLLLAvAv*AfAf	.AAAAAvAAAvAAAAAAAAAAAAAAAAf	AAAfAAAAAA	AAA	AAAAAAAAAAAAAAB B4B Bs-   MM
MLM
>	MM

MMNrQ  rR  s      @@r9   *test_generate_summary_prompt_requests_jsonzBTestKeyTopicsExtraction.test_generate_summary_prompt_requests_json  sA     c(m<!2OER	B 	CEr;   N)r<   r=   r>   r?   r  r  r  r@   r;   r9   r  r  v  s    ,6&r;   r  c                       e Zd ZdZd Zy)TestInsightDMSendingu   insight DM 전송 테스트c                      y)u`   cleanup 시 _send_insight_to_owner가 호출되는지 확인은 main_bot 통합 테스트에서.Nr@   )r0   r   s     r9   ,test_send_insight_to_owner_called_on_cleanupzATestInsightDMSending.test_send_insight_to_owner_called_on_cleanup  s     	r;   N)r<   r=   r>   r?   r  r@   r;   r9   r  r    s
    %r;   r  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestInsightEventu)   insight 이벤트 파일 생성 테스트c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u;   generate_insight 후 _create_insight_event가 호출된다.r   i.  r   r  Fr   c                    K   t        dt              5 } d| _        t        j                  d      5 }j	                         d {    |j                          d d d        d d d        y 7 %# 1 sw Y   xY w# 1 sw Y   y xY ww)Nr:  r;     인사이트 결과_create_insight_event)r   r   r?  r5  r  r  )r=  
mock_eventr2   r1   s     r9   rA  zFTestInsightEvent.test_generate_insight_calls_create_event.<locals>.run  s     8yQ 4U`+@(\\#'>? 4:..w77711344 4 84 44 4sE   BA<A0A.	A0A<%	B.A00A9	5A<<BBNrQ  rR  s      @@r9   (test_generate_insight_calls_create_eventz9TestInsightEvent.test_generate_insight_calls_create_event  s@     c(m<!2OER	4 	CEr;   c                    t        t        |            }|dz  }|j                  |       t        |j	                  d            }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                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }	t        t        j                  |	            dx}x}}y)u:   _create_insight_event가 이벤트 파일을 생성한다.r   events
events_dirzgroupchat-insight-*.eventr   r   r   r   event_filesr   r   r   N)r	   r   r  rN  rO  r   r&   r'   r(   r)   r*   r+   r,   r-   )
r0   r   r1   r   r!  r3   r4   r5   r6   r7   s
             r9   &test_create_insight_event_creates_filez7TestInsightEvent.test_create_insight_event_creates_file  s     c(m<(
!!Z!8:??+FGH;$1$1$$$$1$$$$$$s$$$s$$$$$$;$$$;$$$$$$1$$$$$$$r;   c                 t   t        t        |            }|dz  }|j                  |       t        |j	                  d            }t        j                  |d   j                  d            }|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}||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)u7   이벤트 파일이 올바른 JSON 구조를 가진다.r   r  r  z*.eventr   r   r   typezgroupchat-insightr   r   r   r   r   Nr   rx   rz   r   r|   r}   r~   r  )r	   r   r  rN  rO  r   r   r   r&   r'   r+   r,   r-   r(   r)   r*   )r0   r   r1   r   r!  r   r8   r   r3   r   r6   r   r   s                r9   (test_create_insight_event_json_structurez9TestInsightEvent.test_create_insight_event_json_structure  sb    c(m<(
!!Z!8:??956zz+a.22G2DEF|222|22222|2222|22222222222"{d""""{d"""{""""""d"""d"""""""%~%%%%~%%%~%%%%%%%%%%%%%%%%r;   c                 d    t        t        |            }|j                  t        d             y)uD   이벤트 파일 생성 실패 시 예외가 발생하지 않는다.r   z/nonexistent/readonly/pathr  N)r	   r   r  r   )r0   r   r1   s      r9   *test_create_insight_event_failure_no_raisez;TestInsightEvent.test_create_insight_event_failure_no_raise  s'     c(m<!!T2N-O!Pr;   N)r<   r=   r>   r?   r  r"  r%  r'  r@   r;   r9   r  r    s    3%&Qr;   r  c                       e Zd ZdZd Zy)TestSummaryCounterRecoveryud   _summary_counter 복구 테스트 - 재시작 시 기존 파일 수를 확인해 번호 이어받기c                    t        t        |            d|dz  j                  d       t        j                         j                  d      t        dd      D ]:  } d	|d
dz  }|j                  t        j                  d| g d      d       < j                  ddd       fd}t        j                   |              y)uX   기존 요약 파일 3개 존재 시, 새 요약이 004번으로 생성되는지 확인.r   i!N  rI  Trq  r   r   ro   	_general_r  r  r  r  r   r   r   u
   새 논의r   c            
        K   t        dt              5 } t        j                  ddgdg g ddd	      | _        j                         d {    d d d        t        j                   d
            }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}|d   }d}|j"                  }||v }|st        j                  d|fd||f      t        j                  |      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}x}}y 7 # 1 sw Y   xY ww)Nr:  r;  u
   새 요약r  generalexploratoryrZ  r[  	topic_tagkey_decisionsaction_itemsconsensus_levelFrw  z_*.jsonro   r   r   r   rM  r   r   r   rL   _004rx   )z,%(py1)s in %(py5)s
{%(py5)s = %(py3)s.name
}new_filer   r   r~   r   r   )r   r   r   r  r?  r2  sortedrO  r   r&   r'   r(   r)   r*   r+   r,   r-   r  )r=  rM  r3   r4   r5   r6   r7   r5  r8   r   r   r2   r1   rK  r   s              r9   rA  zQTestSummaryCounterRecovery.test_counter_recovery_from_existing_files.<locals>.run  s    8yQ 5U`+/::#/'/j%.)+(*+8 "'
,( ++G4445   2 2eWG3D EFJz?'a'?a''''?a''''''3'''3''''''z'''z'''?'''a'''''''!"~H*X]]*6]****6]***6******X***X***]******* 55 5s/   I%9IIIHI%II"I%N)r	   r   r}  r   r   r   rN   r  r   r  r$   rC  rA  )	r0   r   rO   fprA  r2   r1   rK  r   s	        @@@@r9   )test_counter_recovery_from_existing_fileszDTestSummaryCounterRecovery.test_counter_recovery_from_existing_files  s     c(m< !;.D)''
3q! 	AE7)Ac7%!@@BMM

s|2FG   	 	l4H	+, 	CEr;   N)r<   r=   r>   r?   r9  r@   r;   r9   r)  r)    s
    n)r;   r)  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestPIIMaskinguJ   PII 마스킹 테스트 - JSONL에는 마스킹, 인메모리에는 원본c                    t        t        |            }d}d}|j                  |d|d       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }d}	|d   }
|	|
v }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
d}	|d   }
|	|
v}|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)uN   전화번호(010-1234-5678 → [전화번호])가 JSONL에서 마스킹된다.r   i	R  u%   제 번호는 010-1234-5678입니다.r   Fr   r   r   r   r   u   [전화번호]r/   rx   rX  r   r   r   Nz010-1234-5678r   z%(py1)s not in %(py4)sr	   r   r$   r   r   r   r   r   r   r   r&   r'   r+   r,   r-   r0   r   r1   r2   original_textr   r   r  r   r8   r   r3   r   r6   s                 r9   !test_phone_number_masked_in_jsonlz0TestPIIMasking.test_phone_number_masked_in_jsonl)  s    c(m<?!2M%P''
35' 00
##W#5;;=zz$/4</<////<//////<///////2d6l2l2222l222222l2222222r;   c                    t        t        |            }d}d}|j                  |d|d       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }d}	|d   }
|	|
v }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
d}	|d   }
|	|
v}|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)uO   주민번호(900101-1234567 → [주민번호])가 JSONL에서 마스킹된다.r   i
R  u(   주민번호는 900101-1234567입니다.r   Fr   r   r   r   r   u   [주민번호]r/   rx   rX  r   r   r   Nz900101-1234567r   r=  r>  r?  s                 r9   $test_resident_number_masked_in_jsonlz3TestPIIMasking.test_resident_number_masked_in_jsonl9  "    c(m<B!2M%P''
35' 00
##W#5;;=zz$/4</<////<//////<///////3tF|3|3333|333333|3333333r;   c                    t        t        |            }d}d}|j                  |d|d       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }d}	|d   }
|	|
v }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
d}	|d   }
|	|
v}|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)uO   계좌번호(110-123-456789 → [계좌번호])가 JSONL에서 마스킹된다.r   iR  u(   계좌번호는 110-123-456789입니다.r   Fr   r   r   r   r   u   [계좌번호]r/   rx   rX  r   r   r   Nz110-123-456789r   r=  r>  r?  s                 r9   #test_account_number_masked_in_jsonlz2TestPIIMasking.test_account_number_masked_in_jsonlI  rD  r;   c                 z   t        t        |            }d}d}|j                  |d|d       |j                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }|j                  }||k(  }|st        j                  d|fd||f      t        j                  |      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}x}}d}|d   }|j                  }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}y)u:   인메모리 deque에는 원본 텍스트가 저장된다.r   iR  u(   연락처 010-9999-8888로 전화해줘.r   Fr   r   r   r   r   r   r   r   r   Nr   )z,%(py3)s
{%(py3)s = %(py1)s.text
} == %(py5)sr@  r6  r   r   z010-9999-8888rx   )z,%(py1)s in %(py6)s
{%(py6)s = %(py4)s.text
}r   r   r   )r	   r   r$   r%   r   r&   r'   r(   r)   r*   r+   r,   r-   r/   )r0   r   r1   r2   r@  r   r3   r4   r5   r6   r7   r8   r   r   r   s                  r9   !test_original_preserved_in_memoryz0TestPIIMasking.test_original_preserved_in_memoryY  s    c(m<B!2M%P??7+8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{0{0=0000=000{000000000=000=00000002(1+2+"2"22"22222"2222222+222"22222222r;   N)r<   r=   r>   r?   rA  rC  rF  rI  r@   r;   r9   r;  r;  &  s    T3 4 4 3r;   r;  c                   "    e Zd ZdZd Zd Zd Zy)TestTopicTagu   topic_tag 필드 포함 확인c                 f   t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }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)u4   JSONL 레코드에 topic_tag 필드가 포함된다.r   iU  r   u   오늘 날씨가 좋네요Tr   r   r   r   r   r0  rx   rz   r   r|   r}   r~   N)r	   r   r$   r   r   r   r   r   r   r   r&   r'   r+   r(   r)   r*   r,   r-   )r0   r   r1   r2   r   r   r  r   r8   r3   r   r   s               r9   test_jsonl_record_has_topic_tagz,TestTopicTag.test_jsonl_record_has_topic_tagk  s     c(m<.JSWX''
35' 00
##W#5;;=zz$"{d""""{d"""{""""""d"""d"""""""r;   c                    t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|j                  d	
      j                         }t        j                  |      }|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}
}	y)u*   topic_tag의 기본값이 'general'이다.r   iU  r   u   일반 메시지Tr   r   r   r   r   r0  r-  r   r   r   r   r   Nr>  )r0   r   r1   r2   r   r   r  r   r8   r   r3   r   r6   s                r9   'test_topic_tag_default_value_is_generalz4TestTopicTag.test_topic_tag_default_value_is_generaly  s     c(m<.@N''
35' 00
##W#5;;=zz$K -I- I---- I--- ---I-------r;   c                 p   t               }d}t        ||      }|sd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        t	        j                  |            dx}}|j                  }t        |t              }|sd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                  |      d	t        j                         v st	        j
                  t              rt	        j                  t              nd	t	        j                  |      d
z  }t        t	        j                  |            dx}}y)u/   _current_topic 딕셔너리가 초기화된다._current_topicr  r  r1   r  NzYassert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s._current_topic
}, %(py4)s)
}r  r  r   )r	   r  r(   r)   r&   r*   r+   r,   r-   rQ  r  r  r  s          r9   )test_current_topic_initialized_to_generalz6TestTopicTag.test_current_topic_initialized_to_general  s%    ",-ws,--------w---w------s---s---,----------,,3z,d33333333z333z333333#333#333,333333d333d3333333333r;   N)r<   r=   r>   r?   rM  rO  rR  r@   r;   r9   rK  rK  h  s    (#.4r;   rK  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestTopicChangeDetectionu   주제 전환 감지 테스트c                    t        t        |            }d}|j                  |ddd       t        ddt	        j
                         d      }|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)uC   전환 키워드('다른 주제')로 주제 전환이 감지된다.r   iY  r      일반 이야기Fr      다른 주제로 넘어가죠r.   r/   r   r   Tr"   z%(py0)s is %(py3)sr{   r   r}   r~   Nr	   r   r$   r   r   r   _detect_topic_changer&   r'   r(   r)   r*   r+   r,   r-   
r0   r   r1   r2   r  r{   r3   r   r   r   s
             r9   !test_keyword_detects_topic_changez:TestTopicChangeDetection.test_keyword_detects_topic_change  s     c(m< 	!24FuU$0lln	
 ))'37v~vvvr;   c                    t        t        |            }d}|j                  |ddd       t        ddt	        j
                         d	
      }|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)uF   전환 키워드('그건 그렇고')로 주제 전환이 감지된다.r   iY  r   rV  Fr   r   u%   그건 그렇고 오늘 회의는요?TrX  r"   rY  r{   r   r}   r~   NrZ  r\  s
             r9   )test_keyword_geugeon_detects_topic_changezBTestTopicChangeDetection.test_keyword_geugeon_detects_topic_change  s     c(m<!24FuU8lln	
 ))'37v~vvvr;   c                 D   ddl m} t        t        |            }d}t        j                          |d      z
  |j
                  |<   t        ddt        j                         d	
      }|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)u<   침묵 갭(5분 초과)으로 주제 전환이 감지된다.r   	timedeltar   iY  ih  r  r   u   새로운 이야기FrX  Tr"   rY  r{   r   r}   r~   Nr   rb  r	   r   r   r  r   r[  r&   r'   r(   r)   r*   r+   r,   r-   r0   r   rb  r1   r2   r  r{   r3   r   r   r   s              r9   %test_silence_gap_detects_topic_changez>TestTopicChangeDetection.test_silence_gap_detects_topic_change  s    & c(m< '/llny7M&M7#$&lln	
 ))'37v~vvvr;   c                 D   ddl m} t        t        |            }d}t        j                          |d      z
  |j
                  |<   t        ddt        j                         d	
      }|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)uB   일반 메시지에서는 주제 전환이 감지되지 않는다.r   ra  r   iY  r  rc  r   u   계속 이야기해요TrX  Fr"   rY  r{   r   r}   r~   Nrd  re  s              r9   #test_normal_message_no_topic_changez<TestTopicChangeDetection.test_normal_message_no_topic_change  s    & c(m< '/llny7L&L7#)lln	
 ))'37vvvvr;   c           	         t        t        |            }d}|j                  |ddd       |j                  |ddd       |j                  }|j                  } ||      }d}||k(  }|st        j                  d	|fd
||f      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}}y)uH   주제 전환 감지 시 _current_topic이 'pending'으로 변경된다.r   iY  r   rV  Fr   rW  pendingr   )zn%(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._current_topic
}.get
}(%(py5)s)
} == %(py10)sr1   r2   r   r   r   r~   r   r^   assert %(py12)sr&  N)r	   r   r$   rQ  r)  r&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   r2   r   r   r*  r  r+  rc   r.  s              r9   test_topic_change_sets_pendingz7TestTopicChangeDetection.test_topic_change_sets_pending  s    c(m<!24FuU!24S\ab!!;!%%;%g.;);.);;;;.);;;;;;s;;;s;;;!;;;%;;;;;;g;;;g;;;.;;;);;;;;;;;r;   N)	r<   r=   r>   r?   r]  r_  rf  rh  rm  r@   r;   r9   rT  rT    s    (" &&<r;   rT  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestSummaryMetadataSchemau'   요약 메타데이터 스키마 확인c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u>   저장된 요약 JSON에 key_decisions 필드가 존재한다.r   i]  r   u   결정 사항 토론Tr   c            
        K   t        dt              5 } t        j                  ddgddgdgdd	d
      | _        j                         d {    d d d        dz  }t        |j                  d            }t        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   }dg}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            d x}x}}y 7 {# 1 sw Y   {xY ww)Nr:  r;  r  r  meetingu   결정1u   액션1agreedr/  Frw  rI  rL  r   r   r   r1  rx   rz   r   r|   r}   r~   r   r   r   r   r   r   r   r   r  r?  r2  rN  rO  r   r   r&   r'   r+   r(   r)   r*   r,   r-   r=  rK  rM  r   r8   r3   r   r   r   r   r6   r2   r1   r   s              r9   rA  zETestSummaryMetadataSchema.test_summary_has_key_decisions.<locals>.run  sf    8yQ 5U`+/::#+'/j%.*3)2+3 "'
,( ++G4445 %{2Mm00:;J::jm55w5GHD"*?d****?d***?******d***d*******(7YK7(K7777(K777(777K7777777 55 5s/   G;GGGE6GGGGNrQ  rR  s    ` @@r9   test_summary_has_key_decisionsz8TestSummaryMetadataSchema.test_summary_has_key_decisions  s@     c(m<.DTR	8, 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u=   저장된 요약 JSON에 action_items 필드가 존재한다.r   i]  r   u   액션 아이템 논의Tr   c            
        K   t        dt              5 } t        j                  dg dg ddgddd	
      | _        j                         d {    d d d        dz  }t        |j                  d            }t        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   }ddg}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            d x}x}}y 7 |# 1 sw Y   |xY ww)Nr:  r;  r  actionu   할 일1u   할 일2	tentativer/  Frw  rI  rL  r   r   r   r2  rx   rz   r   r|   r}   r~   r   r   r   r   r   rt  ru  s              r9   rA  zDTestSummaryMetadataSchema.test_summary_has_action_items.<locals>.run
  sk    8yQ 5U`+/::#+&(%-)+)3Z(@+6 "'
,( ++G4445 %{2Mm00:;J::jm55w5GHD!)>T))))>T)))>))))))T)))T)))))))'CJ
+CC'+CCCCC'+CCCC'CCC+CCCCCCCC 55 5s/   G:GGGE7GGGGNrQ  rR  s    ` @@r9   test_summary_has_action_itemsz7TestSummaryMetadataSchema.test_summary_has_action_items  sB     c(m<.GPTU	D, 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u@   저장된 요약 JSON에 consensus_level 필드가 존재한다.r   i]  r   u   합의 수준 테스트Tr   c            
        K   t        dt              5 } t        j                  dg dg g ddd      | _        j                         d {    d d d        d	z  }t        |j                  d
            }t        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   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            d x}x}}y 7 z# 1 sw Y   zxY ww)Nr:  r;  r  	consensusdecidedr/  Frw  rI  rL  r   r   r   r3  rx   rz   r   r|   r}   r~   r   r   r   r   r   rt  ru  s              r9   rA  zGTestSummaryMetadataSchema.test_summary_has_consensus_level.<locals>.run(  sb    8yQ 5U`+/::#+&(%0)+(*+4 "'
,( ++G4445 %{2Mm00:;J::jm55w5GHD$,$,,,,$,,,$,,,,,,,,,,,,,,,,)*7i7*i7777*i777*777i7777777 55 5s/   G8G
GG
E5GG

GGNrQ  rR  s    ` @@r9    test_summary_has_consensus_levelz:TestSummaryMetadataSchema.test_summary_has_consensus_level"  sA     c(m<.GPTU	8, 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u.   요약 파일명에 topic_tag가 포함된다.r   i]  r   u   주제 파일명 테스트Tr   c            
      z  K   t        dt              5 } t        j                  dg dg g ddd      | _        j                  
       d {    d d d        d	z  }t        |j                  d
            }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}}d}|d   }	|	j"                  }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |	      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}x}x}	}y 7 # 1 sw Y   xY ww)Nr:  r;  r  ai_techr.  r/  Frw  rI  rL  r   r   r   r   rM  r   r   r   r   rx   )z,%(py1)s in %(py6)s
{%(py6)s = %(py4)s.name
}rH  )r   r   r   r  r?  r2  rN  rO  r   r&   r'   r(   r)   r*   r+   r,   r-   r  )r=  rK  rM  r3   r4   r5   r6   r7   r8   r   r2   r1   r   s             r9   rA  zPTestSummaryMetadataSchema.test_summary_filename_includes_topic_slug.<locals>.runF  s    8yQ 5U`+/::#+&(%.)+(*+8 "'
,( ++G4445 %{2Mm00:;Jz?'a'?a''''?a''''''3'''3''''''z'''z'''?'''a'''''''2
12 2 229 222229 22229222222 22222222 55 5s/   H;8H.H+H.GH;+H..H83H;NrQ  rR  s    ` @@r9   )test_summary_filename_includes_topic_slugzCTestSummaryMetadataSchema.test_summary_filename_includes_topic_slug@  sA     c(m<.JSWX	3* 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	u5   저장된 요약 JSON에 date 필드가 존재한다.r   i]  r   u   날짜 필드 확인Tr   c            
      J  K   t        dt              5 } t        j                  dg dg g ddd      | _        
j                  	       d {    d d d        d	z  }t        |j                  d
            }t        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}}t%        j&                         j)                  d      }|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}}y 7 # 1 sw Y   xY ww)Nr:  r;  r  r-  r.  r/  Frw  rI  rL  r   r   r   daterx   rz   r   r|   r}   r~   r   r   r   r   )r   r   r   r  r?  r2  rN  rO  r   r   r&   r'   r+   r(   r)   r*   r,   r-   r   r   r   )r=  rK  rM  r   r8   r3   r   r   r   r2   r1   r   s            r9   rA  zBTestSummaryMetadataSchema.test_summary_has_date_field.<locals>.runc  s~    8yQ 5U`+/::#+&(%.)+(*+8 "'
,( ++G4445 %{2Mm00:;J::jm55w5GHD!6T>!!!6T!!!6!!!!!!T!!!T!!!!!!!LLN++J7E<(<5((((<5(((<((((((5(((5((((((( 55 5s/   H#8HHHGH#HH H#NrQ  rR  s    ` @@r9   test_summary_has_date_fieldz5TestSummaryMetadataSchema.test_summary_has_date_field]  s@     c(m<.DTR	). 	CEr;   c                     t        t                    dj                  ddd       fd}t        j                   |              y)	uQ   JSON 파싱 실패(fallback) 시 key_decisions, action_items가 빈 배열이다.r   i]  r   u   fallback 테스트Tr   c                  0  K   t        dt              5 } d| _        
j                  	       d {    d d d        dz  }t	        |j                  d            }t        j                  |d   j                  d            }|d	   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|d   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|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}}y 7 # 1 sw Y   xY ww)Nr:  r;  u'   일반 텍스트 응답 (파싱 불가)rI  rL  r   r   r   r1  r   r   r   r   r   r2  r3  r.  r  r  s            r9   rA  zMTestSummaryMetadataSchema.test_summary_fallback_has_empty_fields.<locals>.run  s    8yQ 5U`+T(++G4445 %{2Mm00:;J::jm55w5GHD(.B.(B....(B...(...B.......'-2-'2----'2---'---2-------)*;m;*m;;;;*m;;;*;;;m;;;;;;; 55 5s,   HH	HH	GHH		HHNrQ  rR  s    ` @@r9   &test_summary_fallback_has_empty_fieldsz@TestSummaryMetadataSchema.test_summary_fallback_has_empty_fields|  s@     c(m<.B4P	< 	CEr;   N)
r<   r=   r>   r?   rv  r{  r  r  r  r  r@   r;   r9   ro  ro    s#    1<<<:>r;   ro  c                       e Zd ZdZd Zd Zy)TestXMLTagSeparationu   XML 태그 분리 확인c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uH   _generate_summary 프롬프트에 <user_content> 태그가 포함된다.r   ia  r   u   XML 태그 테스트Fr   c            
        K   t        dt              5 } t        j                  dg dg g ddd      | _        j                         d {    | j                  d	   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}}d d d        y 7 j# 1 sw Y   y xY ww)Nr:  r;  r  r-  r.  r/  Frw  r   <user_content>rx   rz   r>  r|   r}   r~   </user_content>)r   r   r   r  r?  r2  r>  r&   r'   r+   r(   r)   r*   r,   r-   r=  r>  r8   r3   r   r   r2   r1   s         r9   rA  zWTestXMLTagSeparation.test_generate_summary_prompt_has_xml_user_content_tag.<locals>.run  sD    8yQ 6U`+/::#+&(%.)+(*+8 "'
,( ++G444'11!4Q7	'4'94444'9444'444444944494444444(5(I5555(I555(555555I555I55555556 6 56 6s/   G8F:F7E F:.	G7F::G?GNrQ  rR  s      @@r9   5test_generate_summary_prompt_has_xml_user_content_tagzJTestXMLTagSeparation.test_generate_summary_prompt_has_xml_user_content_tag  sB     c(m<!24JSXY	6$ 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uG   generate_insight 프롬프트에 <user_content> 태그가 포함된다.r   ia  r   u!   인사이트 XML 태그 테스트Fr   c                  X  K   t        dt              5 } d| _        j                         d {    | j                  d   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}}d d d        y 7 j# 1 sw Y   y xY ww)Nr:  r;  r  r   r  rx   rz   r>  r|   r}   r~   r  )r   r   r?  r  r>  r&   r'   r+   r(   r)   r*   r,   r-   r  s         r9   rA  zWTestXMLTagSeparation.test_generate_insight_prompt_has_xml_user_content_tag.<locals>.run  s    8yQ 6U`+@(**7333'11!4Q7	'4'94444'9444'444444944494444444(5(I5555(I555(555555I555I55555556 636 6s-   F*FFE F	F*FF'#F*NrQ  rR  s      @@r9   5test_generate_insight_prompt_has_xml_user_content_tagzJTestXMLTagSeparation.test_generate_insight_prompt_has_xml_user_content_tag  sB     c(m<!24W`ef	6 	CEr;   N)r<   r=   r>   r?   r  r  r@   r;   r9   r  r    s    "4r;   r  c                       e Zd ZdZd Zd Zy)TestFilePermissions   파일 퍼미션 테스트c                    t        t        |            }d}|j                  |ddd       t        t	        j
                  |      j                        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}}y)u/   JSONL 디렉토리가 0o700으로 설정된다.r   ie  r   u   퍼미션 테스트Fr   N700r   r  dir_moder   r}   r~   )r	   r   r$   octosstatst_moder&   r'   r(   r)   r*   r+   r,   r-   )	r0   r   r1   r2   r  r3   r   r   r   s	            r9   'test_jsonl_directory_has_700_permissionz;TestFilePermissions.test_jsonl_directory_has_700_permission  s     c(m<!24IRWX rwwx(001"#6  x5    x5      x   x   5       r;   c                    t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|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}}t        t        j                   |      j"                        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}}
y)u)   JSONL 파일이 0o600으로 설정된다.r   ie  r   r  Fr   r   r   rJ  r   r   Nr  600r   r  	file_moder   r}   r~   )r	   r   r$   r   r   r   r   r(   r)   r&   r*   r+   r,   r-   r  r  r  r  r'   )r0   r   r1   r2   r   r   r   r   r   r  r3   r   r   s                r9   "test_jsonl_file_has_600_permissionz6TestFilePermissions.test_jsonl_file_has_600_permission  s*    c(m<!24PY^_''
35' 00
  " """"""""z"""z""" """"""""""
+334RS9	!!yE!!!!yE!!!!!!y!!!y!!!E!!!!!!!r;   N)r<   r=   r>   r?   r  r  r@   r;   r9   r  r    s    $	!"r;   r  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestSummaryLocku2   요약 중복 실행 방지 세마포어 테스트c                 p   t               }d}t        ||      }|sd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        t	        j                  |            dx}}|j                  }t        |t              }|sd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                  |      d	t        j                         v st	        j
                  t              rt	        j                  t              nd	t	        j                  |      d
z  }t        t	        j                  |            dx}}y)u=   _summary_lock 딕셔너리가 __init__에서 초기화된다._summary_lockr  r  r1   r  NzXassert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s._summary_lock
}, %(py4)s)
}r  r  r   )r	   r  r(   r)   r&   r*   r+   r,   r-   r  r  r  r  s          r9    test_summary_lock_exists_in_initz0TestSummaryLock.test_summary_lock_exists_in_init  s#    "+,wsO,,,,,,,,w,,,w,,,,,,s,,,s,,,O,,,,,,,,,,++2z+T22222222z222z222222#222#222+222222T222T2222222222r;   c                     t        t        |            dj                  ddd       g fd}t        j                   |              y)	uB   _generate_summary 실행 중 _summary_lock[chat_id]가 True이다.r   i1u  r   r   Tr   c            	      J  K   t               } fd}|| _        t        d|       5  j                         d {    d d d        d   }d}||u }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d x}x}}j                  }|j                  } |      }d
}	||	u }
|
st	        j
                  d|
fd||	f      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}
}	y 7 # 1 sw Y   xY ww)Nc           	         K   j                  j                  j                  d             t        j                  dg dg g dd      S w)NFr  r-  r.  r/  )r
  r  r)  r   r  )promptr2   lock_during_executionr1   s    r9   capture_lockzYTestSummaryLock.test_summary_lock_set_during_execution.<locals>.run.<locals>.capture_lock  sP     %,,S->->-B-B7E-RSzz#+&(%.)+(*+8	 	s   AAr:  r   Tr"   r   r   r   r   Fzm%(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._summary_lock
}.get
}(%(py5)s)
} is %(py10)sr1   r2   rk  rl  r&  )r   rd  r   r2  r&   r'   r+   r,   r-   r  r)  r(   r)   r*   )original_call_clauder  r8   r   r3   r   r6   r   r*  r  r+  rc   r.  r2   r  r1   s                r9   rA  zCTestSummaryLock.test_summary_lock_set_during_execution.<locals>.run  sp    #,;  0< ,8:NO 5++G4445 )+3t3+t3333+t333+333t3333333$$:$((:(1:U:1U::::1U::::::3:::3:::$:::(::::::::::::1:::U:::::::: 55 5s-   %H#HHHGH#HH H#NrQ  )r0   r   rA  r2   r  r1   s      @@@r9   &test_summary_lock_set_during_executionz6TestSummaryLock.test_summary_lock_set_during_execution  sF     c(m<.CDQ "	;4 	CEr;   c                     t        t        |            dj                  ddd       dj                  <   fd}t	        j
                   |              y)	u:   lock이 걸려있으면 _generate_summary가 스킵된다.r   i2u  r   r   Tr   c                     K   t        dt              5 } j                         d {    | j                          d d d        y 7 # 1 sw Y   y xY wwNr:  r;  r   r   r2  r  re  s    r9   rA  z=TestSummaryLock.test_summary_skipped_when_locked.<locals>.run  sR     8yQ 0U`++G444--/0 040 0+   AA
AA
	AA

AAN)r	   r   r$   r  rC  rA  rR  s      @@r9    test_summary_skipped_when_lockedz0TestSummaryLock.test_summary_skipped_when_locked
  sR     c(m<.CDQ &*'"	0 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u4   요약 생성 실패 시에도 lock이 해제된다.r   i3u  r   r   Tr   c            
      D  K   t        dt              5 } t        d      | _        	j	                         d {    	j
                  }|j                  } |      }d}||u }|st        j                  d|fd||f      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}}d d d        y 7 W# 1 sw Y   y xY ww)Nr:  r;  rb  Fr"   r  r1   r2   rk  rl  r&  )r   r   rc  rd  r2  r  r)  r&   r'   r(   r)   r*   r+   r,   r-   )
r=  r   r   r*  r  r+  rc   r.  r2   r1   s
           r9   rA  zBTestSummaryLock.test_summary_lock_released_on_failure.<locals>.run!  s    8yQ ?U`*3L*A'++G444((>(,,>,W5>>5>>>>5>>>>>>s>>>s>>>(>>>,>>>>>>W>>>W>>>5>>>>>>>>>>	? ?4? ?s-   F %FFEF	F FFF NrQ  rR  s      @@r9   %test_summary_lock_released_on_failurez5TestSummaryLock.test_summary_lock_released_on_failure  s@     c(m<.CDQ	? 	CEr;   N)r<   r=   r>   r?   r  r  r  r  r@   r;   r9   r  r    s    <3"H"r;   r  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestRateLimitu"   일일 LLM 호출 예산 테스트c                 p   t               }d}t        ||      }|sd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        t	        j                  |            dx}}|j                  }t        |t              }|sd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                  |      d	t        j                         v st	        j
                  t              rt	        j                  t              nd	t	        j                  |      d
z  }t        t	        j                  |            dx}}y)u0   _llm_call_count 딕셔너리가 초기화된다._llm_call_countr  r  r1   r  NzZassert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s._llm_call_count
}, %(py4)s)
}r  r  r   )r	   r  r(   r)   r&   r*   r+   r,   r-   r  r  r  r  s          r9   "test_llm_call_count_exists_in_initz0TestRateLimit.test_llm_call_count_exists_in_init.  s%    "-.ws-........w...w......s...s...-..........--4z-t44444444z444z444444#444#444-444444t444t4444444444r;   c                 |   t               }d}t        ||      }|sd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        t	        j                  |            dx}}|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                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}y)u+   _daily_llm_budget의 기본값이 50이다._daily_llm_budgetr  r  r1   r  Nr   r   )z9%(py2)s
{%(py2)s = %(py0)s._daily_llm_budget
} == %(py5)sr   r   r   )r	   r  r(   r)   r&   r*   r+   r,   r-   r  r'   )r0   r1   r3   r5   r   r   r   r   s           r9    test_daily_llm_budget_default_50z.TestRateLimit.test_daily_llm_budget_default_504  s     "/0ws/00000000w000w000000s000s000/0000000000$$**$****$******s***s***$**********r;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u2   요약 생성 시 _llm_call_count가 증가한다.r   iy  r   r  Tr   c            
      f  K   t        dt              5 } t        j                  dg dg g dd      | _        t        j                         j                  d      }
j                  j                  |d      }
j                  	       d {    
j                  j                  |d      }d	}||z   }||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t        j                   |      dz  }dd|iz  }t#        t        j$                  |            d x}x}}d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  r  r-  r.  r/  r   r   r   r   ri  afterbeforer   r   r   )r   r   r   r  r?  r   r   r   r  r)  r2  r&   r'   r(   r)   r*   r+   r,   r-   )r=  r   r  r  r   r4   r   r   r   r2   r1   s            r9   rA  zDTestRateLimit.test_llm_call_count_increments_on_summary.<locals>.run@  s9    8yQ +U`+/::#+&(%.)+(*+8	,( !//
;,,00:++G444++//q9)**
*u
****u
******u***u*******************+ + 5+ +s0   F1A5F%
F"DF%	F1"F%%F.*F1NrQ  rR  s      @@r9   )test_llm_call_count_increments_on_summaryz7TestRateLimit.test_llm_call_count_increments_on_summary:  s?     c(m<k$G	+$ 	CEr;   c                    t        t        |            d_        dj                  ddd       t	        j
                         j                  d      }dj                  |<   fd	}t        j                   |              y
)u0   예산 초과 시 요약 생성이 스킵된다.r   rj   iy  r   r  Tr   r   c                     K   t        dt              5 } j                         d {    | j                          d d d        y 7 # 1 sw Y   y xY wwr  r  re  s    r9   rA  zDTestRateLimit.test_summary_skipped_when_budget_exceeded.<locals>.run_  sR     8yQ 0U`++G444--/0 040 0r  N
r	   r   r  r$   r   r   r   r  rC  rA  r0   r   r   rA  r2   r1   s       @@r9   )test_summary_skipped_when_budget_exceededz7TestRateLimit.test_summary_skipped_when_budget_exceededT  so     c(m< !k$G ''
3%&E"	0
 	CEr;   c                    t        t        |            d_        dj                  ddd       t	        j
                         j                  d      }dj                  |<   fd	}t        j                   |              y
)uG   예산 초과 시 generate_insight가 에러 메시지를 반환한다.r   rh   iy  r   r  Tr   r   c                  <  K   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 7 w)N   예산   초과rx   r  r{   r  r  r   r  r  r  r'  r   r  r  )r  r&   r'   r+   r(   r)   r*   r
  r  r,   r-   r{   r   r3   r5   r8   r  r,  r   r   r.  r/  r  r  r2   r1   s                r9   rA  zJTestRateLimit.test_insight_returns_error_when_budget_exceeded.<locals>.runp  s     //88F;8;8v%;;V);;;;;8v;;;8;;;;;;v;;;v;;;;;;;V;;;;;;;;;V;;;V;;;;;;;;;;;;;; 9s   FFFFNr  r  s       @@r9   /test_insight_returns_error_when_budget_exceededz=TestRateLimit.test_insight_returns_error_when_budget_exceededf  sm     c(m< !k$G''
3%&E"	< 	CEr;   N)	r<   r=   r>   r?   r  r  r  r  r  r@   r;   r9   r  r  +  s    ,5+4$r;   r  c                   "    e Zd ZdZd Zd Zd Zy)TestDateTransitionBoundaryu6   날짜 전환 경계 시간순 역전 방지 테스트c                    t        t        |            }t        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                  |      dz  }dd|iz  }t        t	        j                  |            dx}}d}	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                  |      rt	        j                  |      ndt	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
y)u9   _today_jsonl_path가 timestamp 파라미터를 받는다.r   i  rh   r      ;   )tsNis notz%(py0)s is not %(py3)spathr   r}   r~   z
2026-03-14rx   )z0%(py1)s in %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}r   )r   r   r   r   r   r   )r	   r   r   _today_jsonl_pathr&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   r  r  r3   r   r   r   r8   r4   r6   r7   s                r9   'test_today_jsonl_path_accepts_timestampzBTestDateTransitionBoundary.test_today_jsonl_path_accepts_timestampz  s    c(m<dAr2r2.$$$+t4t4tt4(s4y(|y((((|y(((|((((((s(((s((((((4(((4(((y(((((((r;   c                 P   t        t        |            }|j                         }t        j                         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z  }dd	|iz  }t        t        j                  |            dx}}t        |      }	||	v }|s#t        j                  d
|fd||	f      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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)u6   timestamp 미제공 시 현재 날짜를 사용한다.r   r   Nr  r  r  r   r}   r~   rx   )z0%(py0)s in %(py5)s
{%(py5)s = %(py2)s(%(py3)s)
}r   r   r  r   r   )r	   r   r  r   r   r   r&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   r  r   r3   r   r   r   r5   r   s              r9   &test_today_jsonl_path_default_is_todayzATestDateTransitionBoundary.test_today_jsonl_path_default_is_today  s(    c(m<$$&''
3t4t4tt4D	!u	!!!!u	!!!!!!u!!!u!!!!!!!!!!!!!!!D!!!D!!!	!!!!!!!r;   c                    t        t        |            }d}|j                  |ddd       t        j                         j                  d      }|| dz  }|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)uI   메시지 timestamp 기준으로 올바른 날짜 파일에 기록된다.r   i}  r   u   오늘 메시지Fr   r   r   rJ  r   r   N)r	   r   r$   r   r   r   r   r(   r)   r&   r*   r+   r,   r-   r   s	            r9   )test_message_written_to_correct_date_filezDTestDateTransitionBoundary.test_message_written_to_correct_date_file  s     c(m<!24FuU''
35' 00
  " """"""""z"""z""" """"""""""r;   N)r<   r=   r>   r?   r  r  r  r@   r;   r9   r  r  w  s    @)"	#r;   r  c                   "    e Zd ZdZd Zd Zd Zy)TestTopicConfirmationu9   pending topic이 요약 시 확정되는 로직 테스트c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	u>   pending 상태의 topic이 _generate_summary 후 확정된다.r   i  r   u   다른 주제로 가죠Tr   c            
        K   t        dt              5 } t        j                  dg dg g dd      | _        dj
                  <   j                         d {    j
                     }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 d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  r  r  r.  r/  rj  r   r   r   r   r   )r   r   r   r  r?  rQ  r2  r&   r'   r+   r,   r-   r=  r8   r   r3   r   r6   r2   r1   s         r9   rA  zMTestTopicConfirmation.test_pending_topic_confirmed_after_summary.<locals>.run  s     8yQ @U`+/::#+&(%.)+(*+8	,( /8""7+++G444))'2?i?2i????2i???2???i???????@ @ 5@ @s0   D AC4C2BC4)	D 2C44C=9D NrQ  rR  s      @@r9   *test_pending_topic_confirmed_after_summaryz@TestTopicConfirmation.test_pending_topic_confirmed_after_summary  sB     c(m<.GPTU	@  	CEr;   c                     t        t        |            dj                  ddd       dj                  <   fd}t	        j
                   |              y	)
uJ   pending이 아닌 topic은 _generate_summary에서 변경되지 않는다.r   i  r   u   이야기 계속Tr   existing_topicc            
        K   t        dt              5 } t        j                  dg dg g dd      | _        j                         d {    j                     }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 d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  r  	new_topicr.  r/  r  r   r   r   r   r   )r   r   r   r  r?  r2  rQ  r&   r'   r+   r,   r-   r  s         r9   rA  zETestTopicConfirmation.test_non_pending_topic_not_changed.<locals>.run  s     8yQ GU`+/::#+&(%0)+(*+8	,( ++G444))'2F6FF26FFFFF26FFFF2FFF6FFFFFFFFG G 5G Gs/   C16C%C#BC%	C1#C%%C.*C1Nr	   r   r$   rQ  rC  rA  rR  s      @@r9   "test_non_pending_topic_not_changedz8TestTopicConfirmation.test_non_pending_topic_not_changed  sQ     c(m<.@N&67#	G 	CEr;   c                     t        t        |            dj                  ddd       dj                  <   fd}t	        j
                   |              y	)
uJ   LLM이 topic_tag를 주지 않으면 pending이 'general'로 확정된다.r   i  r   r  Tr   rj  c                    K   t        dt              5 } d| _        j                         d {    j                     }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 d d        y 7 # 1 sw Y   y xY ww)
Nr:  r;  u%   일반 텍스트 (JSON 파싱 실패)r-  r   r   r   r   r   )
r   r   r?  r2  rQ  r&   r'   r+   r,   r-   r  s         r9   rA  zNTestTopicConfirmation.test_pending_topic_with_fallback_to_general.<locals>.run  s     8yQ @U`+R(++G444))'2?i?2i????2i???2???i???????	@ @4@ @s-   CCC	BC 	C	CCCNr  rR  s      @@r9   +test_pending_topic_with_fallback_to_generalzATestTopicConfirmation.test_pending_topic_with_fallback_to_general  sP     c(m<k$G&/7#	@ 	CEr;   N)r<   r=   r>   r?   r  r  r  r@   r;   r9   r  r    s    C20r;   r  c                       e Zd ZdZd Zd Zy)TestGetAllSummaryFilesu)   get_all_summary_files 메서드 테스트c                    t        t        |            }|dz  }|j                  d       |j                         }t	        |t
              }|sd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
}y
)u3   get_all_summary_files가 리스트를 반환한다.r   rI  Trq  r  r  r{   rN  r  N)r	   r   r}  get_all_summary_filesr  rN  r(   r)   r&   r*   r+   r,   r-   )r0   r   r1   rK  r{   r   r   s          r9   'test_get_all_summary_files_returns_listz>TestGetAllSummaryFiles.test_get_all_summary_files_returns_list  s     c(m< ;.D)**,&$''''''''z'''z''''''&'''&''''''$'''$''''''''''r;   c                 X   t        t        |            }|dz  }|j                  d       t        j                         j                  d      }|| dz  }|j                  t        j                  d|dg g g d	d
      d       |j                         }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                  |      rt        j                   |      ndt        j                   |      t        j                   |      dz  }
dd|
iz  }t#        t        j$                  |            dx}x}	}d}|d   }||v }|slt        j                  d|fd||f      t        j                   |      t        j                   |      dz  }dd|iz  }
t#        t        j$                  |
            dx}x}}|d   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}}y)u2   반환된 각 항목에 filename 필드가 있다.r   rI  Trq  r   z_general_001.jsonr  r-  r.  )rZ  r  r0  r[  r1  r2  r3  r   r   r   r   r   r   r{   r   r   r   Nfilenamer   rx   rX  r   r   r   _general_001r   )r	   r   r}  r   r   r   r  r   r  r  r   r&   r'   r(   r)   r*   r+   r,   r-   )r0   r   r1   rK  r   r8  r{   r3   r4   r5   r6   r7   r8   r   r   s                  r9   ,test_get_all_summary_files_includes_filenamezCTestGetAllSummaryFiles.test_get_all_summary_files_includes_filename  s    c(m< ;.D)''
3w&788
JJ'!!*"$%'$&'4
  	 	
 **,6{a{a{ass66{a&VAY&zY&&&&zY&&&z&&&Y&&&&&&&ay$>5'(>>$(>>>>>$(>>>>$>>>(>>>>>>>>r;   N)r<   r=   r>   r?   r  r  r@   r;   r9   r  r    s    3(?r;   r  c                   6    e Zd ZdZd	dZd Zd Zd Zd Zd Z	y)
TestSmartSearchu0   smart_search(query, chat_id) 메서드 테스트Nc           
      
   t        j                         j                  d      }||z  }t        j                         j                         ||||g|xs g g ddgd	}|j	                  t        j                  |d      d       |S )	u7   테스트용 요약 JSON 파일을 생성하는 헬퍼.r   r.  r   	r   r  rZ  r0  r[  r1  r2  r3  r\  Frw  r   r   )r   r   r   r~  r  r   r  )	r0   rK  r  rZ  r0  r1  r   r8  r   s	            r9   _make_summary_filez"TestSmartSearch._make_summary_file  s    ''
3X%!113"$+*0b,./

 	djjE:WM	r;   c                    t        t        |            d|dz  }|j                  d       t        j                         j                  d      }| j                  || ddd	       fd
}t        j                   |              y)u*   smart_search가 문자열을 반환한다.r   iA  rI  Trq  r   _ai_tech_001.jsonu   AI 기술 동향 논의r  c                    K   t        dt              5 } d| _        j                  d       d {   }t	        |t
              }|sd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 }d d d        y 7 .# 1 sw Y   y xY ww)
Nr:  r;  u!   AI 관련 검색 결과입니다.ru  r  r  r{   r   r  )r   r   r?  smart_searchr  r   r(   r)   r&   r*   r+   r,   r-   )r=  r{   r   r   r2   r1   s       r9   rA  z=TestSmartSearch.test_smart_search_returns_string.<locals>.run-  s     8yQ /U`+N("//g>>!&#........z...z......&...&......#...#........../ />/ /s-   E/E#E D$E#	E/ E##E,(E/N	r	   r   r}  r   r   r   r  rC  rA  r0   r   rK  r   rA  r2   r1   s        @@r9    test_smart_search_returns_stringz0TestSmartSearch.test_smart_search_returns_string  s~     c(m< ;.D)''
3g&'%		
	/ 	CEr;   c                    t        t        |            d|dz  }|j                  d       t        j                         j                  d      }| j                  || ddd	d
g       fd}t        j                   |              y)uQ   요약 파일에 키워드가 매칭되면 LLM 호출 후 결과를 반환한다.r   iB  rI  Trq  r   r  u(   AI 기술 동향에 대한 심층 논의r  u   AI 도입 결정)r1  c                    K   t        dt              5 } d| _        j                  d       d {   }| j                  }|syddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }t        |t              }|sd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  }|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                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}x}}d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  u(   AI 기술 관련 내용이 있습니다.ru  r3  r=  r  r  r  r{   r   r  r   r   r   r   r   r   r   )r   r   r?  r  r6  r(   r)   r&   r*   r+   r,   r-   r  r   r   r'   )r=  r{   r   r  r   r   r3   r4   r5   r6   r7   r2   r1   s              r9   rA  zDTestSmartSearch.test_smart_search_with_matching_keyword.<locals>.runE  s    8yQ 'U`+U("//g>>"))))))))){))){))))))))))!&#........z...z......&...&......#...#..........6{&Q&{Q&&&{Q&&&&&&s&&&s&&&&&&6&&&6&&&{&&&Q&&&&&&&' '>' 's-   LK<K9J=K<0	L9K<<LLNr  r  s        @@r9   'test_smart_search_with_matching_keywordz7TestSmartSearch.test_smart_search_with_matching_keyword5  s     c(m< ;.D)''
3g&'6-. 	  	
	' 	CEr;   c                    t        t        |            d|dz  }|j                  d       t        j                         j                  d      }| j                  || ddd	       fd
}t        j                   |              y)uV   매칭 결과가 없을 때 '검색 결과가 없습니다' 메시지를 반환한다.r   iC  rI  Trq  r   z_weather_001.jsonu   날씨 이야기weatherc                    K   t        dt              5 } j                  d       d {   }| j                          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 d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  u   블록체인u   검색 결과가 없습니다rx   rz   r{   r|   r}   r~   )r   r   r  r  r&   r'   r+   r(   r)   r*   r,   r-   )r=  r{   r8   r3   r   r   r2   r1   s         r9   rA  z9TestSmartSearch.test_smart_search_no_results.<locals>.run^  s     8yQ AU`"//HH--/6@6&@@@@6&@@@6@@@@@@&@@@&@@@@@@@	A AHA As-   C<C0C.B9C0%	C<.C00C95C<Nr  r  s        @@r9   test_smart_search_no_resultsz,TestSmartSearch.test_smart_search_no_resultsO  s     c(m< ;.D)''
3g&'		
	A 	CEr;   c                 D   t        t        |            d_        d|dz  }|j                  d       t	        j
                         j                  d      }| j                  || dd	d
       dj                  |<   fd}t        j                   |              y)u>   LLM 예산 초과 시 예산 초과 메시지를 반환한다.r   r   iD  rI  Trq  r   _ai_001.jsonu   AI 논의 내용aic                  >  K   j                  d       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 7 w)Nru  r  r  rx   r  r{   r  r  r   r  r  r  r'  r   r  r  )r  r&   r'   r+   r(   r)   r*   r
  r  r,   r-   r  s                r9   rA  z9TestSmartSearch.test_smart_search_rate_limit.<locals>.runz  s     ++D'::F;8;8v%;;V);;;;;8v;;;8;;;;;;v;;;v;;;;;;;V;;;;;;;;;V;;;V;;;;;;;;;;;;;; ;s   FFFFN)r	   r   r  r}  r   r   r   r  r  rC  rA  r  s        @@r9   test_smart_search_rate_limitz,TestSmartSearch.test_smart_search_rate_limitg  s     c(m< ! ;.D)''
3g\"		
 &'E"	< 	CEr;   c                    t        t        |            d|dz  }|j                  d       t        j                         j                  d      }| j                  || ddd	       fd
}t        j                   |              y)uN   LLM에 전달되는 프롬프트에 <user_content> XML 태그가 포함된다.r   iE  rI  Trq  r   r  u   AI 관련 대화r  c                  l  K   t        dt              5 } d| _        	j                  d       d {    | j                  }|syddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }| j                  d   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}}d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  u   검색 결과ru  r3  r=  r  r   r  rx   rz   r>  r|   r}   r~   r  )r   r   r?  r  r6  r(   r)   r&   r*   r+   r,   r-   r>  r'   r@  s
           r9   rA  zATestSmartSearch.test_smart_search_xml_tag_separation.<locals>.run  sf    8yQ 6U`+:(&&tW555"))))))))){))){))))))))))'11!4Q7	'4'94444'9444'444444944494444444(5(I5555(I555(555555I555I55555556 656 6s-   H4H(H%G)H(	H4%H((H1-H4Nr  r  s        @@r9   $test_smart_search_xml_tag_separationz4TestSmartSearch.test_smart_search_xml_tag_separation  s}     c(m< ;.D)''
3g\"		
	6 	CEr;   r  )
r<   r=   r>   r?   r  r	  r  r  r  r  r@   r;   r9   r  r  	  s#    :$.402r;   r  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestDeferredIndexinguR   지연 인덱싱 — summaries/ 파일 200개 초과 시 _index.json 자동 생성c                 4   t        j                         j                  d      }t        |      D ]g  }|| d|ddz  }t        j                         j	                         |d| ddgg g ddgd		}|j                  t        j                  |d
      d       i y)u;   테스트용 요약 파일을 count개 생성하는 헬퍼.r   r+  04dr  u   요약 내용 r-  r.  r   r  Frw  r   r   N)r   r   r   rN   r~  r  r   r  )r0   rK  countr   rO   r8  r   s          r9   _create_summary_filesz*TestDeferredIndexing._create_summary_files  s    ''
3u 	RAE7)Ac7%!@@B%\\^557+A3/&(k!# "#0!2 3
D MM$**T>MQ	Rr;   c                     t        t        |            }|dz  }|j                  d       | j                  |d       |j	                          |dz  }|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}x}}y
)uE   파일이 200개 이하일 때 _index.json이 생성되지 않는다.r   rI  Trq  rU   _index.jsonzEassert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
index_pathr   N)r	   r   r}  r  r  r   r(   r)   r&   r*   r+   r,   r-   )	r0   r   r1   rK  r!  r   r   r4   r   s	            r9    test_index_not_created_under_200z5TestDeferredIndexing.test_index_not_created_under_200  s     c(m< ;.D)""=#6 	!!#"]2
$$&$&&&&&&&&&&&:&&&:&&&$&&&&&&&&&&r;   c                 "   t        t        |            }|dz  }|j                  d       | j                  |d       |j	                          |dz  }|j
                  } |       }|st        j                  d      dz   d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }t        t        j                  |            dx}}y)uJ   파일이 200개 초과(201개)일 때 _index.json이 자동 생성된다.r   rI  Trq     r   u7   _index.json이 201개 초과 시 생성되어야 한다r   r!  r   N)r	   r   r}  r  r  r   r&   r   r(   r)   r*   r+   r,   r-   )r0   r   r1   rK  r!  r   r   r   s           r9   test_index_created_over_200z0TestDeferredIndexing.test_index_created_over_200  s     c(m< ;.D)""=#6 	!!#"]2
  ] "]"]]$]]]]]]]z]]]z]]] ]]]"]]]]]]r;   c                    t        t        |            }|dz  }|j                  d       t        j                         j                  d      }| d|ddd	d
 dg}|dz  }|j                  t        j                  |d      d       |j                         }t        |t              }|sd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 |D              }
|
s{t        j(                  d      dz   ddt        j                         v st        j                  |
      rt        j                   |
      ndiz  }t#        t        j$                  |            y	)uP   _index.json이 존재할 때 get_all_summary_files가 인덱스를 사용한다.r   rI  Trq  r   _ai_001r  u   AI 관련 요약Nr   r  r  r0  rZ  r   Frw  r   r   r  r  r{   rN  r  c              3   D   K   | ]  }|j                  d       dk(    yw)r0  r  N)r)  )r  items     r9   r  zMTestDeferredIndexing.test_get_all_summary_files_uses_index.<locals>.<genexpr>  s     EdDHH[)T1Es    u:   인덱스의 ai 항목이 결과에 포함되어야 한다z
>assert %(py0)sr   found)r	   r   r}  r   r   r   r  r   r  r  r  rN  r(   r)   r&   r*   r+   r,   r-   r  r   )r0   r   r1   rK  r   
index_datar!  r{   r   r   r+  @py_format1s               r9   %test_get_all_summary_files_uses_indexz:TestDeferredIndexing.test_get_all_summary_files_uses_index  sx    c(m< ;.D)''
3
  %gW-!-cr2	

 #]2
djj%HSZ[ **,&$''''''''z'''z''''''&'''&''''''$'''$''''''''''EfEERRRRRRRRRuRRRuRRRRRur;   c                    t        t        |            d|dz  }|j                  d       t        j                         j                  d      }| d|dd	d
g}|dz  j                  t        j                  |d      d       j                  ddd       fd}t        j                   |              y)u?   새 요약이 생성될 때 _index.json이 자동 갱신된다.r   i,  rI  Trq  r   _old_001	old_topic   이전 요약r(  r   Frw  r   r   r   u   새로운 논의 내용r   c            
        K   t        dt              5 } t        j                  ddgdg g ddd	      | _        
j                         d {    d d d        t        j                  	j                  d
            }|D cg c]  }|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  }t        j                  d      dz   d|iz  }t        t        j                   |            d x}}y 7 # 1 sw Y   xY wc c}w w)Nr:  r;  u   새로운 요약 내용u	   신주제r  r.  r/  Frw  r   r   r0  rx   rz   
new_topicsr|   u>   새 요약의 topic_tag가 인덱스에 추가되어야 한다
>assert %(py5)sr~   )r   r   r   r  r?  r2  r   r   r&   r'   r+   r(   r)   r*   r   r,   r-   )r=  updated_indexr*  r4  r8   r3   r   r   r2   r!  r1   s           r9   rA  zCTestDeferredIndexing.test_index_updated_on_new_summary.<locals>.run   s    8yQ 5U`+/::#<'2m%0)+(*+8 "'
,( ++G4445 !JJz';';W';'MNM8EF${+FJFn;*,nnn;*nnn;nnnnnn*nnn*nnnn.nnnnnnn 55 5  Gs;   E!9EEE1E!EB>E!EEE!N)r	   r   r}  r   r   r   r  r   r  r$   rC  rA  )	r0   r   rK  r   r,  rA  r2   r!  r1   s	         @@@r9   !test_index_updated_on_new_summaryz6TestDeferredIndexing.test_index_updated_on_new_summary  s     c(m< ;.D)''
3
  %gX.(*	

 #]2
djj%HSZ[ 	.GPTU	o( 	CEr;   N)	r<   r=   r>   r?   r  r"  r%  r.  r7  r@   r;   r9   r  r    s!    \R$'^S8,r;   r  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestJSONLRotationuB   JSONL rotation — 단일 파일 5만 줄 초과 시 자동 분할c                    t        t        |            }d}t        j                         j	                  d      }|| dz  }|j                  dd       t        j                  |dd	d
      5  |j                  |ddd       ddd       || dz  }|j                  } |       }| }	|	st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }
t!        t        j"                  |
            dx}x}}	y# 1 sw Y   xY w)ud   5만 줄 미만일 때 기존 파일에 계속 append하고 _part2 파일이 생성되지 않는다.r   i  r   r   r   r   r   _get_jsonl_line_countiO  T)r?  creater   u   마지막 메시지Fr   N_part2.jsonlu.   _part2.jsonl 파일이 생성되면 안 된다zG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
part2_pathr   )r	   r   r   r   r   r  r   r5  r$   r   r&   r   r(   r)   r*   r+   r,   r-   )r0   r   r1   r2   r   r   r>  r   r   r4   r   s              r9    test_rotation_under_50k_no_splitz2TestJSONLRotation.test_rotation_under_50k_no_split  s    c(m< ''
35' 00
b73\\#6USWX 	]OOG%68MV[O\	] 5' 66
$$X$&X&&X&XX(XXXXXXX:XXX:XXX$XXX&XXXXXX		] 	]s   /EE c                    t        t        |            }d}t        j                         j	                  d      }|| dz  }|j                  dd       d|j                  t        |      <   |j                  |d	d
d       || dz  }|j                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	t        t        j                   |	            dx}}y)u8   5만 줄 초과 시 _part2.jsonl 파일이 생성된다.r   i  r   r   r   r   r   iQ  r   u    로테이션 트리거 메시지Fr   r=  u:   50001줄 초과 시 _part2.jsonl이 생성되어야 한다r   r>  r   N)r	   r   r   r   r   r  _jsonl_line_countr$   r   r&   r   r(   r)   r*   r+   r,   r-   )
r0   r   r1   r2   r   r   r>  r   r   r   s
             r9   $test_rotation_over_50k_creates_part2z6TestJSONLRotation.test_rotation_over_50k_creates_part2*  s     c(m<''
35' 00
b73 27c*o.!24V_de5' 66
  ` "`"``$```````z```z``` ```"``````r;   c           	      X   t        j                         j                  d      }d}|| dz  }t        j                  ddt        j                         j                         d|ddd	      }|j                  |d
z   d       || dz  }t        j                  ddt        j                         j                         d|ddd	      }|j                  |d
z   d       t        t        |            }|j                  |       |j                  |d      }	|	D 
cg c]  }
|
j                   }}
d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                          v st        j"                  |      rt        j                  |      nddz  }t        j$                  d      dz   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  }t        j$                  d      dz   d|iz  }t'        t        j(                  |            dx}}yc c}
w )uB   load_today가 같은 날짜의 모든 part 파일을 로드한다.r   i  r   r   u   파트1 메시지Fr-  )r.   r/   r   r   r2   r0  rw  r  r   r   r=  r   u   파트2 메시지Tr   rT   rI   rx   rz   textsr|   u3   part1 파일의 메시지가 로드되어야 한다r5  r~   Nu3   part2 파일의 메시지가 로드되어야 한다)r   r   r   r   r  r~  r  r	   r   r  r%   r/   r&   r'   r+   r(   r)   r*   r   r,   r-   )r0   r   r   r2   
part1_pathrecord1r>  record2r1   r   mrD  r8   r3   r   r   s                   r9   test_load_today_loads_all_partsz1TestJSONLRotation.test_load_today_loads_all_parts;  s   ''
3 5' 00
**++%\\^557"& 

 	gnw? 5' 66
**%+%\\^557"& 

 	gnw? c(m<w??7#?6!)*A**"b"e+bbb"ebbb"bbbbbbebbbebbbb-bbbbbbb"b"e+bbb"ebbb"bbbbbbebbbebbbb-bbbbbbb +s   J'c                 J	   t        t        |            }t        j                         j	                  d      }|| dz  }|j                  dd       |j                  |      }t        |      }|j                  }||v }|sMt        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                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	t        j                  d      dz   d|	iz  }
t        t        j                   |
            dx}x}}|j                  t        |         }t#        dt%        d            5  |j                  |      }ddd       ||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}|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                  t        |         }d}||z   }||k(  }|st        j                  d|fd||f      t        j                  |      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}x}x}}y# 1 sw Y   xY w)!uA   줄 수가 캐시되어 매번 파일을 전체 읽지 않는다.r   r   r   zline1
line2
line3
r   r   rx   )zW%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} in %(py7)s
{%(py7)s = %(py5)s._jsonl_line_count
}r   r   r1   )r   r   r   r~   r   u,   첫 호출 후 캐시가 채워져야 한다z
>assert %(py9)sr%  Nr  u   파일 접근 금지r  r   )z%(py0)s == %(py2)spath1r  r	  r   path2r   )z%(py1)s == (%(py3)s + %(py5)s)cached_countr6  r   r   )r	   r   r   r   r   r  _get_jsonl_path_with_rotationrA  r&   r'   r(   r)   r*   r+   r   r,   r-   r   IOError)r0   r   r1   r   r   rK  r3   r*  r5   r   @py_format10rM  rL  r   r  r   r8   r6   r7   s                      r9   test_line_count_cachedz(TestJSONLRotation.test_line_count_cachedf  s    c(m<''
35' 00
5H 11*=:g#"7"7g"77ggg"7ggggggsgggsgggggg:ggg:ggggggggg#ggg#ggg"7ggg9gggggggg,,S_= ?8N0OP 	B55jAE	B 
""""u
""""""u"""u""""""
"""
"""""""
""""u
""""""u"""u""""""
"""
"""""""$$S_5II9II59IIIII59IIII5IIIIIIIIIIIIIIIIIII	B 	Bs   RR"N)r<   r=   r>   r?   r?  rB  rI  rQ  r@   r;   r9   r9  r9    s    LY a")cVJr;   r9  c                       e Zd ZdZd Zd Zy)TestKoreanPromptHintsu2   한국어 특성 대응 프롬프트 힌트 확인c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uV   _do_generate_summary 프롬프트에 '주어는 자주 생략' 힌트가 포함된다.r   i  r   u   주어 생략 테스트Fr   c            
        K   t        dt              5 } t        j                  dg dg g ddd      | _        j                         d {    | j                  d	   d	   }g }d
}||v }|}|r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                  |	       |rt        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}}d d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  r  r-  r.  r/  Frw  r   u   주어u   생략rx   r  r>  r  r  r   r  r  r  r'  r  r  )r   r   r   r  r?  _do_generate_summaryr>  r&   r'   r+   r(   r)   r*   r
  r  r,   r-   r  s                 r9   rA  zDTestKoreanPromptHints.test_korean_subject_omission_hint.<locals>.run  sV    8yQ GU`+/::#+&(%.)+(*+8 "'
,( ..w777'11!4Q7	FxFx9,FFY1FFFFFx9FFFxFFFFFF9FFF9FFFFFFFYFFFFFFFFFYFFFYFFFFFFFFFFFFFFG G 8G Gs/   G78G+G(FG+	G7(G++G40G7NrQ  rR  s      @@r9   !test_korean_subject_omission_hintz7TestKoreanPromptHints.test_korean_subject_omission_hint  sC     c(m<!24MV[\	G" 	CEr;   c                     t        t        |            dj                  ddd       fd}t        j                   |              y)	uO   _do_generate_summary 프롬프트에 '감탄사' 무시 힌트가 포함된다.r   i  r   u   감탄사 테스트 ㅋㅋFr   c            
      D  K   t        dt              5 } t        j                  dg dg g ddd      | _        j                         d {    | j                  d	   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 d d        y 7 # 1 sw Y   y xY ww)Nr:  r;  r  r-  r.  r/  Frw  r   u	   감탄사rx   rz   r>  r|   r}   r~   )r   r   r   r  r?  rV  r>  r&   r'   r+   r(   r)   r*   r,   r-   r  s         r9   rA  zGTestKoreanPromptHints.test_korean_interjection_ignore_hint.<locals>.run  s     8yQ 0U`+/::#+&(%.)+(*+8 "'
,( ..w777'11!4Q7	"/{i////{i///{//////i///i///////0 0 80 0s/   D 8DDB;D		D DDD NrQ  rR  s      @@r9   $test_korean_interjection_ignore_hintz:TestKoreanPromptHints.test_korean_interjection_ignore_hint  sB     c(m<!24PY^_	0" 	CEr;   N)r<   r=   r>   r?   rW  rZ  r@   r;   r9   rS  rS  }  s    <2r;   rS  c                       e Zd ZdZd Zy)TestGetContextDefaultLimitu+   get_context() 기본 limit 변경 테스트c                    t        d      }d}t        d      D ]  }|j                  |d| d| d        |j                  |      }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                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u"   기본 limit이 30으로 변경됨Nr   i  #   userzmsg Fr      r   r   r   r{   r   r   r   r   )
r0   r1   r2   rO   r{   r3   r4   r5   r6   r7   s
             r9   test_default_limit_is_30z3TestGetContextDefaultLimit.test_default_limit_is_30  s     d3r 	KAOOGtA3Z4sEOJ	K)6{ b {b    {b      s   s      6   6   {   b       r;   N)r<   r=   r>   r?   ra  r@   r;   r9   r\  r\    s
    5!r;   r\  c                   "    e Zd ZdZd Zd Zd Zy)TestRollingSummaryu   롤링 서머리 테스트c                     ddl mm t        d      dt	        d      D ]  }j                  d| d| d	
        fd}t        j                   |              y)uB   generate_rolling_summary() 호출 시 _rolling_summaries에 저장r   )r   r   Nr   i  rj   bot   의견 Tr   c                    K    d
      5 } d| _         j                         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                     }d}||k(  }|slt        j                  d|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}y 7 ># 1 sw Y   >xY ww)Nr:  r;  u   요약: 핵심 논점 정리...r   r  r{   r   r}   r~   r   r   r   r   )r?  generate_rolling_summaryr&   r'   r(   r)   r*   r+   r,   r-   _rolling_summaries)r=  r{   r3   r   r   r   r8   r   r   r6   r   r2   r1   r   s             r9   rA  z;TestRollingSummary.test_rolling_summary_stored.<locals>.run  s	    8yQ EU`+L(";;GDDE ?>6>>>>>6>>>>>>>6>>>6>>>>>>>>>>>))'2W6WW26WWWWW26WWWW2WWW6WWWWWWWW EE Es,   E8E+E(E+D9E8(E++E50E8)unittest.mockr   r   r	   rN   r$   rC  rA  )r0   rO   rA  r   r2   r1   r   s      @@@@r9   test_rolling_summary_storedz.TestRollingSummary.test_rolling_summary_stored  s`    2 d3q 	LAOOGs1#Y'!dOK	L	X 	CEr;   c                 \    t        d      fd}t        j                   |              y)u(   메시지 없으면 빈 문자열 반환Nr   c                    K   j                  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}}y 7 w)	Ni  r   r   r  r{   r   r}   r~   	rh  r&   r'   r(   r)   r*   r+   r,   r-   )r{   r3   r   r   r   r1   s        r9   rA  zCTestRollingSummary.test_rolling_summary_empty_messages.<locals>.run  sw     77<<F6R<6R66R =   CCB,C)r	   rC  rA  )r0   rA  r1   s     @r9   #test_rolling_summary_empty_messagesz6TestRollingSummary.test_rolling_summary_empty_messages  s"     d3	  	CEr;   c                 <   t        d      dt        d      D ]  }j                  d| d| d        t        j                         j                  d	      }d
j                  |<   dj                  <   fd}t        j                   |              y)u-   LLM 예산 초과 시 기존 서머리 반환Nr   i  rj   re  rf  Tr   r   r   r2  c                    K   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)Nr2  r   r  r{   r   r}   r~   rn  )r{   r3   r   r   r   r2   r1   s        r9   rA  zDTestRollingSummary.test_rolling_summary_budget_exceeded.<locals>.run  sy     77@@F,,6_,,,,6_,,,,,,6,,,6,,,_,,,,,,, Aro  )
r	   rN   r$   r   r   r   r  ri  rC  rA  )r0   rO   r   rA  r2   r1   s       @@r9   $test_rolling_summary_budget_exceededz7TestRollingSummary.test_rolling_summary_budget_exceeded  s     d3q 	LAOOGs1#Y'!dOK	L ''
3%'E"*9w'	- 	CEr;   N)r<   r=   r>   r?   rk  rp  rs  r@   r;   r9   rc  rc    s    $$r;   rc  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestFormatContextWithPhaseu-   format_context() Phase 프롬프트 테스트c                    t        d      }d}|j                  |ddd       |j                  |d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*   phase='diverge'일 때 발산 프롬프트Nr   i  r      의견입니다Tr   rv   divergephaseu=   새로운 관점과 아이디어를 자유롭게 제시하라rx   rz   r{   r|   r}   r~   r   r   s           r9   test_diverge_phase_promptz4TestFormatContextWithPhase.test_diverge_phase_prompt  s     d3.?M##G->i#PNXNRXXXXXNRXXXXNXXXXXXRXXXXRXXXXXXXXr;   c                    t        d      }d}|j                  |ddd       |j                  |d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+   phase='converge'일 때 수렴 프롬프트Nr   i  r   rw  Tr   rv   convergery  u    공통점을 먼저 정리하라rx   rz   r{   r|   r}   r~   r   r   s           r9   test_converge_phase_promptz5TestFormatContextWithPhase.test_converge_phase_prompt	  s     d3.?M##G->j#Q1;1V;;;;1V;;;1;;;;;;V;;;V;;;;;;;r;   c                    t        d      }d}|j                  |ddd       |j                  |d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,   phase='consensus'일 때 합의 프롬프트Nr   i  r   rw  Tr   rv   r~  ry  u    최종 합의문을 작성하라rx   rz   r{   r|   r}   r~   r   r   s           r9   test_consensus_phase_promptz6TestFormatContextWithPhase.test_consensus_phase_prompt	  s     d3.?M##G->k#R1;1V;;;;1V;;;1;;;;;;V;;;V;;;;;;;r;   c                    t        d      }d}|j                  |dd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,   phase=None일 때 기존 기본 프롬프트Nr   i  r   rw  Tr   rv   u4   중복되지 않는 새로운 관점을 제시해라rx   rz   r{   r|   r}   r~   r   r   s           r9   test_no_phase_default_promptz7TestFormatContextWithPhase.test_no_phase_default_prompt	  s     d3.?M##G->?EOEOOOOEOOOEOOOOOOOOOOOOOOOOr;   c                     t        d      }d}|j                  |ddd       d|j                  |<   |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)u3   롤링 서머리가 format_context 결과에 포함Nr   i  r   rw  Tr   u#   이것은 롤링 서머리입니다rv   u   [현재 토론 요약]rx   rz   r{   r|   r}   r~   )r	   r$   ri  r   r&   r'   r+   r(   r)   r*   r,   r-   r   s           r9   test_rolling_summary_includedz8TestFormatContextWithPhase.test_rolling_summary_included	  s    d3.?M*Ow'##G->?'1'61111'6111'1111116111611111114>4>>>>4>>>4>>>>>>>>>>>>>>>>r;   N)	r<   r=   r>   r?   r{  r~  r  r  r  r@   r;   r9   ru  ru    s     7Y<<P?r;   ru  )Abuiltinsr(   _pytest.assertion.rewrite	assertionrewriter&   rC  r   r  sysr   pathlibr   rj  r   r   r   r  insertjoindirname__file__pytestconversation_memoryr   r	   r   rB   rR   rf   rt   r   r   r   r   r   r   r  r#  ro  r  r  r  r  r  r  r)  r;  rK  rT  ro  r  r  r  r  r  r  r  r  r  r9  rS  r\  rc  ru  r@   r;   r9   <module>r     s       	 
   0 0 277<< 94@ A  ?* *(3 3"0 00" "4C C&%q %qP/ /<F F>' '$& &NM* M*`K3 K3\z zzn# n#bJ JZ06 06fP. P.f? ?D -Q -Qj, ,^?3 ?3D#4 #4LR< R<jl l^+ +\" ":K K\I IX# #>B BJ&? &?\O Ody yxcJ cJL3 3v! !/ /d+? +?r;   