
    Ki'                    n   d Z ddlZddlmc mZ ddlZddl	Z	ddl
Z
ddl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j0                  j3                  de      ZdZeeuZes ej:                  defdeef      d	 ej<                         v s ej>                  e      r ej@                  e      nd	 ej@                  e      d
z  Z!dde!iz  Z" e# ejH                  e"            dxZZej0                  jK                  e      Z&ejN                  ZdZ(ee(uZ)e)s ej:                  de)fdee(f      d	 ej<                         v s ej>                  e      r ej@                  e      nd	 ej@                  e       ej@                  e(      dz  Z"dde"iz  Z* e# ejH                  e*            dxZxZ)Z(ejN                  jW                  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,defdZ/dedededefdZ0dede,defdZ1dedede,defdZ2dedededefdZ3 G d d       Z4 G d! d"      Z5 G d# d$      Z6 G d% d&      Z7 G d' d(      Z8 G d) d*      Z9 G d+ d,      Z: G d- d.      Z; G d/ d0      Z< G d1 d2      Z= G d3 d4      Z> G d5 d6      Z? G d7 d8      Z@ G d9 d:      ZA G d; d<      ZBdede,defd=ZCdLd>eded?ed@eDdef
dAZEdMdededBedefdCZF G dD dE      ZG G dF dG      ZH G dH dI      ZI G dJ dK      ZJy)Nu  
test_whisper_compile.py

scripts/whisper-compile.py 단위 테스트 (TDD)

테스트 항목:
1. 빈 상태 (모든 파일 없음) → 빈 브리핑 정상 출력
2. 정상 상태 → 모든 섹션 포함
3. 부분 파일만 존재 → 해당 섹션만 포함
4. 유휴 팀 3시간 이상 → [유휴경고] 출력
5. 질문 있음/없음
6. 보고서 SCQA 추출 정확성
7. bot-activity.json 파일 손상 시 graceful 처리
    N)datetime	timedeltatimezone)Pathzwhisper-compile.pywhisper_compileis notz%(py0)s is not %(py3)sspecpy0py3assert %(py5)spy5)z2%(py2)s
{%(py2)s = %(py0)s.loader
} is not %(py5)s)r   py2r   zassert %(py7)spy7tmp_pathdatareturnc                     | dz  }|j                  dd       |dz  }|j                  t        j                  |      d       | S )NeventsTparentsexist_okbot-activity.jsonutf-8encodingmkdir
write_textjsondumps)r   r   
events_dirfs       9/home/jay/workspace/scripts/tests/test_whisper_compile.pymake_bot_activityr'   *   sH    H$JTD1((ALLD!GL4O    tasksc                 `    | dz  }|j                  t        j                  d|i      d       | S )Ntask-timers.jsonr)   r   r   r!   r"   r#   )r   r)   r%   s      r&   make_task_timersr-   2   s0    %%ALLWe,-L@Or(   filenamec                     | dz  }|j                  dd       ||z  }|j                  t        j                  |      d       | S )Nr   Tr   r   r   r   )r   r.   r   r$   r%   s        r&   make_done_filer0   8   sG    H$JTD1XALLD!GL4Or(   contentc                 f    | dz  }|j                  dd       ||z  }|j                  |d       | S )NreportsTr   r   r   r    r!   )r   r.   r1   reports_dirr%   s        r&   make_reportr6   @   s>    Y&KdT2hALL7L+Or(   c                     | dz  }|j                  dd       |dz  }|j                  t        j                  |      d       | S )NwhisperTr   zsession-guidance.jsonr   r   r   )r   r   whisper_dirr%   s       r&   make_guidancer:   H   sH    Y&KdT2--ALLD!GL4Or(   c                     | dz  dz  }|j                  dd       ||z  }|j                  t        j                  |      d       | S )Nr   	questionsTr   r   r   r   )r   r.   r   q_dirr%   s        r&   make_questionr>   P   sI    x+-E	KKtK,ALLD!GL4Or(   projectc                 l    | dz  |z  }|j                  dd       |dz  }|j                  |d       | S )NprojectsTr   z
context.mdr   r   r4   )r   r?   r1   proj_dirr%   s        r&   make_project_contextrC   X   s@    *$w.HNN4$N/<ALL7L+Or(   c                   p    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
defd	Zdefd
Zy)TestEmptyStateu+   모든 파일이 없을 때 graceful 처리r   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y Nbase_dir==z%(py0)s == %(py3)sresultr   r   r   )
r   load_bot_activity
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationselfr   rM   @py_assert2@py_assert1@py_format4@py_format6s          r&   test_load_bot_activity_missingz-TestEmptyState.test_load_bot_activity_missingh   sm     22H2Ev|vvvr(   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   
r   load_task_timersrO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   test_load_task_timers_missingz,TestEmptyState.test_load_task_timers_missingl   m     1181Dv|vvvr(   c                 |   t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   )
r   scan_done_filesrO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   test_scan_done_files_missingz+TestEmptyState.test_scan_done_files_missingp   sm     00(0Cv|vvvr(   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   
r   load_guidancerO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   test_load_guidance_missingz)TestEmptyState.test_load_guidance_missingt   m     ...Av|vvvr(   c                 |   t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   
r   load_questionsrO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   test_load_questions_missingz*TestEmptyState.test_load_questions_missingx   m     ///Bv|vvvr(   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   )
r   load_project_contextrO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   !test_load_project_context_missingz0TestEmptyState.test_load_project_context_missing|   sm     55x5Hv|vvvr(   c           	      D   t         j                  |      d   }|j                  }d} ||      }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            d x}x}}|j                  } |       }|j                  }d} ||      }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d	z  }	t        t        j                  |	            d x}x}x}x}}d
}
|
|v }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d x}
}d}
|
|v }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d x}
}y )NrH   r   <whisper-briefing>zLassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}output)r   r   py4py6</whisper-briefing>~assert %(py10)s
{%(py10)s = %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.strip
}()
}.endswith
}(%(py8)s)
}r   r   rv   rw   py8py10   [팀]inz%(py1)s in %(py3)spy1r   r   r      [가이던스])r   compile_briefing
startswithrQ   rR   rO   rS   rT   rU   rV   stripendswithrP   )rX   r   ru   rZ   @py_assert3@py_assert5@py_format7@py_assert7@py_assert9@py_format11@py_assert0rY   r[   r\   s                 r&   test_compile_briefing_emptyz*TestEmptyState.test_compile_briefing_empty   s    1181DQG  6!56 !566666666v666v666 666!56666666666||=|~=~&&='<=&'<========v===v===|===~===&==='<=========== w&    w&   w      &   &       )6))))6)))))))))6)))6)))))))r(   c                     	 t         j                  |      d    y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)u/   어떤 파일도 없어도 예외 없이 실행rH   r   u    빈 상태에서 예외 발생: N)r   r   	Exceptionpytestfail)rX   r   es      r&   "test_compile_briefing_no_exceptionz1TestEmptyState.test_compile_briefing_no_exception   sG    	@,,h,?B 	@KK:1#>??	@s    	AAAN)__name__
__module____qualname____doc__r   r]   ra   re   ri   rn   rr   r   r    r(   r&   rE   rE   e   sg    5t d T 4 D $ *D *@4 @r(   rE   c                   L    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	y)	TestBotActivityu.   bot-activity.json 로드 및 팀 상태 파싱r   c                    t        j                  t        j                        j	                         }dd|dd|ddi}t        ||       t        j                  |      }d}||v }|st        j                  d|fd	||f      t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}|d   d   }d}	||	k(  }|slt        j                  d|fd||	f      t        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            d x}x}}	y )Nbots
processingstatussinceidledev1dev2rH   r   r~   r   rM   r   r   r   r   rJ   z%(py1)s == %(py4)sr   rv   assert %(py6)srw   )r   nowr   utc	isoformatr'   r   rN   rO   rP   rT   rQ   rR   rS   rU   rV   )rX   r   r   r   rM   r   rY   r[   r\   r   @py_format5r   s               r&   test_load_bot_activity_normalz-TestBotActivity.test_load_bot_activity_normal   s
   ll8<<(224#/#>#)C8
 	(D) 22H2Evvvf~h'7<7'<7777'<777'777<7777777r(   c                    t         j                  }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d	x}x}x}x}}|j                  }d
} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d	x}x}x}x}}|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d	x}x}x}x}}|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d	x}x}x}x}}y	)uA   dev1→1팀, dev2→2팀, dev3→3팀, anu→아누 매핑 확인r      1팀rJ   zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)smappingr   r   rv   rw   py9assert %(py11)spy11Nr      2팀dev3u   3팀anu   아누)r   TEAM_NAME_MAPgetrO   rP   rQ   rR   rS   rT   rU   rV   
rX   r   r   rZ   r   r   @py_assert8r   @py_format10@py_format12s
             r&   test_team_name_mappingz&TestBotActivity.test_team_name_mapping   sd   !//{{,6,{6",f,"f,,,,"f,,,,,,w,,,w,,,{,,,6,,,",,,f,,,,,,,{{,6,{6",f,"f,,,,"f,,,,,,w,,,w,,,{,,,6,,,",,,f,,,,,,,{{,6,{6",f,"f,,,,"f,,,,,,w,,,w,,,{,,,6,,,",,,f,,,,,,,{{-5-{5!-X-!X----!X------w---w---{---5---!---X--------r(   c                    |dz  }|j                  dd       |dz  }|j                  dd       t        j                  |      }i }||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}}y)u4   손상된 JSON → graceful 처리 (빈 dict 반환)r   Tr   r   { not valid json !!!r   r   rH   rJ   rL   rM   r   r   r   N)r    r!   r   rN   rO   rP   rQ   rR   rS   rT   rU   rV   	rX   r   r$   r%   rM   rY   rZ   r[   r\   s	            r&    test_load_bot_activity_corruptedz0TestBotActivity.test_load_bot_activity_corrupted   s    (
5,,	+g> 22H2Ev|vvvr(   c                    t        |i        t        j                  |      }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u   빈 JSON 객체 → 빈 dictrH   rJ   rL   rM   r   r   r   Nr'   r   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   !test_load_bot_activity_empty_jsonz1TestBotActivity.test_load_bot_activity_empty_json   sw    (B' 22H2Ev|vvvr(   c                    t        |ddi       t        j                  |      }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}}y
)u!   bots 키 없는 JSON → 빈 dictotherr   rH   rJ   rL   rM   r   r   r   Nr   rW   s          r&   "test_load_bot_activity_no_bots_keyz2TestBotActivity.test_load_bot_activity_no_bots_key   s|    (Wf$56 22H2Ev|vvvr(   N)
r   r   r   r   r   r   r   r   r   r   r   r(   r&   r   r      sA    88d 8.t . $ 4 r(   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)TestTaskTimersu7   task-timers.json 로드 및 running/completed 필터링r   c                 H   t        j                  t        j                        j	                         }t        |dddd|d ddddd	||dd
       t        j                  |      }|j                         D cg c]  }|d   dk(  s| }}t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            d x}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            d x}x}}y c c}w )Ntask-1.1	dev1-teamu   테스트 작업runningtask_idteam_iddescriptionr   
start_timeend_timetask-2.1	dev2-team   완료된 작업	completed)r   r   rH   r      rJ   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   r   r   rw   assert %(py8)sr{   r   r   r   r   r   rw   )r   r   r   r   r   r-   r   r`   valuesr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   r   rM   tr   rY   r   @py_assert4r   @py_format9r   r   r   s                 r&   test_load_running_tasksz&TestTaskTimers.test_load_running_tasks   s   ll8<<(224  **#5'"% $  **#5)"% #	
* !1181D$mmoJ8	1I1JJ7| q |q    |q      s   s      7   7   |   q       qz)$2
2$
2222$
222$222
2222222 Ks   7HHc                     t        j                  t        j                        t	        d      z
  j                         }t        |ddddd||di       t        j                  |      }d}||v }|st        j                  d	|fd
||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}y)u'   24시간 이내 완료 작업은 포함r   hoursztask-3.1r   u   최근 완료r   r   rH   r~   r   rM   r   r   r   Nr   r   r   r   r   r   r-   r   r`   rO   rP   rT   rQ   rR   rS   rU   rV   )rX   r   one_hour_agorM   r   rY   r[   r\   s           r&   %test_load_recent_completed_within_24hz4TestTaskTimers.test_load_recent_completed_within_24h   s     X\\2YQ5GGRRT)*#2)". ,		
 !1181D#zV####zV###z######V###V#######r(   c                     t        j                  t        j                        t	        d      z
  j                         }t        |ddddd||di       t        j                  |      }d}||v}|st        j                  d	|fd
