
    Ti3A                     p   d Z ddlZddlZddlZddlZddlmZ ddlmZmZ ddl	Z	ej                  j                  dd       ddlmZ dedefd	Zdedefd
ZdedefdZ G d de	j$                        Z G d de	j$                        Z G d de	j$                        Zedk(  r e	j.                  d       yy)u  
server.py 3가지 버그 수정 단위 테스트
테스터: 아르고스 (Argos)
대상: DataLoader 클래스
    - Bug 1: get_member_status() 30분 TTL (working 상태 since 타임스탬프 검사)
    - Bug 2: get_running_tasks_by_team() 2시간 TTL (running 태스크 start_time stale 검사)
    - Bug 3: load_member_status() JSON 자동 복구 (깨진 JSON → {"members":{}} 덮어쓰기)

실행:
    pytest /home/jay/workspace/teams/dev1/test_task_112_1.py -v
    N)Path)datetime	timedeltaz/home/jay/workspace/dashboard
DataLoadertmp_dirreturnc                     | dz  }|j                  dd       |dz  j                  dd       |dz  j                  dd       | S )u+   임시 workspace 디렉토리 구조 생성memoryT)parentsexist_okeventslogs)mkdir)r   
memory_dirs     1/home/jay/workspace/teams/dev1/test_task_112_1.pymake_workspacer      sT    8#JTD1(!!$!>&t<N    c                     t        |       S )u?   임시 workspace를 가리키는 DataLoader 인스턴스 반환r   )r   s    r   make_loaderr   (   s    gr   dtc                 "    | j                         S )u&   datetime → ISO 8601 문자열 변환)	isoformat)r   s    r   isor   -   s    <<>r   c                   `    e Zd ZdZd Zd ZddededefdZded	efd
Z	d Z
d Zd Zd Zd Zy)TestBug1MemberStatusTTLu   
    Bug 1: member-status.json의 working 상태에서 since 타임스탬프가
    30분(1800초) 초과이면 working을 무시해야 한다.

    테스트 대상: DataLoader.get_member_status()
    c                     t        j                         | _        t        t	        | j                  j
                              | _        t        | j                        | _        y<   각 테스트마다 새 임시 workspace + DataLoader 준비N	tempfileTemporaryDirectorytmpr   r   name	workspacer   loaderselfs    r   setUpzTestBug1MemberStatusTTL.setUp>   ;    ..0'TXX]](;<!$..1r   c                 8    | j                   j                          y Nr#   cleanupr'   s    r   tearDownz TestBug1MemberStatusTTL.tearDownD       r   N	member_idstatussincec                     d|i}|||d<   d||ii}| j                   dz  dz  dz  }|j                  t        j                  |dd	
      d       | j                  j                          y)u.   memory/events/member-status.json 작성 헬퍼r2   Nr3   membersr   r   member-status.jsonF   ensure_asciiindentutf-8encoding)r%   
write_textjsondumpsr&   load_member_status)r(   r1   r2   r3   entrydatapaths          r   _write_member_statusz,TestBug1MemberStatusTTL._write_member_statusG   sq    6""E'NIu-.~~(836JJ

4eAFQXY&&(r   r	   c                     ||dS )u   테스트용 멤버 dict 반환)idr$    )r(   r1   s     r   _make_memberz$TestBug1MemberStatusTTL._make_memberQ   s    33r   c                    d}t        j                         t        d      z
  }| j                  |dt	        |             | j                  |      }| j                  j                  |di d      }| j                  |dd|        y	)
uT   since가 10분 전이면 working이 유효하므로 'working'을 반환해야 한다alice
   minutesworking	dev1-teamFis_leadu1   10분 전 since면 working이어야 함, 실제: N	r   nowr   rE   r   rI   r&   get_member_statusassertEqualr(   r1   since_dtmemberresults        r    test_member_working_within_30minz8TestBug1MemberStatusTTL.test_member_working_within_30minW   s}    	<<>Ib$99!!)YHF""9-..v{BPU.VI?xH	
r   c                    d}t        j                         t        d      z
  }| j                  |dt	        |             | j                  |      }| j                  j                  |di d      }| j                  |dd|        y	)
uY   since가 40분 전이면 stale이므로 'working'이 아닌 상태를 반환해야 한다bob(   rM   rO   rP   FrQ   u@   40분 전 since면 working이 아니어야 함 (stale), 실제: N)	r   rT   r   rE   r   rI   r&   rU   assertNotEqualrW   s        r   test_member_working_over_30minz6TestBug1MemberStatusTTL.test_member_working_over_30ming   s}    	<<>Ib$99!!)YHF""9-..v{BPU.VINvhW	
r   c                    d}t        j                         t        d      z
  }| j                  |dt	        |             | j                  |      }| j                  j                  |di d      }| j                  |dd|        y	)
