
     ii5                        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
Z
ddlmZmZmZ ddlmZ ddlmZ ddlZ e
j&                         Zeej*                  d<   e	j,                  j/                  d e ee      j4                  j4                                ed      Z G d d	      Z G d
 d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z#e$dk(  r ejJ                  edg       yy)u*   bot-status-watchdog.py 테스트 스위트    N)datetime	timedeltatimezone)Path)patchWORKSPACE_ROOTzbot-status-watchdogc                   (    e Zd ZdZd Zd Zd Zd Zy)TestParseSinceTimeu   since 시간 파싱 테스트c                 F   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                  }t        j                  }||k(  }|st        j                  d	|fd
||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                  }d}
||
k(  }|st        j                  d	|fd||
f      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   UTC Z 포맷 파싱z2026-03-17T06:00:00ZNis notz%(py0)s is not %(py3)sresultpy0py3assert %(py5)spy5==)zG%(py2)s
{%(py2)s = %(py0)s.tzinfo
} == %(py6)s
{%(py6)s = %(py4)s.utc
}r   )r   py2py4py6zassert %(py8)spy8   )z,%(py2)s
{%(py2)s = %(py0)s.hour
} == %(py5)s)r   r   r   zassert %(py7)spy7)watchdogparse_since_time
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationtzinfor   utchour)selfr   @py_assert2@py_assert1@py_format4@py_format6@py_assert5@py_assert3@py_format7@py_format9@py_assert4@py_format8s               =/home/jay/workspace/scripts/tests/test_bot_status_watchdog.pytest_parse_utc_z_formatz*TestParseSinceTime.test_parse_utc_z_format   si   **+AB!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!}},,},,,,},,,,,,v,,,v,,,},,,,,,,,,,,,,,,,,,,{{a{a{avv{a    c                 x   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   ISO 오프셋 포맷 파싱z2026-03-17T06:00:00+09:00Nr   r   r   r   r   r   
r   r   r   r    r!   r"   r#   r$   r%   r&   r*   r   r+   r,   r-   r.   s         r5   test_parse_iso_with_offsetz-TestParseSinceTime.test_parse_iso_with_offset    sl    **+FG!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!r7   c                 x   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
not-a-dateNisz%(py0)s is %(py3)sr   r   r   r   r9   r:   s         r5   test_parse_invalid_returns_nonez2TestParseSinceTime.test_parse_invalid_returns_none%   sj    **<8v~vvvr7   c                 x   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 반환 Nr=   r?   r   r   r   r   r9   r:   s         r5   test_parse_empty_returns_nonez0TestParseSinceTime.test_parse_empty_returns_none*   sj    **2.v~vvvr7   N)__name__
__module____qualname____doc__r6   r;   r@   rC    r7   r5   r
   r
      s    ' "

r7   r
   c                   "    e Zd ZdZd Zd Zd Zy)TestLoadBotActivityu"   bot-activity.json 로드 테스트c           	         |dz  }|j                  t        j                  dddddii             |t        _        t        j                         }|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}}y)u   유효한 JSON 로드bot-activity.jsonbotsdev1idle2026-03-17T00:00:00ZstatussincerR   r   z%(py1)s == %(py4)spy1r   assert %(py6)sr   N)