||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}y)u'   25시간 이전 완료 작업은 제외   r   ztask-4.1r   u   오래된 완료r   r   rH   not inz%(py1)s not in %(py3)srM   r   r   r   Nr   )rX   r   old_timerM   r   rY   r[   r\   s           r&   test_exclude_old_completedz)TestTaskTimers.test_exclude_old_completed   s    LL.1DDOOQ)*#5)"* (		
 !1181D'z''''z'''z''''''''''''''''r(   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   r_   rW   s          r&   ra   z,TestTaskTimers.test_load_task_timers_missing  rb   r(   N)	r   r   r   r   r   r   r   r   ra   r   r(   r&   r   r      s5    A3 38$d $&(4 (&d r(   r   c                   p    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
defd	Zdefd
Zy)TestDoneFilesu]   events/*.done 파일 스캔 (.done.acked, .done.escalated 등 확장자 파일 자동 제외)r   c                 v   t        |dddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  t              rt	        j                  t              nd	d
t        j                         v st	        j                  |      rt	        j                  |      nd
t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d   }d}	||	k(  }|slt	        j
                  d|fd||	f      t	        j                  |      t	        j                  |	      dz  }
dd|
iz  }t        t	        j                  |            d x}x}}	y )Nztask-10.1.donez	task-10.1doner   r   rH   r   rJ   r   r   rM   r   r   r{   r   r   r   r   r   rw   r0   r   rd   r   rO   rP   rQ   rR   rS   rT   rU   rV   rX   r   rM   rY   r   r   r   r   r   r   r   s              r&   test_scan_done_files_basicz(TestDoneFiles.test_scan_done_files_basic  s   #v6	

 !00(0C6{a{a{ass66{aay#2{2#{2222#{222#222{2222222r(   c                 p   t        |dddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  t              rt	        j                  t              nd	d
t        j                         v st	        j                  |      rt	        j                  |      nd
t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y)uA   *.done.acked 파일은 제외해야 함 (새 프로토콜 반영)ztask-11.1.done.ackedz	task-11.1r   r   rH   r   rJ   r   r   rM   r   r   r{   Nr   rX   r   rM   rY   r   r   r   r   s           r&   1test_scan_done_files_excludes_non_done_extensionsz?TestDoneFiles.test_scan_done_files_excludes_non_done_extensions$  s    "#v6	

 !00(0C6{a{a{ass66{ar(   c                    t        |dddi       t        |dddi       t        j                  |      }|D cg c]  }|j                  d       }}d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd	z  }d
d|iz  }t        t	        j                  |            dx}}t        |      }d}	||	k(  }
|
st	        j
                  d|
fd||	f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |	      dz  }dd|iz  }t        t	        j                  |            dx}x}
}	yc c}w )u7   *.done.acked 파일은 스캔에서 제외되어야 함ztask-15.1.doner   z	task-15.1ztask-15.1.done.ackedrH   r~   r   task_idsr   r   r   Nr   rJ   r   r   rM   r   r   r{   )r0   r   rd   r   rO   rP   rT   rQ   rR   rS   rU   rV   r   )rX   r   rM   rr   r   rY   r[   r\   r   r   r   r   s                r&   (test_scan_done_files_excludes_done_ackedz6TestDoneFiles.test_scan_done_files_excludes_done_acked.  s?   x!1I{3KLx!7)[9QR 00(0C.45AEE)$55&{h&&&&{h&&&{&&&&&&h&&&h&&&&&&&6{a{a{ass66{a 6s   H	c                 p   t        |dddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  t              rt	        j                  t              nd	d
t        j                         v st	        j                  |      rt	        j                  |      nd
t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y)u;   *.done.escalated 파일은 스캔에서 제외되어야 함ztask-16.1.done.escalatedz	task-16.1r   r   rH   r   rJ   r   r   rM   r   r   r{   Nr   r   s           r&   ,test_scan_done_files_excludes_done_escalatedz:TestDoneFiles.test_scan_done_files_excludes_done_escalated7  s    &#v6	

 !00(0C6{a{a{ass66{ar(   c                 p   t        |dddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  t              rt	        j                  t              nd	d
t        j                         v st	        j                  |      rt	        j                  |      nd
t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y)uM   *.done.notified 파일은 스캔에서 제외되어야 함 (레거시 호환)ztask-17.1.done.notifiedz	task-17.1r   r   rH   r   rJ   r   r   rM   r   r   r{   Nr   r   s           r&   +test_scan_done_files_excludes_done_notifiedz9TestDoneFiles.test_scan_done_files_excludes_done_notifiedA  s    %#v6	

 !00(0C6{a{a{ass66{ar(   c                    t        |dddi       t        |dddi       t        |dddi       t        j                  |      }|D cg c]  }|j                  d       }}d}||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}}d}||v }|st	        j
                  d	|fd
||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}}d}||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}}y c c}w )Nztask-12.1.doner   z	task-12.1ztask-13.1.donez	task-13.1ztask-14.1.done.ackedz	task-14.1rH   r~   r   r   r   r   r   r   r   )r0   r   rd   r   rO   rP   rT   rQ   rR   rS   rU   rV   )	rX   r   rM   r  r   r   rY   r[   r\   s	            r&   test_scan_done_files_multiplez+TestDoneFiles.test_scan_done_files_multipleK  sw   x!1I{3KLx!1I{3KLx!7)[9QR 00(0C.45AEE)$55&{h&&&&{h&&&{&&&&&&h&&&h&&&&&&&&{h&&&&{h&&&{&&&&&&h&&&h&&&&&&&*{(****{(***{******(***(******* 6s   Ic                    |dz  }|j                  dd       |dz  }|j                  dd       t        j                  |      }g }||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}}y)u    손상된 .done 파일은 스킵r   Tr   ztask-bad.1.doneznot jsonr   r   rH   rJ   rL   rM   r   r   r   N)r    r!   r   rd   rO   rP   rQ   rR   rS   rT   rU   rV   r   s	            r&   #test_scan_done_files_corrupted_jsonz1TestDoneFiles.test_scan_done_files_corrupted_jsonU  s    (
5**	Z'2 00(0Cv|vvvr(   c                    t        |dddi       t        |dddi       t        |dddi       t        j                  |      \  }}|d	   }d
}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)ue   compile_briefing의 status_dict에서 done_pending이 .done.acked를 제외하고 카운트해야 함ztask-20.1.doner   z	task-20.1ztask-21.1.donez	task-21.1ztask-22.1.done.ackedz	task-22.1rH   done_pending   rJ   r   r   r   rw   Nr0   r   r   rO   rP   rT   rU   rV   	rX   r   _status_dictr   r   rY   r   r   s	            r&   &test_done_pending_count_excludes_ackedz4TestDoneFiles.test_done_pending_count_excludes_acked^  s    x!1I{3KLx!1I{3KLx!7)[9QR(9989L;>*/a/*a////*a///*///a///////r(   N)r   r   r   r   r   r   r   r  r  r  r  r
  r  r   r(   r&   r   r     se    g34 3 $      T   D  +d +D 0t 0r(   r   c                   P    e Zd 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
y	)
TestReportSCQAu7   extract_report_scqa() - 보고서 SCQA 추출 정확성u   # task-564.1 완료 보고서

**S**: done-watcher 개선 사항이 있었다.
**C**: cokacdir가 추가되어 상황이 바뀌었다.
**Q**: 개선 효과를 측정할 수 있는가?
**A**: 테스트 PASS로 확인됨.
r   c                    t        |d| j                         t        j                  d|      }d }||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d x}}d
}|d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}d}|d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}d}|d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}d}|d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            d x}x}}y )Nztask-564.1.mdz
task-564.1rH   r   r
   rM   r   r   r   zdone-watcherSr~   z%(py1)s in %(py4)sr   r   rw   cokacdirCu   개선 효과Q	   테스트A)r6   SAMPLE_REPORTr   extract_report_scqarO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   rM   rY   rZ   r[   r\   r   r   r   r   s              r&   test_extract_scqa_basicz&TestReportSCQA.test_extract_scqa_basicx  s   Hot/A/AB 44\H4U!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!,,~,,,,~,,,~,,,,,,,,,,(VC[(z[((((z[(((z((([(((((((-&+-+----+------+-------)fSk){k)))){k))){)))k)))))))r(   c                 |   t         j                  d|      }d}||u }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}y)
u   보고서 없으면 None 반환ztask-nonexistent.1rH   Nis)z%(py0)s is %(py3)srM   r   r   r   )
r   r  rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&    test_extract_scqa_missing_reportz/TestReportSCQA.test_extract_scqa_missing_report  sq     445IT\4]v~vvvr(   c                    d}t        |d|       t        j                  d|      }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}|j                  }d} ||      }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}x}	x}}
|j                  }d} ||      }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}x}	x}}
y)u(   S, C만 있는 경우도 graceful 처리u   **S**: 상황
**C**: 변화
ztask-partial.1.mdztask-partial.1rH   Nr   r
   rM   r   r   r   r  u   상황rJ   r   r   r   r   r  u   변화)r6   r   r  rO   rP   rQ   rR   rS   rT   rU   rV   r   )rX   r   partialrM   rY   rZ   r[   r\   r   r   r   r   r   r   s                 r&   test_extract_scqa_partialz(TestReportSCQA.test_extract_scqa_partial  s   2H17; 445EPX4Y!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!zz*#*z#*(*(****(******v***v***z***#******(*******zz*#*z#*(*(****(******v***v***z***#******(********r(   c                    t        |d| j                         t        |dddi       t        j	                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}y)u>   [완료] 섹션에 SCQA 포맷이 올바르게 표시되는지task-100.1.mdtask-100.1.doner   
task-100.1rH   r   r~   r   ru   r   r   r   NzS:)r6   r  r0   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   rX   r   ru   r   rY   r[   r\   s          r&   $test_extract_scqa_format_in_briefingz3TestReportSCQA.test_extract_scqa_format_in_briefing  s    Hot/A/ABx!2Y4MN 1181DQG%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%tv~tvtvvr(   c                    t         j                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}y
)u1   done 파일 없으면 [완료] 섹션은 '없음'rH   r      [완료]r~   r   ru   r   r   r   N   없음
r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&   test_extract_scqa_no_done_filesz.TestReportSCQA.test_extract_scqa_no_done_files  s     1181DQG#zV####zV###z######V###V#######!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!r(   N)r   r   r   r   r  r   r  r#  r&  r,  r1  r   r(   r&   r  r  l  sH    AM* * 
+$ +T " "r(   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)TestGuidanceu   session-guidance.json 로드r   c                 R   t        |ddg g d       t        j                  |      }|d   }d}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }d	d
|iz  }t        t        j                  |            d x}x}}y )Nz2026-03-15T00:00:00Zu   이전 세션 키워드)last_sessionguidancepending_dispatches
idle_teamsrH   r6  rJ   r   r   r   rw   )r:   r   rh   rO   rP   rT   rU   rV   )rX   r   rM   r   r   rY   r   r   s           r&   test_load_guidance_normalz&TestGuidance.test_load_guidance_normal  s     65&( 		
 !...Aj!>%>>!%>>>>>!%>>>>!>>>%>>>>>>>>r(   c                 |   t         j                  |      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   rg   rW   s          r&   ri   z'TestGuidance.test_load_guidance_missing  rj   r(   c                    t        |ddi       t        j                  |      d   }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            d x}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            d x}}y )Nr6  u   테스트 가이던스rH   r   r   r~   r   ru   r   r   r   r:   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&   test_guidance_in_briefingz&TestGuidance.test_guidance_in_briefing  s    h-E FG 1181DQG)6))))6)))))))))6)))6)))))))'1'61111'6111'111111611161111111r(   c                    t         j                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}y
)u0   guidance 없으면 '이전 세션 정보 없음'rH   r   u   이전 세션 정보 없음r~   r   ru   r   r   r   Nr0  r+  s          r&   "test_guidance_fallback_in_briefingz/TestGuidance.test_guidance_fallback_in_briefing  sv     1181DQG,6,6666,666,6666666666666666r(   N)	r   r   r   r   r   r9  ri   r=  r?  r   r(   r&   r3  r3    s5    &?$ ?4 2$ 274 7r(   r3  c                   L    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	y)	TestQuestionsu1   events/questions/*.json 로드 (.answered 제외)r   c                 x   t        |ddddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd	||f      d
t        j                         v st	        j                  t              rt	        j                  t              nd
dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}x}}|d   d   }d}	||	k(  }|slt	        j
                  d|fd||	f      t	        j                  |      t	        j                  |	      dz  }
dd|
iz  }t        t	        j                  |            d x}x}}	y )Nzq-task-565.jsonhermesu   설계 확인 필요ztask-565)fromquestionr   rH   r   rJ   r   r   rM   r   r   r{   r   rD  r   r   r   rw   )r>   r   rm   r   rO   rP   rQ   rR   rS   rT   rU   rV   r   s              r&   test_load_questions_basicz'TestQuestions.test_load_questions_basic  s    2%	
 !///B6{a{a{ass66{aay ,H, H,,,, H,,, ,,,H,,,,,,,r(   c                    |dz  dz  }|j                  dd       |dz  }|j                  t        j                  ddi      d	       t        j                  |
      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u   .answered 파일은 제외r   r<   Tr   zq-answered.json.answeredrE  u   이미 답변됨r   r   rH   r   rJ   r   r   rM   r   r   r{   N)r    r!   r"   r#   r   rm   r   rO   rP   rQ   rR   rS   rT   rU   rV   )
rX   r   r=   r%   rM   rY   r   r   r   r   s
             r&   %test_load_questions_excludes_answeredz3TestQuestions.test_load_questions_excludes_answered  s    8#k1D40..	TZZ-? @AGT ///B6{a{a{ass66{ar(   c                    t        |dddd       t        j                  |      d   }d}||v }|st        j                  d|fd	||f      t        j
                  |      d
t        j                         v st        j                  |      rt        j
                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}d}||v }|st        j                  d|fd	||f      t        j
                  |      d
t        j                         v st        j                  |      rt        j
                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}y )Nzq-test.jsonr   u   테스트 질문rD  rE  rH   r      [질문]r~   r   ru   r   r   r   )r>   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&   "test_questions_in_briefing_presentz0TestQuestions.test_questions_in_briefing_present  s    );<	

 !1181DQG#zV####zV###z######V###V#######!+!V++++!V+++!++++++V+++V+++++++r(   c                    t         j                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d x}}d
}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d x}}y )NrH   r   rK  r~   r   ru   r   r   r   r/  r0  r+  s          r&   test_questions_in_briefing_nonez-TestQuestions.test_questions_in_briefing_none  s     1181DQG#zV####zV###z######V###V#######!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!r(   c                 |   t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y rG   rl   rW   s          r&   test_load_questions_no_dirz(TestQuestions.test_load_questions_no_dir  ro   r(   N)
r   r   r   r   r   rF  rH  rL  rN  rP  r   r(   r&   rA  rA    sA    ;-$ - d  ,4 ," "
4 r(   rA  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	)
TestIdleTeamsu6   detect_idle_teams() - 3시간 이상 유휴 팀 탐지r   c                    t        j                  t        j                        t	        d      z
  j                  d      }dd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            d x}x}}|d   d   }
d}|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                   |            d x}
x}}y )N   r   %Y-%m-%dT%H:%M:%SZr   r   r   r   rJ   r   r   rM   r   r   r{   r   r   r   r   r   rw   r   r   r   r   r   strftimer   detect_idle_teamsr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   four_hours_agor   rM   rY   r   r   r   r   r   r   r   s                r&   test_detect_idle_over_3hz&TestIdleTeams.test_detect_idle_over_3h  s+   ",,x||4yq7IISSThi6NCD 22486{a{a{ass66{aay#-v-#v----#v---#---v-------r(   c                    t        j                  t        j                        t	        d      z
  j                  d      }dd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            d x}x}}y )Nr  r   rU  r   r   r   r   rJ   r   r   rM   r   r   r{   rV  )
rX   r   two_hours_agor   rM   rY   r   r   r   r   s
             r&   test_no_idle_under_3hz#TestIdleTeams.test_no_idle_under_3h  s    !hll3ia6HHRRSgh6MBC 22486{a{a{ass66{ar(   c                    t        j                  t        j                        t	        d      z
  j                  d      }dd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}y)u.   processing 상태 팀은 유휴 경고 안 함rT  r   rU  r   r   r   r   rJ   r   r   rM   r   r   r{   NrV  
rX   r   rY  r   rM   rY   r   r   r   r   s
             r&   test_processing_team_not_idlez+TestIdleTeams.test_processing_team_not_idle  s    ",,x||4yq7IISSThi<.IJ 22486{a{a{ass66{ar(   c                 r   t        j                  t        j                        t	        d      z
  j                  d      }ddd|dii}t        ||       t        j                  |      d	   }d
}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            d x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            d x}}y )NrT  r   rU  r   r   r   r   rH   r      [유휴경고]r~   r   ru   r   r   r   r   r   r   r   r   r   rW  r'   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   )	rX   r   rY  r   ru   r   rY   r[   r\   s	            r&   test_idle_warning_in_briefingz+TestIdleTeams.test_idle_warning_in_briefing  s   ",,x||4yq7IISSThiF^!LMN(D) 1181DQG)6))))6)))))))))6)))6)))))))vvvr(   c                 (   t        j                  t        j                        t	        d      z
  j                  d      }ddd|dii}t        ||       t        j                  |      d	   }d
}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}y)u?   유휴 팀 없으면 [유휴경고] 줄 자체가 없어야 함r  r   rU  r   r   r   r   rH   r   rb  r   r   ru   r   r   r   Nrc  )	rX   r   r\  r   ru   r   rY   r[   r\   s	            r&    test_no_idle_warning_when_activez.TestIdleTeams.test_no_idle_warning_when_active  s    !hll3ia6HHRRSghF]!KLM(D) 1181DQG-v----v---------v---v-------r(   c                    t        j                  t        j                        t	        dd      z
  j                  d      }dd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}y)u2   정확히 3시간(180분) 유휴 → 경고 포함   r   )r   minutesrU  r   r   r   rJ   r   r   rM   r   r   r{   NrV  )
rX   r   three_hours_agor   rM   rY   r   r   r   r   s
             r&   test_exactly_3h_idle_is_warnedz,TestIdleTeams.test_exactly_3h_idle_is_warned"  s    #<<5	ST8UU__`tu6ODE 22486{a{a{ass66{ar(   N)r   r   r   r   r   rZ  r]  r`  rd  rf  rk  r   r(   r&   rR  rR    sM    @. . d   d   d  . . t  r(   rR  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)TestProjectContextu   프로젝트 context.md 로드r   c                    t        |dd       t        j                  d|      }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }d	d
|iz  }t        t        j                  |            d x}}y )Ninsuwikiu   # InsuWiki
- Phase: 미시작
z/home/jay/projects/insuwiki/srccwdrI   r~   r   rM   r   r   r   rC   r   rq   rO   rP   rT   rQ   rR   rS   rU   rV   rX   r   rM   r   rY   r[   r\   s          r&    test_load_project_context_by_cwdz3TestProjectContext.test_load_project_context_by_cwd2  s    Xz3UV 55:[fn5o#zV####zV###z######V###V#######r(   c                    t        |dd       t        j                  d|      }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }d	d
|iz  }t        t        j                  |            d x}}y )N
threadautou   # ThreadAuto
- Remotion 대기
z/home/jay/projects/threadauto/rp  r~   r   rM   r   r   r   rr  rs  s          r&   $test_load_project_context_threadautoz7TestProjectContext.test_load_project_context_threadauto7  s    X|5XY 55:Zem5n%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r(   c                    t        |dd       t        |dd       t        j                  |      }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            d x}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            d x}}y )Nro  u   # InsuWiki 요약
rv  u   # ThreadAuto 요약
rH   r~   r   rM   r   r   r   rr  rs  s          r&   "test_load_all_projects_when_no_cwdz5TestProjectContext.test_load_all_projects_when_no_cwd<  s    Xz3HIX|5LM 55x5H#zV####zV###z######V###V#######%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r(   c                    t        |dd       t        j                  |      d   }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            d x}}y )Nro  u   # InsuWiki
- 상태: PENDING
rH   r      [프로젝트]r~   r   ru   r   r   r   )rC   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&    test_project_context_in_briefingz3TestProjectContext.test_project_context_in_briefingC  s    Xz3TU 1181DQG)6))))6)))))))))6)))6)))))))r(   N)	r   r   r   r   r   rt  rw  ry  r|  r   r(   r&   rm  rm  /  s5    ($ $
&T &
&4 &* *r(   rm  c                   L    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	y)	TestFullBriefingu!   전체 브리핑 통합 테스트r   c           	      .   t         j                  |      d   }|j                  } |       }|j                  }d} ||      }|sddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            d x}x}x}x}}|j                  } |       }|j                  }d} ||      }|sddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            d x}x}x}x}}y )	NrH   r   rt   zassert %(py10)s
{%(py10)s = %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.strip
}()
}.startswith
}(%(py8)s)
}ru   rz   rx   ry   )r   r   r   r   rQ   rR   rO   rS   rT   rU   rV   r   )	rX   r   ru   rZ   r   r   r   r   r   s	            r&   test_xml_structurez#TestFullBriefing.test_xml_structureQ  sR    1181DQG||>|~>~((>)=>()=>>>>>>>>v>>>v>>>|>>>~>>>(>>>)=>>>>>>>>>>>||=|~=~&&='<=&'<========v===v===|===~===&==='<===========r(   c                 h   t        j                  t        j                        j	                  d      }t        j                  t        j                        t        d      z
  j	                  d      }t        |ddd|dii       t        |ddd	d
d|ddi       t        |dddi       t        |dd       t        |ddi       t        |dddd       t        |dd       t        j                  |      d   }d}||v }|st        j                   d|fd||f      t        j"                  |      d t%        j&                         v st        j(                  |      rt        j"                  |      nd d!z  }d"d#|iz  }t+        t        j,                  |            dx}}d$}||v }|st        j                   d|fd||f      t        j"                  |      d t%        j&                         v st        j(                  |      rt        j"                  |      nd d!z  }d"d#|iz  }t+        t        j,                  |            dx}}d%}||v }|st        j                   d|fd||f      t        j"                  |      d t%        j&                         v st        j(                  |      rt        j"                  |      nd d!z  }d"d#|iz  }t+        t        j,                  |            dx}}d&}||v }|st        j                   d|fd||f      t        j"                  |      d t%        j&                         v st        j(                  |      rt        j"                  |      nd d!z  }d"d#|iz  }t+        t        j,                  |            dx}}d'}||v }|st        j                   d|fd||f      t        j"                  |      d t%        j&                         v st        j(                  |      rt        j"                  |      nd d!z  }d"d#|iz  }t+        t        j,                  |            dx}}y)(u)   정상 데이터 → 모든 섹션 포함rU  r   r   r   r   r   r   z
task-200.1r   u   통합 테스트 작업r   Nr   r)  r   r*  r(  u8   **S**: 상황
**C**: 변화
**Q**: 질문
**A**: 답변
r6  u    테스트 가이던스 키워드z
q-200.jsonr   u   확인 요청rJ  ro  z# InsuWiki
rH   r   r}   r~   r   ru   r   r   r   r.  r{  r   rK  )r   r   r   r   rW  r   r'   r-   r0   r6   r:   r>   rC   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   )	rX   r   now_isor   ru   r   rY   r[   r\   s	            r&   test_all_sections_presentz*TestFullBriefing.test_all_sections_presentV  s   ,,x||,556JK X\\2YQ5GGQQRfg 	fHIJ	

 	+*#<'") $		
 	x!2Y4MNJ	
 	h-O PQhv?.[\Xz>B 1181DQG w&    w&   w      &   &       #zV####zV###z######V###V#######)6))))6)))))))))6)))6))))))))6))))6)))))))))6)))6)))))))#zV####zV###z######V###V#######r(   c                 0   t        |ddi       t        j                  |      d   }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            dx}}y)uB   guidance만 있을 때 나머지 섹션은 빈/없음으로 처리r6  u   부분 테스트rH   r   r   r~   r   ru   r   r   r   Nr}   r<  r+  s          r&   test_partial_files_onlyz(TestFullBriefing.test_partial_files_only  s;   h-? @A 1181DQG)6))))6)))))))))6)))6)))))))!+!V++++!V+++!++++++V+++V+++++++ w&    w&   w      &   &       r(   c                 0   	 t         j                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}y
# t        $ r"}t        j                  d|        Y d
}~y
d
}~wt        $ r"}t        j                  d|        Y d
}~y
d
}~ww xY w)u0   파일 전혀 없어도 예외 없이 XML 반환rH   r   rt   r~   r   ru   r   r   r   Nu   SystemExit 발생: u   예외 발생: )r   r   rO   rP   rT   rQ   rR   rS   rU   rV   
SystemExitr   r   r   )rX   r   ru   r   rY   r[   r\   r   s           r&    test_no_exception_on_all_missingz1TestFullBriefing.test_no_exception_on_all_missing  s    	/$55x5HKF'1'61111'6111'111111611161111111 	3KK-aS122 	/KK/!-..	/s$   B>C 	D
C''D3DDc                 N   t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        j                  |      d   }|j                         D cg c]  }|j                  d      s| }}t        |      }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  t              rt        j                   t              nddt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      dz  }	dd|	iz  }
t#        t        j$                  |
            dx}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                   |      t        j                   |      dz  }dd|iz  }	t#        t        j$                  |	            dx}x}}yc c}w )u!   [팀] 줄이 '|' 구분자 포맷rU  r   r   r   r   rH   r   r}   r   rJ   r   r   	team_liner   r   r{   N|r~   r  r   r   rw   )r   r   r   r   rW  r'   r   r   
splitlinesr   r   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   r  ru   lr  rY   r   r   r   r   r   r   r   s                 r&   test_team_line_formatz&TestFullBriefing.test_team_line_format  si   ,,x||,556JK'-@'-@	
 !1181DQG & 1 1 3M1q||G7LQM	M9~""~""""~""""""s"""s""""""9"""9"""~"""""""""""il"sl""""sl"""s"""l""""""" Ns   5H"H"N)
r   r   r   r   r   r  r  r  r  r  r   r(   r&   r~  r~  N  sB    +>4 >
,$$ ,$\! !/ /#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	)
TestAnuExclusionuX   아누는 대화 주체이므로 [팀] 섹션 및 유휴경고에서 제외되어야 함r   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        j                  |      d   }t        d |j                         D        d	      }d
}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}y)uN   아누가 bot-activity에 있어도 [팀] 섹션에 표시되지 않아야 함rU  r   r   r   r   r   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr}   Nr   .0r  s     r&   	<genexpr>z@TestAnuExclusion.test_anu_not_in_team_section.<locals>.<genexpr>       RALL<Q!R      r   r   r   r  r   %   아누가 [팀] 섹션에 포함됨: 
>assert %(py5)sr   Nr   r   r   r   rW  r'   r   r   nextr  rO   rP   rT   rQ   rR   rS   _format_assertmsgrU   rV   	rX   r   r  ru   r  r   rY   r[   r\   s	            r&   test_anu_not_in_team_sectionz-TestAnuExclusion.test_anu_not_in_team_section  s    ,,x||,556JK'3gF&2WE	
 !1181DQGRV%6%6%8RTVW	]xy(]]]xy]]]x]]]]]]y]]]y]]]],QR[Q\*]]]]]]]r(   c                 <   t        j                  t        j                        j	                  d      }t        |ddd|dii       t        j                  |      d   }d}||v }|st        j                  d	|fd
||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d|iz  }t        t        j                   |            dx}}y)u4   아누만 있는 bot-activity → [팀] 정보없음rU  r   r   r   r   rH   r   u   [팀] 정보없음r~   r   ru   r   u*   아누만 있을 때 정보없음 기대: r  r   N)r   r   r   r   rW  r'   r   r   rO   rP   rT   rQ   rR   rS   r  rU   rV   )rX   r   r  ru   r   rY   r[   r\   s           r&    test_anu_only_team_shows_no_infoz1TestAnuExclusion.test_anu_only_team_shows_no_info  s    ,,x||,556JKewGHI	
 !1181DQG#d#v-ddd#vddd#ddddddvdddvdddd1[\b[c/dddddddr(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        j                  |      d   }t        d	 |j                         D        d
      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}y)u.   dev1 + anu → anu 제외되고 dev1만 표시rU  r   r   r   r   r  rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zETestAnuExclusion.test_dev1_and_anu_shows_only_dev1.<locals>.<genexpr>  r  r  r  r   r~   r   r  r   u&   dev1(1팀)이 [팀] 섹션에 없음: r  r   Nr   r   r   r  r  r  s	            r&   !test_dev1_and_anu_shows_only_dev1z2TestAnuExclusion.test_dev1_and_anu_shows_only_dev1  sX   ,,x||,556JK'-@&2WE	
 !1181DQGRV%6%6%8RTVW	Xv"XXXvXXXvXXXXXXXXXXXXX&LYK$XXXXXXX]xy(]]]xy]]]x]]]]]]y]]]y]]]],QR[Q\*]]]]]]]r(   c                    t        j                  t        j                        t	        d      z
  j                  d      }dd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }	t!        t        j"                  |	            dx}x}}y)uQ   아누가 장기 유휴 상태여도 [유휴경고]에 포함되지 않아야 함rT  r   rU  r   r   r   r   rJ   r   r   rM   r   u&   아누는 유휴경고 대상 아님: z
>assert %(py8)sr{   N)r   r   r   r   r   rW  r   rX  r   rO   rP   rQ   rR   rS   rT   r  rU   rV   r_  s
             r&   !test_anu_idle_not_in_idle_warningz2TestAnuExclusion.test_anu_idle_not_in_idle_warning  s    ",,x||4yq7IISSThi&>BC 22486{RaR{aRRR{aRRRRRRsRRRsRRRRRR6RRR6RRR{RRRaRRR#I&!RRRRRRRRr(   c                 V   t        j                  t        j                        t	        d      z
  j                  d      }t        |ddd|dii       t        j                  |      d	   }d
}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d|iz  }t!        t        j"                  |            dx}}y)uN   아누가 장기 유휴여도 [유휴경고] 줄이 출력되지 않아야 함rT  r   rU  r   r   r   r   rH   r   rb  r   r   ru   r   u,   아누 유휴 시 [유휴경고] 출력됨: r  r   N)r   r   r   r   r   rW  r'   r   r   rO   rP   rT   rQ   rR   rS   r  rU   rV   )rX   r   rY  ru   r   rY   r[   r\   s           r&   *test_anu_idle_not_in_briefing_idle_warningz;TestAnuExclusion.test_anu_idle_not_in_briefing_idle_warning  s    ",,x||4yq7IISSThieHIJ	
 !1181DQGfv-fffvfffffffffvfffvffff1]^d]e/fffffffr(   c                 L   t         j                  }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d	|iz  }	t        t        j                  |	            d
x}x}x}x}}y
)u\   TEAM_NAME_MAP에서 anu→아누 매핑은 여전히 존재해야 함 (다른 용도 가능)r   r   rJ   r   r   r   u6   TEAM_NAME_MAP에서 anu 매핑이 제거되면 안 됨
>assert %(py11)sr   N)r   r   r   rO   rP   rQ   rR   rS   rT   r  rU   rV   r   s
             r&   'test_team_name_mapping_anu_still_existsz8TestAnuExclusion.test_team_name_mapping_anu_still_exists  s    !//{{g5g{5!gXg!X-ggg!Xggggggwgggwggg{ggg5ggg!gggXggg/ggggggggr(   N)r   r   r   r   r   r  r  r  r  r  r  r   r(   r&   r  r    sY    b^T ^ e e^$ ^"S$ Sg4 gh hr(   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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defdZy)TestSaveStatusu@   save_status() - status.json 기록 및 status_dict 구조 검증r   c           
         dddddddddd	}t         j                  ||       |d	z  d
z  }|j                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u2   save_status() 호출 시 status.json 파일 생성2026-03-15T04:00:00+00:00oku   1팀:작업중r   r   N	last_runr   briefing_summaryteams_active
teams_idler  questions_pendingguidance_last_savederrorrH   r8   status.jsonu)   status.json 파일이 생성되지 않음zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}status_path)r   r   rv   )r   save_statusexistsrO   r  rQ   rR   rS   rT   rU   rV   )rX   r   r  r  rZ   r   r   s          r&   test_save_status_creates_filez,TestSaveStatus.test_save_status_creates_file  s     4 0!"#'

 	##K(#C*]:!!P!#P#PP%PPPPPPP{PPP{PPP!PPP#PPPPPPr(   c           
         dddddddddd	}t         j                  ||	       |d
z  dz  }t        j                  |j	                  d            }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u0   저장된 status.json 내용이 입력과 일치r  r  u   1팀:작업중 | 2팀:유휴r   r  z2026-03-15T03:50:00+00:00Nr  rH   r8   r  r   r   r   rJ   r   r   r   rw   r  r  r  r  r  r  r!  z%(py1)s is %(py4)s
r   r  r"   loads	read_textrO   rP   rT   rU   rV   
rX   r   r  r  savedr   r   rY   r   r   s
             r&    test_save_status_content_correctz/TestSaveStatus.test_save_status_content_correct  s    4 >!"#>

 	##K(#C*]:

;00'0BCX&$&$&&&&$&&&&&&$&&&&&&&^$))$))))$)))$))))))))))\"'a'"a''''"a'''"'''a'''''''^$))$))))$)))$))))))))))().Q.)Q....)Q...)...Q.......*+J/JJ+/JJJJJ+/JJJJ+JJJ/JJJJJJJJW~%%~%%%%~%%%~%%%%%%%%%%r(   c           
         dddddddddd	}t         j                  ||       |d	z  d
