
    SiGD                        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mZmZ ddlZ e
ej                  j!                  dd            Zedz  Zd	ej&                  vr@ e       Z e       ej*                  _         e       ej&                  d
<   eej&                  d	<   ej.                  j1                  d ee            Zeej6                  J ej.                  j9                  e      Zej6                  j=                  e       ej>                  Zedz  Z ej.                  j1                  d ee             Z!e!e!j6                  J ej.                  j9                  e!      Z"e!j6                  j=                  e"       e"jF                  Z#de
defdZ$de
de#fdZ% G d d      Z& G d d      Z'de(de(fdZ)de
de(ddfdZ* G d d      Z+y)u  
test_task_timer_qc.py - task-timer qc_result 필드 + 히스토리 API 테스트

테스트 항목:
1. test_end_task_with_qc_result_pass: end_task에 qc_result="PASS" 전달 → task_data에 기록
2. test_end_task_without_qc_result: end_task에 qc_result 미전달 → task_data에 qc_result=None
3. test_end_task_with_qc_result_fail: end_task에 qc_result="FAIL" 전달 → task_data에 기록
4. test_end_task_with_qc_result_warn: end_task에 qc_result="WARN" 전달 → task_data에 기록
5. test_cli_end_with_qc_result: CLI end --qc-result PASS → 정상 동작
6. test_get_history_stats_completed_only: completed 작업만 집계
7. test_get_history_stats_team_classification: 팀별 분류 정확
8. test_get_history_stats_qc_pass_rate: QC 통과율 계산 정확
9. test_get_history_stats_empty: 데이터 없을 때 빈 dict 반환
10. test_get_history_stats_avg_duration: 평균 소요시간 계산 정확
    N)datetime)Path)	MagicMockpatchWORKSPACE_ROOTz/home/jay/workspacezmemory/task-timer.pyzutils.loggerutils
