
    (<i(                       d Z ddl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	Z	ej                  j                  d e ee      j                  j                  j                               ddlmZmZ e	j$                  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y)uB   utils/session_store.py 테스트 스위트 (TDD — RED → GREEN)    )annotationsN)Path)SessionStoresanitize_titlec              #  h   K   t        | dz        }t        |      }| |j                          yw)u;   임시 DB 경로를 사용하는 SessionStore 인스턴스.ztest_sessions.db)db_pathN)strr   close)tmp_pathr   ss      O/home/jay/workspace/.worktrees/task-2057-dev2/utils/tests/test_session_store.pystorer      s0      (//0GW%A
GGGIs   02c                  @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestSessionCRUDu-   세션 생성 / 조회 / 종료 기본 동작c                *    |j                  dd       y)u/   create_session()이 예외 없이 완료된다.zsess-001apisourceN)create_sessionselfr   s     r   test_create_sessionz#TestSessionCRUD.test_create_session(   s    Z6    c                    |j                  ddd       |j                  d      }|J |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y)	u>   create_session() 후 get_session()으로 조회할 수 있다.zsess-002clizgpt-4)r   modelN
session_idr   r   r   get_sessionr   r   rows      r    test_get_session_returns_createdz0TestSessionCRUD.test_get_session_returns_created,   sh    ZWE
+< J...8}%%%7|w&&&r   c                *    |j                  d      J y)u6   존재하지 않는 session_id 조회 시 None 반환.zdoes-not-existN)r   r   s     r   %test_get_session_missing_returns_nonez5TestSessionCRUD.test_get_session_missing_returns_none5   s      !12:::r   c                    |j                  dd       |j                  dd       |j                  d      }|d   dk(  sJ |d   J y)	u<   end_session()으로 세션이 종료 상태로 변경된다.zsess-003r   r   	user_exit
end_reasonr(   ended_atN)r   end_sessionr   r    s      r   test_end_sessionz TestSessionCRUD.test_end_session9   s[    Z6*=
+< K///:***r   c                    |j                  dd       |j                  dd       |j                         }|D cg c]  }|d   	 }}d|v sJ d|v sJ yc c}w )u4   list_sessions()가 생성된 세션을 반환한다.zsess-list-1r   r   zsess-list-2r   r   Nr   list_sessionsr   r   rowsridss        r   "test_list_sessions_returns_createdz2TestSessionCRUD.test_list_sessions_returns_createdA   sn    ]59]59""$(,-1q--###### .s   Ac                    |j                  dd       |j                  dd       |j                  d      }|D cg c]  }|d   	 }}d|v sJ d|vsJ yc c}w )u+   source 필터가 올바르게 동작한다.z	src-api-1r   r   z	src-cli-1r   r   Nr-   r/   s        r   #test_list_sessions_filter_by_sourcez3TestSessionCRUD.test_list_sessions_filter_by_sourceJ   ss    [7[7""%"0(,-1q--c!!!#%%% .s   Ac                    t        d      D ]  }|j                  d| d        |j                  d      }t        |      dk  sJ y)u4   limit 파라미터가 반환 개수를 제한한다.   zlim-testr      limitN)ranger   r.   len)r   r   ir0   s       r   test_list_sessions_limitz(TestSessionCRUD.test_list_sessions_limitS   sQ    q 	<A  4sF ;	<"""+4yA~~r   c                    |j                  dd       |j                  ddd       |j                  d      }|d   dk(  sJ y)u5   parent_session_id가 DB에 올바르게 저장된다.zparent-1r   r   zchild-1)r   parent_session_idrA   Nr   r    s      r   test_parent_session_id_storedz-TestSessionCRUD.test_parent_session_id_storedZ   sN    Z6Yu
S	*&':555r   N)__name__
__module____qualname____doc__r   r"   r$   r+   r3   r5   r?   rB    r   r   r   r   %   s-    77';+$&6r   r   c                  (    e Zd ZdZd Zd Zd Zd Zy)TestMessagesu   메시지 append / get 동작c                R    |j                  dd       |j                  ddd       y)u/   append_message()가 예외 없이 완료된다.z
msg-sess-1r   r   userhellorolecontentN)r   append_messager   s     r   test_append_messagez TestMessages.test_append_messagej   s*    \%8\Hr   c                    |j                  dd       |j                  ddd       |j                  ddd       |j                  d      }t        |      d	k(  sJ |d
   d   dk(  sJ |d   d   dk(  sJ y)u6   get_messages()가 추가된 메시지를 반환한다.z
msg-sess-2r   r   rK   hirM   	assistantrL      r   rN      N)r   rP   get_messagesr=   r   r   msgss      r   "test_get_messages_returns_appendedz/TestMessages.test_get_messages_returns_appendedo   s    \%8\E\WM!!,/4yA~~Awv&(((Awv+---r   c                    |j                  dd       |j                  ddddddid	g
       |j                  d      }|d   d   J y)u4   tool_calls JSON이 올바르게 저장/조회된다.z