z  }t        j                  |j	                  d            }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u   에러 상태 기록 검증r  r  r  r   Nz.FileNotFoundError: bot-activity.json not foundr  rH   r8   r  r   r   r   rJ   r   r   r   rw   FileNotFoundErrorr~   r  r  r  s
             r&   test_save_status_error_statez+TestSaveStatus.test_save_status_error_state#  s    4 "!"#'E

 	##K(#C*]:

;00'0BCX)')'))))'))))))')))))))"4eGn4"n4444"n444"444n4444444r(   c                 F	   t         j                  |      }t        |t              }|s!t	        j
                  d      dz   dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }t        t	        j                  |            d}t        |      }d	}||k(  }|st	        j                  d
|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|\  }
}t        |
t              }|sddt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |
      rt	        j                  |
      nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }t        t	        j                  |            d}t        |t              }|sddt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }t        t	        j                  |            d}y)uB   compile_briefing()이 (str, dict) 튜플을 반환하는지 확인rH   u0   compile_briefing()은 튜플을 반환해야 함z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancerM   tuple)r   r   r   rv   Nr  rJ   r   r   r   r   r{   z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}ru   strr  dict)r   r   r  r  rO   r  rQ   rR   rS   rT   rU   rV   r   rP   r  r  )rX   r   rM   r   r   rY   r   r   r   r   ru   r  s               r&   )test_compile_briefing_returns_status_dictz8TestSaveStatus.test_compile_briefing_returns_status_dict6  sO    1181D&%(\(\\*\\\\\\\z\\\z\\\\\\&\\\&\\\\\\%\\\%\\\(\\\\\\6{a{a{ass66{a$&#&&&&&&&&z&&&z&&&&&&&&&&&&&&&&&#&&&#&&&&&&&&&&+t,,,,,,,,z,,,z,,,,,,+,,,+,,,,,,t,,,t,,,,,,,,,,r(   c                    t         j                  |      \  }}g d}|D ]  }||v }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d| d	      d
z   d|iz  }t        t        j                  |            d} y)u7   status_dict에 필수 키가 모두 포함되어야 함rH   r  r~   )z%(py0)s in %(py2)skeyr  r   r   u   status_dict에 'u   ' 키 없음
>assert %(py4)srv   N)r   r   rO   rP   rQ   rR   rS   rT   r  rU   rV   )	rX   r   r  r  required_keysr  rZ   @py_format3r   s	            r&   test_status_dict_required_keysz-TestSaveStatus.test_status_dict_required_keys?  s    (9989L;

 ! 	LC+%KKK3+KKKKKK3KKK3KKKKKK+KKK+KKKK)9#l'KKKKKKK	Lr(   c                 4   t         j                  |      \  }}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|d
   }d	}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	)u   정상 실행 시 status='ok'rH   r   r  rJ   r   r   r   rw   Nr  r!  r  )r   r   rO   rP   rT   rU   rV   r  s	            r&   test_status_dict_ok_on_successz-TestSaveStatus.test_status_dict_ok_on_successP  s    (9989L;8$,,$,,,,$,,,$,,,,,,,,,,7#+t+#t++++#t+++#+++t+++++++r(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|dd|ddi       t        |dddd	d
|ddi       t        j                  |      \  }}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u-   teams_active / teams_idle 카운트 정확성rU  r   r   r   r   r   r   r   ztask-active.1r      활성 작업r   Nr   rH   r  r   rJ   r   r   r   rw   r  r  r   r   r   r   rW  r'   r-   r   r   rO   rP   rT   rU   rV   
rX   r   r  r  r  r   r   rY   r   r   s
             r&   test_status_dict_teams_countz+TestSaveStatus.test_status_dict_teams_countV  s9   ,,x||,556JK'3gF'-@'-@		
 	.*#2'") $"		
 )9989L;>*/a/*a////*a///*///a///////<(-A-(A----(A---(---A-------r(   c                 r   t        |dddi       t        |dddi       t        j                  |      \  }}|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u0   done_pending: events/*.done 파일 수와 일치ztask-50.1.doner   z	task-50.1ztask-51.1.donez	task-51.1rH   r  r  rJ   r   r   r   rw   Nr  r  s	            r&   #test_status_dict_done_pending_countz2TestSaveStatus.test_status_dict_done_pending_countt  s    x!1I{3KLx!1I{3KL(9989L;>*/a/*a////*a///*///a///////r(   c                 v   t        |dddd       t        |dddd       t        j                  |      \  }}|d	   }d
}||k(  }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u1   questions_pending: 미응답 질문 수와 일치zq-a.jsonr   u   질문1rJ  zq-b.jsonr   u   질문2rH   r  r  rJ   r   r   r   rw   N)r>   r   r   rO   rP   rT   rU   rV   r  s	            r&   (test_status_dict_questions_pending_countz7TestSaveStatus.test_status_dict_questions_pending_count{  s    h
V,STh
V,ST(9989L;./414/14444/1444/44414444444r(   c                    d}t        |d|d       t        j                  |      \  }}|d   }||k(  }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndd	z  }d
d|iz  }t        t        j                  |            dx}}y)uG   guidance_last_saved: session-guidance.json의 saved_at 필드와 일치z2026-03-15T03:50:00Zr  )r6  saved_atrH   r  rJ   )z%(py1)s == %(py3)sr  r   r   r   Nr<  )	rX   r   r  r  r  r   rY   r[   r\   s	            r&   $test_status_dict_guidance_last_savedz3TestSaveStatus.test_status_dict_guidance_last_saved  s    )h[h OP(9989L;01=1X====1X===1======X===X=======r(   c           
         d}||z  }|j                   } |       }| }|sddt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |      t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}x}x}x}}dddd	d	d	d	ddd
	}t        j                  ||       d}||z  }d}||z  }|j                   }	 |	       }
