
    (<i$                     4   d Z ddlZddl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
Z
 e	ej                  j                  dd            dz  dz  Zd	 Z e       Zej                   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 G d d      Zej6                  Zej:                  Z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%ejL                  Z&ejN                  Z' G d( d)      Z( G d* d+      Z) G d, d-      Z* G d. d/      Z+ G d0 d1      Z, G d2 d3      Z- G d4 d5      Z. G d6 d7      Z/ G d8 d9      Z0y):u  
test_task_timer.py

memory/task-timer.py 단위 테스트 (아르고스 작성)

테스트 항목:
- start_task(): 올바른 JSON 구조 반환 (status="started")
- end_task(): 올바른 JSON 구조 반환 (status="completed", duration 포함)
- end_task() 존재하지 않는 task_id → status="error"
- list_tasks(): 전체 / 상태별 조회
- _format_duration(): 초/분/시간 변환
- add_log_entry(): 일일 로그 파일 생성 확인

격리: TaskTimer(workspace_path=str(tmp_path)) 로 파일시스템 완전 격리
    N)datetime)PathWORKSPACE_ROOTz/home/jay/workspacememorytask-timer.pyc                     t         j                  j                  dt              } | J dt                t         j                  j	                  |       }| j
                  J d       | j
                  j                  |       |S )u<   하이픈이 포함된 파일명을 importlib.util로 로드
