
    Ri8G                     f   d Z ddlZddlZddlZddlmZmZmZ ddlm	Z	 ddl
Z
 e	e      j                  j                  Zej                  j                  d ee             edz  Zej$                  j'                  de      ZeJ ej$                  j+                  e      Zej.                  J ej.                  j1                  e       dede	d	e	fd
Zdede	d	e	fdZdede	d	e	fdZd	efdZded	ef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)uw  
test_whisper_save_guidance.py

scripts/whisper-save-guidance.py 단위 테스트

테스트 항목:
- 빈 상태 → 기본 guidance 생성
- active 작업 있음 → guidance에 포함
- done 파일 있음 → pending_dispatches에 포함
- idle 팀 있음 → idle_teams에 포함
- 기존 session-guidance.json 존재 시 덮어쓰기
- 파일 손상 시 graceful 처리
    N)datetime	timedeltatimezone)Pathzwhisper-save-guidance.pywhisper_save_guidancebots	base_pathreturnc                     |dz  }|j                  dd       |dz  }|j                  t        j                  d| i      d       |S )u*   bot-activity.json 픽스처 파일 생성.eventsTparentsexist_okbot-activity.jsonr   utf-8encoding)mkdir
write_textjsondumps)r   r	   
events_dirpaths       Y/home/jay/workspace/.worktrees/task-2117-dev1/scripts/tests/test_whisper_save_guidance.pymake_bot_activityr   )   sL    X%JTD1++DOODJJ~.OAK    tasksc                 `    |dz  }|j                  t        j                  d| i      d       |S )u)   task-timers.json 픽스처 파일 생성.task-timers.jsonr   r   r   )r   r   r   )r   r	   r   s      r   make_task_timersr    2   s0    ))DOODJJ/07OCKr   task_idc                 l    |dz  }|j                  dd       ||  dz  }|j                  dd       |S )u   *.done 픽스처 파일 생성.r   Tr   z.done r   r   )r   r   )r!   r	   r   	done_paths       r   make_done_filer%   9   sI    X%JTD1y..Ig.r   c                  d    t        j                  t        j                        j	                         S )N)r   nowr   utc	isoformat r   r   now_isor+   B   s    <<%//11r   hoursc                     t        j                  t        j                        t	        |       z
  }|j                         S )N)r,   )r   r'   r   r(   r   r)   )r,   dts     r   hours_ago_isor/   F   s*    	hll	#ie&<	<B<<>r   c                   @    e Zd ZdZdefdZdefdZdefdZdefdZy)TestLoadBotActivityu,   load_bot_activity() 함수 단위 테스트.tmp_pathc                 >    t         j                  |      }|i k(  sJ y)uE   events 디렉토리가 없어도 빈 dict 반환 (graceful fallback).base_dirN)r   load_bot_activityselfr2   results      r   !test_empty_dir_returns_empty_dictz5TestLoadBotActivity.test_empty_dir_returns_empty_dictS   s!    &88(8K||r   c                     ddt               di}t        ||       t        j                  |      }d|v sJ |d   d   dk(  sJ y)u    정상 bot-activity.json 파싱.dev1idlestatussincer4   r?   N)r+   r   r   r6   )r8   r2   r   r9   s       r   test_valid_file_parsedz*TestLoadBotActivity.test_valid_file_parsedX   sU    6GI>?$)&88(8Kf~h'6111r   c                     |dz  }|j                  d       |dz  j                  dd       t        j                  |      }|i k(  sJ y	)
u*   손상된 JSON 파일 → 빈 dict 반환.r   Tr   r   z
NOT_JSON{{r   r   r4   N)r   r   r   r6   r8   r2   r   r9   s       r   &test_corrupted_file_returns_empty_dictz:TestLoadBotActivity.test_corrupted_file_returns_empty_dict`   sU    (
&	)	)55lW5U&88(8K||r   c                     |dz  }|j                  d       |dz  j                  t        j                  ddi      d       t        j                  |	      }|i k(  sJ y
)u,   bots 키가 없는 JSON → 빈 dict 반환.r   TrC   r   other   r   r   r4   N)r   r   r   r   r   r6   rD   s       r   (test_missing_bots_key_returns_empty_dictz<TestLoadBotActivity.test_missing_bots_key_returns_empty_dicth   sc    (
&	)	)55djj'16NY`5a&88(8K||r   N)	__name__
__module____qualname____doc__r   r:   rA   rE   rI   r*   r   r   r1   r1   P   s5    6$ 
2t 2t  r   r1   c                   @    e Zd ZdZdefdZdefdZdefdZdefdZy)TestLoadTaskTimersu+   load_task_timers() 함수 단위 테스트.r2   c                 >    t         j                  |      }|i k(  sJ y)u+   task-timers.json 없으면 빈 dict 반환.r4   N)r   load_task_timersr7   s      r   $test_missing_file_returns_empty_dictz7TestLoadTaskTimers.test_missing_file_returns_empty_dicty   s!    &777J||r   c                 z    ddddt               dddi}t        ||       t        j                  |      }d|v sJ y)u/   running 상태 작업이 반환 dict에 포함.
task-565.1	dev1-teamu   테스트 작업Nrunning)r!   team_iddescription
start_timeend_timer?   r4   )r+   r    r   rQ   )r8   r2   r   r9   s       r   test_running_task_includedz-TestLoadTaskTimers.test_running_task_included~   sT     '&1%i #	
 	)&777Jv%%%r   c                 j    |dz  j                  dd       t        j                  |      }|i k(  sJ y)u   손상된 JSON → 빈 dict.r   z	{bad jsonr   r   r4   N)r   r   rQ   r7   s      r   rE   z9TestLoadTaskTimers.test_corrupted_file_returns_empty_dict   s:    	&	&22;2Q&777J||r   c                     |dz  j                  t        j                  di i      d       t        j	                  |      }|i k(  sJ y)u   tasks 키 없으면 빈 dict.r   rG   r   r   r4   N)r   r   r   r   rQ   r7   s      r   )test_missing_tasks_key_returns_empty_dictz<TestLoadTaskTimers.test_missing_tasks_key_returns_empty_dict   sH    	&	&224::wm3LW^2_&777J||r   N)	rJ   rK   rL   rM   r   rR   r[   rE   r^   r*   r   r   rO   rO   v   s5    5T 
&4 & t $ r   rO   c                   @    e Zd ZdZdefdZdefdZdefdZdefdZy)TestScanDoneFilesu*   scan_done_files() 함수 단위 테스트.r2   c                 >    t         j                  |      }|g k(  sJ y)u,   events 디렉토리 없으면 빈 리스트.r4   N)r   scan_done_filesr7   s      r   %test_no_events_dir_returns_empty_listz7TestScanDoneFiles.test_no_events_dir_returns_empty_list   s!    &666I||r   c                 T    t        d|       t        j                  |      }d|v sJ y)uA   *.done 파일이 있으면 task_id가 반환 리스트에 포함.
task-564.1r4   Nr%   r   rb   r7   s      r   test_done_file_detectedz)TestScanDoneFiles.test_done_file_detected   s-    |X.&666Iv%%%r   c                     |dz  }|j                  d       |dz  j                  dd       |dz  j                  d	d       t        j                  |
      }d|vsJ y)u#   *.done 이 아닌 파일은 무시.r   TrC   r   z{}r   r   ztask-100.done.clearr#   r4   ztask-100N)r   r   r   rb   rD   s       r   test_non_done_files_ignoredz-TestScanDoneFiles.test_non_done_files_ignored   sp    (