|
sddt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |      t        j
                  |      t        j
                  |	      t        j
                  |
      dz  }t        t        j                  |            dx}x}x}x}x}	}
y)u/   whisper 디렉토리가 없어도 자동 생성r8   zQassert not %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
}r   )r   r   r   r   Nr  r  r  r   r  rH   r  z[assert %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = ((%(py0)s / %(py2)s) / %(py5)s).exists
}()
})r   r   r   r{   r|   )
r  rQ   rR   rO   rS   rT   rU   rV   r   r  )rX   r   rZ   r   r   @py_assert6r   r   r  r   r   r   s               r&   #test_save_status_creates_parent_dirz2TestSaveStatus.test_save_status_creates_parent_dir  sU   (2Hy(2(002022222222222H222H222y2220222222222223 "!"#'

 	##K(#C$>9$>}>$}4>4<<><>>>>>>>>>>>>>>9>>>}>>><>>>>>>>>>>>r(   N)r   r   r   r   r   r  r  r  r  r  r  r  r  r  r  r  r   r(   r&   r  r    s    JQd Q"& &05T 5&-$ -Lt L",t ,.T .<0D 05 5>T >?D ?r(   r  c                   L    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	y)	TestProcessingButNoRunningTasksuU   bot-activity가 processing이지만 실제 running task가 없으면 유휴로 표시r   c                    t        j                  t        j                        j	                  d      }t        |ddd|dii       t        j                  |      d   }t        d |j                         D        d	      }d
}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}y)uN   processing 상태 + running task 없음 → 브리핑에서 '유휴'로 표시rU  r   r   r   r   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zdTestProcessingButNoRunningTasks.test_processing_no_running_shows_idle_in_briefing.<locals>.<genexpr>  r  r  r     유휴r~   r   r  r   u)   processing+no running → 유휴 기대: r  r   Nr   r   u    processing이 직접 표시됨: r  r  s	            r&   1test_processing_no_running_shows_idle_in_briefingzQTestProcessingButNoRunningTasks.test_processing_no_running_shows_idle_in_briefing  sO   ,,x||,556JKfHIJ	

 !1181DQGRV%6%6%8RTVW	]x9$]]]x9]]]x]]]]]]9]]]9]]]](QR[Q\&]]]]]]]\|9,\\\|9\\\|\\\\\\9\\\9\\\\0PQZP[.\\\\\\\r(   c                    t        j                  t        j                        j	                  d      }t        |ddd|dii       t        |ddddd	|d
di       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }t        j"                  d|       dz   d|iz  }t%        t        j&                  |            d
x}}y
)uD   processing 상태 + running task 있음 → '작업중'으로 표시rU  r   r   r   r   z
task-500.1r   r  r   Nr   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  z]TestProcessingButNoRunningTasks.test_processing_with_running_shows_working.<locals>.<genexpr>  r  r  r  	   작업중r~   r   r  r   u)   processing+running → 작업중 기대: r  r   r   r   r   r   rW  r'   r-   r   r   r  r  rO   rP   rT   rQ   rR   rS   r  rU   rV   r  s	            r&   *test_processing_with_running_shows_workingzJTestProcessingButNoRunningTasks.test_processing_with_running_shows_working  s   ,,x||,556JKfHIJ	
 	+*#2'") $		
 !1181DQGRV%6%6%8RTVW	`{i'```{i```{``````i```i````+TU^T_)```````r(   c                     t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        j                  |      \  }}|d   }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }t        j                  d|d          dz   d|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)uL   processing + no running → status_dict에서 teams_idle 카운트에 포함rU  r   r   r   )r   r   rH   r  r  rJ   r   r   u   teams_idle=2 기대: z
>assert %(py6)srw   Nr  r   r   )r   r   r   r   rW  r'   r   r   rO   rP   rT   r  rU   rV   r  s
             r&   1test_processing_no_running_status_dict_idle_countzQTestProcessingButNoRunningTasks.test_processing_no_running_status_dict_idle_count  s   ,,x||,556JK'3gF'3gF	
 )9989L;<(bAb(A-bbb(Abbb(bbbAbbb1F{S_G`Fa/bbbbbbbb>*/a/*a////*a///*///a///////r(   c                 ~   t        j                  t        j                        j	                  d      }t        |ddd|dii       t        j                  |      d   }t        d |j                         D        d	      }d
}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}y)u/   알 수 없는 상태 → '상태불명' 표시rU  r   r   r  r   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zTTestProcessingButNoRunningTasks.test_unknown_status_shows_unknown.<locals>.<genexpr>  r  r  r  u   상태불명r~   r   r  r   u(   unknown status → 상태불명 기대: r  r   Nr  r  s	            r&   !test_unknown_status_shows_unknownzATestProcessingButNoRunningTasks.test_unknown_status_shows_unknown  s    ,,x||,556JKf7CDE	
 !1181DQGRV%6%6%8RTVW	b~*bbb~bbb~bbbbbbbbbbbbb.VW`Va,bbbbbbbr(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|dd|ddi       t        |dddd	d
|ddi       t        j                  |      \  }}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)ud   혼합 상태: dev1(running), dev2(processing+no running), dev3(idle) → 각각 올바르게 표시rU  r   r   r   r   r  z
task-600.1r   u   활성r   Nr   rH   r  r   rJ   r   r   r   rw   r  r  r  )
rX   r   r  ru   r  r   r   rY   r   r   s
             r&   test_mixed_teams_statusz7TestProcessingButNoRunningTasks.test_mixed_teams_status  s:   ,,x||,556JK'3gF'3gF'-@		
 	+*#+'") $		
 .>>>Q>*/a/*a////*a///*///a///////<(-A-(A----(A---(---A-------r(   N)
r   r   r   r   r   r  r  r  r  r  r   r(   r&   r  r    sG    _]$ ]a4 a00$ 0 	c$ 	c. .r(   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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)TestGhostTasksuH   detect_ghost_tasks() - 4시간 이상 running 태스크 고스트 감지r   c                    t        j                  t        j                        t	        d      z
  j                         }ddddd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}|d   d   }
d}|
|k(  }|slt        j                  d	|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                   |            dx}
x}}|d   d   }
d}|
|k(  }|slt        j                  d	|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                   |            dx}
x}}|d   d   }
d}|
|k\  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                   |            dx}
x}}y)u+   4시간 이상 running → 고스트 감지   r   z
task-700.1r   u   고스트 태스크r   r   r   r   r   r   r   rJ   r   r   rM   r   r   r{   Nr   r   r   r   r   rw   	team_namer   r   g      @>=z%(py1)s >= %(py4)sr   r   r   r   r   r   r   detect_ghost_tasksr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   five_hours_agotask_timersrM   rY   r   r   r   r   r   r   r   s                r&   test_detect_ghost_over_4hz(TestGhostTasks.test_detect_ghost_over_4h
  s   ",,x||4yq7IITTV'&4#,
 !33K@6{a{a{ass66{aay#3|3#|3333#|333#333|3333333ay%//%////%///%//////////ay!(S(!S((((!S(((!(((S(((((((r(   c                    t        j                  t        j                        t	        d      z
  j                         }ddddd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}y)u+   4시간 미만 running → 고스트 아님r  r   z
task-701.1r   u   정상 태스크r   r  r   rJ   r   r   rM   r   r   r{   Nr  )
rX   r   r\  r	  rM   rY   r   r   r   r   s
             r&   test_no_ghost_under_4hz%TestGhostTasks.test_no_ghost_under_4h  s    !hll3ia6HHSSU'&1#+
 !33K@6{a{a{ass66{ar(   c                    t        j                  t        j                        t	        d      z
  j                         }ddddd|di}t        j                  |      }t        |      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}y)u+   completed 상태는 고스트 대상 아님r   r   z
task-702.1r   r   r   r  r   rJ   r   r   rM   r   r   r{   Nr  )
rX   r   r  r	  rM   rY   r   r   r   r   s
             r&   test_completed_task_not_ghostz,TestGhostTasks.test_completed_task_not_ghost+  s    ",,x||4yq7IITTV'&1%,
 !33K@6{a{a{ass66{ar(   c                    t        j                  t        j                        t	        d      z
  j                         }t        |ddddd|ddi       t        j                  |	      d
   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}y)u7   고스트 태스크 → [고스트경고] 섹션 출력r   r   z
task-703.1r   u   고스트 의심 작업r   Nr   rH   r      [고스트경고]r~   r   ru   r   r   r   	   고스트r   r   r   r   r   r   r-   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   )rX   r   r  ru   r   rY   r[   r\   s           r&   test_ghost_warning_in_briefingz-TestGhostTasks.test_ghost_warning_in_briefing:  sy   ",,x||4yq7IITTV+*#<'"0 $		
 !1181DQG","f,,,,"f,,,",,,,,,f,,,f,,,,,,,%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%${f$$$${f$$${$$$$$$f$$$f$$$$$$$r(   c                 &   t        j                  t        j                        t	        d      z
  j                         }t        |ddddd|ddi       t        j                  |	      d
   }d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                   |            dx}}y)u>   최근 태스크만 있으면 [고스트경고] 출력 안 함r   r   z
task-704.1r   u   최근 작업r   Nr   rH   r   r  r   r   ru   r   r   r   r  )rX   r   r   ru   r   rY   r[   r\   s           r&   !test_no_ghost_warning_when_recentz0TestGhostTasks.test_no_ghost_warning_when_recentO  s     X\\2YQ5GGRRT+*#2'". $		
 !1181DQG"0"&0000"&000"000000&000&0000000r(   c                 <   t        j                  t        j                        t	        d      z
  j                         }t        j                  t        j                        j                  d      }t        |ddd|dii       t        |ddd	d
d|ddi       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt!        j"                         v st        j$                  |      rt        j                  |      nddz  }dd|iz  }	t'        t        j(                  |	            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt!        j"                         v st        j$                  |      rt        j                  |      nddz  }dd|iz  }	t'        t        j(                  |	            dx}}y)uJ   고스트 태스크는 [팀] 섹션에서 '⚠️고스트?' 마커 표시r   r   rU  r   r   r   r   z
task-705.1r   r  r   Nr   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zKTestGhostTasks.test_ghost_task_team_display_ghost_marker.<locals>.<genexpr>x  r  r  r  u   ⚠️고스트?r~   r   r  r   r   r   u   실질유휴r   r   r   r   r   r   rW  r'   r-   r   r   r  r  rO   rP   rT   rQ   rR   rS   rU   rV   
rX   r   r  r  ru   r  r   rY   r[   r\   s
             r&   )test_ghost_task_team_display_ghost_markerz8TestGhostTasks.test_ghost_task_team_display_ghost_markerb  sy   ",,x||4yq7IITTV,,x||,556JKfHIJ	
 	+*#.'"0 $		
 !1181DQGRV%6%6%8RTVW	!.!Y....!Y...!......Y...Y.......*~****~***~****************r(   c                 <   t        j                  t        j                        t	        d      z
  j                         }t        j                  t        j                        j                  d      }t        |ddd|dii       t        |ddd	d
d|ddi       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt!        j"                         v st        j$                  |      rt        j                  |      nddz  }dd|iz  }	t'        t        j(                  |	            dx}}d}||v}|st        j                  d|fd||f      t        j                  |      dt!        j"                         v st        j$                  |      rt        j                  |      nddz  }dd|iz  }	t'        t        j(                  |	            dx}}y)u2   고스트만 있는 팀은 '(실질유휴)' 표시r   r   rU  r   r   r   r   z
task-706.1r   r  r   Nr   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zFTestGhostTasks.test_ghost_only_shows_practical_idle.<locals>.<genexpr>  r  r  r  u   (실질유휴)r~   r   r  r   r   r   r  r   r   r  r  s
             r&   $test_ghost_only_shows_practical_idlez3TestGhostTasks.test_ghost_only_shows_practical_idle|  sy   ",,x||4yq7IITTV,,x||,556JKfHIJ	
 	+*#.'"0 $		
 !1181DQGRV%6%6%8RTVW	,9,,,,9,,,,,,,,,9,,,9,,,,,,,+{)++++{)+++{++++++)+++)+++++++r(   c                 ~   t        j                  t        j                        t	        d      z
  j                         }t        j                  t        j                        t	        d      z
  j                         }t        j                  t        j                        j                  d      }t        |ddd|dii       t        |d	d
dd|dddd
dd|ddd       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt!        j"                         v st        j$                  |      rt        j                  |      nddz  }	dd|	iz  }
t'        t        j(                  |
            dx}}y)u8   고스트+정상 태스크 혼합 → '작업중' 유지r   r   r   rU  r   r   r   r   
task-707.1r   r  r   Nr   
task-708.1u   정상 작업)r  r   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zKTestGhostTasks.test_mixed_ghost_and_normal_shows_working.<locals>.<genexpr>  r  r  r  r  r~   r   r  r   r   r   r  )rX   r   r  r   r  ru   r  r   rY   r[   r\   s              r&   )test_mixed_ghost_and_normal_shows_workingz8TestGhostTasks.test_mixed_ghost_and_normal_shows_working  sZ   ",,x||4yq7IITTV X\\2YQ5GGRRT,,x||,556JKfHIJ	
 	  ,*#.'"0 $  ,*#2'". $	
* !1181DQGRV%6%6%8RTVW	'{i''''{i'''{''''''i'''i'''''''r(   c                    t        j                  t        j                        t	        d      z
  j                         }t        |ddddd|ddi       t        j                  |	      d
   }|j                         j                         }d}d}t        |      D ]  \  }}d|v r|}d|v s|} d}	||	u}
|
st        j                  d|
fd||	f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |	      dz  }t        j$                  d      dz   d|iz  }t'        t        j(                  |            dx}
}	d}	||	u}
|
st        j                  d|
fd||	f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |	      dz  }dd|iz  }t'        t        j(                  |            dx}
}	||k  }
|
st        j                  d|
fd||f      dt        j                         v st        j                   |      rt        j"                  |      nddt        j                         v st        j                   |      rt        j"                  |      nddz  }t        j$                  d      dz   d|iz  }t'        t        j(                  |            d}
y)u6   [고스트경고]가 </whisper-briefing> 앞에 위치r   r   z
task-709.1r   r  r   Nr   rH   r   r  rx   r   r
   	ghost_idxr   u   [고스트경고] 줄이 없음r  r   	close_idxr   )<)z%(py0)s < %(py2)sr  u6   [고스트경고]가 </whisper-briefing> 뒤에 위치r  rv   )r   r   r   r   r   r   r-   r   r   r   r  	enumeraterO   rP   rQ   rR   rS   rT   r  rU   rV   )rX   r   r  ru   output_linesr$  r%  ilinerY   rZ   r[   r\   r  r   s                  r&   %test_ghost_warning_before_closing_tagz4TestGhostTasks.test_ghost_warning_before_closing_tag  s   ",,x||4yq7IITTV+*#.'"0 $		
 !1181DQG||~002		 . 	GAt"d*	$,			
 !%Gy$GGGyGGGGGGyGGGyGGGGGG&GGGGGGG $$y$$$$y$$$$$$y$$$y$$$$$$$$$$9$^^^y9^^^^^^y^^^y^^^^^^9^^^9^^^^&^^^^^^^r(   N)r   r   r   r   r   r
  r  r  r  r  r  r  r"  r+  r   r(   r&   r  r    st    R)$ )$ t   d  %t %*1$ 1&+$ +4,T ,4 ($  (D_d _r(   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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)TestBotOccupationuK   논리적 팀이 물리 봇을 점유할 때 dev팀 표시 변경 테스트r   c                 F   t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        |dddd	d
|dddi       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }t        j"                  d|       dz   d|iz  }t%        t        j&                  |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t%        t        j&                  |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t%        t        j&                  |            dx}}y)uL   design이 bot-b 점유 → dev1이 '봇점유(design:task-1446.1)'로 표시rU  r   r   r   r   r   designtask-1446.1r0     디자인 작업r   Nbot-br   r   r   r   r   r   botrH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zGTestBotOccupation.test_occupied_bot_shows_occupation.<locals>.<genexpr>  r  r  r  	   봇점유r~   r   r  r   u   봇점유 표시 기대: r  r   r   r  r  s	            r&   "test_occupied_bot_shows_occupationz4TestBotOccupation.test_occupied_bot_shows_occupation  s   ,,x||,556JK#)G<%1GD 	
 	,'#5'") $" 
	
 !1181DQGRV%6%6%8RTVW	P{i'PPP{iPPP{PPPPPPiPPPiPPPP+DYK)PPPPPPP$x9$$$$x9$$$x$$$$$$9$$$9$$$$$$$)}	))))}	)))}))))))	)))	)))))))r(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|dd|dd|dd|ddi       t        |ddd	d
|ddddddd
|ddddddd
|dddd       t        j                  |      d   }t        d |j                         D        d      }|j                  }d} ||      }d}||k\  }	|	st        j                  d|	fd||f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      t        j"                  |      t        j"                  |      t        j"                  |      dz  }
t        j$                  d|       d z   d!|
iz  }t'        t        j(                  |            dx}x}x}x}	}d"}||v }|st        j                  d#|fd$||f      t        j"                  |      dt        j                         v st        j                   |      rt        j"                  |      ndd%z  }d&d'|iz  }t'        t        j(                  |            dx}}y)(uI   design이 bot-b, bot-c, bot-d 점유 → dev1/2/3 모두 봇점유 표시rU  r   r   r   r   )r   r   r   dev4r0  r1  r0  u   디자인 작업 1r   Nr3  r4  task-1447.1u   디자인 작업 2bot-ctask-1448.1u   디자인 작업 3bot-d)r1  r;  r=  rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  z@TestBotOccupation.test_multiple_bots_occupied.<locals>.<genexpr>*  r  r  r  r7  rh  r  )zK%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.count
}(%(py4)s)
} >= %(py9)sr  r   u   3개 봇점유 기대: r  r   r  r~   r   r   r   r   )r   r   r   r   rW  r'   r-   r   r   r  r  countrO   rP   rQ   rR   rS   rT   r  rU   rV   )rX   r   r  ru   r  rZ   r   r   r   r   r   r   r   rY   r[   r\   s                   r&   test_multiple_bots_occupiedz-TestBotOccupation.test_multiple_bots_occupied  s   ,,x||,556JK#)G<#)G<#)G<#)G<%1GD 		
 	  -'#7'") $"   -'#7'") $"   -'#7'") $" '	
@ !1181DQGRV%6%6%8RTVW	W{W{+WqW+q0WWW+qWWWWWWyWWWyWWWWWW{WWW+WWWqWWW4KI;2WWWWWWWW$x9$$$$x9$$$x$$$$$$9$$$9$$$$$$$r(   c                    t        j                  t        j                        j	                  d      }t        |ddd|dii       t        |ddddd	|d
ddi       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            d
x}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            d
x}}y
)uW   dev팀이 자체 작업 running 중이면 봇 점유와 무관하게 '작업중' 표시rU  r   r   r   r   r*  r   u   dev1 자체 작업r   Nr3  r4  rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zMTestBotOccupation.test_dev_team_with_own_task_not_affected.<locals>.<genexpr>H  r  r  r  r  r~   r   r  r   r   r   r7  r   r   r   r   r   r   rW  r'   r-   r   r   r  r  rO   rP   rT   rQ   rR   rS   rU   rV   r  s	            r&   (test_dev_team_with_own_task_not_affectedz:TestBotOccupation.test_dev_team_with_own_task_not_affected0  sY   ,,x||,556JK<'B 	
 	+*#7'") $"
	
 !1181DQGRV%6%6%8RTVW	'{i''''{i'''{''''''i'''i'''''''+{)++++{)+++{++++++)+++)+++++++r(   c                    t        j                  t        j                        j	                  d      }t        |ddd|dii       t        |ddddd|d	d
di       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }t        j"                  d|       dz   d|iz  }t%        t        j&                  |            d	x}}y	)u9   논리적 팀 표시에 봇 ID 포함: task-1446.1(bot-b)rU  r   r0  r   r   r1  r2  r   Nr3  r4  rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zCTestBotOccupation.test_logical_team_shows_bot_id.<locals>.<genexpr>d  r  r  r  z(bot-b)r~   r   r  r   u   봇 ID 표시 기대: r  r   r  r  s	            r&   test_logical_team_shows_bot_idz0TestBotOccupation.test_logical_team_shows_bot_idL  s   ,,x||,556JK\GD 	
 	,'#5'") $" 
	
 !1181DQGRV%6%6%8RTVW	KyI%KKKyIKKKyKKKKKKIKKKIKKKK)?	{'KKKKKKKr(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|ddi       t        |dddd	d
|ddi       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}y)u9   bot 필드 없는 작업은 봇점유 탐지에서 무시rU  r   r   r   r   r/  z
task-old.1r0  u   봇 필드 없는 작업r   Nr   rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  z?TestBotOccupation.test_no_bot_field_graceful.<locals>.<genexpr>  r  r  r  r  r~   r   r  r   r   r   r7  r   r   rD  r  s	            r&   test_no_bot_field_gracefulz,TestBotOccupation.test_no_bot_field_gracefulg  s\   ,,x||,556JK#)G<%1GD 	
 	+'#='") $
	
 !1181DQGRV%6%6%8RTVW	$x9$$$$x9$$$x$$$$$$9$$$9$$$$$$$+{)++++{)+++{++++++)+++)+++++++r(   c                    t        j                  t        j                        j	                  d      }t        |ddd|dii       t        |ddddd	||d
di       t        j                  |      d   }t        d |j                         D        d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}y)uC   논리적 팀 작업 완료 후 dev팀이 다시 '유휴'로 표시rU  r   r   r   r   r1  r0  u   완료된 디자인 작업r   r3  r4  rH   r   c              3   D   K   | ]  }|j                  d       s|  ywr  r  r  s     r&   r  zPTestBotOccupation.test_occupation_cleared_after_task_complete.<locals>.<genexpr>  r  r  r  r  r~   r   r  r   r   r   Nr7  r   r   rD  r  s	            r&   +test_occupation_cleared_after_task_completez=TestBotOccupation.test_occupation_cleared_after_task_complete  sY   ,,x||,556JK6G< 	
 	,'#?)") '" 
	
 !1181DQGRV%6%6%8RTVW	$x9$$$$x9$$$x$$$$$$9$$$9$$$$$$$+{)++++{)+++{++++++)+++)+++++++r(   c                    t        j                  t        j                        t	        d      z
  j                  d      }t        j                  t        j                        j                  d      }t        |dd|dd|ddi       t        |d	d	d
dd|dddi       t        j                  |      d   }d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}}y)u7   봇 점유 중인 dev팀은 [유휴경고]에서 제외rT  r   rU  r   r   r   r   r/  r1  r0  r2  r   Nr3  r4  rH   r   rb  r   r   ru   r   u&   봇점유 팀은 유휴경고 제외: r  r   )r   r   r   r   r   rW  r'   r-   r   r   rO   rP   rT   rQ   rR   rS   r  rU   rV   )	rX   r   rY  r  ru   r   rY   r[   r\   s	            r&   %test_bot_occupied_not_in_idle_warningz7TestBotOccupation.test_bot_occupied_not_in_idle_warning  s*   ",,x||4yq7IISSThi,,x||,556JK#)NC%1GD 	
 	,'#5'") $" 
	
 !1181DQG`v-```v`````````v```v````1WX^W_/```````r(   c                    t        j                  t        j                        j	                  d      }t        |dd|dd|dd|ddi       t        |dddd	d
|dddi       t        j                  |      \  }}|d   }d}||k\  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u@   봇 점유 dev팀은 status_dict에서 teams_active로 카운트rU  r   r   r   r   )r   r   r0  r1  r0  r2  r   Nr3  r4  rH   r  r  r  r  r   r   rw   r7  r  r~   r  r  r  s
             r&   .test_status_dict_bot_occupied_counts_as_activez@TestBotOccupation.test_status_dict_bot_occupied_counts_as_active  s=   ,,x||,556JK#)G<#)G<%1GD 	
 	,'#5'") $" 
	
 )9989L;>*/a/*a////*a///*///a///////=k*<=={====={===={===========r(   c                 f   t        j                  t        j                        j	                  d      }dddd|dddd	d