task_timeru$   모듈 스펙을 찾을 수 없음: u   모듈 로더가 없음)	importlibutilspec_from_file_location_TIMER_MODULE_PATHmodule_from_specloaderexec_module)specmodules     F/home/jay/workspace/.worktrees/task-2057-dev2/tests/test_task_timer.py_load_task_timer_moduler   #   su    >>11,@RSDXCDVCWXX^^,,T2F;;"=$=="KKF#M    c                 X    | dz  j                  dd       t        t        |             S )u<   tmp_path를 workspace로 사용하는 TaskTimer 인스턴스r   Tparentsexist_okworkspace_path)mkdir	TaskTimerstr)tmp_paths    r   timerr    6   s+     t<CM22r   c                   F    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zy)TestStartTasku?   start_task()가 올바른 JSON 구조를 반환하는지 확인c                 :    |j                  d      }|d   dk(  sJ y )Ntask-1.1statusstarted
start_taskselfr    results      r   test_returns_status_startedz)TestStartTask.test_returns_status_startedE   %    !!*-h9,,,r   c                 :    |j                  d      }|d   dk(  sJ y Nr$   task_idr'   r)   s      r   test_returns_task_idz"TestStartTask.test_returns_task_idI   s%    !!*-i J...r   c                 b    |j                  d      }d|v sJ t        j                  |d          y Nr$   
start_time)r(   r   fromisoformatr)   s      r   test_returns_start_timez%TestStartTask.test_returns_start_timeM   s3    !!*-v%%%vl34r   c                     |j                  ddd       |dz  dz  }|j                         sJ t        j                  |j	                               }d|d   v sJ y )Nr$   	dev1-team   테스트 작업team_iddescriptionr   task-timers.jsontasks)r(   existsjsonloads	read_textr*   r    r   
timer_filedatas        r   test_task_written_to_timer_filez-TestStartTask.test_task_written_to_timer_fileS   sc    [FXY(+==
  """zz*..01T']***r   c                     |j                  d       |dz  dz  }t        j                  |j                               }|d   d   d   dk(  sJ y )Ntask-2.1r   r=   r>   r%   runningr(   r@   rA   rB   rC   s        r   #test_task_status_is_running_in_filez1TestStartTask.test_task_status_is_running_in_fileZ   sS    $(+==
zz*..01G}Z(2i???r   c                 @    |j                  ddd      }|d   dk(  sJ y )Nr$   	dev2-teamu
   API 개발r:   r%   r&   r'   r)   s      r   )test_start_task_with_team_and_descriptionz7TestStartTask.test_start_task_with_team_and_description`   s,    !!*k|!\h9,,,r   c                     |j                  dddd       |dz  dz  }t        j                  |j                               }|d   d   d	   dk(  sJ y )
Nr$   r8   u   작업myproj)r;   r<   
project_idr   r=   r>   rQ   rJ   rC   s        r   test_start_task_with_project_idz-TestStartTask.test_start_task_with_project_idd   s]    [h[cd(+==
zz*..01G}Z(6(BBBr   c                     |j                  d       |dz  dz  }t        j                  |j                               }|d   d   d   J y )Nr$   r   r=   r>   end_timerJ   rC   s        r   *test_start_task_end_time_is_none_initiallyz8TestStartTask.test_start_task_end_time_is_none_initiallyj   sQ    $(+==
zz*..01G}Z(4<<<r   c                     |j                  d       |dz  dz  }t        j                  |j                               }|d   d   d   J y )Nr$   r   r=   r>   duration_secondsrJ   rC   s        r   *test_start_task_duration_is_none_initiallyz8TestStartTask.test_start_task_duration_is_none_initiallyp   sR    $(+==
zz*..01G}Z();<DDDr   N)__name__
__module____qualname____doc__r,   r1   r6   rF   rK   rN   rR   rU   rX    r   r   r"   r"   B   s5    I-/5+@-C=Er   r"   c                   "    e Zd ZdZd Zd Zd Zy)TestStartTaskCompletedGuardu@   completed 상태 task 덮어쓰기 방지 테스트 (task-464.1)c                     |j                  d       |j                  d       |j                  d      }|d   dk(  sJ d|d   j                         v sd|d   j                         v sJ yy)u<   이미 completed된 task_id로 start 시도 → error 반환r$   r%   error	completedreasonzalready completedN)r(   end_tasklowerr)   s      r   'test_start_completed_task_returns_errorzCTestStartTaskCompletedGuard.test_start_completed_task_returns_errorz   sy    $z"!!*-h7***fX.4466:MQWX`QaQgQgQi:iii:i6r   c                    |j                  ddd       |j                  d       |j                  ddd       |dz  dz  }d	d
l} |j                  |j	                               }|d   d   d   dk(  sJ |d   d   d   dk(  sJ y
)u7   completed task start 시도 후 원본 데이터 유지r$   r8   u   원본 작업r:   rM      덮어쓰기 시도r   r=   r   Nr>   r%   rb   r;   r(   rd   r@   rA   rB   )r*   r    r   rD   r@   rE   s         r   ,test_start_completed_task_preserves_originalzHTestStartTaskCompletedGuard.test_start_completed_task_preserves_original   s    [oVz"[F[\(+==
tzz*..01G}Z(2kAAAG}Z(3{BBBr   c                 \    |j                  d       |j                  d      }|d   dk(  sJ y)u1   기존 running 중복 등록 거부 동작 유지r$   r%   already_runningNr'   r)   s      r    test_running_task_still_rejectedz<TestStartTaskCompletedGuard.test_running_task_still_rejected   s4    $!!*-h#4444r   N)rY   rZ   r[   r\   rf   rj   rm   r]   r   r   r_   r_   w   s    Jj
C5r   r_   c                   L    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)TestEndTasku=   end_task()가 올바른 JSON 구조를 반환하는지 확인c                 \    |j                  d       |j                  d      }|d   dk(  sJ y Nr$   r%   rb   r(   rd   r)   s      r   test_returns_status_completedz)TestEndTask.test_returns_status_completed   s1    $
+h;...r   c                 \    |j                  d       |j                  d      }|d   dk(  sJ y r/   rr   r)   s      r   r1   z TestEndTask.test_returns_task_id   s1    $
+i J...r   c                     |j                  d       |j                  d      }d|v sJ t        j                  |d          y r3   r(   rd   r   r5   r)   s      r   r6   z#TestEndTask.test_returns_start_time   s?    $
+v%%%vl34r   c                     |j                  d       |j                  d      }d|v sJ t        j                  |d          y )Nr$   rT   rv   r)   s      r   test_returns_end_timez!TestEndTask.test_returns_end_time   s?    $
+V###vj12r   c                     |j                  d       |j                  d      }d|v sJ t        |d   t        t        f      sJ |d   dk\  sJ y )Nr$   rW   r   )r(   rd   
isinstanceintfloatr)   s      r   test_returns_duration_secondsz)TestEndTask.test_returns_duration_seconds   sZ    $
+!V+++&!34sElCCC()Q...r   c                     |j                  d       |j                  d      }d|v sJ t        |d   t              sJ t	        |d         dkD  sJ y )Nr$   duration_humanr   )r(   rd   rz   r   lenr)   s      r   test_returns_duration_humanz'TestEndTask.test_returns_duration_human   s[    $
+6)))&!12C8886*+,q000r   c                     |j                  d       |j                  d       |dz  dz  }t        j                  |j	                               }|d   d   d   dk(  sJ y )Nr$   r   r=   r>   r%   rb   ri   rC   s        r    test_task_status_updated_in_filez,TestEndTask.test_task_status_updated_in_file   s_    $z"(+==
zz*..01G}Z(2kAAAr   c                     |j                  d       |j                  d       |dz  dz  dz  }|j                         sJ y)uC   end_task() 호출 시 .done 이벤트 파일이 생성되어야 함r$   r   eventstask-1.1.doneN)r(   rd   r?   r*   r    r   	done_files       r   test_done_event_file_createdz(TestEndTask.test_done_event_file_created   sB    $z"x'(2_D	!!!r   c                     |j                  d       |j                  d       |dz  dz  dz  }t        j                  |j	                               }|d   dk(  sJ d|v sJ d|v sJ y )Nr$   r   r   r   r0   rT   rW   ri   )r*   r    r   r   
event_datas        r   (test_done_event_file_contains_valid_jsonz4TestEndTask.test_done_event_file_contains_valid_json   sv    $z"x'(2_D	ZZ	 3 3 56
)$
222Z'''!Z///r   c                     |j                  d       |j                  d      }t        j                  |d         }t        j                  |d         }||k\  sJ y )Nr$   r4   rT   rv   )r*   r    r+   startends        r   !test_end_time_is_after_start_timez-TestEndTask.test_end_time_is_after_start_time   sT    $
+&&vl';<$$VJ%78e||r   N)rY   rZ   r[   r\   rs   r1   r6   rx   r}   r   r   r   r   r   r]   r   r   ro   ro      s8    G/
/
53/1B"0r   ro   c                   "    e Zd ZdZd Zd Zd Zy)TestEndTaskNotFounduI   존재하지 않는 task_id로 end_task() 호출 시 에러 응답 검증c                 :    |j                  d      }|d   dk(  sJ y )Ntask-nonexistentr%   ra   )rd   r)   s      r   test_returns_status_errorz-TestEndTaskNotFound.test_returns_status_error   s$     23h7***r   c                 p    |j                  d      }d|v sJ d|d   v sd|d   j                         v sJ y y )Nr   rc   nonexistentz	not found)rd   re   r)   s      r   #test_error_response_contains_reasonz7TestEndTaskNotFound.test_error_response_contains_reason   sM     236!!!x 00K6(CSCYCYC[4[[[4[0r   c                 `    |j                  d       |dz  dz  dz  }|j                         rJ y )Nz
task-ghostr   r   ztask-ghost.done)rd   r?   r   s       r   "test_no_done_file_created_on_errorz6TestEndTaskNotFound.test_no_done_file_created_on_error   s8    |$x'(25FF	##%%%%r   N)rY   rZ   r[   r\   r   r   r   r]   r   r   r   r      s    S+\
&r   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestGetTaskStatusuF   get_task_status()가 올바른 작업 상태를 반환하는지 확인c                 .    |j                  d      }|J y )Nr   get_task_statusr)   s      r   &test_returns_none_for_nonexistent_taskz8TestGetTaskStatus.test_returns_none_for_nonexistent_task   s    &&'9:~~r   c                 d    |j                  d       |j                  d      }|J |d   dk(  sJ y )Nr$   r%   rI   r(   r   r)   s      r   test_returns_running_statusz-TestGetTaskStatus.test_returns_running_status  s?    $&&z2!!!h9,,,r   c                 ~    |j                  d       |j                  d       |j                  d      }|d   dk(  sJ y rq   )r(   rd   r   r)   s      r   test_returns_completed_statusz/TestGetTaskStatus.test_returns_completed_status	  s?    $z"&&z2h;...r   c                     |j                  ddd       |j                  d      }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y )Nr$   r8   	   테스트r:   r0   r;   r<   r   r)   s      r   test_returns_task_data_dictz-TestGetTaskStatus.test_returns_task_data_dict  s`    [kR&&z2i J...i K///m$333r   N)rY   rZ   r[   r\   r   r   r   r   r]   r   r   r   r      s    P-/4r   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
)TestListTasksuD   list_tasks()가 올바르게 작업 목록을 반환하는지 확인c                 L    |j                         }|d   dk(  sJ |d   g k(  sJ y )Ntotalr   r>   
list_tasksr)   s      r   test_empty_list_initiallyz'TestListTasks.test_empty_list_initially  s4    !!#g!###g"$$$r   c                 |    |j                  d       |j                  d       |j                         }|d   dk(  sJ y Nr$   rH   r      r(   r   r)   s      r   test_total_count_after_startz*TestListTasks.test_total_count_after_start$  s>    $$!!#g!###r   c                     |j                  d       |j                  d       |j                  d       |j                         }|d   dk(  sJ y r   r(   rd   r   r)   s      r   test_list_all_tasksz!TestListTasks.test_list_all_tasks*  sJ    $$z"!!#g!###r   c                     |j                  d       |j                  d       |j                  d       |j                  d      }|d   dk(  sJ |d   d   d	   dk(  sJ y )
Nr$   rH   rI   r%   r      r>   r   r0   r   r)   s      r   test_list_filter_by_runningz)TestListTasks.test_list_filter_by_running1  sk    $$z"!!!3g!###gq!),
:::r   c                     |j                  d       |j                  d       |j                  d       |j                  d      }|d   dk(  sJ |d   d   d	   dk(  sJ y )
Nr$   rH   rb   r   r   r   r>   r   r0   r   r)   s      r   test_list_filter_by_completedz+TestListTasks.test_list_filter_by_completed9  sk    $$z"!!!5g!###gq!),
:::r   c                 <    |j                         }d|v sJ d|v sJ y )Nr   r>   r   r)   s      r   0test_list_returns_dict_with_total_and_tasks_keysz>TestListTasks.test_list_returns_dict_with_total_and_tasks_keysA  s,    !!#&   &   r   c                     |j                  ddd       |j                         }|d   d   }d|v sJ d|v sJ d	|v sJ y
)u>   각 task 항목에 필수 필드가 포함되어 있어야 함r$   r8   r   r:   r>   r   r0   r%   r4   Nr   )r*   r    r+   tasks       r   test_list_tasks_data_structurez,TestListTasks.test_list_tasks_data_structureF  s]    [kR!!#gq!D   4t###r   c                 r    |j                  d       |j                  d      }|d   dk(  sJ |d   g k(  sJ y )Nr$   r   r   r   r   r>   r   r)   s      r   /test_list_with_nonexistent_status_returns_emptyz=TestListTasks.test_list_with_nonexistent_status_returns_emptyO  sG    $!!!7g!###g"$$$r   N)rY   rZ   r[   r\   r   r   r   r   r   r   r   r   r]   r   r   r   r     s-    N%
$$;;!
$%r   r   c                       e Zd ZdZ ej
                  d      d        Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zd Zd Zy)TestFormatDurationu_   _format_duration()이 초/분/시간을 올바른 한국어 문자열로 변환하는지 확인T)autousec                     || _         y N)r    )r*   r    s     r   _timer_instancez"TestFormatDuration._timer_instance^  s	    
r   c                 D    | j                   j                  d      dk(  sJ y )N      30초r    _format_durationr*   s    r   test_seconds_under_60z(TestFormatDuration.test_seconds_under_60b      zz**2.'999r   c                 D    | j                   j                  d      dk(  sJ y )Nr   u   0초r   r   s    r   test_seconds_zeroz$TestFormatDuration.test_seconds_zeroe  s    zz**1-777r   c                 D    | j                   j                  d      dk(  sJ y )N;   u   59초r   r   s    r   test_seconds_59z"TestFormatDuration.test_seconds_59h  r   r   c                 R    | j                   j                  d      }d|v sJ d|v sJ y )N<   u   분   1분r   r*   r+   s     r   %test_seconds_exactly_60_is_one_minutez8TestFormatDuration.test_seconds_exactly_60_is_one_minutek  s0    ,,R0r   c                 R    | j                   j                  d      }d|v sJ d|v sJ y )NZ   r   r   r   r   s     r   test_minutes_and_secondsz+TestFormatDuration.test_minutes_and_secondsp  s2    ,,R0&   r   c                 F    | j                   j                  d      }d|v sJ y )Nx   u   2분r   r   s     r   test_minutes_without_secondsz/TestFormatDuration.test_minutes_without_secondsu  s$    ,,S1r   c                 R    | j                   j                  d      }d|v sJ d|vsJ y )Ni  u   59분u   시간r   r   s     r   test_just_under_one_hourz+TestFormatDuration.test_just_under_one_houry  s2    ,,T2&   v%%%r   c                 F    | j                   j                  d      }d|v sJ y )Ni     1시간r   r   s     r   test_exactly_one_hourz(TestFormatDuration.test_exactly_one_hour~  $    ,,T2F"""r   c                 R    | j                   j                  d      }d|v sJ d|v sJ y )Ni  r   u   30분r   r   s     r   test_one_hour_thirty_minutesz/TestFormatDuration.test_one_hour_thirty_minutes  s2    ,,T2F"""&   r   c                 F    | j                   j                  d      }d|v sJ y )Ni   u   2시간r   r   s     r   test_two_hoursz!TestFormatDuration.test_two_hours  r   r   c                 Z    t        | j                  j                  d      t              sJ y )Nd   )rz   r    r   r   r   s    r   test_format_returns_stringz-TestFormatDuration.test_format_returns_string  s!    $**55c:C@@@r   N)rY   rZ   r[   r\   pytestfixturer   r   r   r   r   r   r   r   r   r   r   r   r]   r   r   r   r   [  sZ    iV^^D! ":8: 
!
 &
#!
#A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d
 Zd Zd Zd Zd Zy)TestAddLogEntryuU   add_log_entry()가 일일 로그 파일을 올바르게 생성/추가하는지 확인c                 :    |j                  d      }|d   dk(  sJ y )N   테스트 메시지r%   loggedadd_log_entryr)   s      r   test_returns_status_loggedz*TestAddLogEntry.test_returns_status_logged  s&    $$%:;h8+++r   c                 :    |j                  d      }|d   dk(  sJ y )Nr   messager   r)   s      r   test_returns_messagez$TestAddLogEntry.test_returns_message  s'    $$%:;i $9999r   c                 >    |j                  dd      }|d   dk(  sJ y )Nr   note
entry_typetyper   r)   s      r   test_returns_entry_typez'TestAddLogEntry.test_returns_entry_type  s)    $$[V$Df~'''r   c                 b    |j                  d      }d|v sJ t        j                  |d          y )Nr   	timestamp)r   r   r5   r)   s      r   test_returns_timestampz&TestAddLogEntry.test_returns_timestamp  s3    $$[1f$$$vk23r   c                     |j                  d       |dz  dz  }t        |j                  d            }t        |      dk(  sJ y )Nu   로그 항목 테스트r   daily*.mdr   )r   listglobr   r*   r    r   	daily_dir	log_filess        r   test_creates_daily_log_filez+TestAddLogEntry.test_creates_daily_log_file  sE    56x''1	/0	9~"""r   c                     |j                  d       t        j                         j                  d      }|dz  dz  }|| dz  }|j	                         sJ y )Nu   날짜 테스트%Y-%m-%dr   r  .md)r   r   nowstrftimer?   )r*   r    r   	today_strr  expected_files         r   !test_daily_log_file_name_is_todayz1TestAddLogEntry.test_daily_log_file_name_is_today  sY    ./LLN++J7	x''1	!yk$55##%%%r   c                     |j                  d       t        j                         j                  d      }|dz  dz  | dz  }|j	                  d      }d|v sJ y )Nu!   고유한 메시지 내용 XYZ123r  r   r  r  utf-8encodingr   r   r  r  rB   r*   r    r   r  log_filecontents         r   test_log_file_contains_messagez.TestAddLogEntry.test_log_file_contains_message  sc    ?@LLN++J7	h&0i[3DD$$g$62g===r   c                     |j                  d       |j                  d       t        j                         j                  d      }|dz  dz  | dz  }|j	                  d      }d|v sJ d|v sJ y )	Nu   첫 번째 메시지u   두 번째 메시지r  r   r  r  r  r  r  r  s         r   "test_multiple_log_entries_appendedz2TestAddLogEntry.test_multiple_log_entries_appended  s    2323LLN++J7	h&0i[3DD$$g$6%000%000r   c                 R    |j                  dd      }|d   dk(  sJ |d   dk(  sJ y )Nu   팀 위임 기록dispatchr   r%   r   r  r   r)   s      r   test_dispatch_type_log_entryz,TestAddLogEntry.test_dispatch_type_log_entry  s=    $$%8Z$Ph8+++f~+++r   c                     |j                  dd       t        j                         j                  d      }|dz  dz  | dz  }|j	                  d	      }d|v sJ y )
Nu   중요 의사결정 내용decisionr   r  r   r  r  r  r  r  r  s         r   test_decision_type_log_entryz,TestAddLogEntry.test_decision_type_log_entry  sh    8ZPLLN++J7	h&0i[3DD$$g$6+w666r   c                     |j                  dd       t        j                         j                  d      }|dz  dz  | dz  }|j	                  d	      }d|v sJ y )
Nu   시스템 설정 변경systemr   r  r   r  r  r  r  r  r  s         r   test_system_type_log_entryz*TestAddLogEntry.test_system_type_log_entry  sh    5(KLLN++J7	h&0i[3DD$$g$6(G333r   c                     |j                  ddd       |dz  dz  }t        |j                  d            }t        |      dk(  sJ y	)
uR   start_task()에 team_id 또는 description이 있으면 일일 로그에 기록됨r$   r8   u   로그 테스트r:   r   r  r  r   Nr(   r	  r
  r   r  s        r   #test_start_task_writes_to_daily_logz3TestAddLogEntry.test_start_task_writes_to_daily_log  sL    [FXYx''1	/0	9~"""r   c                     |j                  ddd       |j                  d       t        j                         j	                  d      }|dz  dz  | dz  }|j                  d	
      }d|v sd|v sJ yy)uM   end_task()도 team_id/description이 있으면 일일 로그에 완료 기록r$   r8   u   완료 로그 테스트r:   r  r   r  r  r  r  rb   u   완료N)r(   rd   r   r  r  rB   r  s         r   !test_end_task_writes_to_daily_logz1TestAddLogEntry.test_end_task_writes_to_daily_log  s    [F_`z"LLN++J7	h&0i[3DD$$g$6g%W)<<<)<%r   N)rY   rZ   r[   r\   r   r   r  r  r  r  r  r!  r$  r'  r*  r-  r/  r]   r   r   r   r     sF    _,:(4
#&>1,
74#=r   r   c                      d fd	}|S )uB   재귀 방지를 위해 원본 __init__을 캡처한 패치 생성c                 0    t        | t                     y r   )_original_initr   )r*   r   r   s     r   patched_initz(_make_patched_init.<locals>.patched_init  s    tS]+r   r   r]   )r   r3  s   ` r   _make_patched_initr4    s    , r   c                   R    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zy)TestCLIMainu/   CLI main() 함수의 주요 명령어 테스트c                    |j                  t        j                  dt        |             |j                  t        dg d       t                |j                         }t        j                  |j                        }|d   dk(  sJ y)u8   start 명령어가 JSON 출력을 생성하는지 확인__init__argv)r   r   r$   --teamr8   z--descu   CLI 테스트r%   r&   N
setattr_task_timer_modr   r4  sys
_main_func
readouterrr@   rA   outr*   r   monkeypatchcapsyscapturedoutputs         r   test_start_commandzTestCLIMain.test_start_command  sq    O55zCUV^C_`q	
 	$$&HLL)h9,,,r   c                 $   |j                  t        j                  dt        |             |j                  t        dg d       t                |j                  t        dg d       t                |j                         }d|j                  v sJ y)u8   start 후 end 명령어가 정상 동작하는지 확인r8  r9  )r   r   rH   )r   r   rH   rb   Nr<  r=  r   r4  r>  r?  r@  rA  r*   r   rC  rD  rE  s        r   test_end_command_after_startz(TestCLIMain.test_end_command_after_start  sq    O55zCUV^C_`C)OPC)MN$$&hll***r   c                    |j                  t        j                  dt        |             |j                  t        dddg       t                |j                         }t        j                  |j                        }d|v sJ d|v sJ y)u9   list 명령어가 작업 목록을 반환하는지 확인r8  r9  r   r	  r   r>   Nr;  rB  s         r   test_list_commandzTestCLIMain.test_list_command  sy    O55zCUV^C_`C/6)BC$$&HLL)&   &   r   c                 $   |j                  t        j                  dt        |             |j                  t        dg d       t                |j                  t        dg d       t                |j                         }d|j                  v sJ y)u;   status 명령어가 작업 상태를 반환하는지 확인r8  r9  )r   r   task-3.1)r   r%   rO  rI   NrI  rJ  s        r   test_status_commandzTestCLIMain.test_status_command  sq    O55zCUV^C_`C)OPC)PQ$$&HLL(((r   c                 0   |j                  t        j                  dt        |             |j                  t        dg d       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u6   존재하지 않는 task status 조회 시 sys.exit(1)r8  r9  )r   r%   z
task-999.9Nr   r<  r=  r   r4  r>  r   raises
SystemExitr?  valuecoder*   r   rC  exc_infos       r   test_status_not_found_exitsz'TestCLIMain.test_status_not_found_exits(  st    O55zCUV^C_`C)RS]]:& 	(L	~~""a'''	 	   BBc                    |j                  t        j                  dt        |             |j                  t        dg d       t                |j                         }t        j                  |j                        }|d   dk(  sJ y)u8   log 명령어가 로그 항목을 생성하는지 확인r8  r9  )r   logu   테스트 로그 메시지--typer   r%   r   Nr;  rB  s         r   test_log_commandzTestCLIMain.test_log_command0  sl    O55zCUV^C_`C)qr$$&HLL)h8+++r   c                     |j                  t        ddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u$   인자 없이 실행 시 sys.exit(1)r9  r   Nr   r<  r>  r   rS  rT  r?  rU  rV  r*   rC  rX  s      r   test_no_args_exitszTestCLIMain.test_no_args_exits9  sW    C/):;]]:& 	(L	~~""a'''	 	s   A!!A*c                     |j                  t        dddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u(   알 수 없는 명령어 시 sys.exit(1)r9  r   invalid_cmdNr   r`  ra  s      r   test_unknown_command_exitsz&TestCLIMain.test_unknown_command_exits@  sY    C/=)IJ]]:& 	(L	~~""a'''	 	s   A""A+c                 0   |j                  t        j                  dt        |             |j                  t        dg d       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u"   잘못된 log type 시 sys.exit(1)r8  r9  )r   r\  msgr]  invalid_typeNr   rR  rW  s       r   test_log_invalid_type_exitsz'TestCLIMain.test_log_invalid_type_exitsG  st    O55zCUV^C_`C)bc]]:& 	(L	~~""a'''	 	rZ  c                    |j                  t        j                  dt        |             |j                  t        dg d       t                |j                         }t        j                  |j                        }|d   dk(  sJ y)u=   CLI에서 "start task-500" → started (task-N 형식 허용)r8  r9  )r   r   task-500r%   r&   Nr;  rB  s         r    test_start_command_task_n_formatz,TestCLIMain.test_start_command_task_n_formatO  sl    O55zCUV^C_`C)OP$$&HLL)h9,,,r   c                 $   |j                  t        j                  dt        |             |j                  t        dg d       t                |j                  t        dg d       t                |j                         }d|j                  v sJ y)u;   start task-500.1 후 CLI에서 "end task-500" → completedr8  r9  )r   r   z
task-500.1)r   r   rk  rb   NrI  rJ  s        r   test_end_command_fuzzy_matchz(TestCLIMain.test_end_command_fuzzy_matchX  sq    O55zCUV^C_`C)QRC)MN$$&hll***r   N)rY   rZ   r[   r\   rG  rK  rM  rP  rY  r^  rb  re  ri  rl  rn  r]   r   r   r6  r6    s<    9	-	+!	)(,(((-	+r   r6  c                       e Zd ZdZd Zd Zy)TestLoadTimersErroru7   _load_timers() 에러 핸들링 테스트 (lines 55-57)c                     |dz  j                  dd       |dz  dz  }|j                  dd       t        t        |            }|j                  d	i ik(  sJ y
)u'   깨진 JSON 파일 → 빈 tasks 반환r   Tr   r=   zNOT VALID JSON {{{r  r  r   r>   N)r   
write_textr   r   timers)r*   r   rD   r    s       r   !test_corrupted_json_returns_emptyz5TestLoadTimersError.test_corrupted_json_returns_emptyl  sb    	H	##D4#@(+==
2WEX7||},,,r   c                 t   ddl }|dz  j                  dd       |dz  dz  }|j                  dd	        |j                  t	        |      d       	 t        t	        |      
      }|j                  di ik(  sJ 	  |j                  t	        |      d       y#  |j                  t	        |      d       w xY w)u0   읽기 권한 없는 파일 → 빈 tasks 반환r   Nr   Tr   r=   z{"tasks": {}}r  r  r   r>   i  )osr   rr  chmodr   r   rs  )r*   r   rv  rD   r    s        r   "test_unreadable_file_returns_emptyz6TestLoadTimersError.test_unreadable_file_returns_emptyt  s    	H	##D4#@(+==
o@Z%(	-S];E<<GR=000BHHS_e,HBHHS_e,s   (B B7N)rY   rZ   r[   r\   rt  rx  r]   r   r   rp  rp  i  s    A--r   rp  c                       e Zd ZdZd Zy)TestSaveTimersErroru7   _save_timers() 에러 핸들링 테스트 (lines 72-73)c                 p   ddl }|dz  j                  dd       t        t        |            } |j                  t        |dz        d       	 dd	i|j
                  d
   d	<   |j                           |j                  t        |dz        d       y#  |j                  t        |dz        d       w xY w)uJ   읽기 전용 디렉토리에 저장 시도 → 에러 무시하고 계속r   Nr   Tr   r   im  r%   testr>   i  )rv  r   r   r   rw  rs  _save_timers)r*   r   rv  r    s       r   test_save_to_readonly_dirz-TestSaveTimersError.test_save_to_readonly_dir  s    	H	##D4#@X7X()51	6-5v,>ELL!&) BHHSH,-u5HBHHSH,-u5s   $B !B5N)rY   rZ   r[   r\   r~  r]   r   r   rz  rz    s
    A6r   rz  c                       e Zd ZdZd Zd Zy)!TestUpdatePipelineStatusEdgeCasesuC   _update_pipeline_status() 에지 케이스 (lines 186-187, 231-233)c                    |dz  j                  dd       |dz  dz  }|j                  dd       t        t        |            }|j	                  d	d
      }|d   dk(  sJ t        j                  |j                               }d|v sJ y)uM   pipeline-status.json이 깨진 JSON → 빈 dict로 초기화 (lines 186-187)r   Tr   zpipeline-status.jsonzINVALID{{{JSONr  r  r   ztask-5.1r8   r;   r%   r&   active_tasksN)r   rr  r   r   r(   r@   rA   rB   )r*   r   pipeline_filer    r+   rE   s         r   test_corrupted_pipeline_jsonz>TestUpdatePipelineStatusEdgeCases.test_corrupted_pipeline_json  s    	H	##D4#@ 8+.DD  !1G DX7!!*k!Bh9,,,zz-1134%%%r   c                 Z   ddl m}m} |dz  j                  dd       t	        t        |            } |       }t        d      |j                  j                  _        d|j                  _
        |j                  |d	|      5  |j                  d
ddi       ddd       y# 1 sw Y   yxY w)uU   pipeline-status 업데이트 실패해도 task-timer 기능은 계속 (lines 231-233)r   )	MagicMockpatchr   Tr   r   u   강제 오류Fpipeline_status_filer   r0   z	test-taskN)unittest.mockr  r  r   r   r   OSErrorparentside_effectr?   return_valueobject_update_pipeline_status)r*   r   r  r  r    	mock_paths         r   &test_pipeline_update_exception_ignoredzHTestUpdatePipelineStatusEdgeCases.test_pipeline_update_exception_ignored  s    2	H	##D4#@X7K	-4_-E	*(-	%\\%!7C 	M))'I{3KL	M 	M 	Ms   B!!B*N)rY   rZ   r[   r\   r  r  r]   r   r   r  r    s    M&Mr   r  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestAppendToSectionu<   _append_to_section() 함수 상세 테스트 (lines 313-346)c                 h   t        j                         j                  d      }|dz  dz  }|j                  dd       || dz  }d| d}|j	                  |d	
       |j                  dd       |j                  d	
      }d|v sJ |j                  d      }|j                  d      }	||	k  sJ y)uc   기존 섹션이 있고 다음 섹션이 있을 때, 다음 섹션 직전에 삽입 (lines 324-340)r  r   r  Tr   r  # u    업무일지