&	)	)55dW5M	+	+77W7M&666I'''r   c                 x    t        d|       t        d|       t        j                  |      }d|v sJ d|v sJ y)u%   여러 .done 파일이 모두 포함.re   rT   r4   Nrf   r7   s      r   test_multiple_done_filesz*TestScanDoneFiles.test_multiple_done_files   sE    |X.|X.&666Iv%%%v%%%r   N)	rJ   rK   rL   rM   r   rc   rg   ri   rk   r*   r   r   r`   r`      s5    4d 
& &(D (& &r   r`   c                   @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestGenerateGuidanceu,   generate_guidance() 함수 단위 테스트.c                 p    t         j                  i i g       }d|v sJ d|v sJ d|v sJ d|v sJ d|v sJ y)u,   빈 상태에서도 출력 구조가 완전.r   r   done_task_idslast_sessionguidancepending_dispatches
idle_teamsactive_tasksN)r   generate_guidance)r8   r9   s     r   (test_empty_state_returns_valid_structurez=TestGenerateGuidance.test_empty_state_returns_valid_structure   sh    &88 9 

 '''V####v---v%%%'''r   c                 ~    dddddt               ddi}t        j                  i |g       }d|d   v sJ d|d	   v sJ y)
u?   running 작업이 guidance 텍스트와 active_tasks에 포함.rT   rU   	   테스트rV   Nr!   rW   rX   r?   rY   rZ   ro   ru   rr   )r+   r   rv   r8   r   r9   s      r   $test_active_task_appears_in_guidancez9TestGenerateGuidance.test_active_task_appears_in_guidance   so     '&*#%i 	
 '88 9 

 vn5555vj1111r   c                 |    t         j                  i i dg      }dj                  |d         }d|v sJ d|d   v sJ y)u4   done 파일 task_id가 pending_dispatches에 포함.re   ro    rs   rr   N)r   rv   join)r8   r9   pending_texts      r   ,test_done_task_appears_in_pending_dispatcheszATestGenerateGuidance.test_done_task_appears_in_pending_dispatches   sY    &88'. 9 
 xx'; <=|+++vj1111r   c                     ddt        d      di}t        j                  |i g       }t        d |d   D              sJ y)	u3   3시간 이상 idle인 팀이 idle_teams에 포함.dev2r=      r>   ro   c              3   $   K   | ]  }d |v  
 yw)r   Nr*   .0ts     r   	<genexpr>zFTestGenerateGuidance.test_idle_team_3h_plus_appears.<locals>.<genexpr>  s     =16Q;=   rt   Nr/   r   rv   anyr8   r   r9   s      r   test_idle_team_3h_plus_appearsz3TestGenerateGuidance.test_idle_team_3h_plus_appears   sY      &q)
 '88 9 

 =|(<====r   c                     ddt        d      di}t        j                  |i g       }t        d |d   D              rJ y)	u9   3시간 미만 idle은 idle_teams에 포함되지 않음.r<   r=   rH   r>   ro   c              3   $   K   | ]  }d |v  
 ywr<   Nr*   r   s     r   r   zHTestGenerateGuidance.test_idle_team_under_3h_excluded.<locals>.<genexpr>       Aqv{Ar   rt   Nr   r   s      r    test_idle_team_under_3h_excludedz5TestGenerateGuidance.test_idle_team_under_3h_excluded  s\      &q)
 '88 9 

 AF<,@AAAAAr   c                     ddt        d      di}t        j                  |i g       }t        d |d   D              rJ y)	u;   processing 상태 봇은 idle_teams에 포함되지 않음.r<   