d|ddd}dddd}t
        j                  ||      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            dx}}|d   d   }d}
||
k(  }|slt        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}}
|d   d   }d}
||
k(  }|slt        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}}
d}||v}|st        j                  d|fd ||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            dx}}y)!u-   _build_bot_occupation 함수 직접 테스트rU  r1  r0  u	   디자인r   r3  )r   r   r   r   r   r5  r*  r   u   dev2 자체 작업r<  )r1  r*  r   r   r   )r3  r<  r>  r~   r   rM   r   r   r   NteamrJ   r   r   r   rw   r   r   r   )r   r   r   r   rW  r   _build_bot_occupationrO   rP   rT   rQ   rR   rS   rU   rV   )rX   r   r  r	  
bot_to_devrM   r   rY   r[   r\   r   r   r   s                r&   "test_build_bot_occupation_functionz4TestBotOccupation.test_build_bot_occupation_function  s   ,,x||,556JK )#*#% (&3#%
$  &H
 66{JOvvvf~f%11%1111%111%1111111111f~i(9M9(M9999(M999(999M9999999#vV####vV###v######V###V#######r(   N)r   r   r   r   r   r8  rA  rE  rH  rK  rN  rP  rR  rW  r   r(   r&   r-  r-    sv    U*4 *<2%D 2%h, ,8Lt L6,4 ,<,D ,8ad a:>t ><$4 $r(   r-  c                 \    | dz  }|j                  t        j                  |      d       | S )Nactive-projects.jsonr   r   r,   )r   r   r%   s      r&   make_active_projectsrZ    s+    ))ALLD!GL4Or(   dir_pathnamemtime_offset_daysc                     | j                  dd       d| d}| |z  }|j                  |d       |dkD  r2ddl}|j                         |d	z  z
  }ddl}|j	                  |||f       |S )
uK   feedback_*.md 파일 생성. mtime_offset_days: 0=오늘, 1=하루 전, ...Tr   z
---
name: u-   
description: test
type: feedback
---

내용r   r   r   NiQ )r    r!   timeosutime)	r[  r.   r\  r]  r1   r%   r_  	new_mtimer`  s	            r&   make_feedback_filerc    sw    NN4$N/D6!STG8ALL7L+1IIK#4u#<=	
Y	*+Hr(   r   c                 r    | dz  }|j                  dd       d| d}||z  }|j                  |d       | S )N	learningsTr   z---
status: u   
---

학습 내용r   r   r4   )r   r.   r   learnings_dirr1   r%   s         r&   make_learning_filerg    sM    {*Mt4fX%;<G ALL7L+Or(   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	)
TestProjectProgressuE   load_project_progress() - active-projects.json에서 진행률 로드r   c                 t   t        |dddddddgi       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|D cg c]  }|d   	 }	}d}
|
|	v }|st	        j
                  d|fd|
|	f      t	        j                  |
      dt        j                         v st	        j                  |	      rt	        j                  |	      nddz  }dd|iz  }t        t	        j                  |            dx}
}d}
|
|	v }|st	        j
                  d|fd|
|	f      t	        j                  |
      dt        j                         v st	        j                  |	      rt	        j                  |	      nddz  }dd|iz  }t        t	        j                  |            dx}
}|D ci c]  }|d   |d    }}|d   }
d}|
|k(  }|slt	        j
                  d	|fd|
|f      t	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}
x}}|d   }
d}|
|k(  }|slt	        j
                  d	|fd|
|f      t	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}
x}}yc c}w c c}w )u*   정상 데이터 → name, progress 추출activeProjectAZ   r\  progressProjectB(   rH   r  rJ   r   r   rM   r   r   r{   Nr\  r~   r   namesr   r   r   ro  r   r   r   rw   )rZ  r   load_project_progressr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   rM   rY   r   r   r   r   prr  r   r[   r\   progress_mapr   r   s                   r&   !test_load_project_progress_normalz5TestProjectProgress.test_load_project_progress_normal#  s^   <z_a>bcd	
 !666I6{a{a{ass66{a$*+q6++"zU""""zU"""z""""""U"""U""""""""zU""""zU"""z""""""U"""U""""""":@AQ&	1Z=0AAJ'-2-'2----'2---'---2-------J'-2-'2----'2---'---2------- , Bs   N0N5c                 |   t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y	u   파일 없음 → 빈 리스트rH   rJ   rL   rM   r   r   r   N)
r   rs  rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   'test_load_project_progress_missing_filez;TestProjectProgress.test_load_project_progress_missing_file2  sm     666Iv|vvvr(   c                    t        |dg i       t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
u,   active 배열 비어있음 → 빈 리스트rk  rH   rJ   rL   rM   r   r   r   N)rZ  r   rs  rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   'test_load_project_progress_empty_activez;TestProjectProgress.test_load_project_progress_empty_active7  s{    X"~6 666Iv|vvvr(   c                    |dz  }|j                  dd       t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}y)u   손상 JSON → 빈 리스트rY  r   r   r   rH   rJ   rL   rM   r   r   r   N)r!   r   rs  rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   r%   rM   rY   rZ   r[   r\   s           r&   $test_load_project_progress_corruptedz8TestProjectProgress.test_load_project_progress_corrupted=  s    --	+g> 666Iv|vvvr(   c                    t        |ddddgi       t        j                  |      d   }d}||v }|st        j                  d|fd	||f      t        j
                  |      d
t        j                         v st        j                  |      rt        j
                  |      nd
dz  }dd|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd	||f      t        j
                  |      d
t        j                         v st        j                  |      rt        j
                  |      nd
dz  }dd|iz  }t        t        j                  |            dx}}y)u7   compile_briefing에서 [진행률] 섹션 출력 확인rk  rl  K   rn  rH   r      [진행률]r~   r   ru   r   r   r   N)rZ  r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&   test_progress_in_briefingz-TestProjectProgress.test_progress_in_briefingD  s    <=>	
 !1181DQG&}&&&&}&&&}&&&&&&&&&&&&&&&&#zV####zV###z######V###V#######r(   c                    t         j                  |      d   }d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}y
)u2   active-projects.json 없을 때 [진행률] 없음rH   r   r  r   r   ru   r   r   r   Nr0  r+  s          r&   'test_no_progress_in_briefing_when_emptyz;TestProjectProgress.test_no_progress_in_briefing_when_emptyN  ss     1181DQG*}F****}F***}******F***F*******r(   N)r   r   r   r   r   rv  ry  r{  r}  r  r  r   r(   r&   ri  ri     sM    O.$ . 
 T $$ $+ +r(   ri  c                   L    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	y)	TestStaleTasksuE   load_stale_tasks() - pending 상태 + 3일 이상 경과한 태스크r   c           
      8   t        j                  t        j                        t	        d      z
  j                         }|dz  }|j                  t        j                  ddd|ddii      d	
       t        j                  |      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                   t              rt        j"                  t              nddt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      t        j"                  |      dz  }dd|iz  }	t%        t        j&                  |	            dx}x}}|d   d   }
d}|
|k(  }|slt        j                  d|fd|
|f      t        j"                  |
      t        j"                  |      dz  }dd|iz  }t%        t        j&                  |            dx}
x}}y)u'   pending 상태 + 4일 전 → 반환됨rT  daysr+   r)   ztask-Xpendingu   오래된 대기 작업r   r   r   r   r   rH   r   rJ   r   r   rM   r   r   r{   Nr   r   r   r   r   rw   )r   r   r   r   r   r   r!   r"   r#   r   load_stale_tasksr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   four_days_agor%   rM   rY   r   r   r   r   r   r   r   s                r&   )test_load_stale_tasks_pending_over_3_daysz8TestStaleTasks.test_load_stale_tasks_pending_over_3_days\  si   !hll3iQ6GGRRT))	JJ"+&3'@   	 	
 !1181D6{a{a{ass66{aay#/x/#x////#x///#///x///////r(   c           
      ^   t        j                  t        j                        t	        d      z
  j                         }|dz  }|j                  t        j                  ddd|ddii      d	
       t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }dd|iz  }t#        t        j$                  |            dx}}y)u$   pending + 1일 전 → 빈 리스트r   r  r+   r)   ztask-Yr  u   최근 대기 작업r  r   r   rH   rJ   rL   rM   r   r   r   Nr   r   r   r   r   r   r!   r"   r#   r   r  rO   rP   rQ   rR   rS   rT   rU   rV   )	rX   r   one_day_agor%   rM   rY   rZ   r[   r\   s	            r&   *test_load_stale_tasks_pending_under_3_daysz9TestStaleTasks.test_load_stale_tasks_pending_under_3_daysp  s    ||HLL1I14EEPPR))	JJ"+&1'=   	 	
 !1181Dv|vvvr(   c           
      h   t        j                  t        j                        t	        d      z
  j                         }|dz  }|j                  t        j                  dd|ddd|d	dd
i      d       t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }dd|iz  }t#        t        j$                  |            dx}}y)u&   running/completed만 → 빈 리스트rT  r  r+   r)   r   u   실행 중 작업r  r   r   )ztask-Rztask-Cr   r   rH   rJ   rL   rM   r   r   r   Nr  )	rX   r   r  r%   rM   rY   rZ   r[   r\   s	            r&    test_load_stale_tasks_no_pendingz/TestStaleTasks.test_load_stale_tasks_no_pending  s    !hll3iQ6GGRRT))	JJ"+&3': #.&3'9   	 	
" !1181Dv|vvvr(   c                 |   t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}yrx  )
r   r  rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   "test_load_stale_tasks_missing_filez1TestStaleTasks.test_load_stale_tasks_missing_file  sm     1181Dv|vvvr(   c           
         t        j                  t        j                        t	        d      z
  j                         }|dz  }|j                  t        j                  ddd|ddii      d	
       t        j                  |      d   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                   |      rt        j                  |      nddz  }dd|iz  }t#        t        j$                  |            dx}}y)u7   compile_briefing에서 [미착수] 섹션 출력 확인rT  r  r+   r)   ztask-stale-1r  u   미착수 태스크r  r   r   rH   r   u   [미착수]r~   r   ru   r   r   r   N)r   r   r   r   r   r   r!   r"   r#   r   r   rO   rP   rT   rQ   rR   rS   rU   rV   )	rX   r   r  r%   ru   r   rY   r[   r\   s	            r&   test_stale_tasks_in_briefingz+TestStaleTasks.test_stale_tasks_in_briefing  s>   !hll3iQ6GGRRT))	JJ""+&3'<%   	 	
 !1181DQG&}&&&&}&&&}&&&&&&&&&&&&&&&&'~''''~'''~''''''''''''''''r(   N)
r   r   r   r   r   r  r  r  r  r  r   r(   r&   r  r  Y  sA    O0$ 0(4 & 04 
(T (r(   r  c                   L    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	y)	TestRecentMistakesuF   load_recent_mistakes() - feedback_*.md 파일에서 최신 3개 추출r   c                 	   |dz  }t        |ddd       t        |ddd       t        |d	d
d       t        |ddd       t        |ddd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}|D 	cg c]  }	|	d   	 }
}	d}||
v }|st	        j
                  d|fd||
f      t	        j                  |      dt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }d d!|iz  }t        t	        j                  |            dx}}d}||
v }|st	        j
                  d|fd||
f      t	        j                  |      dt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }d d!|iz  }t        t	        j                  |            dx}}d
}||
v }|st	        j
                  d|fd||
f      t	        j                  |      dt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }d d!|iz  }t        t	        j                  |            dx}}d}||
v}|st	        j
                  d"|fd#||
f      t	        j                  |      dt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }d d!|iz  }t        t	        j                  |            dx}}d}||
v}|st	        j
                  d"|fd#||
f      t	        j                  |      dt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }d d!|iz  }t        t	        j                  |            dx}}yc c}	w )$u%   5개 파일 중 최신 3개만 반환feedbackzfeedback_01.mdu   실수1rT  )r]  zfeedback_02.mdu   실수2rh  zfeedback_03.mdu   실수3r  zfeedback_04.mdu   실수4r   zfeedback_05.mdu   실수5r   feedback_dirrJ   r   r   rM   r   r   r{   Nr\  r~   r   rr  r   r   r   r   r   rc  r   load_recent_mistakesr   rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   r  rM   rY   r   r   r   r   r  rr  r   r[   r\   s                 r&    test_load_recent_mistakes_normalz3TestRecentMistakes.test_load_recent_mistakes_normal  s   *,<)99XYZ<)99XYZ<)99XYZ<)99XYZ<)99XYZ 55<5P6{a{a{ass66{a$*+q6++!yE!!!!yE!!!y!!!!!!E!!!E!!!!!!!!yE!!!!yE!!!y!!!!!!E!!!E!!!!!!!!yE!!!!yE!!!y!!!!!!E!!!E!!!!!!!%y%%%%y%%%y%%%%%%%%%%%%%%%%%y%%%%y%%%y%%%%%%%%%%%%%%%% ,s   ;Sc                    |dz  }t         j                  |      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
u%   디렉토리 없음 → 빈 리스트nonexistent_feedbackr  rJ   rL   rM   r   r   r   N)
r   r  rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   missing_dirrM   rY   rZ   r[   r\   s           r&   %test_load_recent_mistakes_missing_dirz8TestRecentMistakes.test_load_recent_mistakes_missing_dir  sx    !77 55;5Ov|vvvr(   c                 x   |dz  }t        |dd       t        j                  |      }t        |      }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              ndd	t        j                         v st	        j                  |      rt	        j                  |      nd	t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}|d   d   }	d}
|	|
k(  }|slt	        j
                  d|fd|	|
f      t	        j                  |	      t	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
d}	|d   }
|	|
v }|slt	        j
                  d|fd|	|
f      t	        j                  |	      t	        j                  |
      dz  }dd|iz  }t        t	        j                  |            dx}	x}}
y)u%   frontmatter name 정확 추출 확인r  zfeedback_test.mdu   정확한이름r  r   rJ   r   r   rM   r   r   r{   Nr   r\  r   r   r   rw   dater~   r  r  )rX   r   r  rM   rY   r   r   r   r   r   r   r   s               r&   )test_load_recent_mistakes_name_extractionz<TestRecentMistakes.test_load_recent_mistakes_name_extraction  sa   *,<);=NO 55<5P6{a{a{ass66{aay 5$55 $55555 $5555 555$55555555""v""""v"""v""""""""""r(   c                    |dz  }|j                  dd       t        j                  |      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}}y)u<   디렉토리 존재하지만 파일 없음 → 빈 리스트r  Tr   r  rJ   rL   rM   r   r   r   N)r    r   r  rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   r  rM   rY   rZ   r[   r\   s           r&   +test_load_recent_mistakes_no_feedback_filesz>TestRecentMistakes.test_load_recent_mistakes_no_feedback_files  s    *,4$7 55<5Pv|vvvr(   c                    |dz  }t        |dd       t        j                  ||      d   }d}||v }|st        j                  d|fd||f      t        j
                  |      d	t        j                         v st        j                  |      rt        j
                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd||f      t        j
                  |      d	t        j                         v st        j                  |      rt        j
                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}y)u3   compile_briefing에서 [최근실수] 출력 확인r  zfeedback_recent.mdu   최근실수항목)rI   r  r   u   [최근실수]r~   r   ru   r   r   r   N)rc  r   r   rO   rP   rT   rQ   rR   rS   rU   rV   )rX   r   r  ru   r   rY   r[   r\   s           r&   test_mistakes_in_briefingz,TestRecentMistakes.test_mistakes_in_briefing  s    *,<)=?ST 118R^1_`ab)6))))6)))))))))6)))6)))))))#-#v----#v---#------v---v-------r(   N)