## 완료된 작업
- 기존 항목

## 의사결정
- 기존 의사결정

## 시스템 변경
- 기존 시스템
r  r  u   - 새 의사결정 항목   의사결정u   새 의사결정 항목   ## 시스템 변경N)r   r  r  r   rr  _append_to_sectionrB   index)
r*   r    r   r  r  r  r  r+   idx_new
idx_systems
             r   .test_section_exists_insert_before_next_sectionzBTestAppendToSection.test_section_exists_insert_before_next_section  s    LLN++J7	x''1	t4)C00yk  "n  oGg6  !<nM##W#5(F222,,89\\"78
###r   c                    t        j                         j                  d      }|dz  dz  }|j                  dd       || dz  }d| d}|j	                  |d	
       |j                  dd       |j                  d	
      }d|v sJ y)uY   기존 섹션이 있고 다음 섹션이 없을 때, 파일 끝에 추가 (lines 337-338)r  r   r  Tr   r  r  uS    업무일지

## 완료된 작업
- 기존 항목

## 의사결정
- 기존 결정r  r  u   - 새 의사결정r  u   새 의사결정Nr   r  r  r   rr  r  rB   r*   r    r   r  r  r  r  r+   s           r   #test_section_exists_no_next_sectionz7TestAppendToSection.test_section_exists_no_next_section  s    LLN++J7	x''1	t4)C00yk!z{Gg6  !5~F##W#5!V+++r   c                 "   t        j                         j                  d      }|dz  dz  }|j                  dd       || dz  }d| d}|j	                  |d	
       |j                  dd       |j                  d	
      }d|v sJ d|v sJ y)uC   섹션이 없으면 파일 끝에 새 섹션 추가 (lines 343-346)r  r   r  Tr   r  r  2    업무일지