processing
   r>   ro   c              3   $   K   | ]  }d |v  
 ywr   r*   r   s     r   r   zMTestGenerateGuidance.test_processing_bot_not_in_idle_teams.<locals>.<genexpr>!  r   r   rt   Nr   r   s      r   %test_processing_bot_not_in_idle_teamsz:TestGenerateGuidance.test_processing_bot_not_in_idle_teams  s\     &&r*
 '88 9 

 AF<,@AAAAAr   c                 l    t         j                  i i g       }t        j                  |d         }|J y)u&   last_session 필드가 ISO8601 형식.ro   rq   N)r   rv   r   fromisoformat)r8   r9   r.   s      r   test_last_session_is_iso_formatz4TestGenerateGuidance.test_last_session_is_iso_format#  sA    &88 9 
 ##F>$:;~~r   c           	          dddddt        d      t        d      di}t        j                  i |g       }d|d	   vsJ y
)u?   completed 상태 작업은 active_tasks에 포함되지 않음.z
task-500.1rU   u   완료된 작업	completed   rH   rz   ro   ru   N)r/   r   rv   r{   s      r   'test_completed_task_not_in_active_tasksz<TestGenerateGuidance.test_completed_task_not_in_active_tasks.  sd     '&1%+A.)!,	
 '88 9 

 6.#9999r   N)rJ   rK   rL   rM   rw   r|   r   r   r   r   r   r   r*   r   r   rm   rm      s/    6(2(