write_textjsondumpsr   BOT_ACTIVITY_FILEload_bot_activityr   r    r$   r%   r&   )	r*   tmp_pathbot_filer   @py_assert0r0   r+   @py_format5r1   s	            r5   test_load_valid_jsonz(TestLoadBotActivity.test_load_valid_json3   s    11DJJFUk9l0m'nop%-"++-f~f%h/969/69999/6999/99969999999r7   c                    |dz  t         _        t         j                         }di 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    파일 없으면 빈 dict 반환znonexistent.jsonrM   r   z%(py0)s == %(py3)sr   r   r   r   N)r   r[   r\   r   r    r!   r"   r#   r$   r%   r&   )r*   r]   r   r+   r,   r-   r.   s          r5   $test_load_missing_file_returns_emptyz8TestLoadBotActivity.test_load_missing_file_returns_empty;   s{    %-0B%B"++- "%v%%%%v%%%%%%v%%%v%%%%%%%%%%r7   c                    |dz  }|j                  d       |t        _        t        j                         }di 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은 빈 dict 반환rL   z	{invalid}rM   r   rc   r   r   r   r   N)rX   r   r[   r\   r   r    r!   r"   r#   r$   r%   r&   )r*   r]   r^   r   r+   r,   r-   r.   s           r5   $test_load_invalid_json_returns_emptyz8TestLoadBotActivity.test_load_invalid_json_returns_emptyA   s    11K(%-"++- "%v%%%%v%%%%%%v%%%v%%%%%%%%%%r7   N)rD   rE   rF   rG   ra   rd   rf   rH   r7   r5   rJ   rJ   0   s    ,:&&r7   rJ   c                       e Zd ZdZd Zd Zy)TestSaveBotActivityu"   bot-activity.json 저장 테스트c                 F   |dz  }|t         _        ddddiii}t         j                  |      }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                  } |       }	|	sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }
t        t        j                  |
            dx}}	t        j                  |j                               }|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}}	y)u   파일 생성 확인rL   rM   rN   rR   rO   Tr=   r?   r   r   r   r   NAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r^   r   r   r   r   rT   rU   rW   r   )r   r[   save_bot_activityr   r    r!   r"   r#   r$   r%   r&   existsrY   loads	read_text)r*   r]   r^   datar   r+   r,   r-   r.   r0   r`   savedr_   r1   s                 r5   test_save_creates_filez*TestSaveBotActivity.test_save_creates_fileM   s]   11%-"(F!345++D1v~vvv         x   x             

8--/0V}V$X.8&8.&8888.&888.888&8888888r7   c                    |dz  }|t         _        di i}t         j                  |       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}}y)u8   원자적 쓰기 - tmp 파일이 최종 파일로 대체rL   rM   zbot-activity.tmpzQassert not %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
}r]   r   r   r   r   N)r   r[   rl   rm   r!   r"   r   r#   r$   r%   r&   )
r*   r]   r^   rp   r,   r0   r3   @py_assert6@py_assert8r2   s
             r5   test_save_atomic_writez*TestSaveBotActivity.test_save_atomic_writeX   s    11%-"|""4(1;H11;199;9;;;;;;;;;;;H;;;H;;;1;;;9;;;;;;;;;;;r7   N)rD   rE   rF   rG   rr   rw   rH   r7   r5   rh   rh   J   s    ,	9<r7   rh   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestShouldTransitionToIdleu   idle 전환 조건 테스트c                    t        j                  t        j                        t	        d      z
  }t        j                  t        dg       5  t        j                  d|d      \  }}dd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}|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# 1 sw Y   VxY w)u   30분 미만 → 전환 안 함
   minutesfind_bot_processreturn_valuerN   g      $@NFr=   r?   shouldr   r   r   not_timeoutr   rc   reasonr   nowr   r(   r   r   objectr   should_transition_to_idler   r    r!   r"   r#   r$   r%   r&   )	r*   r]   
since_timer   r   r+   r,   r-   r.   s	            r5   test_not_timeout_returns_falsez9TestShouldTransitionToIdle.test_not_timeout_returns_falsee   s   \\(,,/)B2GG
\\($6RH 	Z%??
TXYNFF	Zvvvv&&v&&&&v&&&&&&v&&&v&&&&&&&&&&	Z 	Zs   F<<Gc                    t        j                  t        j                        t	        d      z
  }t        j                  t        ddg      5  t        j                  d|d      \  }}dd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}	|	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# 1 sw Y   xY w)u?   30분 초과지만 프로세스 살아있음 → 전환 안 함(   r|   r~   90  r   rN         D@NFr=   r?   r   r   r   r   still_runninginz%(py1)s in %(py3)sr   rV   r   12345r   )