r   r   r   r   r   r  r  r  r  r  r   r(   r&   r  r    sA    P& &$d #$ #D .$ .r(   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)TestPendingLearningsuH   load_pending_learnings() - learnings/*.md에서 status=pending 카운트r   c                    t        |dd       t        |dd       t        |dd       t        |dd       t        |dd       t        j                  |	      }d
}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)u'   3개 pending, 2개 applied → 3 반환zlearn_01.mdr  r   zlearn_02.mdzlearn_03.mdzlearn_04.mdappliedzlearn_05.mdrH   rh  rJ   rL   rM   r   r   r   N)rg  r   load_pending_learningsrO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   "test_load_pending_learnings_normalz7TestPendingLearnings.test_load_pending_learnings_normal  s    8]9E8]9E8]9E8]9E8]9E 777Jv{vvvr(   c                 |   t         j                  |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
u   폴더 없음 → 0 반환rH   r   rJ   rL   rM   r   r   r   N)
r   r  rO   rP   rQ   rR   rS   rT   rU   rV   rW   s          r&   "test_load_pending_learnings_no_dirz7TestPendingLearnings.test_load_pending_learnings_no_dir   sm     777Jv{vvvr(   c                    |dz  }|j                  dd       t        j                  |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}y)u+   폴더 있지만 파일 없음 → 0 반환re  Tr   rH   r   rJ   rL   rM   r   r   r   N)r    r   r  rO   rP   rQ   rR   rS   rT   rU   rV   )rX   r   rf  rM   rY   rZ   r[   r\   s           r&   %test_load_pending_learnings_empty_dirz:TestPendingLearnings.test_load_pending_learnings_empty_dir  s     ;.D48 777Jv{vvvr(   c                    t        |dd       t        |dd       t        j                  |      d   }d}||v }|st        j                  d|fd	||f      t        j
                  |      d
