
    iI              	      `   U d Z ddlmZ ddlZddlZddlmZ ddlZej                  j                  d e
 ee      j                  j                  j                               ddlmZ dddd	d
ddddZded<   dddddddddZded<   g dg dddg dg ddg d g d!dd"Zd#ed$<   d:d%Zd;d&Zd<d=d'Z	 d>	 	 	 	 	 	 	 	 	 d?d(Z	 	 	 	 	 	 	 	 d@d)Z G d* d+      Z G d, d-      Z G d. d/      Z G d0 d1      Z G d2 d3      Z G d4 d5      Z G d6 d7      Zed8k(  r ej@                  ed9g       yy)Au   utils/bot_status.py — BotStatusManager 단위 테스트 스위트

작성자: 하누만 (dev4-team)
대상:   BotStatusManager 클래스 전 메서드
전략:   tmp_path로 격리된 파일시스템, 실제 constants.json 데이터 재현
    )annotationsN)Path)BotStatusManagerbot-bbot-cbot-dbot-ebot-fbot-gbot-hbot-i)	dev1-team	dev2-team	dev3-teamz	dev4-teamz	dev5-teamz	dev6-teamz	dev7-teamz	dev8-teamzdict[str, str]TEAM_TO_BOTdev1dev2dev3dev4dev5dev6dev7dev8TEAMS)u   배너u	   이미지u	   디자인u   시안u   광고 크리에이티브u   카드뉴스 디자인u	   비주얼u	   포스터)u   HTML 수정u
   CSS 버그u   코드 수정u   렌더러 수정u8   비주얼 창작 전문. 코드 수정은 dev팀 소관.)keywordsanti_keywordsdescription)u   카피u   마케팅 전략u   광고 문구SEOu   콘텐츠 전략u   캠페인 기획u   마케팅/카피 전문.)u   블로그 작성u   콘텐츠 제작u   포스팅 작성u   콘텐츠 제작 전문.)design	marketingcontentdictLOGICAL_TEAMSc                    | dz  }|j                  dd       |dz  }|j                  t        j                  t        t
        t        dd             |S )u8   tmp_path/config/constants.json 생성 후 경로 반환.configTparentsexist_okzconstants.json)team_to_botteamslogical_teamsFensure_ascii)mkdir
write_textjsondumpsr   r   r#   )tmp_path
config_dirconstants_paths      L/home/jay/workspace/.worktrees/task-2116-dev1/utils/tests/test_bot_status.pymake_constantsr6   J   s_    H$JTD1"22N

*!.
 	
	     c                    | dz  }|j                  dd       |dz  }|j                  t        j                  d|id             |S )u:   tmp_path/memory/task-timers.json 생성 후 경로 반환.memoryTr&   ztask-timers.jsontasksFr,   )r.   r/   r0   r1   )r2   r:   
memory_dirtimers_paths       r5   make_task_timersr=   \   sN    H$JTD111K4::w&6UKLr7   c                L    t        |        |t        | |       t        |       S )u   constants + task-timers를 tmp_path에 준비하고 BotStatusManager 반환.

    tasks=None 이면 task-timers.json을 생성하지 않는다.
    )workspace_root)r6   r=   r   )r2   r:   s     r5   make_managerr@   e   s'    
 85)844r7   c                    | ||ddd |dS )Nrunning2026-04-04T00:00:00+00:00task_idteam_idr   status
start_timeend_timebot )rE   rF   rJ   r   s       r5   running_taskrL   u   s#     "1 r7   c                    | |dddd|dS )Nu   완료된 작업	completedrC   z2026-04-04T01:00:00+00:00rD   rK   )rE   rF   rJ   s      r5   completed_taskrO      s#     )1/ r7   c                  @    e Zd ZdZd	dZd	dZd	dZd	dZd	dZd	dZ	y)
TestGetBusyBotsu0   get_busy_bots() — running 봇 조회 테스트c           	         t        |dt        ddd      i      }|j                         }d|v sJ d       |d   d   dk(  sJ d       |d   d   dk(  sJ d       y	)
u1   running 작업 1개 → 해당 봇 정보 반환.z
task-100.1r   r   u,   bot-b는 busy 봇 목록에 있어야 한다rE      task_id가 일치해야 한다rF   u   team_id가 일치해야 한다Nr@   rL   get_busy_botsselfr2   mgrresults       r5   $test_single_running_task_returns_botz4TestGetBusyBots.test_single_running_task_returns_bot   s~    <k7KL

 ""$& P"PP gy)\9[;[[9gy)[8Z:ZZ8r7   c           	         t        |dt        ddd      i      }|j                         }d|v sJ d       |d   d   dk(  sJ y)uF   논리적 팀(design)이 bot-b 점유 → bot-b가 busy 봇에 포함.z
task-200.1r   r   uC   design 팀이 bot-b를 점유하면 bot-b가 반환되어야 한다rF   NrT   rV   s       r5   'test_logical_team_design_occupies_bot_bz7TestGetBusyBots.test_logical_team_design_occupies_bot_b   s[    <hHI

 ""$& g"gg gy)X555r7   c           	     l    t        |dt        ddd      i      }|j                         }d|vsJ d       y)u*   completed 작업은 busy 봇에서 제외.z
task-300.1r   r   u:   completed 작업의 봇은 busy 목록에 없어야 한다N)r@   rO   rU   rV   s       r5   test_completed_task_excludedz,TestGetBusyBots.test_completed_task_excluded   sD    >,WMN

 ""$f$b&bb$r7   c           	     p    t        |dt        ddd      i      }|j                  d      }d|vsJ d       y)u:   exclude_task_id 지정 시 해당 작업의 봇은 제외.z
task-400.1r   r   )exclude_task_idu?   자기 자신 task_id를 제외하면 bot-c는 없어야 한다NrT   rV   s       r5   %test_exclude_task_id_removes_own_taskz5TestGetBusyBots.test_exclude_task_id_removes_own_task   sI    <k7KL

 ""<"@f$g&gg$r7   c                V    t        |d      }|j                         }|i k(  sJ d       y)uE   task-timers.json 없으면 빈 dict 반환 (파일 없음 케이스).Nr:   u=   task-timers.json이 없으면 빈 dict를 반환해야 한다)r@   rU   rV   s       r5   +test_missing_timers_file_returns_empty_dictz;TestGetBusyBots.test_missing_timers_file_returns_empty_dict   s.    840""$|\\\|r7   c           
         t        |t        ddd      t        ddd      t        ddd	      d
      }|j                         }d|v sJ d       d|v sJ d       d	|v sJ d       t        |      dk(  sJ d       y)u3   여러 running 작업 → 관련 봇 모두 반환.
task-500.1r   r   
task-500.2r   r   
task-500.3r   r   )rf   rg   rh   u   bot-b가 포함되어야 한다u   bot-c가 포함되어야 한다u   bot-d가 포함되어야 한다   u.   총 3개의 busy 봇이 반환되어야 한다N)r@   rL   rU   lenrV   s       r5   (test_multiple_running_tasks_all_returnedz8TestGetBusyBots.test_multiple_running_tasks_all_returned   s    *<gN*<gN*<gN
 ""$& C"CC & C"CC & C"CC 6{aQ!QQr7   Nr2   r   returnNone)
__name__
__module____qualname____doc__rZ   r\   r^   ra   rd   rk   rK   r7   r5   rQ   rQ      s(    :[
6	c	h]Rr7   rQ   c                  (    e Zd ZdZddZddZddZy)TestGetIdleBotsu/   get_idle_bots() — 유휴 봇 목록 테스트c                    t        |i       }|j                         }t        |      dk(  sJ dt        |              y)u,   모든 봇이 유휴 상태 → 8개 반환.rc      u)   유휴 봇은 8개여야 한다, 실제: N)r@   get_idle_botsrj   rV   s       r5    test_all_bots_idle_returns_eightz0TestGetIdleBots.test_all_bots_idle_returns_eight   s@    82.""$6{aZ#LSQW[M!ZZr7   c           	         t        |t        ddd      t        ddd      d      }|j                         }t        |      dk(  sJ d	t        |              d|vsJ d
       d|vsJ d       y)u"   2개 봇 busy → 유휴 봇 6개.
task-600.1r   r   
task-600.2r   r   )rz   r{      u)   유휴 봇은 6개여야 한다, 실제: u*   bot-b는 유휴 목록에 없어야 한다u*   bot-c는 유휴 목록에 없어야 한다N)r@   rL   rw   rj   rV   s       r5   test_two_busy_returns_six_idlez.TestGetIdleBots.test_two_busy_returns_six_idle   s    *<gN*<gN
 ""$6{aZ#LSQW[M!ZZf$R&RR$f$R&RR$r7   c                    t        g dd      D ci c]  \  }}d| t        d| d| d|       }}}t        ||      }|j                         }|g k(  sJ d       yc c}}w )	u'   8개 봇 모두 busy → 빈 리스트.)r   r   r   r	   r
   r   r   r      )startz	task-700.devz-teamu=   모든 봇이 busy이면 빈 리스트를 반환해야 한다N)	enumeraterL   r@   rw   )rW   r2   irJ   r:   rX   rY   s          r5   %test_all_bots_busy_returns_empty_listz5TestGetIdleBots.test_all_bots_busy_returns_empty_list  s     $X	
 3 sO\A33qc 
 
 8U+""$|\\\|
s   "A!Nrl   )ro   rp   rq   rr   rx   r}   r   rK   r7   r5   rt   rt      s    9[S ]r7   rt   c                       e Zd ZdZddZddZy)TestIsBotAvailableu2   is_bot_available() — 봇 가용 여부 테스트c                R    t        |i       }|j                  d      du sJ d       y)u   유휴 봇 → True.rc   r   Tu'   유휴 봇은 available이어야 한다N)r@   is_bot_availablerW   r2   rX   s      r5   test_idle_bot_is_availablez-TestIsBotAvailable.test_idle_bot_is_available"  s-    82.##G,4_6__4r7   c           	     j    t        |dt        ddd      i      }|j                  d      du sJ d       y)u   busy 봇 → False.z
task-800.1r   r   Fu=   running 작업 중인 봇은 available이 아니어야 한다N)r@   rL   r   r   s      r5   test_busy_bot_is_not_availablez1TestIsBotAvailable.test_busy_bot_is_not_available(  sA    <k7KL

 ##G,5v7vv5r7   Nrl   )ro   rp   rq   rr   r   r   rK   r7   r5   r   r     s    <`wr7   r   c                  (    e Zd ZdZddZddZddZy)TestGetTeamStatusu4   get_team_status() — 팀 상태 문자열 테스트c           	     v    t        |dt        ddd      i      }|j                  d      }|dk(  s
J d|       y)u0   dev팀이 running 작업 있음 → '작업중'.z
task-900.1r   r   u	   작업중uE   running 작업이 있는 팀은 '작업중'이어야 한다, 실제: Nr@   rL   get_team_statusrW   r2   rX   rG   s       r5   /test_dev_team_with_running_task_returns_workingzATestGetTeamStatus.test_dev_team_with_running_task_returns_working:  sO    <k7KL

 $$[1$x(mntmw&xx$r7   c           	     t    t        |dt        ddd      i      }|j                  d      }d|v s