## 완료된 작업
- 기존 항목r  r  u   - 아키텍처 논의 내용u   아키텍처 논의u   ## 아키텍처 논의u   아키텍처 논의 내용Nr  r  s           r   %test_new_section_created_when_missingz9TestAppendToSection.test_new_section_created_when_missing  s    LLN++J7	x''1	t4)C00yk!VWGg6  !?AVW##W#5'6111+v555r   c                 <   t        j                         j                  d      }|dz  dz  }|j                  dd       || dz  }d| d}|j	                  d	      rJ |j                  |d
       |j                  dd       |j                  d
      }d|v sJ y)u3   content가 \n으로 끝나지 않을 때 (line 344)r  r   r  Tr   r  r  r  
r  r  u   - 새 시스템 변경u   시스템 변경r  N)r   r  r  r   endswithrr  r  rB   r  s           r   #test_content_without_newline_endingz7TestAppendToSection.test_content_without_newline_ending  s    LLN++J7	x''1	t4)C00 yk!VW##D)))Gg6  !9;MN##W#5$...r   c                    t        j                         j                  d      }|dz  dz  }|j                  dd       || dz  }d}|j	                  |d	       |j                  d
d       |j                  d	      }d| |v sJ y)u5   날짜 헤더가 맞지 않을 때 prepend (line 318)r  r   r  Tr   r  u>   # 2020-01-01 업무일지