t        j                         v st        j                  |      rt        j
                  |      nd
dz  }dd|iz  }t        t        j                  |            dx}}y)uD   compile_briefing에서 [미처리학습] 섹션 항상 출력 확인z
learn_a.mdr  r  z
learn_b.mdrH   r   u   [미처리학습]r~   r   ru   r   r   r   N)rg  r   r   rO   rP   rT   rQ   rR   rS   rU   rV   r+  s          r&   test_learnings_in_briefingz/TestPendingLearnings.test_learnings_in_briefing  s    8\)D8\)D 1181DQG","f,,,,"f,,,",,,,,,f,,,f,,,,,,,r(   N)	r   r   r   r   r   r  r  r  r  r   r(   r&   r  r    s5    R4 4 
d -4 -r(   r  )r   )r  )Kr   builtinsrQ   _pytest.assertion.rewrite	assertionrewriterO   importlib.util	importlibr"   sysr   r   r   pathlibr   r   __file__parent_SCRIPTS_DIRpathinsertr  _MODULE_PATHutilspec_from_file_locationr   rY   rZ   rP   rR   rS   rT   r[   r\   rU   rV   module_from_specr   loaderr   r   @py_format8exec_moduler  r'   r-   r0   r6   r:   r>   rC   rE   r   r   r   r  r3  rA  rR  rm  r~  r  r  r  r  r-  rZ  intrc  rg  ri  r  r  r  r   r(   r&   <module>r     st       
 2 2   H~$$++ 3|$ % 22~~--.?N t4   t4     t   t   4      ..11$7{{ $ {$   {$     t   t   {  $          ( D T t D T T S   $ #   D   D C t  4 #   (@ (@`+ +fG G^M0 M0j/" /"n7 7J+ +f,  , h* *>W# W#~Bh BhTa? a?R_. _.NL_ L_h[$ [$F	4 t    C TW `d   c RV 1+ 1+rY( Y(B1. 1.r- -r(   