2>BB	:r   rm   c                   @    e Zd ZdZdefdZdefdZdefdZdefdZy)TestSaveGuidanceu(   save_guidance() 함수 단위 테스트.r2   c                 v    ddg g g d}t         j                  ||       |dz  dz  }|j                         sJ y)u*   session-guidance.json 파일이 생성됨.2026-03-15T02:05:00u   테스트 가이던스rq   rr   rs   rt   ru   r4   whispersession-guidance.jsonN)r   save_guidanceexists)r8   r2   dataout_paths       r   test_creates_filez"TestSaveGuidance.test_creates_fileJ  sO     20"$
 	++D8+Di'*AA   r   c                     dddgdgdgd}t         j                  ||       |dz  d	z  }t        j                  |j	                  d
            }||k(  sJ y)u   저장된 파일이 valid JSON.r   ry   u   task-1 확인 필요z	dev2-teamrT   r   r4   r   r   r   r   N)r   r   r   loads	read_text)r8   r2   r   r   loadeds        r   "test_written_content_is_valid_jsonz3TestSaveGuidance.test_written_content_is_valid_jsonW  sn     2##9":&-)N
 	++D8+Di'*AAH...@A~~r   c                 4   |dz  }|j                  d       |dz  }|j                  t        j                  ddi      d       d	d