## 완료된 작업
- 옛날 항목r  r  u   - 새 항목u   위임 기록r  Nr  r  s           r   %test_wrong_date_header_gets_prependedz9TestAppendToSection.test_wrong_date_header_gets_prepended  s    LLN++J7	x''1	t4)C00 VGg6  A##W#5I;6)))r   N)	rY   rZ   r[   r\   r  r  r  r  r  r]   r   r   r  r    s    F$&,6 /"*r   r  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestCLIMainEdgeCasesu,   CLI main() 추가 에지 케이스 테스트c                 0   |j                  t        j                  dt        |             |j                  t        dddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)uA   start 명령어에 task_id 누락 시 sys.exit(1) (lines 396-397)r8  r9  r   r   Nr   rR  rW  s       r    test_start_without_task_id_exitsz5TestCLIMainEdgeCases.test_start_without_task_id_exits$  sx    O55zCUV^C_`C/7)CD]]:& 	(L	~~""a'''	 	rZ  c                 0   |j                  t        j                  dt        |             |j                  t        dddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u?   end 명령어에 task_id 누락 시 sys.exit(1) (lines 430-431)r8  r9  r   r   Nr   rR  rW  s       r   test_end_without_task_id_exitsz3TestCLIMainEdgeCases.test_end_without_task_id_exits,  x    O55zCUV^C_`C/5)AB]]:& 	(L	~~""a'''	 	rZ  c                 0   |j                  t        j                  dt        |             |j                  t        dddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)uB   status 명령어에 task_id 누락 시 sys.exit(1) (lines 439-440)r8  r9  r   r%   Nr   rR  rW  s       r   !test_status_without_task_id_exitsz6TestCLIMainEdgeCases.test_status_without_task_id_exits4  sx    O55zCUV^C_`C/8)DE]]:& 	(L	~~""a'''	 	rZ  c                 0   |j                  t        j                  dt        |             |j                  t        dddg       t        j                  t              5 }t                ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)uA   log 명령어에 메시지 누락 시 sys.exit(1) (lines 458-459)r8  r9  r   r\  Nr   rR  rW  s       r   test_log_without_message_exitsz3TestCLIMainEdgeCases.test_log_without_message_exits<  r  rZ  c                    |j                  t        j                  dt        |             |j                  t        dg d       t                |j                         }t        j                  |j                        }|d   dk(  sJ y)uO   start 명령어에 위치 인자로 team_id, description 전달 (lines 414-423)r8  r9  )r   r   z	task-10.1r8   u   위치인자 설명r%   r&   Nr;  rB  s         r   test_start_with_positional_argsz4TestCLIMainEdgeCases.test_start_with_positional_argsD  sl    O55zCUV^C_`C)tu$$&HLL)h9,,,r   c                    |j                  t        j                  dt        |             |j                  t        dg d       t                |j                         }t        j                  |j                        }|d   dk(  sJ y)u=   start 명령어에 --project 플래그 전달 (lines 414-416)r8  r9  )r   r   z	task-11.1r:  r8   z	--projectrP   r%   r&   Nr;  rB  s         r   test_start_with_project_flagz1TestCLIMainEdgeCases.test_start_with_project_flagM  sq    O55zCUV^C_`n	
 	$$&HLL)h9,,,r   N)