J d|       y)uW   dev1-team의 봇(bot-b)이 논리적 팀(design)에 점유 → '봇점유(...)' 포함.ztask-1000.1r   r   r   u	   봇점유uA   봇 점유 상태는 '봇점유'를 포함해야 한다, 실제: Nr   r   s       r5   6test_dev_team_bot_occupied_by_logical_returns_occupiedzHTestGetTeamStatus.test_dev_team_bot_occupied_by_logical_returns_occupiedE  sO    L'JK

 $$[1f$t(ijpis&tt$r7   c                ^    t        |i       }|j                  d      }|dk(  s
J d|       y)u&   아무 작업 없는 팀 → '유휴'.rc   r   u   유휴u4   작업 없는 팀은 '유휴'여야 한다, 실제: N)r@   r   r   s       r5   $test_team_with_no_tasks_returns_idlez6TestGetTeamStatus.test_team_with_no_tasks_returns_idleP  s;    82.$$[1!d%YZ`Yc#dd!r7   Nrl   )ro   rp   rq   rr   r   r   r   rK   r7   r5   r   r   7  s    >	y	uer7   r   c                  0    e Zd ZdZddZddZddZddZy)TestGetBotOccupationu;   get_bot_occupation() — 물리 봇 점유 탐지 테스트c           	         t        |dt        ddd      i      }|j                         }d|v sJ d       |d   d   dk(  sJ d       |d   d   dk(  sJ d	       |d   d
   dk(  sJ d       y)u9   design 팀이 bot-b 점유 → dev1 점유 정보 반환.ztask-1100.1r   r   r   u8   bot-b를 점유하면 dev1 엔트리가 있어야 한다teamu$   점유 팀이 design이어야 한다bot_idu#   점유 봇이 bot-b이어야 한다rE   rS   Nr@   rL   get_bot_occupationrV   s       r5   ,test_design_occupies_bot_b_returns_dev1_infozATestGetBotOccupation.test_design_occupies_bot_b_returns_dev1_infoa  s    L'JK

 '')[![[f~f%1Y3YY1f~h'72Y4YY2f~i(M9[;[[9r7   c           	     l    t        |dt        ddd      i      }|j                         }d|vsJ d       y)uQ   dev1-team 자체 작업은 봇 점유(cross-team occupation)로 잡히지 않음.ztask-1200.1r   r   r   u:   자기 팀 작업은 타팀 점유로 잡혀선 안 된다Nr   rV   s       r5   (test_dev_team_own_task_not_in_occupationz=TestGetBotOccupation.test_dev_team_own_task_not_in_occupationo  sD    LWMN

 '')V#a%aa#r7   c                j    ddddddd}t        |d|i      }|j                         }|i k(  sJ d       y)	u7   bot 필드 없는 작업은 점유 탐지에서 무시.ztask-1300.1r   u   봇 미지정 작업rB   rC   N)rE   rF   r   rG   rH   rI   u=   bot 필드 없는 작업은 점유 결과가 비어야 한다)r@   r   )rW   r2   task_no_botrX   rY   s        r5   &test_task_without_bot_field_is_ignoredz;TestGetBotOccupation.test_task_without_bot_field_is_ignoredz  sP     %15
 8m[%AB'')|\\\|r7   c           	     l    t        |dt        ddd      i      }|j                         }d|vsJ d       y)u6   completed 작업은 봇 점유에 포함되지 않음.ztask-1400.1r   r   r   u2   완료된 작업은 점유로 잡혀선 안 된다N)r@   rO   r   rV   s       r5   %test_completed_task_not_in_occupationz:TestGetBotOccupation.test_completed_task_not_in_occupation  sD    N=(GLM

 '')V#Y%YY#r7   Nrl   )ro   rp   rq   rr   r   r   r   r   rK   r7   r5   r   r   ^  s    E\	b]"	Zr7   r   c                  8    e Zd ZdZddZddZddZddZddZy)	TestSuggestTeamu8   suggest_team() — 키워드 기반 팀 추천 테스트c                ^    t        |i       }|j                  d      }|dk(  s
J d|       y)u(   디자인 키워드 → 'design' 추천.rc   !   신제품 배너 디자인 제작r   u@   디자인 키워드는 'design'을 추천해야 한다, 실제: Nr@   suggest_teamrV   s       r5   "test_design_keyword_returns_designz2TestSuggestTeam.test_design_keyword_returns_design  s<    82.!!"EF!p%efleo#pp!r7   c                ^    t        |i       }|j                  d      }|dk(  s
J d|       y)u+   마케팅 키워드 → 'marketing' 추천.rc   u)   신규 캠페인 기획 및 카피 작성r    uC   마케팅 키워드는 'marketing'을 추천해야 한다, 실제: Nr   rV   s       r5   (test_marketing_keyword_returns_marketingz8TestSuggestTeam.test_marketing_keyword_returns_marketing  s<    82.!!"MN$v(klrku&vv$r7   c                X    t        |i       }|j                  d      }|
J d|       y)u!   매칭 키워드 없음 → None.rc   u$   서버 배포 파이프라인 점검Nu/   매칭 없으면 None이어야 한다, 실제: r   rV   s       r5   %test_no_matching_keyword_returns_nonez5TestSuggestTeam.test_no_matching_keyword_returns_none  s8    82.!!"HI~[!PQWPZ[[~r7   c                ^    t        |i       }|j                  d      }|dk7  s
J d|       y)u?   anti-keyword가 포함되면 해당 팀은 추천에서 제외.rc   u   배너 HTML 수정 요청r   uK   anti-keyword가 있으면 design은 추천되지 않아야 한다, 실제: Nr   rV   s       r5   test_anti_keyword_excludes_teamz/TestSuggestTeam.test_anti_keyword_excludes_team  s>    82. !!"=>!{%pqwpz#{{!r7   c                X    t        |i       }|j                  d      }|
J d|       y)u   빈 문자열 → None.rc    Nu/   빈 문자열은 None이어야 한다, 실제: r   rV   s       r5   test_empty_string_returns_nonez.TestSuggestTeam.test_empty_string_returns_none  s7    82.!!"%~[!PQWPZ[[~r7   Nrl   )	ro   rp   rq   rr   r   r   r   r   r   rK   r7   r5   r   r     s#    Bqw\|\r7   r   c                  0    e Zd ZdZddZddZddZddZy)TestValidateRoutingu8   validate_routing() — 라우팅 검증 경고 테스트c                    t        |i       }|j                  dd      }|J d       t        |t              sJ dt	        |              y)u>   dev팀에 디자인 작업 배정 → 경고 문자열 반환.rc   r   r   Nu;   dev팀에 디자인 작업은 경고를 반환해야 한다u-   경고는 문자열이어야 한다, 실제: )r@   validate_routing
isinstancestrtyperV   s       r5   .test_dev_team_with_design_task_returns_warningzBTestValidateRouting.test_dev_team_with_design_task_returns_warning  sX    82.%%k3VW!`#``!&#&f*WX\]cXdWe(ff&r7   c                Z    t        |i       }|j                  dd      }|
J d|       y)u1   dev팀에 코딩 작업 → 경고 없음 (None).rc   r   u6   API 엔드포인트 구현 및 유닛 테스트 작성NuD   dev팀 코딩 작업은 경고 없이 None이어야 한다, 실제: r@   r   rV   s       r5   +test_dev_team_with_coding_task_returns_nonez?TestValidateRouting.test_dev_team_with_coding_task_returns_none  s:    82.%%k3kl~p!efleopp~r7   c                Z    t        |i       }|j                  dd      }|
J d|       y)u<   논리적 팀(design)에 디자인 작업 → 경고 없음.rc   r   u   배너 디자인 시안 작성NuF   design 팀의 디자인 작업은 경고가 없어야 한다, 실제: r   rV   s       r5   ,test_logical_team_with_own_task_returns_nonez@TestValidateRouting.test_logical_team_with_own_task_returns_none  s:    82.%%h0PQ~r!ghngqrr~r7   c                ^    t        |i       }|j                  ddd      }|
J d|       y)u6   override_routing=True → 경고 억제 (None 반환).rc   r   r   T)override_routingNuG   override_routing=True 이면 경고가 억제되어야 한다, 실제: r   rV   s       r5   (test_override_routing_suppresses_warningz<TestValidateRouting.test_override_routing_suppresses_warning  s@    82.%%k3Vim%n~s!hiohrss~r7   Nrl   )ro   rp   rq   rr   r   r   r   r   rK   r7   r5   r   r     s    Bgqstr7   r   __main__z-v)r2   r   rm   r   )r2   r   r:   r"   rm   r   )N)r2   r   r:   zdict | Nonerm   r   )u   작업 설명)
rE   r   rF   r   rJ   r   r   r   rm   r"   )rE   r   rF   r   rJ   r   rm   r"   )!rr   
__future__r   r0   syspathlibr   pytestpathinsertr   __file__parentutils.bot_statusr   r   __annotations__r   r#   r6   r=   r@   rL   rO   rQ   rt   r   r   r   r   r   ro   mainrK   r7   r5   <module>r      s   #  
   3tH~,,33::; < - 	^ 	 	~ 	 T[Q s1 Q1t 0$5( '	 
 	
 
" 
 
	*JR JRd*] *]dw w0e eN6Z 6Z|*\ *\d"t "tJ zFKK4 ! r7   