g g g d}t        j                  ||       t        j                  |j                  d            }|d   d
k(  sJ d|vsJ y)u4   기존 session-guidance.json이 있으면 덮어씀.r   TrC   r   oldr   r   r   z2026-03-15T09:00:00u   새 가이던스r   r4   rr   N)r   r   r   r   r   r   r   r   )r8   r2   whisper_dirr   new_datar   s         r   test_overwrites_existing_filez.TestSaveGuidance.test_overwrites_existing_filee  s    *$'!88DJJv7'J 2*"$
 	++Hx+HH...@Aj!%7777F"""r   c                 l    ddg g g d}t         j                  ||       |dz  j                         sJ y)u0   whisper 디렉토리가 없으면 자동 생성.r   r#   r   r4   r   N)r   r   is_dir)r8   r2   r   s      r   #test_creates_whisper_dir_if_missingz4TestSaveGuidance.test_creates_whisper_dir_if_missingx  sF     2"$
 	++D8+D9$,,...r   N)	rJ   rK   rL   rM   r   r   r   r   r   r*   r   r   r   r   G  s5    2!$ !4 #d #&
/D 
/r   r   c                   X    e Zd ZdZdefdZdefdZdefdZdefdZdefdZ	defdZ
y	)
TestIntegrationu(   전체 파이프라인 통합 테스트.r2   c                     t         j                  |       |dz  dz  }|j                         sJ t        j                  |j                  d            }d|v sJ y)uF   모든 입력 파일 없어도 정상 완료 및 출력 파일 생성.r4   r   r   r   r   rr   N)r   runr   r   r   r   )r8   r2   r   r   s       r   test_empty_state_no_crashz)TestIntegration.test_empty_state_no_crash  s]    !!8!4i'*AA   zz(,,g,>?T!!!r   c                     dddddt               ddi}t        ||       t        j                  |       |dz  d	z  }t	        j
                  |j                  d
            }d|d   v sJ d|d   v sJ y)u,   active 작업이 있으면 출력에 포함.rT   rU   u   구현 작업rV   Nrz   r4   r   r   r   r   ru   rr   )r+   r    r   r   r   r   r   r8   r2   r   r   r   s        r   test_active_task_in_outputz*TestIntegration.test_active_task_in_output  s     '&.#%i 	
 	)!!8!4i'*AAzz(,,g,>?tN3333tJ////r   c                     t        d|       t        j                  |       |dz  dz  }t        j                  |j                  d            }dj                  |d         }d|v sJ y	)
u6   done 파일이 있으면 pending_dispatches에 포함.re   r4   r   r   r   r   r~   rs   N)r%   r   r   r   r   r   r   )r8   r2   r   r   r   s        r   test_done_file_in_outputz(TestIntegration.test_done_file_in_output  sk    |X.!!8!4i'*AAzz(,,g,>?xx%9 :;|+++r   c                     ddt        d      di}t        ||       t        j                  |       |dz  dz  }t	        j
                  |j                  d	            }t        d
 |d   D              sJ y)u0   3시간 이상 idle 팀이 idle_teams에 포함.dev3r=      r>   r4   r   r   r   r   c              3   $   K   | ]  }d |v  
 yw)r   Nr*   r   s     r   r   z;TestIntegration.test_idle_team_in_output.<locals>.<genexpr>  s     ;16Q;;r   rt   N)r/   r   r   r   r   r   r   r   )r8   r2   r   r   r   s        r   test_idle_team_in_outputz(TestIntegration.test_idle_team_in_output  s|     va0@A
 	$)!!8!4i'*AAzz(,,g,>?;\(:;;;;r   c                    t         j                  |       dddddt               ddi}t        ||       t         j                  |       |dz  d	z  }t	        j
                  |j                  d
            }d|d   v sJ y)u*   기존 session-guidance.json 덮어쓰기.r4   z
task-999.1rU   u
   새 작업rV   Nrz   r   r   r   r   ru   )r   r   r+   r    r   r   r   r   s        r    test_overwrite_existing_guidancez0TestIntegration.test_overwrite_existing_guidance  s     	!!8!4 '&+#%i 	
 	)!!8!4i'*AAzz(,,g,>?tN3333r   c                     |dz  }|j                  d       |dz  j                  dd       |dz  j                  dd       t        j                  |	       |d
z  dz  }|j	                         sJ y)u6   손상된 입력 파일들이 있어도 정상 완료.r   TrC   r   zCORRUPT{{{{r   r   r   r4   r   r   N)r   r   r   r   r   )r8   r2   r   r   s       r   test_corrupted_inputs_gracefulz.TestIntegration.test_corrupted_inputs_graceful  s    (
&	)	)55mg5V	&	&22=72S!!8!4i'*AA   r   N)rJ   rK   rL   rM   r   r   r   r   r   r   r   r*   r   r   r   r     sM    2"$ "04 0&, ,	< 	<4 4.	!t 	!r   r   )&rM   importlib.util	importlibr   sysr   r   r   pathlibr   pytest__file__parent_SCRIPTS_DIRr   insertstr_MODULE_PATHutilspec_from_file_locationspecmodule_from_specr   loaderexec_moduledictr   r    r%   r+   floatr/   r1   rO   r`   rm   r   r   r*   r   r   <module>r      si     
 2 2   H~$$++ 3|$ % 88~~--.E|T !77= {{    - .D T d D T d C D T 2 2 3  L" "T& &Ly: y:B;/ ;/FR! R!r   