rY   rZ   r[   r\   r  r  r  r  r  r  r]   r   r   r  r  !  s#    6((((-	-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d
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zy)TestTaskIdValidationu   task_id 형식 검증 테스트c                 "    t        d      du sJ y )Nr$   Tvalidate_task_idr   s    r   test_valid_task_id_simplez.TestTaskIdValidation.test_valid_task_id_simplee      
+t333r   c                 "    t        d      du sJ y )Nz
task-401.1Tr  r   s    r    test_valid_task_id_large_numbersz5TestTaskIdValidation.test_valid_task_id_large_numbersh      -555r   c                 "    t        d      du sJ y )Nz	task-12.3Tr  r   s    r   "test_valid_task_id_multi_digit_subz7TestTaskIdValidation.test_valid_task_id_multi_digit_subk      ,444r   c                 "    t        d      du sJ y )Nz401.1Fr  r   s    r   test_invalid_task_id_no_prefixz3TestTaskIdValidation.test_invalid_task_id_no_prefixn  s    (E111r   c                 "    t        d      du sJ y Nodin-2Fr  r   s    r   !test_invalid_task_id_wrong_prefixz6TestTaskIdValidation.test_invalid_task_id_wrong_prefixq      )U222r   c                 "    t        d      du sJ y )Nztask-401Tr  r   s    r   test_valid_task_id_no_dotz.TestTaskIdValidation.test_valid_task_id_no_dott  r  r   c                 "    t        d      du sJ y )Nz	task-1000Tr  r   s    r   test_valid_task_id_large_no_dotz4TestTaskIdValidation.test_valid_task_id_large_no_dotw  r  r   c                 :    |j                  d      }|d   dk(  sJ y )Nrk  r%   r&   r'   r)   s      r   %test_start_task_accepts_task_n_formatz:TestTaskIdValidation.test_start_task_accepts_task_n_formatz  r-   r   c                 "    t        d      du sJ y )Nz
task-abc.1Fr  r   s    r   test_invalid_task_id_alphaz/TestTaskIdValidation.test_invalid_task_id_alpha~  s    -666r   c                 "    t        d      du sJ y )N Fr  r   s    r   test_invalid_task_id_emptyz/TestTaskIdValidation.test_invalid_task_id_empty  s    #u,,,r   c                 "    t        d      du sJ y )Nz	task- 1.1Fr  r   s    r   test_invalid_task_id_spacesz0TestTaskIdValidation.test_invalid_task_id_spaces  s    ,555r   c                 "    t        d      du sJ y )Nztask-1845_3.3Tr  r   s    r   test_valid_task_id_phasez-TestTaskIdValidation.test_valid_task_id_phase      0D888r   c                 "    t        d      du sJ y )Nztask-1845_aTr  r   s    r   test_valid_task_id_parallelz0TestTaskIdValidation.test_valid_task_id_parallel      .$666r   c                 "    t        d      du sJ y )Nztask-1845_3.2_bTr  r   s    r   !test_valid_task_id_phase_parallelz6TestTaskIdValidation.test_valid_task_id_phase_parallel       12d:::r   c                 "    t        d      du sJ y )Nztask-1845+1Tr  r   s    r   test_valid_task_id_retryz-TestTaskIdValidation.test_valid_task_id_retry  r  r   c                 "    t        d      du sJ y )Nztask-1845_3.2_b+1Tr  r   s    r   test_valid_task_id_full_v2z/TestTaskIdValidation.test_valid_task_id_full_v2  s     34<<<r   c                 "    t        d      du sJ y )Nztask-1845_3.3+2Tr  r   s    r   test_valid_task_id_phase_retryz3TestTaskIdValidation.test_valid_task_id_phase_retry  r  r   c                 "    t        d      du sJ y )Nztask-1845_a+1Tr  r   s    r   !test_valid_task_id_parallel_retryz6TestTaskIdValidation.test_valid_task_id_parallel_retry  r  r   c                 "    t        d      du sJ y )Nztask-1845_AFr  r   s    r   'test_invalid_task_id_uppercase_parallelz<TestTaskIdValidation.test_invalid_task_id_uppercase_parallel      .%777r   c                 "    t        d      du sJ y )Nztask-1845_3Fr  r   s    r   test_invalid_task_id_bad_phasez3TestTaskIdValidation.test_invalid_task_id_bad_phase  r  r   c                 "    t        d      du sJ y )Nztask-1845__3.3Fr  r   s    r   &test_invalid_task_id_double_underscorez;TestTaskIdValidation.test_invalid_task_id_double_underscore  s     01U:::r   c                 L    |j                  d      }|d   dk(  sJ d|d   v sJ y )Nz
invalid-idr%   ra   zInvalid task_idrc   r'   r)   s      r   'test_start_task_rejects_invalid_task_idz<TestTaskIdValidation.test_start_task_rejects_invalid_task_id  s8    !!,/h7*** F8$4444r   c                 :    |j                  d      }|d   dk(  sJ y )N
task-100.1r%   r&   r'   r)   s      r   %test_start_task_accepts_valid_task_idz:TestTaskIdValidation.test_start_task_accepts_valid_task_id  s%    !!,/h9,,,r   N)rY   rZ   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   r  r  b  sx    )4652345-7-697;7=;988;5
-r   r  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestFuzzyMatchEndu    end_task() fuzzy match 테스트c                 \    |j                  d       |j                  d      }|d   dk(  sJ y)uC   task-1.1을 start한 뒤 end_task("task-1")로 종료 → completedr$   task-1r%   rb   Nrr   r)   s      r   #test_end_fuzzy_match_single_runningz5TestFuzzyMatchEnd.test_end_fuzzy_match_single_running  s1    $)h;...r   c                     |j                  d       |j                  d       |j                  d      }|d   dk(  sJ |d   dk(  sJ y)uZ   task-1.1과 task-1.2를 start한 뒤 end_task("task-1") → task-1.2가 종료됨 (최신)r$   ztask-1.2r  r%   rb   r0   Nrr   r)   s      r   2test_end_fuzzy_match_multiple_running_picks_latestzDTestFuzzyMatchEnd.test_end_fuzzy_match_multiple_running_picks_latest  sR    $$)h;...i J...r   c                     |j                  d       |j                  d       |j                  d      }|d   dk(  sJ |d   dk(  sJ y)ui   task-1과 task-1.1 둘 다 start한 뒤 end_task("task-1") → task-1이 종료됨 (정확 매칭 우선)r  r$   r%   rb   r0   Nrr   r)   s      r   )test_end_exact_match_preferred_over_fuzzyz;TestFuzzyMatchEnd.test_end_exact_match_preferred_over_fuzzy  sR    "$)h;...i H,,,r   c                 ~    |j                  d       |j                  d       |j                  d      }|d   dk(  sJ y)uN   task-1.1을 start + end한 뒤 (completed 상태) end_task("task-1") → errorr$   r  r%   ra   Nrr   r)   s      r   'test_end_fuzzy_no_running_returns_errorz9TestFuzzyMatchEnd.test_end_fuzzy_no_running_returns_error  s=    $z")h7***r   c                 \    |j                  d       |j                  d      }|d   dk(  sJ y)uL   task-1.1 start 후 end_task("task-1.1") → completed (기존 동작 유지)r$   r%   rb   Nrr   r)   s      r   test_end_exact_still_worksz,TestFuzzyMatchEnd.test_end_exact_still_works  s1    $
+h;...r   N)	rY   rZ   r[   r\   r  r  r   r  r  r]   r   r   r  r    s    *//-+/r   r  c                   "    e Zd ZdZd Zd Zd Zy)TestFuzzyMatchStatusu'   get_task_status() fuzzy match 테스트c                 d    |j                  d       |j                  d      }|J |d   dk(  sJ y)uF   task-1.1 start 후 get_task_status("task-1") → running 상태 반환r$   r  Nr%   rI   r   r)   s      r   test_status_fuzzy_match_singlez3TestFuzzyMatchStatus.test_status_fuzzy_match_single  s?    $&&x0!!!h9,,,r   c                     |j                  d       |j                  d       |j                  d      }|J |d   dk(  sJ y)uN   task-1, task-1.1 둘 다 start 후 get_task_status("task-1") → task-1 반환r  r$   Nr0   r   r)   s      r   !test_status_exact_match_preferredz6TestFuzzyMatchStatus.test_status_exact_match_preferred  sM    "$&&x0!!!i H,,,r   c                 .    |j                  d      }|J y)u$   get_task_status("task-999") → Noneztask-999Nr   r)   s      r   !test_status_no_match_returns_nonez6TestFuzzyMatchStatus.test_status_no_match_returns_none  s    &&z2~~r   N)rY   rZ   r[   r\   r  r
  r  r]   r   r   r  r    s    1--r   r  c                   v    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zy)TestTeamIdValidationu   team_id 형식 검증 테스트c                 "    t        d      du sJ y )Nr8   Tvalidate_team_idr   s    r   test_valid_dev1_teamz)TestTeamIdValidation.test_valid_dev1_team  r  r   c                 "    t        d      du sJ y )NrM   Tr  r   s    r   test_valid_dev2_teamz)TestTeamIdValidation.test_valid_dev2_team  r  r   c                 "    t        d      du sJ y )Nz	dev3-teamTr  r   s    r   test_valid_dev3_teamz)TestTeamIdValidation.test_valid_dev3_team  r  r   c                 "    t        d      du sJ y )Nz