task_timerzdashboard/server.pydashboard_servertmp_pathreturnc                     | dz  j                  dd       | dz  dz  j                  dd       | dz  dz  j                  dd       t        t        |             S )u8   임시 디렉토리 기반 TaskTimer 인스턴스 생성memoryTparentsexist_okeventsdaily)workspace_path)mkdir	TaskTimerstrr   s    I/home/jay/workspace/.worktrees/task-2117-dev1/tests/test_task_timer_qc.py
make_timerr   ?   sc    t<8#**4$*G7"))$)FCM22    c                 D    | dz  j                  dd       t        |       S )u9   임시 디렉토리 기반 DataLoader 인스턴스 생성r   Tr   )r   
DataLoaderr   s    r   make_data_loaderr   G   s%    t<hr   c                   6    e Zd Zd Zd Zd Zd Zd Zd Zd Z	y)	TestEndTaskQcResultc                     t        |      }|j                  ddd       |j                  dd      }|d   dk(  sJ |j                  d   d   }|d	   dk(  sJ y
)uH   end_task에 qc_result='PASS' 전달 → task_data['qc_result'] == 'PASS'ztask-792	dev1-teamu   QC 테스트PASS	qc_resultstatus	completedtasksr%   Nr   
start_taskend_tasktimers)selfr   timerresult	task_datas        r   !test_end_task_with_qc_result_passz5TestEndTaskQcResult.test_end_task_with_qc_result_passS   si    8$[.A
f=h;...LL)*5	%///r   c                     t        |      }|j                  ddd       |j                  d       |j                  d   d   }|d   J y)uB   end_task에 qc_result 미전달 → task_data['qc_result'] == Noneztask-793r"   u   QC 없는 테스트r(   r%   Nr)   r-   r   r.   r0   s       r   test_end_task_without_qc_resultz3TestEndTaskQcResult.test_end_task_without_qc_result]   sO    8$[2GHz"LL)*5	%---r   c                     t        |      }|j                  ddd       |j                  dd       |j                  d   d   }|d   dk(  sJ y)	uH   end_task에 qc_result='FAIL' 전달 → task_data['qc_result'] == 'FAIL'ztask-794	dev2-teamu   FAIL 테스트FAILr$   r(   r%   Nr)   r3   s       r   !test_end_task_with_qc_result_failz5TestEndTaskQcResult.test_end_task_with_qc_result_failf   U    8$[2BCzV4LL)*5	%///r   c                     t        |      }|j                  ddd       |j                  dd       |j                  d   d   }|d   dk(  sJ y)	uH   end_task에 qc_result='WARN' 전달 → task_data['qc_result'] == 'WARN'ztask-795	dev3-teamu   WARN 테스트WARNr$   r(   r%   Nr)   r3   s       r   !test_end_task_with_qc_result_warnz5TestEndTaskQcResult.test_end_task_with_qc_result_warno   r9   r   c                     t        |      }|j                  ddd       |j                  dd       |j                  d   d   }|d   J y)	uR   end_task에 qc_result='' (빈 문자열) 전달 → task_data['qc_result'] == Noneztask-796r"   u   빈 문자열 테스트 r$   r(   r%   Nr)   r3   s       r   $test_end_task_empty_string_qc_resultz8TestEndTaskQcResult.test_end_task_empty_string_qc_resultx   sS    8$[2KLzR0LL)*5	%---r   c                 (   t        |      }|j                  ddd       |j                  dd       |dz  dz  }|j                         sJ t	        |d	      5 }t        j                  |      }d
d
d
       d   d   d   dk(  sJ y
# 1 sw Y   xY w)u?   end_task 후 task-timers.json에 qc_result 저장 여부 확인ztask-797r"   u   JSON 저장 테스트r#   r$   r   task-timers.jsonutf-8encodingNr(   r%   )r   r*   r+   existsopenjsonload)r-   r   r.   
timer_filefdatas         r   )test_end_task_qc_result_persisted_in_jsonz=TestEndTaskQcResult.test_end_task_qc_result_persisted_in_json   s    8$[2IJzV4 (+==
  """*w/ 	 199Q<D	 G}Z(5???	  	 s   BBc                     t        |      }|j                  ddd       |j                  dd       |j                  dd      }|j                  d      du sJ |j                  d	   d   d
   dk(  sJ y)u`   이미 completed 태스크 재호출 → already_completed 반환, qc_result 덮어쓰기 없음ztask-798r"   u   멱등성 테스트r#   r$   r7   already_completedTr(   r%   N)r   r*   r+   getr,   )r-   r   r.   r/   s       r   *test_end_task_idempotent_already_completedz>TestEndTaskQcResult.test_end_task_idempotent_already_completed   s|    8$[2GHzV4 
f=zz-.$666||G$Z0=GGGr   N)
__name__
__module____qualname__r1   r4   r8   r=   r@   rM   rQ    r   r   r    r    R   s'    0.00.@
Hr   r    c                       e Zd Zd Zd Zy)TestCliQcResultc           
         t        j                  t        j                  t	        t
              ddddgddi t        d      j                  dt	        |      i      }|j                  d	k(  sJ d
|j                          t        j                  t        j                  t	        t
              ddddgddi t        d      j                  dt	        |      i      }|j                  d	k(  sJ d|j                          t        j                  |j                        }|d   dk(  sJ |dz  dz  }t        |d      5 }t        j                  |      }ddd       d   d   d   dk(  sJ y# 1 sw Y   xY w)uL   CLI: python3 task-timer.py end task-792.1 --qc-result PASS → 정상 실행startz
task-792.1--teamr"   Tosr   capture_outputtextenvr   u   start 실패: endz--qc-resultr#   u   end 실패: r&   r'   r   rB   rC   rD   Nr(   r%   )
subprocessrunsys
executabler   _TIMER_PATH
__import__environ
returncodestderrrH   loadsstdoutrG   rI   )r-   r   result_start
result_endoutrJ   rK   rL   s           r    test_cli_end_with_qc_result_passz0TestCliQcResult.test_cli_end_with_qc_result_pass   s    "~~K   M:d#++M-=s8}M
 &&!+S~l>Q>Q=R-SS+  ^^K   M:d#++M-=s8}M

 $$)M\*:K:K9L+MM)jj**+8}+++ (+==
*w/ 	 199Q<D	 G}\*;76AAA	  	 s   =E,,E5c           
      *   t        j                  t        j                  t	        t
              ddddgddi t        d      j                  dt	        |      i       t        j                  t        j                  t	        t
              d	dgddi t        d      j                  dt	        |      i      }|j                  d
k(  sJ |dz  dz  }t        |d      5 }t        j                  |      }ddd       d   d   d   J y# 1 sw Y   xY w)uQ   CLI: python3 task-timer.py end task-792.2 (--qc-result 없이) → qc_result=NonerY   z
task-792.2rZ   r"   Tr[   r   r\   r`   r   r   rB   rC   rD   Nr(   r%   )ra   rb   rc   rd   r   re   rf   rg   rh   rG   rH   rI   )r-   r   rm   rJ   rK   rL   s         r   test_cli_end_without_qc_resultz.TestCliQcResult.test_cli_end_without_qc_result   s    	^^S-whP[\M:d#++M-=s8}M		
  ^^^^S-ulCM:d#++M-=s8}M	

 $$)))(+==
*w/ 	 199Q<D	 G}\*;7???	  	 s   D		DN)rR   rS   rT   ro   rq   rU   r   r   rW   rW      s    (BT@r   rW   r(   c                 
    d| iS )u%   task-timers.json 구조 생성 헬퍼r(   rU   )r(   s    r   _build_task_timersrs      s    Ur   pathc                     | j                  dd       t        | dz  dd      5 }t        j                  t	        |      |d       d	d	d	       y	# 1 sw Y   y	xY w)
u   task-timers.json 파일 쓰기Tr   rB   wrC   rD   F)ensure_asciiN)r   rG   rH   dumprs   )rt   r(   rK   s      r   _write_task_timersry      sU    JJtdJ+	d''w	? D1		$U+QUCD D Ds   "AAc                   B    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestGetHistoryStatsc                     dddddddddddddddd	}t        |d
z  |       t        |      }|j                          |j                         }d|v sJ |d   d   dk(  sJ |d   d   dk(  sJ y)uC   get_history_stats: completed 작업만 집계, running/stale 제외r"   r'   r#   g      ^@team_idr&   r%   duration_secondsrunningNstale)ztask-1ztask-2ztask-3r   total_tasks   qc_passry   r   
load_tasksget_history_statsr-   r   r(   loaderstatss        r   test_completed_onlyz'TestGetHistoryStats.test_completed_only   s     '%#$)	 '#!$(	 '!!$(	
( 	8h.6!(+((* e###[!-0A555[!),111r   c                 "   ddddddddddddd	d
dd}t        |dz  |       t        |      }|j                          |j                         }d|v sJ d|v sJ |d   d   dk(  sJ |d   d   dk(  sJ |d   d   dk(  sJ |d   d   dk(  sJ y)u'   get_history_stats: 팀별 분류 정확r"   r'   r#         N@r}   r6   r7        V@r<   g      >@)ztask-10ztask-11ztask-12r   r   r      qc_failqc_warnNr   r   s        r   test_team_classificationz,TestGetHistoryStats.test_team_classification  s     '%#$(	 '%#$(	 '%#$(	
( 	8h.6!(+((*e###e###[!-0A555[!-0A555[!),111[!),111r   c                    dddddddddddddddddd	d
dd}t        |dz  |       t        |      }|j                          |j                         }|d   }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   d
k(  sJ y	)u-   get_history_stats: QC 통과율 계산 정확r"   r'   r#         Y@r}         i@r7   g     b@Ng      I@)ztask-20ztask-21ztask-22ztask-23r   r      r   r   r   r   qc_noneqc_pass_rater   r-   r   r(   r   r   ss         r   test_qc_pass_ratez%TestGetHistoryStats.test_qc_pass_rate:  s     '%#$)	 '%#$)	 '%#$)	 '%!$(	'
4 	8h.6!(+((*+1$$$|q   |q   |q    D(((r   c                     t        |dz  i        t        |      }|j                          |j                         }|i k(  sJ y)u7   get_history_stats: 데이터 없을 때 빈 dict 반환r   Nr   )r-   r   r   r   s       r   test_empty_dataz#TestGetHistoryStats.test_empty_datad  sA    8h.3!(+((*{{r   c                     ddddddddddd}t        |dz  |       t        |      }|j                          |j                         }|d   }|d	   d
k(  sJ y)u4   get_history_stats: 평균 소요시간 계산 정확r;   r'   r#   r   r}   g     r@)ztask-30ztask-31r   avg_duration_secondsr   Nr   r   s         r   test_avg_durationz%TestGetHistoryStats.test_avg_durationn  s     '%#$)	 '%#$)	
 	8h.6!(+((*+'(E111r   c                     ddddddi}t        |dz  |       t        |      }|j                          |j                         }|d   }|d   d	k(  sJ y
)u3   get_history_stats: avg_duration_human 포맷 확인ztask-40r"   r'   r#   r   r}   r   avg_duration_humanu
   1분 30초Nr   r   s         r   test_avg_duration_human_formatz2TestGetHistoryStats.test_avg_duration_human_format  sr     &%#$(	
 	8h.6!(+((*+%&,666r   c                     ddddddddddd}t        |dz  |       t        |      }|j                          |j                         }|d   }|d	   d
k(  sJ |d   dk(  sJ y)uP   get_history_stats: duration_seconds=None 태스크는 평균 계산에서 제외r"   r'   r#   Nr}   r   )ztask-50ztask-51r   r   r   r   r   r   r   s         r   test_no_duration_tasksz*TestGetHistoryStats.test_no_duration_tasks  s     '%#$(	 '%#$)	
 	8h.6!(+((*+1$$$'(E111r   c                     ddddddi}t        |dz  |       t        |      }|j                          |j                         }|d   }|d   d	k(  sJ |d
   dk(  sJ |d   dk(  sJ |d   dk(  sJ y)uC   get_history_stats: qc_result=None 태스크는 qc_none으로 집계ztask-60r"   r'   Nr   r}   r   r   r   r   r   r   r   r   r   s         r   test_qc_none_countz&TestGetHistoryStats.test_qc_none_count  s     &%!$(	
 	8h.6!(+((*+|q   |q   |q   |q   r   c                     dddddi}t        |dz  |       t        |      }|j                          |j                         }d|v sJ |d   d   d	k(  sJ y
)uI   get_history_stats: team_id 없는 태스크는 'unknown' 팀으로 집계ztask-70r'   r#   r   )r&   r%   r   r   unknownr   r   Nr   r   s        r   test_unknown_team_idz(TestGetHistoryStats.test_unknown_team_id  sw     %#$(
 	8h.6!(+((*E!!!Y.!333r   N)rR   rS   rT   r   r   r   r   r   r   r   r   r   rU   r   r   r{   r{      s2    2B!2F()T247&26!,4r   r{   ),__doc__importlib.util	importlibrH   r[   ra   rc   tempfiler   pathlibr   unittest.mockr   r   pytestrg   rP   
_WORKSPACEre   modules_mock_logger
get_loggerreturn_valueutilspec_from_file_locationr   specr   module_from_spectask_timer_modexec_moduler   _SERVER_PATHspec2dashboard_modr   r   r   r    rW   dictrs   ry   r{   rU   r   r   <module>r      s      	  
    *  "**..!13HIJ
11 $;L+4;L($;CKK".CKK ~~--lC<LMDKK3 33006    '$$	 11../A3|CTUU\\5 55//6    '%%
3 3) 3 t  
  FH FH\A@ A@Rd t 
DT D$ D4 Dh4 h4r   