u\   since가 30분 직전(1795초 전)이면 경계값이므로 'working'을 반환해야 한다caroli  )secondsrO   rP   FrQ   uN   정확히 30분(1800초) 전은 경계값으로 working이어야 함, 실제: NrS   rW   s        r   !test_member_working_exactly_30minz9TestBug1MemberStatusTTL.test_member_working_exactly_30minw   s    	<<>Id$;;!!)YHF""9-..v{BPU.VI\]c\de	
r   c                     d}| j                  |dd       | j                  |      }| j                  j                  |di d      }| j	                  |dd|        y)	uU   since 필드가 없으면 TTL 검사를 건너뛰고 'working'을 반환해야 한다daverO   Nr3   rP   FrQ   uE   since 없으면 방어적으로 working을 반환해야 함, 실제: rE   rI   r&   rU   rV   r(   r1   rY   rZ   s       r   "test_member_working_no_since_fieldz:TestBug1MemberStatusTTL.test_member_working_no_since_field   si    	!!)Yd!C""9-..v{BPU.VISTZS[\	
r   c                     d}| j                  |dd       | j                  |      }| j                  j                  |di d      }| j	                  |dd|        y	)
ue   since가 파싱 불가능한 형식이면 TTL 검사를 건너뛰고 'working'을 반환해야 한다everO   znot-a-valid-timestamprg   rP   FrQ   uR   잘못된 since 형식이면 방어적으로 working을 반환해야 함, 실제: Nrh   ri   s       r   !test_member_working_invalid_sincez9TestBug1MemberStatusTTL.test_member_working_invalid_since   sj    	!!)Y>U!V""9-..v{BPU.VI`ag`hi	
r   r,   )__name__
__module____qualname____doc__r)   r/   strrE   dictrI   r[   r`   rd   rj   rm   rH   r   r   r   r   6   sW    2)c )3 )s )4c 4d 4
 
 
"
 
r   r   c                   :    e Zd ZdZd Zd ZdefdZd Zd Z	d Z
y	)
TestBug2RunningTasksTTLu   
    Bug 2: task-timers.json의 running 태스크에서 start_time이
    2시간(7200초) 초과이면 stale로 판단하여 결과에서 제외해야 한다.

    테스트 대상: DataLoader.get_running_tasks_by_team()
    c                     t        j                         | _        t        t	        | j                  j
                              | _        t        | j                        | _        yr   r    r'   s    r   r)   zTestBug2RunningTasksTTL.setUp   r*   r   c                 8    | j                   j                          y r,   r-   r'   s    r   r/   z TestBug2RunningTasksTTL.tearDown   r0   r   tasksc                     d|i}| j                   dz  dz  }|j                  t        j                  |dd      d       | j                  j                          y	)
u%   memory/task-timers.json 작성 헬퍼rx   r   ztask-timers.jsonFr7   r8   r;   r<   N)r%   r>   r?   r@   r&   
load_tasks)r(   rx   rC   rD   s       r   _write_task_timersz*TestBug2RunningTasksTTL._write_task_timers   sN    ~~(+==

4eAFQXY r   c           	      B   t        j                         t        d      z
  }| j                  ddddt	        |      di       | j
                  j                         }| j                  d|d       |d   D cg c]  }|d	   	 }}| j                  d|d
|        yc c}w )uR   start_time이 1시간 전인 running 태스크는 결과에 포함되어야 한다   hoursztask-fresh-1runningrP   u   신선한 태스크r2   team_iddescription
start_timeuJ   1시간 전 start_time인 running 태스크가 팀 목록에 있어야 함task_idu4   task-fresh-1이 포함되어야 함, 실제 목록: N)r   rT   r   r{   r   r&   get_running_tasks_by_teamassertInr(   start_dtrZ   ttask_idss        r   test_running_task_within_2hoursz7TestBug2RunningTasksTTL.test_running_task_within_2hours   s    <<>IA$66#&4!(m	!
 	 668X	
 +1*=>QAiL>>HB8*M	
 ?s   7Bc           	      &   t        j                         t        d      z
  }| j                  ddddt	        |      di       | j
                  j                         }d|v r,|d   D cg c]  }|d   	 }}| j                  d|d	|        y
y
c c}w )ud   start_time이 3시간 전인 running 태스크는 stale이므로 결과에서 제외되어야 한다   r~   ztask-stale-1r   rP   u   오래된 태스크r   r   uT   3시간 전 start_time인 태스크는 stale로 제외되어야 함, 실제 목록: N)r   rT   r   r{   r   r&   r   assertNotInr   s        r   test_running_task_over_2hoursz5TestBug2RunningTasksTTL.test_running_task_over_2hours   s    <<>IA$66#&4!(m	!
 	 668 & .4[.AB)BHBfgofpq  Cs   (Bc                     | j                  dddddi       | j                  j                         }| j                  d|d       |d   D cg c]  }|d   	 }}| j                  d|d|        y	c c}w )
ue   start_time이 없는 running 태스크는 TTL 검사를 건너뛰고 결과에 포함되어야 한다ztask-no-time-1r   z	dev2-teamu   시작시간 없는 태스크)r2   r   r   uJ   start_time 없는 running 태스크는 방어적으로 포함되어야 함r   u6   task-no-time-1이 포함되어야 함, 실제 목록: N)r{   r&   r   r   )r(   rZ   r   r   s       r   test_running_task_no_start_timez7TestBug2RunningTasksTTL.test_running_task_no_start_time   s    #&>!
 	 668X	
 +1*=>QAiL>>hDXJO	
 ?s   A1N)rn   ro   rp   rq   r)   r/   rs   r{   r   r   r   rH   r   r   ru   ru      s,    2! !
46
r   ru   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy) TestBug3LoadMemberStatusRecoveryu   
    Bug 3: member-status.json이 깨진 JSON이면 {"members": {}} 초기값으로
    파일을 덮어쓰기하여 자동 복구해야 한다.

    테스트 대상: DataLoader.load_member_status()
    c                     t        j                         | _        t        t	        | j                  j
                              | _        t        | j                        | _        | j                  dz  dz  dz  | _	        y)r   r   r   r6   N)
r!   r"   r#   r   r   r$   r%   r   r&   status_filer'   s    r   r)   z&TestBug3LoadMemberStatusRecovery.setUp  sW    ..0'TXX]](;<!$..1>>H4x?BVVr   c                 8    | j                   j                          y r,   r-   r'   s    r   r/   z)TestBug3LoadMemberStatusRecovery.tearDown%  r0   r   c                    | j                   j                  dd       | j                  j                         }| j	                  |t
        d       | j                   j                  d      }	 t        j                  |      }| j                  dd|        | j	                  |d   t
        d|d           y	# t        j                  $ r | j                  d|       Y `w xY w)