anu-directTr  r   s    r   test_valid_anu_directz*TestTeamIdValidation.test_valid_anu_direct  r  r   c                 "    t        d      du sJ y )Nr  Tr  r   s    r   test_valid_empty_stringz,TestTeamIdValidation.test_valid_empty_string  s    #t+++r   c                 "    t        d      du sJ y r  r  r   s    r   test_invalid_odin_2z(TestTeamIdValidation.test_invalid_odin_2   r  r   c                 "    t        d      du sJ y)u   dev4-team은 유효한 팀 IDz	dev4-teamTNr  r   s    r   test_valid_dev4_teamz)TestTeamIdValidation.test_valid_dev4_team      ,444r   c                 "    t        d      du sJ y )Nzrandom-teamFr  r   s    r   test_invalid_randomz(TestTeamIdValidation.test_invalid_random  r  r   c                 P    |j                  dd      }|d   dk(  sJ d|d   v sJ y )Nr  r  r  r%   ra   zInvalid team_idrc   r'   r)   s      r   'test_start_task_rejects_invalid_team_idz<TestTeamIdValidation.test_start_task_rejects_invalid_team_id
  s=    !!,!Ah7*** F8$4444r   c                 >    |j                  dd      }|d   dk(  sJ y )Nr  r  r  r%   r&   r'   r)   s      r   %test_start_task_accepts_empty_team_idz:TestTeamIdValidation.test_start_task_accepts_empty_team_id  s*    !!,!;h9,,,r   c                 >    |j                  dd      }|d   dk(  sJ y )Nr  rM   r  r%   r&   r'   r)   s      r   %test_start_task_accepts_valid_team_idz:TestTeamIdValidation.test_start_task_accepts_valid_team_id  s*    !!,!Dh9,,,r   c                 "    t        d      du sJ y)u4   design 팀 ID가 유효함을 검증 (논리적 팀)designTNr  r   s    r   test_valid_design_teamz+TestTeamIdValidation.test_valid_design_team  s    )T111r   c                 "    t        d      du sJ y)u8   publishing 팀 ID가 유효함을 검증 (논리적 팀)
publishingTNr  r   s    r   test_valid_publishing_teamz/TestTeamIdValidation.test_valid_publishing_team  s    -555r   c                 "    t        d      du sJ y)u6   composite 팀 ID가 유효함을 검증 (복합업무)	compositeTNr  r   s    r   test_valid_composite_teamz.TestTeamIdValidation.test_valid_composite_team  r  r   c                 >    |j                  dd      }|d   dk(  sJ y)u7   start_task()가 design 팀 ID를 수락하는지 검증z
task-100.2r)  r  r%   r&   Nr'   r)   s      r   #test_start_task_accepts_design_teamz8TestTeamIdValidation.test_start_task_accepts_design_team#  s*    !!,!Ah9,,,r   c                 >    |j                  dd      }|d   dk(  sJ y)u;   start_task()가 publishing 팀 ID를 수락하는지 검증z
task-100.3r,  r  r%   r&   Nr'   r)   s      r   'test_start_task_accepts_publishing_teamz<TestTeamIdValidation.test_start_task_accepts_publishing_team(  s*    !!,!Eh9,,,r   c                 >    |j                  dd      }|d   dk(  sJ y)u:   start_task()가 composite 팀 ID를 수락하는지 검증z
task-100.4r/  r  r%   r&   Nr'   r)   s      r   &test_start_task_accepts_composite_teamz;TestTeamIdValidation.test_start_task_accepts_composite_team-  s*    !!,!Dh9,,,r   N)rY   rZ   r[   r\   r  r  r  r  r  r  r  r!  r#  r%  r'  r*  r-  r0  r2  r4  r6  r]   r   r   r  r    sZ    )5556,3585
--265-
-
-r   r  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestAtomicSaveu)   _save_timers() 원자적 쓰기 테스트c                     |j                  ddd       |dz  dz  }t        j                  |j                  d            }d|d	   v sJ |d	   d   d
   dk(  sJ y)u0   저장 후 파일이 유효한 JSON인지 확인r$   r8   u   원자적 저장 테스트r:   r   r=   r  r  r>   r%   rI   NrJ   rC   s        r   test_save_creates_valid_jsonz+TestAtomicSave.test_save_creates_valid_json6  sr    [Fbc(+==
zz*...@AT']***G}Z(2i???r   c                     |j                  d       |dz  }t        |j                  d            }t        |      dk(  s
J d|        y)u4   저장 후 .tmp 파일이 남아있지 않아야 함rH   r   z*.tmpr   u   임시 파일이 남아있음: Nr,  )r*   r    r   
memory_dir	tmp_filess        r   "test_save_does_not_leave_tmp_filesz1TestAtomicSave.test_save_does_not_leave_tmp_files>  sL    $(
12	9~"Q&Ei[$QQ"r   c                 N   |dz  j                  dd       t        t        |            }|j                  dd       t        t        |            }|j                  dd	       |dz  d
z  }t	        j
                  |j                  d            }d|d   v sJ d|d   v sJ y)u[   두 TaskTimer 인스턴스가 순차적으로 저장해도 데이터가 유실되지 않음r   Tr   r   r$   r8   r  rH   rM   r=   r  r  r>   N)r   r   r   r(   r@   rA   rB   )r*   r   timer1timer2rD   rE   s         r   #test_concurrent_save_preserves_dataz2TestAtomicSave.test_concurrent_save_preserves_dataE  s    	H	##D4#@#h-8*k:#h-8*k:(+==