msg-sess-3r   r   rT   call_1functionnamefoo)idtyper]   )rN   
tool_callsr   rb   N)r   rP   rW   rX   s      r   test_message_with_tool_callsz)TestMessages.test_message_with_tool_callsy   sg    \%8'&RWYZ 	 	

 !!,/Aw|$000r   c                    |j                  dd       t        d      D ]  }|j                  ddt        |             ! |j	                  d      }|D cg c]  }|d   	 }}|g dk(  sJ y	c c}w )
u7   메시지가 추가 순서(created_at)로 반환된다.z
msg-sess-4r   r   r9   rK   rM   rO   )012N)r   r<   rP   r	   rW   )r   r   r>   rY   mcontentss         r   #test_message_ordering_by_created_atz0TestMessages.test_message_ordering_by_created_at   s}    \%8q 	LA  FCF K	L!!,/*./QAiL//?*** 0s   A.N)rC   rD   rE   rF   rQ   rZ   rc   rj   rG   r   r   rI   rI   g   s    'I
.	1+r   rI   c                      e Zd ZdZd Zy)TestWALModeu   SQLite WAL 저널 모드 확인c                    t        j                  |j                        }|j                  d      j	                         d   }|j                          |dk(  sJ y)u-   DB의 journal_mode가 WAL임을 확인한다.zPRAGMA journal_mode;r   walN)sqlite3connectr   executefetchoner
   )r   r   connmodes       r   test_wal_mode_enabledz!TestWALMode.test_wal_mode_enabled   sF    u}}-||23<<>qA