u   
        member-status.json이 깨진 JSON이면:
        1. member_status_data가 {"members": {}} (또는 빈 dict)으로 초기화된다
        2. 파일 내용이 {"members": {}}으로 덮어쓰기된다 (자동 복구)
        z{this is not valid json!!!r;   r<      반환값은 dict이어야 함u/   복구 후 파일이 유효한 JSON이 아님: r5   u2   복구된 JSON에 'members' 키가 있어야 함: u.   복구된 'members' 값은 dict이어야 함: N)r   r>   r&   rA   assertIsInstancers   	read_textr?   loadsJSONDecodeErrorfailr   )r(   rZ   recovered_textrecovered_datas       r   !test_load_corrupted_json_recoveryzBTestBug3LoadMemberStatusRecovery.test_load_corrupted_json_recovery*  s     	##( 	$ 	

 //1 	fd,LM ))33W3E	!ZZ7N 	~@@PQ	
 	9%t<^I=V<WX	
 ## 	IIA.AST	s   ,B8 8'C"!C"c                    ddt        t        j                         t        d      z
        dddidi}| j                  j                  t        j                  |d	d
      d       | j                  j                         }| j                  |t        d       | j                  d|d       | j                  d|d   d       | j                  d|d   d       | j                  |d   d   d   dd|d   d           y)uT   member-status.json이 정상이면 해당 데이터를 그대로 반환해야 한다r5   rO      rM   )r2   r3   r2   idle)frankgraceFr7   r8   r;   r<   r   u   'members' 키가 있어야 함r   u!   frank가 members에 있어야 함r   u!   grace가 members에 있어야 함u)   frank의 status가 working이어야 함: N)r   r   rT   r   r   r>   r?   r@   r&   rA   r   rs   r   rV   )r(   expected_datarZ   s      r   test_load_valid_jsonz5TestBug3LoadMemberStatusRecovery.test_load_valid_jsonP  s    $-HLLNY_`Ma<a8bc"F+
 	##JJ}5C 	$ 	

 //1fd,LMi)IJgvi02UVgvi02UV9g&x0)7y8I'8R7ST	
r   c                     | j                  | j                  j                         d       | j                  j	                         }| j                  |t        d       | j                  |i k(  xs |di ik(  d|        y)u]   member-status.json 파일이 없으면 빈 dict(또는 {"members":{}})를 반환해야 한다u,   테스트 시작 시 파일이 없어야 함r   r5   uG   파일 없음이면 빈 dict 또는 members:dict이어야 함, 실제: N)assertFalser   existsr&   rA   r   rs   
assertTrue)r(   rZ   s     r   test_load_nonexistent_filez;TestBug3LoadMemberStatusRecovery.test_load_nonexistent_filej  s}     	##%:	

 //1fd,LM 	bL5Fy"o5UV\U]^	
r   N)	rn   ro   rp   rq   r)   r/   r   r   r   rH   r   r   r   r     s"    W
"
L
4
r   r   __main__r7   )	verbosity)rq   sysr?   osr!   pathlibr   r   r   unittestrD   insertserverr   r   r   rr   r   TestCaser   ru   r   rn   mainrH   r   r   <module>r      s   
   	   (  2 3 D T  * 
H  n
h// n
jd
h// d
Vd
x'8'8 d
V zHMMA r   