zz*...@AT']***T']***r   c                 d    |j                  d       |dz  dz  }|j                         sJ d       y)u0   저장 시 .task-timers.lock 파일이 생성됨rO  r   z.task-timers.locku/   .task-timers.lock 파일이 생성되어야 함N)r(   r?   )r*   r    r   	lock_files       r   test_save_uses_lock_filez'TestAtomicSave.test_save_uses_lock_fileT  s8    $x'*==	!T#TT!r   N)rY   rZ   r[   r\   r:  r>  rB  rE  r]   r   r   r8  r8  3  s    3@R+Ur   r8  c                       e Zd ZdZd Zd Zy)TestStaleGuardu9   stale 상태 task 재시작 방지 테스트 (task-486.1)c                     |j                  ddd       d|j                  d   d   d<   d|j                  d   d   d<   |j                          |j                  ddd	      }|d   d
k(  sJ d|d   v sJ y)u:   stale 상태의 task에 start_task 호출 시 error 반환	task-99.1r8   r9   staler>   r%   timeout_runningstale_reasonu   재시작 시도ra   rc   Nr(   rs  r}  r)   s      r   test_start_task_rejects_stalez,TestStaleGuard.test_start_task_rejects_stale^  s     	k3EF7>Wk*84=NWk*>: !!+{<NOh7***&****r   c                     |j                  ddd       d|j                  d   d   d<   |j                          |j                  ddd       |j                  d   d   }|d   dk(  sJ |d   dk(  sJ y	)
uD   stale 거부 후 기존 task 데이터가 변경되지 않아야 함rI  r8   u   원래 작업rJ  r>   r%   rh   r<   NrM  )r*   r    	task_datas      r   -test_start_task_stale_preserves_original_dataz<TestStaleGuard.test_start_task_stale_preserves_original_datal  s    k?C7>Wk*84 	k3HI LL)+6	"g---'?:::r   N)rY   rZ   r[   r\   rN  rQ  r]   r   r   rG  rG  [  s    C+;r   rG  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestCrossStartu   cross_start 메서드 테스트c                 h    |j                  dddd      }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y	)
u%   유효한 agent로 cross_start 성공lokiz
task-548.1u   보안 취약점 리뷰r8   r%   r&   agentr0   N)cross_startr)   s      r   test_cross_start_valid_agentz+TestCrossStart.test_cross_start_valid_agent  sR    ""6<9RT_`h9,,,g&(((i L000r   c                     |j                  dddd      }|d   dk(  sJ d|d   j                         v sd	|d   j                         v sJ y
y
)   잘못된 agent_name → 에러zunknown-agentr$      설명r8   r%   ra   rV  rc   unknownNrW  re   r)   s      r   test_cross_start_invalid_agentz-TestCrossStart.test_cross_start_invalid_agent  sb    ""?J+Vh7***&*0022i6(CSCYCYC[6[[[6[2r   c                     |j                  dddd      }|d   dk(  sJ d|d   j                         v sd	|d   j                         v sJ y
y
)u   잘못된 task_id → 에러rU  zinvalid-taskr[  r8   r%   ra   r0   rc   invalidNr]  r)   s      r    test_cross_start_invalid_task_idz/TestCrossStart.test_cross_start_invalid_task_id  sb    ""6>8[Qh7***F8,2244	VHEUE[E[E]8]]]8]4r   c                     |j                  dddd       |j                  dddd      }|d   d	k(  sJ |d
   dk(  sJ |j                         }|d   d   d
   dk(  sJ y)u=   이미 active인 agent에 다시 cross_start → 덮어쓰기rU  r$   u   첫 번째 작업r8   rH   u   두 번째 작업rM   r%   r&   r0   cross_functionalN)rW  _load_cross_statusr*   r    r+   
cross_datas       r   !test_cross_start_overwrite_activez0TestCrossStart.test_cross_start_overwrite_active  s    &*.A;O""6:7JKXh9,,,i J... --/
,-f5i@JNNNr   N)rY   rZ   r[   r\   rX  r^  ra  rg  r]   r   r   rS  rS    s    )1\^	Or   rS  c                   "    e Zd ZdZd Zd Zd Zy)TestCrossEndu   cross_end 메서드 테스트c                     |j                  dddd       |j                  d      }|d   dk(  sJ |d   dk(  sJ |j                         }|d   d   d   d	k(  sJ y
)u#   active 상태 agent 종료 → idlevenusr$   u   디자인 리뷰r8   r%   endedrV  rc  idleN)rW  	cross_endrd  re  s       r   test_cross_end_active_agentz(TestCrossEnd.test_cross_end_active_agent  sv    ':/A;O)h7***g')))--/
,-g6x@FJJJr   c                 N    |j                  d      }|d   dk(  sJ |d   dk(  sJ y)u)   이미 idle인 agent → 멱등적 성공maatr%   rl  rV  Nrn  r)   s      r   test_cross_end_idle_agentz&TestCrossEnd.test_cross_end_idle_agent  s5    (h7***g&(((r   c                 :    |j                  d      }|d   dk(  sJ y)rZ  znonexistent-agentr%   ra   Nrr  r)   s      r   test_cross_end_invalid_agentz)TestCrossEnd.test_cross_end_invalid_agent  s$    !45h7***r   N)rY   rZ   r[   r\   ro  rs  ru  r]   r   r   ri  ri    s    'K)+r   ri  c                       e Zd ZdZd Zd Zy)TestCrossFunctionalFileu0   cross-functional-status.json 파일 IO 테스트c                 `    |j                  dddd       |dz  dz  }|j                         sJ y)u   cross_start가 파일을 생성janusr$   u   DevOps 배포r8   r   zcross-functional-status.jsonN)rW  r?   )r*   r    r   
cross_files       r   test_cross_start_creates_filez5TestCrossFunctionalFile.test_cross_start_creates_file  s8    ':L(+II
  """r   c                     |j                  dddd       t        t        |            }|j                         }|d   d   d   dk(  sJ |d   d   d	   dk(  sJ y
)u;   파일에 저장된 상태가 새 인스턴스에서 읽힘rU  r  u   보안 감사r8   r   rc  r%   activer0   N)rW  r   r   rd  )r*   r    r   	new_timerrf  s        r   test_cross_status_persistsz2TestCrossFunctionalFile.test_cross_status_persists  sp    &,M S];	113
,-f5h?8KKK,-f5i@LPPPr   N)rY   rZ   r[   r\   r{  r  r]   r   r   rw  rw    s    :#Qr   rw  )1r\   r
   importlib.utilr@   rv  r>  timer   pathlibr   r   environgetr   r   r=  r   r   r    r"   r_   ro   r   r   r   r   r   mainr?  r8  r2  r4  r6  rp  rz  r  r  r  r  r  r  r  r  r  r8  rG  rS  ri  rw  r]   r   r   <module>r     s       	 
     "**..)9;PQRU]]`oo  *+%%	 3 32E 2Ej5 5FC CV& &.4 4@7% 7%~1A 1ArR= R=r !!
##g+ g+^- ->6 60M MJT* T*x5- 5-z #33 "33 J- J-Z$/ $/N 0B- B-J%U %UP; ;JO OD+ +2Q Qr   