r*   r]   r   r   r   r+   r,   r-   r.   r_   s
             r5    test_process_alive_returns_falsez;TestShouldTransitionToIdle.test_process_alive_returns_falsem   sm   \\(,,/)B2GG
\\($6eWM 	Z%??
TXYNFF	Zvvvv(&((((&(((((((((&(((&((((((( w&    w&   w      &   &       		Z 	Zs   I!!I+c                 h   t        j                  t        j                        t	        d      z
  }|dz  }|j                          t        j                  t        dg       5  t        j                  t        d|      5  t        j                  d|d      \  }}d	d	d	       d	d	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}
|
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	# 1 sw Y   xY w# 1 sw Y   xY w)u4   프로세스 없음 + .done 파일 있음 → 전환r   r|   ztask-123.doner~   r   find_recent_done_filerN   r   NTr=   r?   r   r   r   r   	completedr   r   r   r   z.doner   r   r   r(   r   touchr   r   r   r   r   r    r!   r"   r#   r$   r%   r&   )r*   r]   r   	fake_doner   r   r+   r,   r-   r.   r_   s              r5   "test_done_file_exists_returns_truez=TestShouldTransitionToIdle.test_done_file_exists_returns_truev   s   \\(,,/)B2GG
.	LL#5BG	ZLL#:S	Z &??
TXYNFF		Z 	Z
 v~vvv${f$$$${f$$${$$$$$$f$$$f$$$$$$$ w&    w&   w      &   &       	Z 	Z 	Z 	Zs$   "J'?JJ'J$	J''J1c                    t        j                  t        j                        t	        d      z
  }|dz  }|j                          t        j                  t        dg       5  t        j                  t        dd      5  t        j                  t        d|      5  t        j                  d	|d
      \  }}ddd       ddd       dd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}
|
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# 1 sw Y   
xY w# 1 sw Y   xY w# 1 sw Y   xY w)u1   프로세스 없음 + 보고서 있음 → 전환r   r|   ztask-123.mdr~   r   r   Nfind_recent_reportrN   r   Tr=   r?   r   r   r   r   r   r   r   r   r   u	   보고서r   )r*   r]   r   fake_reportr   r   r+   r,   r-   r.   r_   s              r5   $test_report_file_exists_returns_truez?TestShouldTransitionToIdle.test_report_file_exists_returns_true   s   \\(,,/)B2GG
.LL#5BG	ZLL#:N	Z LL#7kR	Z
 &??
TXYNFF	Z 	Z 	Z v~vvv${f$$$${f$$${$$$$$$f$$$f$$$$$$$${f$$$${f$$${$$$$$$f$$$f$$$$$$$	Z 	Z 	Z 	Z 	Z 	Zs<   "K?KJ?7K?K?K	KK	KK#c                 X   t        j                  t        j                        t	        d      z
  }t        j                  t        dg       5  t        j                  t        dd      5  t        j                  t        dd      5  t        j                  d|d	      \  }}ddd       ddd       dd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}|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# 1 sw Y   exY w# 1 sw Y   jxY w# 1 sw Y   oxY w)uO   프로세스 없음 + .done 없음 + 보고서 없음 + 30분 초과 → 전환r   r|   r~   r   r   Nr   rN   r   Tr=   r?   r   r   r   r   timeoutr   r   r   r   r   )	r*   r   r   r   r+   r,   r-   r.   r_   s	            r5   ,test_timeout_no_process_no_done_returns_truezGTestShouldTransitionToIdle.test_timeout_no_process_no_done_returns_true   sl   \\(,,/)B2GG
LL#5BG	ZLL#:N	Z LL#7dK	Z
 &??
TXYNFF	Z 	Z 	Z v~vvv"yF""""yF"""y""""""F"""F"""""""	Z 	Z 	Z 	Z 	Z 	Zs<   H*HH"H*HH
HH	HH)N)	rD   rE   rF   rG   r   r   r   r   r   rH   r7   r5   ry   ry   b   s    &'!!%
#r7   ry   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestFindBotProcessu   find_bot_process 테스트c                 z   t         j                  d      }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$   알 수 없는 봇 → 빈 리스트unknown_botr   rc   r   r   r   r   N)
r   r~   r   r    r!   r"   r#   r$   r%   r&   r:   s         r5   test_unknown_bot_returns_emptyz1TestFindBotProcess.test_unknown_bot_returns_empty   sj    **=9v|vvvr7   c                    t        dt        d            5  t        j                  d      }ddd       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# 1 sw Y   xY w)u%   pgrep 실패 시 빈 리스트 반환subprocess.runzpgrep not foundside_effectrN   Nr   rc   r   r   r   r   )r   OSErrorr   r~   r   r    r!   r"   r#   r$   r%   r&   r:   s         r5    test_pgrep_failure_returns_emptyz3TestFindBotProcess.test_pgrep_failure_returns_empty   s    #9J1KL 	7..v6F	7v|vvv	7 	7s   CC%c                    ddl }t        d|j                  dgd            5  t        j	                  d      }ddd       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# 1 sw Y   xY w)u+   pgrep 타임아웃 → 빈 리스트 반환r   Nr   pgrep   r   rN   r   rc   r   r   r   r   )
subprocessr   TimeoutExpiredr   r~   r   r    r!   r"   r#   r$   r%   r&   )r*   _subprocessr   r+   r,   r-   r.   s          r5    test_pgrep_timeout_returns_emptyz3TestFindBotProcess.test_pgrep_timeout_returns_empty   s    (#1K1KWIWX1YZ 	7..v6F	7v|vvv	7 	7   C((C1c                     t        ddddd             }t        d|      5  t        j                  d      }d	d	d	       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	# 1 sw Y   xY w)u%   pgrep 매칭 없음 → 빈 리스트RrH      rB   
returncodestdoutr   r   rN   Nr   rc   r   r   r   r   typer   r   r~   r   r    r!   r"   r#   r$   r%   r&   r*   mock_resultr   r+   r,   r-   r.   s          r5   !test_pgrep_no_match_returns_emptyz4TestFindBotProcess.test_pgrep_no_match_returns_empty   s    Dd31$CDF#+> 	7..v6F	7v|vvv	7 	7r   c                     t        ddddd             }t        d|      5  t        j                  d      }d	d	d	       d
d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	# 1 sw Y   xY w)u   pgrep 성공 → PID 목록r   rH   r   z12345
67890
r   r   r   rN   Nr   i2	 r   rc   r   r   r   r   r   r   s          r5   test_pgrep_returns_pidsz*TestFindBotProcess.test_pgrep_returns_pids   s    Rd31@P$QRT#+> 	7..v6F	7'v''''v''''''v'''v''''''''''	7 	7s   C**C3N)	rD   rE   rF   rG   r   r   r   r   r   rH   r7   r5   r   r      s    $
(r7   r   c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestCheckAndRecoverStuckBotsu!   stuck 봇 자동 복구 테스트c                 B   |dz  }t        j                  t        j                        t	        d      z
  j                  d      }ddd|dii}|j                  t        j                  |             |t        _
        |d	z  t        _        t        j                  t        d
g       5  t        j                         }ddd       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}}t        j.                  |j1                               }
|
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}}y# 1 sw Y   ZxY w)uH   30분 초과 processing 봇 → idle 전환 (프로세스 없음 모킹)rL   r   r|   %Y-%m-%dT%H:%M:%SZrM   rN   
processingrQ   watchdog.logr~   r   Nr   r   rc   	recoveredr   r   r   rR   rO   rT   rU   rW   r   r   r   r   r(   r   strftimerX   rY   rZ   r   r[   WATCHDOG_LOGr   r   check_and_recover_stuck_botsr   r    r!   r"   r#   r$   r%   r&   rn   ro   r*   r]   r^   forty_min_agorp   r   r+   r,   r-   r.   rq   r_   r0   r`   r1   s                  r5   test_stuck_bot_recoveredz5TestCheckAndRecoverStuckBots.test_stuck_bot_recovered   sw   11!hll3i6KKUUVjkL=!QRSDJJt,-%-" (> 9\\($6RH 	@ ==?I	@yA~yAyyA

8--/0V}V$X.8&8.&8888.&888.888&8888888	@ 	@   'HHc                 B   |dz  }t        j                  t        j                        t	        d      z
  j                  d      }ddd|dii}|j                  t        j                  |             |t        _
        |d	z  t        _        t        j                  t        d
g       5  t        j                         }ddd       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}}t        j.                  |j1                               }
|
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}}y# 1 sw Y   ZxY w)u&   10분 미만 processing 봇 → 유지rL   r{   r|   r   rM   dev2r   rQ   r   r~   r   Nr   r   rc   r   r   r   r   rR   rT   rU   rW   r   r   )r*   r]   r^   ten_min_agorp   r   r+   r,   r-   r.   rq   r_   r0   r`   r1   s                  r5   $test_recent_processing_not_recoveredzATestCheckAndRecoverStuckBots.test_recent_processing_not_recovered   sw   11||HLL1Ib4IISSThiL;!OPQDJJt,-%-" (> 9\\($6RH 	@ ==?I	@yA~yAyyA

8--/0V}V$X.>,>.,>>>>.,>>>.>>>,>>>>>>>	@ 	@r   c                 p   |dz  }dddddii}|j                  t        j                  |             |t        _        |dz  t        _        t        j                  t        dg 	      5  t        j                         }d
d
d
       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
# 1 sw Y   xY w)u    idle 상태 봇은 변경 없음rL   rM   dev3rO   2026-01-01T00:00:00ZrQ   r   r~   r   Nr   r   rc   r   r   r   r   rX   rY   rZ   r   r[   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   	r*   r]   r^   rp   r   r+   r,   r-   r.   s	            r5   test_idle_bot_not_affectedz7TestCheckAndRecoverStuckBots.test_idle_bot_not_affected   s    11F=S!TUVDJJt,-%-" (> 9\\($6RH 	@ ==?I	@yA~yAyyA	@ 	@   (D,,D5c                 ^   |dz  }|dz  }t        j                  t        j                        t	        d      z
  j                  d      }ddd|d	ii}|j                  t        j                  |             |t        _
        |t        _        t        j                  t        d
g       5  t        j                          ddd       |j                  } |       }|sddt!        j"                         v st%        j&                  |      rt%        j(                  |      ndt%        j(                  |      t%        j(                  |      dz  }t+        t%        j,                  |            dx}}|j/                         }	d}
|
|	v }|st%        j0                  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# 1 sw Y   fxY w)u#   복구 시 [WATCHDOG] 로그 기록rL   r   r   r|   r   rM   rN   r   rQ   r~   r   Nrj   log_filerk   u$   [WATCHDOG] dev1: processing → idler   r   contentr   r   r   )r   r   r   r(   r   r   rX   rY   rZ   r   r[   r   r   r   r   rm   r!   r"   r   r#   r$   r%   r&   ro   r    )r*   r]   r^   r   r   rp   r,   r0   r`   r   r_   r+   r-   r.   s                 r5   test_watchdog_log_entry_writtenz<TestCheckAndRecoverStuckBots.test_watchdog_log_entry_written   sr   11n,!hll3i6KKUUVjkL=!QRSDJJt,-%-" (\\($6RH 	4113	4          x   x             $$&5@5@@@@5@@@5@@@@@@@@@@@@@@@@	4 	4s   )H""H,c                    |dz  }t        j                  t        j                        t	        d      z
  j                  d      }dd|dd|ddd	dd
i}|j                  t        j                  |             |t        _
        |dz  t        _        t        j                  t        dg       5  t        j                         }ddd       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# 1 sw Y   xY w)u;   여러 stuck 봇 모두 복구 (프로세스 없음 모킹)rL   r   r|   r   rM   r   rQ   rO   r   )rN   r   r   r   r~   r   N   r   rc   r   r   r   r   )r   r   r   r(   r   r   rX   rY   rZ   r   r[   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   )
r*   r]   r^   r   rp   r   r+   r,   r-   r.   s
             r5   &test_multiple_stuck_bots_all_recoveredzCTestCheckAndRecoverStuckBots.test_multiple_stuck_bots_all_recovered  s   11!hll3i6KKUUVjk#/-H#/-H#)4JK
 	DJJt,-%-" (> 9\\($6RH 	@ ==?I	@yA~yAyyA	@ 	@s   /E33E<c                 D   |dz  }t        j                  t        j                        t	        d      z
  j                  d      }ddd|dii}|j                  t        j                  |             |t        _
        |d	z  t        _        t        j                  t        d
dg      5  t        j                         }ddd       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}}t        j.                  |j1                               }
|
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}}y# 1 sw Y   ZxY w)u0   프로세스 살아있으면 idle 전환 안 됨rL   r   r|   r   rM   r   r   rQ   r   r~   i r   Nr   r   rc   r   r   r   r   rR   rT   rU   rW   r   r   r   s                  r5   +test_process_alive_prevents_idle_transitionzHTestCheckAndRecoverStuckBots.test_process_alive_prevents_idle_transition  sy   11!hll3i6KKUUVjkL=!QRSDJJt,-%-" (> 9\\($6eWM 	@ ==?I	@yA~yAyyA

8--/0V}V$X.>,>.,>>>>.,>>>.>>>,>>>>>>>	@ 	@s   (HHc                 p   |dz  }dddddii}|j                  t        j                  |             |t        _        |dz  t        _        t        j                  t        dg 	      5  t        j                         }d
d
d
       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
# 1 sw Y   xY w)u$   since 파싱 실패한 봇은 스킵rL   rM   rN   r   zinvalid-daterQ   r   r~   r   Nr   r   rc   r   r   r   r   r   r   s	            r5   test_invalid_since_skippedz7TestCheckAndRecoverStuckBots.test_invalid_since_skipped(  s    11L>!RSTDJJt,-%-" (> 9\\($6RH 	@ ==?I	@yA~yAyyA	@ 	@r   N)rD   rE   rF   rG   r   r   r   r   r   r   r   rH   r7   r5   r   r      s)    +9 ? 
A"&? 
r7   r   c                       e Zd ZdZd Zy)TestRunOnceu   1회 실행 테스트c           	      H   |dz  }|j                  t        j                  dddddii             |t        _        |dz  t        _        t        j                          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}}y)u"   stuck 봇 없을 때 정상 실행rL   rM   rN   rO   rP   rQ   r   zMassert %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
}r]   rt   N)rX   rY   rZ   r   r[   r   run_oncerm   r!   r"   r   r#   r$   r%   r&   )r*   r]   r^   r,   r0   r3   ru   r4   s           r5   test_run_once_no_stuck_botsz'TestRunOnce.test_run_once_no_stuck_bots8  s    11DJJFUk9l0m'nop%-" (> 9 	)3>)3)113133333333333333>33313333333333r7   N)rD   rE   rF   rG   r   rH   r7   r5   r   r   5  s
    
4r7   r   __main__z-v)&rG   builtinsr!   _pytest.assertion.rewrite	assertionrewriter   rY   ossystempfiler   r   r   pathlibr   unittest.mockr   pytestmkdtemp_TEMP_WORKSPACEenvironpathinsertstr__file__parent
__import__r   r
   rJ   rh   ry   r   r   r   rD   mainrH   r7   r5   <module>r     s    0    	 
  2 2    #(""$.

  3tH~,,334 5+, 4& &4< <0;# ;#|"( "(Jm m`4 4  zFKK4 ! r7   