u}}r   N)rC   rD   rE   rF   ru   rG   r   r   rl   rl      s
    )r   rl   c                      e Zd ZdZd Zd Zy)TestFTS5u+   messages_fts 가상 테이블 기본 동작c                    t        j                  |j                        }|j                  d      j	                         D cg c]  }|d   	 }}|j                          d|v sJ yc c}w )u5   messages_fts 가상 테이블이 생성되어 있다.z1SELECT name FROM sqlite_master WHERE type='table'r   messages_ftsN)ro   rp   r   rq   fetchallr
   )r   r   rs   r1   tabless        r   test_fts5_table_existszTestFTS5.test_fts5_table_exists   s[    u}}- $-` a j j lm1!A$mm

''' ns   A&c                   |j                  dd       |j                  ddd       t        j                  |j                        }|j                  d      j                         }|j                          t        |      dk\  sJ y	)
u0   FTS5 검색이 삽입된 내용을 반환한다.z
fts-sess-1r   r   rK   unique_keyword_xyzrM   zNSELECT content FROM messages_fts WHERE messages_fts MATCH 'unique_keyword_xyz'rV   N)	r   rP   ro   rp   r   rq   rz   r
   r=   )r   r   rs   r0   s       r   test_fts5_search_basiczTestFTS5.test_fts5_search_basic   sn    \%8\@TUu}}-||lmvvx

4yA~~r   N)rC   rD   rE   rF   r|   r   rG   r   r   rw   rw      s    5(r   rw   c                      e Zd ZdZd Zd Zy)	TestPruneu   오래된 세션 정리c                :   |j                  dd       |j                  dd       t        j                  |j                        }|j                  d       |j                          |j                          |j                  d       |j                  d      J y	)
uN   older_than_days=0으로 prune하면 ended_at이 있는 세션이 삭제된다.z	prune-oldr   r   timeoutr'   zOUPDATE sessions SET ended_at='2000-01-01T00:00:00' WHERE session_id='prune-old'r   older_than_daysN)
r   r*   ro   rp   r   rq   commitr
   prune_sessionsr   )r   r   rs   s      r   test_prune_removes_old_sessionsz)TestPrune.test_prune_removes_old_sessions   s    [7+)< u}}-fg

Q/  -555r   c                t    |j                  dd       |j                  d       |j                  d      J y)u<   ended_at이 없는 (활성) 세션은 prune되지 않는다.zprune-activer   r   r   r   N)r   r   r   r   s     r    test_prune_keeps_active_sessionsz*TestPrune.test_prune_keeps_active_sessions   s=    ^E:Q/  0<<<r   N)rC   rD   rE   rF   r   r   rG   r   r   r   r      s    !6=r   r   c                      e Zd ZdZd Zy)TestConcurrencyu&   threading.Lock 동시 쓰기 안전성c                  	 g 		fd}t        d      D cg c]  }t        j                  ||f       }}|D ]  }|j                           |D ]  }|j	                           	g k(  s
J d	        j                  d      }|D cg c]  }|d   j                  d      s|d    }}t        |      dk(  sJ y	c c}w c c}w )
uI   여러 스레드에서 동시에 세션을 생성해도 예외가 없다.c                    	 j                  d|  d       j                  d|  dd|         y # t        $ r}j                  |       Y d }~y d }~ww xY w)Nconc-threadr   rK   zmsg-rM   )r   rP   	Exceptionappend)idxeerrorsr   s     r   workerzCTestConcurrency.test_concurrent_writes_no_exception.<locals>.worker   sb    !$$uSE]8$D$$uSE]4PSu$V !a  !s   04 	AAA
   )targetargszErrors in threads:    r:   r   r   N)r<   	threadingThreadstartjoinr.   
startswithr=   )
r   r   r   r>   threadstr0   r1   conc_idsr   s
    `       @r   #test_concurrent_writes_no_exceptionz3TestConcurrency.test_concurrent_writes_no_exception   s    	! HMRyQ!9##6=QQ 	AGGI	 	AFFH	 |;26(;;|""",-1YQ|_5O5OPW5XAlOYY8}""" R Zs   C C&CN)rC   rD   rE   rF   r   rG   r   r   r   r      s
    0#r   r   c                  "    e Zd ZdZd Zd Zd Zy)TestSanitizeTitleu'   제어문자 / zero-width 문자 제거c                >    t        d      }d|vsJ d|vsJ d|v sJ y)u+   ASCII 제어문자( -, )가 제거된다.zhello world! zhelloworld!Nr   r   results     r   #test_sanitize_removes_control_charsz5TestSanitizeTitle.test_sanitize_removes_control_chars   s7     56V###V###&&&r   c                2    t        d      }d|vsJ d|v sJ y)u.   Zero-width 문자(U+200B 등)가 제거된다.u   he​llou   ​rL   Nr   r   s     r   &test_sanitize_removes_zero_width_charsz8TestSanitizeTitle.test_sanitize_removes_zero_width_chars  s(    .v%%%&   r   c                (    d}t        |      |k(  sJ y)u+   일반 텍스트는 변경되지 않는다.u   Hello, World! 안녕하세요.Nr   )r   texts     r   #test_sanitize_normal_text_unchangedz5TestSanitizeTitle.test_sanitize_normal_text_unchanged	  s    /d#t+++r   N)rC   rD   rE   rF   r   r   r   rG   r   r   r   r      s    1'!,r   r   )rF   
__future__r   ro   sysr   timepathlibr   pytestpathinsertr	   __file__parentutils.session_storer   r   fixturer   r   rI   rl   rw   r   r   r   rG   r   r   <module>r      s    H "  
     3tH~,,33::; < <  :6 :6D$+ $+X   2= =:# #@, ,r   