
    Ki                       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ZddlmZmZ ddlmZ ddlmZmZ ddlZdedej(                  fdZ ej,                         d	        Z ej,                         d
        Z	 	 	 	 	 	 d)dededz  dededededz  defdZdededef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% G d# d$      Z& G d% d&      Z' G d' d(      Z(y)*u*  
test_chain_manager.py

chain_manager.py 순차 작업 체이닝 관리 유틸리티 단위 테스트

테스트 항목:
- TestCreate: 정상 생성, max_tasks 초과 거부, 중복 chain_id 거부
- TestNext: 다음 pending 반환, QC FAIL 시 stalled, chain_complete 반환, no_chain 반환, 동일 task_file 중복 차단
- TestUpdate: running/done/failed/stalled 상태 변경
- TestCheckStalled: 정체 작업 검출, 정체 없음
- TestList: 체인 목록 출력
- TestLock: 동시 접근 시 순차 처리
- TestBackup: .bak 파일 생성 확인
    N)datetime	timedelta)Path)	MagicMockpatchtmp_pathreturnc                    t        t        j                  j                  dd            }t	        |      t
        j                  vr)t
        j                  j                  dt	        |             t        t
        j                  j                               D ]  }|dk(  s	t
        j                  |=  t        j                  j                  dd       ddl}| |_        | dz  d	z  |_        |j                  j                  d
d
       |S )uK   chain_manager 모듈을 tmp_path를 WORKSPACE로 설정하여 로드한다.WORKSPACE_ROOTz/home/jay/workspacer   chain_managerCOKACDIR_KEY_ANUztest-dummy-keyNmemorychainsTparentsexist_ok)r   osenvirongetstrsyspathinsertlistmoduleskeys
setdefaultr   	WORKSPACE
CHAINS_DIRmkdir)r   	workspacemod_namecms       //home/jay/workspace/tests/test_chain_manager.py_load_chain_managerr%       s    RZZ^^$46KLMI
9~SXX%3y>* ))+, &&H%& JJ,.>?BLx'(2BMMMt4I    c                     t        |       S )uI   격리된 WORKSPACE를 사용하는 chain_manager 모듈을 반환한다.)r%   )r   s    r$   r#   r#   <   s     x((r&   c                 <    | dz  dz  }|j                  dd       |S )u"   체인 파일 저장 디렉토리.r   r   Tr   )r    )r   ds     r$   
chains_dirr*   B   s(     	8h&AGGD4G(Hr&   chain_idtasksstatus	max_tasksscopewatchdog_cron_idc           	          |dddddddddg}| dt        j                         j                         ||||d	}|||d
<   |S )u.   테스트용 체인 데이터를 생성한다.N   zmemory/tasks/dispatch-001.md	dev1-teampendingautoorder	task_fileteamr-   task_idgate
started_atcompleted_atanur+   
created_by
created_atr-   r/   r.   r,   r0   )r   now	isoformat)r+   r,   r-   r.   r/   r0   datas          r$   _make_chain_datarE   O   st     } ;##" $	
 lln..0D ##3 Kr&   r*   rD   c                     |d   }|j                  d      r| d}nd| d}| |z  }|j                  t        j                  |dd      d       |S )	uB   체인 데이터를 파일로 저장하고 경로를 반환한다.r+   chain-.jsonF   ensure_asciiindentutf-8encoding)
startswith
write_textjsondumps)r*   rD   r+   filenamer   s        r$   _write_chainrU   s   s`    JH8$Zu%H:U+ DOODJJt%BWOUKr&   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)
TestCreateu&   cmd_create() 서브커맨드 테스트c                    ddddddddd	dg}t        j                  d
t        j                  |      ddd      }t	        j
                  |d      5 }t               }d|_        d|_        ||j                  _
        |j                  |       ddd       |dz  }|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
}
||
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$   }	t/        |	      }
d}|
|k(  }|st        j,                  d|fd%|
|f      d&t        j                         v st        j                   t.              rt        j"                  t.              nd&t        j"                  |	      t        j"                  |
      t        j"                  |      d'z  }d(d)|iz  }t%        t        j&                  |            dx}	x}
x}}|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}
||
u }|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}}
|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# 1 sw Y   JxY w)0uD   정상 생성: 체인 파일이 올바른 스키마로 생성된다.r2   memory/tasks/t1.mdr3   r5   r7   r8   r9   r;   rI   zmemory/tasks/t2.md	dev2-teamnonezchain-20260307-001   테스트 범위r>   
   r+   r,   r/   r@   r.   
subprocessr   z{"cron_id": "cron-abc123"}Nzchain-chain-20260307-001.jsonAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}
chain_filepy0py2py4rM   rN   r+   ==z%(py1)s == %(py4)spy1rf   assert %(py6)spy6r-   activer/   r@   r.   r,   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenrd   re   rf   py7assert %(py9)spy9r4   r:   isz%(py1)s is %(py4)sr<   r=   )argparse	NamespacerR   rS   r   objectr   
returncodestdoutrunreturn_value
cmd_createexists@py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprAssertionError_format_explanationloads	read_text_call_reprcomparero   )selfr#   r   r*   r,   argsmock_submock_resultrb   @py_assert1@py_assert3@py_format5rD   @py_assert0@py_assert2@py_format7@py_assert6@py_assert5@py_format8@py_format10ts                        r$   test_create_normalzTestCreate.test_create_normal   s    &:KY_`&:KY_`
 !!)**U#$
 \\"l+ 	 x#+K%&K"!=K(3HLL%MM$	   "AA
  " """"""""z"""z""" """"""""""zz*...@AJ7#77#77777#7777777#77777777H~))~))))~)))~))))))))))G}2 22} 22222} 2222}222 22222222L!*U*!U****!U***!***U*******K &B& B&&&& B&&& &&&B&&&&&&&=&s=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&=&&&!&&&Q&&&&&&&M!{'i'{i''''{i'''{'''i'''''''|#t#|t####|t###|###t#######&$&$&&&&$&&&&&&$&&&&&&& (D( D(((( D((( (((D(((((((+	  	 s   ;[[c                 V   dddddg}t        j                  dt        j                  |      ddd	
      }t	        j
                  |d      5 }t               }d|_        t        j                  ddi      |_        ||j                  _
        |j                  |       |j                  }|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}}	ddd       |dz  }t        j(                  |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# 1 sw Y   xY w)ub   create 성공 시 cokacdir watchdog cron이 등록되고 cron_id가 체인 파일에 저장된다.r2   rY   r3   r5   rZ   zchain-cron-testu   cron 테스트r>   r^   r_   r`   r   cron_idzcron-watchdog-001zCassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.run
}.called
}r   rc   Nzchain-chain-cron-test.jsonrM   rN   r0   inz%(py1)s in %(py3)srD   rk   py3assert %(py5)spy5)rw   rx   rR   rS   r   ry   r   rz   r{   r|   r}   r~   calledr   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r,   r   r   r   r   r   r   rb   rD   r   r   @py_format4@py_format6s                    r$   #test_create_registers_watchdog_cronz.TestCreate.test_create_registers_watchdog_cron   s   +?^def!!&**U#"
 \\"l+ 	'x#+K%&K"!%Y8K,L!MK(3HLL%MM$ <<&<&&&&&&&&&8&&&8&&&<&&&&&&&&&&	'  ">>
zz*...@A!)!T))))!T)))!))))))T)))T)))))))	' 	's   C;HH(c                    t        d      D cg c]  }|dz   d| dddd }}t        j                  dt        j                  |      d	d
d      }t        j                  t              5 }|j                  |       ddd       j                  }|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                   |      t        j                   |	      dz  }dd|iz  }t#        t        j$                  |            dx}x}x}
}	yc c}w # 1 sw Y   xY w)u,   max_tasks 초과 시 exit 1이 발생한다.   r2   memory/tasks/t.mdr3   r5   rZ   zchain-overflowu   초과 테스트r>      r_   Nrg   zG%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.value
}.code
} == %(py7)sexc_inforp   rr   rs   )rangerw   rx   rR   rS   pytestraises
SystemExitr~   valuecoder   r   r   r   r   r   r   r   )r   r#   r   ir,   r   r   r   r   r   r   r   r   s                r$   test_create_max_tasks_exceededz)TestCreate.test_create_max_tasks_exceeded   s"    1X
 !eN1#S*A;`fg
 
 !!%**U#$
 ]]:& 	 (MM$	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''
	  	 s   E0+E55E>c                    dddddg}t        j                  dt        j                  |      ddd	
      }t	        j
                  |d      5 }t               }d|_        d|_        ||j                  _
        |j                  |       ddd       t        j                  t              5 }|j                  |       ddd       j                  }	|	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,                  |
      t#        j,                  |      dz  }dd|iz  }t/        t#        j0                  |            dx}	x}
x}}y# 1 sw Y   (xY w# 1 sw Y   xY w)u9   동일 chain_id 중복 생성 시 exit 1이 발생한다.r2   rY   r3   r5   rZ   z	chain-dupu   중복 테스트r>   r^   r_   r`   r   z{"cron_id": "cron-001"}Nrg   r   r   rp   rr   rs   )rw   rx   rR   rS   r   ry   r   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r,   r   r   r   r   r   r   r   r   r   r   s                  r$   test_create_duplicate_chain_idz)TestCreate.test_create_duplicate_chain_id   sJ   +?^def!! **U#$
 \\"l+ 	 x#+K%&K"!:K(3HLL%MM$	  ]]:& 	 (MM$	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	  	 	  	 s   ;F-(F:-F7:Gc                    t        j                  ddddd      }t        j                  t              5 }|j                  |       ddd       j                  }|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                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}y# 1 sw Y   xY w)u0   잘못된 tasks JSON 시 exit 1이 발생한다.zchain-bad-jsonz{not valid json}u   JSON 오류 테스트r>   r^   r_   Nr2   rg   r   r   rp   rr   rs   )rw   rx   r   r   r   r~   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r   r   r   r   r   r   r   r   s              r$   test_create_invalid_tasks_jsonz)TestCreate.test_create_invalid_tasks_json   s    !!%$)
 ]]:& 	 (MM$	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	  	 s   D99EN)	__name__
__module____qualname____doc__r   r   r   r   r    r&   r$   rW   rW      s    0#)J*2($(.(r&   rW   c                       e Zd ZdZ	 	 	 ddedededz  dedef
dZd	 Zd
 Z	d Z
d Zd Zd Zd Zd Zd Zd Zd Zd Zy)TestNextu$   cmd_next() 서브커맨드 테스트Nr*   r+   r,   chain_statusr	   c           
      .   |6ddddddt        j                         j                         ddd	d
dddddddg}|dt        j                         j                         |ddd|d}|| dz  }|j                  t	        j
                  |dd	      d       |S )u2   테스트용 체인 파일을 생성하는 헬퍼.Nr2   memory/tasks/task-001.mdr3   running	task-10.1r5   r6   rI   memory/tasks/task-002.mdr[   r4   r>   u	   테스트r^   zcron-watch-001r+   r@   rA   r-   r/   r.   r0   r,   rH   FrJ   rM   rN   r   rB   rC   rQ   rR   rS   )r   r*   r+   r,   r   rD   r   s          r$   _setup_chainzTestNext._setup_chain  s     = !;''*""*,,.":":"<$(	 !;''#""&$(	E. !",,.224"  0	
 xj..

4eAFQXYr&   c                 ~   | j                  |       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd	
       |j                  |       |j                         }t        j                  |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}}
|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)uA   다음 pending task가 있으면 action=dispatch를 반환한다.r   r:   r   reportsTr   task-10.1.mdu   작업 완료. 성공.rM   rN   actiondispatchrg   ri   rj   rl   rm   Nr8   r   r9   r[   r+   r   r   outputr   r   r   )r   rw   rx   r    rQ   cmd_next
readouterrrR   r   outr   r   r   r   r   r   r   r   )r   r#   r   r*   capsysr   reports_dircapturedr   r   r   r   r   r   r   r   s                   r$   test_next_returns_dispatchz#TestNext.test_next_returns_dispatch2  s   *%!!+6 )I5$6	~	%112JU\1]
D$$&HLL)h-:-:----:------:-------k"@&@@"&@@@@@"&@@@@"@@@&@@@@@@@@f~,,~,,,,~,,,~,,,,,,,,,,#zV####zV###z######V###V#######r&   c                    | j                  |       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd	
       |j                  |       |j                         }t        j                  |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}}
|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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}}
t!        d |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)u]   gate=auto 조건에서 보고서에 FAIL 키워드가 있으면 재위임(retry)한다 (F12).r   r   r   r   Tr   r   u&   작업 완료. QC FAIL: 오류 발견.rM   rN   r   r   rg   ri   rj   rl   rm   Nretry_attemptr2   r:   chain-test.jsonr-   rn   c              3   J   K   | ]  }|j                  d       dk(  s|  ywr:   r   Nr   .0r   s     r$   	<genexpr>z<TestNext.test_next_qc_fail_triggers_retry.<locals>.<genexpr>]  s      WquuY7G;7V1W   ##r,   retry_countr   )r   rw   rx   r    rQ   r   r   rR   r   r   r   r   r   r   r   r   next)r   r#   r   r*   r   r   r   r   r   r   r   r   r   r   rb   rD   target_tasks                    r$    test_next_qc_fail_triggers_retryz)TestNext.test_next_qc_fail_triggers_retryE  s   *%!!+6 )I5$6	~	%112Zel1m
D$$&HLL)h-:-:----:------:-------o&+!+&!++++&!+++&+++!+++++++i /K/ K//// K/// ///K///////  "33
zz*...@AH~))~))))~)))~)))))))))) Wd7mWW=).Q.)Q....)Q...)...Q.......8$1	1$	1111$	111$111	1111111r&   c                 4   | j                  |       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd	
       |j                  |       |j                         }t        j                  |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)uQ   gate=auto 조건에서 'QC FAIL (범위 외)'는 체인을 stall하지 않는다.r   r   r   r   Tr   r   uQ   작업 완료. QC FAIL (범위 외 기존 테스트 1건 — 에스컬레이션).rM   rN   r   r   rg   ri   rj   rl   rm   Nr   rw   rx   r    rQ   r   r   rR   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   r   r   r   r   r   r   r   r   s                 r$   -test_next_qc_fail_out_of_scope_does_not_stallz6TestNext.test_next_qc_fail_out_of_scope_does_not_stallb  s    *%!!+6 )I5$6	~	%11_jq 	2 	
 	D$$&HLL)h-:-:----:------:-------r&   c           
         ddddddt        j                         j                         ddd	d
dddddddg}| j                  ||       t	        j
                  d      }|dz  dz  }|j                  dd       |dz  j                  dd       |j                  |       |j                         }t        j                  |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)uU   gate=none이면 보고서 내용에 관계없이 다음 pending task를 반환한다.r2   r   r3   r   	task-20.1r\   Nr6   rI   r   r[   r4   r,   r   r   r   Tr   task-20.1.mdu&   QC FAIL 가 있어도 무시해야 함rM   rN   r   r   rg   ri   rj   rl   rm   r   rB   rC   r   rw   rx   r    rQ   r   r   rR   r   r   r   r   r   r   r   r   r#   r   r*   r   r,   r   r   r   r   r   r   r   r   r   s                  r$   test_next_gate_none_skips_qcz%TestNext.test_next_gate_none_skips_qct  s7    7##&&lln668 $	 7##" $	
, 	*E2!!+6 )I5$6	~	%112Zel1m
D$$&HLL)h-:-:----:------:-------r&   c           	         ddddddt        j                         j                         ddg}| j                  ||	       t	        j
                  d
      }|dz  dz  }|j                  dd       |dz  j                  dd       t        j                  |d      5 }t               }	d|	_        d|	_        |	|j                  _        |j                  |       ddd       |j!                         }
t#        j$                  |
j&                        }|d   }d}||k(  }|slt)        j*                  d|fd||f      t)        j,                  |      t)        j,                  |      dz  }dd|iz  }t/        t)        j0                  |            dx}x}}d}||v }|st)        j*                  d|fd||f      t)        j,                  |      d t3        j4                         v st)        j6                  |      rt)        j,                  |      nd d!z  }d"d#|iz  }t/        t)        j0                  |            dx}}|d$z  }t#        j$                  |j9                  d            }|d%   }d&}||k(  }|slt)        j*                  d|fd||f      t)        j,                  |      t)        j,                  |      dz  }dd|iz  }t/        t)        j0                  |            dx}x}}y# 1 sw Y   	xY w)'ua   다음 pending task가 없으면 action=chain_complete를 반환하고 체인이 completed된다.r2   r   r3   r   z	task-30.1r5   Nr6   r   r   r   r   Tr   ztask-30.1.md   작업 완료.rM   rN   r`   r    r   chain_completerg   ri   rj   rl   rm   r+   r   r   r   r   r   r   r   r-   	completed)r   rB   rC   r   rw   rx   r    rQ   r   ry   r   rz   r{   r|   r}   r   r   rR   r   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r,   r   r   r   r   r   r   r   r   r   r   r   r   r   rb   rD   s                        r$   test_next_chain_completez!TestNext.test_next_chain_complete  s3    7##&&lln668 $	
 	*E2!!+6)I5$6	~	%112BW1U\\"l+ 	x#+K%&K"!#K(3HLL%KK	 $$&HLL)h3#33#33333#3333333#33333333#zV####zV###z######V###V#######  "33
zz*...@AH~,,~,,,,~,,,~,,,,,,,,,,	 	s   ;KK)c           	         ddddddt        j                         j                         ddg}| j                  ||	       t	        j
                  d
      }|dz  dz  }|j                  dd       |dz  j                  dd       t        j                  |d      5 }t               }d|_        d|_        ||j                  _        |j                  |       |j                  j                   D 	cg c]  }	t#        |	       }
}	d |
D        }t%        |      }|sddt'        j(                         v st+        j,                  t$              rt+        j.                  t$              ndt+        j.                  |      t+        j.                  |      dz  }t1        t+        j2                  |            dx}}ddd       yc c}	w # 1 sw Y   yxY w)uQ   chain_complete 시 watchdog cron이 제거된다 (cokacdir --cron-remove 호출).r2   r   r3   r   z	task-31.1r5   Nr6   r   r   r   r   Tr   ztask-31.1.md   완료.rM   rN   r`   r   r   c              3   $   K   | ]  }d |v  
 yw)zcron-removeNr   )r   cmds     r$   r   zGTestNext.test_next_removes_watchdog_cron_on_complete.<locals>.<genexpr>  s     C}+Cs   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyrc   )r   rB   rC   r   rw   rx   r    rQ   r   ry   r   rz   r{   r|   r}   r   call_args_listr   r   r   r   r   r   r   r   r   )r   r#   r   r*   r,   r   r   r   r   callcalled_cmdsr   r   r   s                 r$   +test_next_removes_watchdog_cron_on_completez4TestNext.test_next_removes_watchdog_cron_on_complete  s|    7##&&lln668 $	
 	*E2!!+6)I5$6	~	%11)g1N\\"l+ 		Dx#+K%&K"!#K(3HLL%KK 2:1L1LM3t9MKMC{CC3CCCCCCCCC3CCC3CCCCCCCCCCCCCC		D 		D N		D 		Ds    AG0F<B1G<GG
c                    t        j                  d      }|j                  |       |j                         }t	        j
                  |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
)uR   어떤 체인에도 없는 task_id이면 action=no_chain을 반환한다 (exit 0).ztask-unknown-999r   r   no_chainrg   ri   rj   rl   rm   Nr:   )rw   rx   r   r   rR   r   r   r   r   r   r   r   r   r#   r   r*   r   r   r   r   r   r   r   r   r   s                r$   test_next_no_chainzTestNext.test_next_no_chain  s    !!*<=
D$$&HLL)h-:-:----:------:-------i 6$66 $66666 $6666 666$66666666r&   c           
         ddddddt        j                         j                         ddd	dd
ddddddg}| j                  ||       t	        j
                  d      }|dz  dz  }|j                  dd       |dz  j                  dd       |j                  |       |j                         }t        j                  |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)!ui   이미 running/done 상태인 task의 task_file과 동일한 task_file을 가진 pending이 차단된다.r2   zmemory/tasks/same-file.mdr3   r   z	task-40.1r5   Nr6   rI   r[   r4   r   r   r   r   Tr   ztask-40.1.mdr   rM   rN   r   stalledrg   ri   rj   rl   rm   zduplicate task_filereasonr   z%(py1)s in %(py4)sr   r   s                  r$   %test_next_duplicate_task_file_blockedz.TestNext.test_next_duplicate_task_file_blocked  s    8##&&lln668 $	 8##" $	
, 	*E2!!+6)I5$6	~	%11)g1N
D$$&HLL)h,9,9,,,,9,,,,,,9,,,,,,,$8x(88$(88888$(8888$888(88888888r&   c                 R   | j                  |       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd	
       |j                  |       |dz  }t        j                  |j                  d	
            }t        d |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}
|	|
u}|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)uL   next 호출 시 완료된 task의 status=done, completed_at이 설정된다.r   r   r   r   Tr   r   r   rM   rN   r   c              3   J   K   | ]  }|j                  d       dk(  s|  ywr   r   r   s     r$   r   z8TestNext.test_next_marks_completed_at.<locals>.<genexpr>  s      UqQUU95E5TUr   r,   r-   donerg   ri   rj   rl   rm   Nr=   is notz%(py1)s is not %(py4)s)r   rw   rx   r    rQ   r   rR   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   rb   rD   	done_taskr   r   r   r   r   s                 r$   test_next_marks_completed_atz%TestNext.test_next_marks_completed_at  sE   *%!!+6)I5$6	~	%11)g1N
D"33
zz*...@AUDMUU	",f,"f,,,,"f,,,",,,f,,,,,,,(44(4444(444(4444444444r&   c                    | j                  |       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd	
       |j                  |       |j                         }t        j                  |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}}
|j                  |       |j                         }t        j                  |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)uW   이미 done인 task에 대해 next를 다시 호출하면 already_done을 반환한다.r   r   r   r   Tr   r   r   rM   rN   r   r   rg   ri   rj   rl   rm   Nalready_doner   )r   r#   r   r*   r   r   r   r   output1r   r   r   r   r   output2s                  r$   "test_next_idempotency_already_donez+TestNext.test_next_idempotency_already_done#  sZ   *%!!+6)I5$6	~	%11)g1N 	D$$&**X\\*x .J. J.... J... ...J....... 	D$$&**X\\*x 2N2 N2222 N222 222N2222222r&   c                 \   ddddddddd	ddd
ddg}| j                  ||       t        j                  d      }|dz  dz  }|j                  dd       |dz  j	                  dd       |j                  |       |j                         }t        j                  |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)u=   다음 task가 이미 running이면 dispatch하지 않는다.r2   zmemory/tasks/task-10.1.mdr3   r   r   r5   r7   r8   r9   r-   r:   r;   rI   zmemory/tasks/task-10.2.mdz	task-10.2r   r   r   r   Tr   r   r   rM   rN   r   already_runningrg   ri   rj   rl   rm   Nr   r   s                  r$   *test_next_idempotency_next_already_runningz3TestNext.test_next_idempotency_next_already_running8  s   
 8##& 8##&
$ 	*E2!!+6)I5$6	~	%11)g1N
D$$&HLL)h4#44#44444#4444444#44444444r&   c                 2	   |dz  dz  dz  }|j                   j                  dd       |j                  dd       d	d
ddddt        j                         j                         ddddddddg}|dz  }ddt        j                         j                         dddd|d}|j                  t        j                  |dd      d       t        j                  d      }	|dz  d z  }
|
j                  dd       |
d!z  j                  d"d       |j                  |	       |dz  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}}|j(                  }d} ||      }|j(                  }d} ||      }||k(  }|s>t        j*                  d)|fd*||f      d&t        j                         v st        j                   |      rt        j"                  |      nd&t        j"                  |      t        j"                  |      t        j"                  |      d+t        j                         v st        j                   |      rt        j"                  |      nd+t        j"                  |      t        j"                  |      t        j"                  |      d,z  }d-d.|iz  }t%        t        j&                  |            d(x}x}x}x}x}x}}|j-                         }t        j.                  |j0                        }|d/   }d0}||k(  }|slt        j*                  d)|fd1||f      t        j"                  |      t        j"                  |      d2z  }d3d4|iz  }t%        t        j&                  |            d(x}x}}|d5   }d}||k(  }|slt        j*                  d)|fd1||f      t        j"                  |      t        j"                  |      d2z  }d3d4|iz  }t%        t        j&                  |            d(x}x}}y()6u>   task_file이 없을 때 original_task_file에서 복사한다.r   r,   z
task-20.mdTr   u-   # 원본 지시서

Phase 1: ...
Phase 2: ...rM   rN   r2   zmemory/tasks/task-20.1.mdr3   r   r   r5   )r7   r8   r9   r-   r:   r;   r<   rI   zmemory/tasks/task-20.2.mdr4   z	task-20.2r  r   
chain-testr>   rn   r   r^   zmemory/tasks/task-20.md)r+   r@   rA   r-   r/   r.   original_task_filer,   FrJ   r   r   r   r   ztask-20.2.mdu6   Phase 2 지시서 파일이 자동 생성되어야 함C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}phase2_task_filerc   Nrg   )z%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.read_text
}(encoding=%(py4)s)
} == %(py14)s
{%(py14)s = %(py10)s
{%(py10)s = %(py8)s.read_text
}(encoding=%(py12)s)
}r  )rd   re   rf   rm   py8py10py12py14zassert %(py16)spy16r   r   ri   rj   rl   rm   r8   )parentr    rQ   r   rB   rC   rR   rS   rw   rx   r   r   r   _format_assertmsgr   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r  r,   rb   rD   r   r   r  r   r   r   r   @py_assert9@py_assert11@py_assert13@py_assert7@py_format15@py_format17r   r   r   r   r   s                              r$   1test_next_creates_missing_task_file_from_originalz:TestNext.test_next_creates_missing_task_file_from_originalZ  s    &07:\I!!''t'D%%&Xcj%k
 8##&&lln668 8##&
&  "33
$",,.224";	
 	djjE!LW^_ !!+6)I5$6	~	%11)g1N
D $h.8>I&&b&(b(bb*bbbbbbbbbbbbb&bbb(bbbbbb  ))m7m)7;m?Q?[?[melm?[el?mm;?mmmmm;?mmmmmmmmmmmmm)mmm7mmm;mmmmmm?Qmmm?Qmmm?[mmmelmmm?mmmmmmmmm $$&HLL)h-:-:----:------:-------k"A&AA"&AAAAA"&AAAA"AAA&AAAAAAAAr&   )r  Nrn   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r+  r   r&   r$   r   r     s    .
 %!$++ + d{	+
 + 
+Z$&2:.$$.L%-ND@	7$9L5"3* 5D<Br&   r   c                   D    e Zd ZdZddededefdZd Zd Zd Z	d	 Z
d
 Zy)
TestUpdateu&   cmd_update() 서브커맨드 테스트r*   r:   r	   c           	          dddd|ddddg}dd	t        j                         j                         d
ddd|d}|dz  }|j                  t	        j
                  |dd      d       |S )u2   update 테스트용 체인 파일을 생성한다.r2   zmemory/tasks/task-x.mdr3   r4   r5   Nr6   zchain-update-testr>   rn   u   업데이트 테스트r^   cron-001r   chain-update-test.jsonFrI   rJ   rM   rN   r   )r   r*   r:   r,   rD   r   s         r$   _setup_chain_with_taskz!TestUpdate._setup_chain_with_task  s     5##"" $	
 ,",,.224- *	
 44

4eAFQXYr&   c                    | j                  |       t        j                  dd      }|j                  |       |dz  }t	        j
                  |j                  d            }|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}	||	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;   running으로 상태 변경 시 started_at이 설정된다.	task-50.1r   r:   r-   r0  rM   rN   r,   r   r-   rg   ri   rj   rl   rm   Nr<   r  r  r1  rw   rx   
cmd_updaterR   r   r   r   r   r   r   r   r   r#   r   r*   r   rb   rD   taskr   r   r   r   r   s                r$   test_update_to_runningz!TestUpdate.test_update_to_running  s   ##J/!!+iH
d"::
zz*...@AG}QH~**~****~***~**********L!--!----!---!----------r&   c                    | j                  |       t        j                  dd      }|j                  |       |dz  }t	        j
                  |j                  d            }|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}	||	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:   done으로 상태 변경 시 completed_at이 설정된다.r3  r
  r4  r0  rM   rN   r,   r   r-   rg   ri   rj   rl   rm   Nr=   r  r  r5  r7  s                r$   test_update_to_donezTestUpdate.test_update_to_done  s   ##J/!!+fE
d"::
zz*...@AG}QH~''~''''~'''~''''''''''N#/4/#4////#4///#///4///////r&   c                    | j                  |       t        j                  dd      }|j                  |       |dz  }t	        j
                  |j                  d            }|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@   failed로 상태 변경 시 status=failed로 업데이트된다.r3  failedr4  r0  rM   rN   r,   r   r-   rg   ri   rj   rl   rm   Nr5  r7  s                r$   test_update_to_failedz TestUpdate.test_update_to_failed  s    ##J/!!+hG
d"::
zz*...@AG}QH~))~))))~)))~))))))))))r&   c                    | j                  |       t        j                  dd      }|j                  |       |dz  }t	        j
                  |j                  d            }|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)uB   stalled로 상태 변경 시 status=stalled로 업데이트된다.r3  r  r4  r0  rM   rN   r,   r   r-   rg   ri   rj   rl   rm   Nr5  r7  s                r$   test_update_to_stalledz!TestUpdate.test_update_to_stalled  s    ##J/!!+iH
d"::
zz*...@AG}QH~**~****~***~**********r&   c                    | j                  |       t        j                  dd      }t        j                  t
              5 }|j                  |       ddd       j                  }|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                  |      t        j                  |      d	z  }
d
d|
iz  }t        t        j                   |            dx}x}x}	}y# 1 sw Y   xY w)uD   존재하지 않는 task_id 업데이트 시 exit 1이 발생한다.ztask-nonexistentr
  r4  Nr2   rg   r   r   rp   rr   rs   )r1  rw   rx   r   r   r   r6  r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   r   r   r   r   r   r   s               r$    test_update_task_not_found_exitsz+TestUpdate.test_update_task_not_found_exits  s    ##J/!!*<VL]]:& 	 (MM$	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	  	 s   EEN)r3  )r   r   r   r   r   r   r1  r9  r;  r>  r@  rB  r   r&   r$   r-  r-    s9    0  VZ 8
.
0	*	+(r&   r-  c            	       F    e Zd ZdZ	 	 ddedededefdZd Zd Z	d	 Z
d
 Zy)TestCheckStalledu-   cmd_check_stalled() 서브커맨드 테스트r*   r+   started_hours_agor	   c           	      4   t        j                         t        |      z
  j                         }dddddd|dd	g}|d
t        j                         j                         dddd|d}|| dz  }|j	                  t        j                  |dd      d       |S )u<   running 상태 task가 있는 체인 파일을 생성한다.hoursr2   zmemory/tasks/running-task.mdr[   r   	task-60.1r5   Nr6   r>   rn   u   정체 테스트r^   r/  r   rH   FrI   rJ   rM   rN   )r   rB   r   rC   rQ   rR   rS   )r   r*   r+   rE  r<   r,   rD   r   s           r$   _setup_chain_with_running_taskz/TestCheckStalled._setup_chain_with_running_task  s     llny7H'IITTV
 ;##&( $	
 !",,.224' *	
 xj..

4eAFQXYr&   c                 N	   | j                  |d       t        j                  d      }|j                  |       |j	                         }t        j                  |j                        }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#        |      }
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}
}|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
) u3   max_hours 초과한 running 작업이 검출된다.      @rE  rI   	max_hours5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancer   r   rd   rk   re   rf   Nr2   rg   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sro   rd   rk   r   rm   assert %(py8)sr  r   r+   chain-stalled-testri   rj   rl   rm   r:   rI  r9   r[   hours_elapsed>=)z%(py1)s >= %(py4)srJ  rw   rx   cmd_check_stalledr   rR   r   r   rQ  r   r   r   r   r   r   r   r   ro   r   )r   r#   r   r*   r   r   r   r   r   r   r   r   @py_assert4r   @py_format9r   s                   r$   "test_check_stalled_detects_stalledz3TestCheckStalled.test_check_stalled_detects_stalled  s   ++J#+N!!A.
T"$$&HLL)&$''''''''z'''z''''''&'''&''''''$'''$''''''''''6{a{a{ass66{aay$<(<<$(<<<<<$(<<<<$<<<(<<<<<<<<ay#2{2#{2222#{222#222{2222222ay /K/ K//// K/// ///K///////ay).Q.)Q....)Q...)...Q.......r&   c                 6   | j                  |d       t        j                  d      }|j                  |       |j	                         }t        j                  |j                        }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#        |      }
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=   max_hours 미만인 running 작업은 검출되지 않는다.g      ?rM  rI   rN  rP  rQ  r   r   rR  Nr   rg   rS  ro   rT  rU  r  rZ  )r   r#   r   r*   r   r   r   r   r   r   r   r   r\  r   r]  s                  r$   test_check_stalled_no_stalledz.TestCheckStalled.test_check_stalled_no_stalled-  sg   ++J#+N!!A.
T"$$&HLL)&$''''''''z'''z''''''&'''&''''''$'''$''''''''''6{a{a{ass66{ar&   c                    t        j                  d      }|j                  |       |j                         }t	        j
                  |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	)
u6   활성 체인이 없으면 빈 배열을 출력한다.rI   rN  rg   z%(py0)s == %(py3)sr   rd   r   r   r   N)rw   rx   r[  r   rR   r   r   r   r   r   r   r   r   r   r   r   r#   r   r*   r   r   r   r   r   r   r   r   s               r$   test_check_stalled_empty_resultz0TestCheckStalled.test_check_stalled_empty_result8  s    !!A.
T"$$&HLL)v|vvvr&   c           	      "   t        j                         t        d      z
  j                         }dddddd|d	d
g}ddt        j                         j                         ddd|d}|dz  }|j	                  t        j                  |dd      d       t        j                  d      }	|j                  |	       |j                         }
t        j                  |
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	)uE   completed/stalled 체인의 task는 정체 검사에서 제외된다.r   rG  r2   rY   r3   r   z	task-70.1r5   Nr6   zchain-completedr>   r   u   완료 체인r^   r?   zchain-completed.jsonFrI   rJ   rM   rN   rN  rg   rb  r   rc  r   r   )r   rB   r   rC   rQ   rR   rS   rw   rx   r[  r   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r<   r,   rD   r   r   r   r   r   r   r   r   s                   r$   )test_check_stalled_skips_completed_chainsz:TestCheckStalled.test_check_stalled_skips_completed_chainsA  s4   llnyq'99DDF
 1##&( $	
 *",,.224!$
 22

4eAFQXY!!A.
T"$$&HLL)v|vvvr&   N)rV  rL  )r   r   r   r   r   r   floatrJ  r^  r`  re  rg  r   r&   r$   rD  rD    sM    7
 -#&	     !	 
 
 D/	  r&   rD  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestListu$   cmd_list() 서브커맨드 테스트c                    t        j                         }|j                  |       |j                         }t	        j
                  |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)u0   체인이 없을 때 빈 배열을 출력한다.rg   rb  r   rc  r   r   N)rw   rx   cmd_listr   rR   r   r   r   r   r   r   r   r   r   r   rd  s               r$   test_list_emptyzTestList.test_list_emptyl  s    !!#
D$$&HLL)v|vvvr&   c                 8   t        d      D ]"  }t        d|dd|       }t        ||       $ t        j                         }|j                  |       |j                         }t        j                  |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}}
yc c}w )u:   여러 체인이 있을 때 모두 목록에 포함된다.r   rG   03du   범위 )r+   r/   rg   rS  ro   r   rT  rU  r  Nr+   z	chain-000r   r   	chain_idsr   r   r   z	chain-001z	chain-002)r   rE   rU   rw   rx   rl  r   rR   r   r   ro   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   rD   r   r   r   r   r   r\  r   r]  itemrp  r   r   r   s                       r$   test_list_multiple_chainsz"TestList.test_list_multiple_chainsu  s*   q 	+A#vaW-=wqc]SDT*	+ !!#
D$$&HLL)6{a{a{ass66{a289$T*%9	9'{i''''{i'''{''''''i'''i''''''''{i''''{i'''{''''''i'''i''''''''{i''''{i'''{''''''i'''i''''''' :s   Nc                 .   t        d      }t        ||       t        j                         }|j	                  |       |j                         }t        j                  |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}||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)uL   목록의 각 항목에 chain_id, status, scope, tasks 수 필드가 있다.zchain-fields)r+   r2   rg   rS  ro   r   rT  rU  r  Nr   r+   r   r   rq  r   r   r   r-   r/   
task_count)rE   rU   rw   rx   rl  r   rR   r   r   ro   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   rD   r   r   r   r   r   r\  r   r]  rq  r   r   r   s                     r$   test_list_shows_required_fieldsz(TestList.test_list_shows_required_fields  sH   8Z&!!#
D$$&HLL)6{a{a{ass66{aay!zT!!!!zT!!!z!!!!!!T!!!T!!!!!!!x4x4x44w$w$w$$#|t####|t###|######t###t#######r&   c                 >   t        d      D cg c]  }|dz   d| dddddddd	 }}t        d
|      }t        ||       t        j                         }|j                  |       |j                         }	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}}yc c}w )u<   task_count가 실제 tasks 배열의 길이와 일치한다.   r2   r   r   r3   r4   Nr5   r6   zchain-count)r+   r,   r   rt  rg   ri   rj   rl   rm   )r   rE   rU   rw   rx   rl  r   rR   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   r,   rD   r   r   r   r   r   r   r   r   s                   r$   test_list_task_count_correctz%TestList.test_list_task_count_correct  s     1X
  Q-aS4##" $	
 
  eDZ&!!#
D$$&HLL)ay&+!+&!++++&!+++&+++!++++++++
s   DN)r   r   r   r   rm  rr  ru  rx  r   r&   r$   rj  rj  i  s    .("$",r&   rj  c                       e Zd ZdZd Zd Zy)TestLocku@   lock 파일을 이용한 원자적 상태 업데이트 테스트c           	      ~   dddddddddg}d	d
t        j                         j                         dddd|d}|dz  }|j                  t	        j
                  |dd      d       t        j                  dd      }|j                  |       |dz  }|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}	x}
}y)uL   상태 업데이트 시 lock 파일이 생성되고 완료 후 삭제된다.r2   zmemory/tasks/lock-test.mdr3   r4   z	task-80.1r5   Nr6   zchain-lock-testr>   rn   u   락 테스트r^   r/  r   zchain-lock-test.jsonFrI   rJ   rM   rN   r   r4  zchain-lock-test.lockzEassert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}	lock_filerc   r   rB   rC   rQ   rR   rS   rw   rx   r6  r   r   r   r   r   r   r   r   )r   r#   r   r*   r,   rD   rb   r   r|  r   r   r   r   s                r$   "test_lock_file_created_and_removedz+TestLock.test_lock_file_created_and_removed  s    8##&" $	
 *",,.224$ *	
  "88
djjE!LW^_!!+iH
d !77	##%#%%%%%%%%%%%9%%%9%%%#%%%%%%%%%%r&   c                    t        d      D cg c]  }|dz   d| ddddd|z    d	d
ddd }}ddt        j                         j                         dddd|d}|dz  }|j	                  t        j                  |dd      d       t        d      D ]1  }t        j                  dd|z    d	d      }|j                  |       3 t        j                  |j                  d            }	|	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c c}w )#u6   순차 업데이트 시 최종 상태가 정확하다.r   r2   r   r   r3   r4   ztask-Z   z.1r5   Nr6   zchain-seq-testr>   rn   u   순차 테스트r^   r/  r   zchain-seq-test.jsonFrI   rJ   rM   rN   r
  r4  r,   r-   rg   ri   rj   rl   rm   )r   r   rB   rC   rQ   rR   rS   rw   rx   r6  r   r   r   r   r   r   r   )r   r#   r   r*   r   r,   rD   rb   r   
final_datar8  r   r   r   r   r   s                   r$   #test_sequential_updates_consistencyz,TestLock.test_sequential_updates_consistency  s    1X
  Q-aS4##"26("-" $	
 
 )",,.224' *	
  "77
djjE!LW^_ q 	 A%%b1fXR.@PDMM$	  ZZ
 4 4g 4 FG
w' 	,D>+V+>V++++>V+++>+++V+++++++	,A
s    E8N)r   r   r   r   r~  r  r   r&   r$   rz  rz    s    J &D#,r&   rz  c                   "    e Zd ZdZd Zd Zd Zy)
TestBackupu*   체인 파일 .bak 백업 기능 테스트c           	      t   dddddddddg}d	d
t        j                         j                         dddd|d}|dz  }|j                  t	        j
                  |dd      d       t        j                  dd      }|j                  |       |dz  }|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}	}
y)u-   .bak 파일이 update 직전에 생성된다.r2   zmemory/tasks/bak-test.mdr3   r4   
task-100.1r5   Nr6   zchain-bak-testr>   rn   u   백업 테스트r^   r/  r   zchain-bak-test.jsonFrI   rJ   rM   rN   r   r4  zchain-bak-test.json.bakra   bak_filerc   r}  )r   r#   r   r*   r,   rD   rb   r   r  r   r   r   s               r$   test_backup_created_on_updatez(TestBackup.test_backup_created_on_update	  s	    7##'" $	
 )",,.224' *	
  "77
djjE!LW^_!!,yI
d 99         x   x             r&   c           	         dddddddddg}d	d
t        j                         j                         dddd|d}|dz  }t        j                  |dd      }|j                  |d       t        j                  dd      }|j                  |       |dz  }	t        j                  |	j                  d            }
|
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)!uA   .bak 파일에 업데이트 전 원본 데이터가 저장된다.r2   z memory/tasks/bak-content-test.mdr3   r4   z
task-101.1r5   Nr6   zchain-bak-contentr>   rn   u   백업 내용 테스트r^   r/  r   zchain-bak-content.jsonFrI   rJ   rM   rN   r   r4  zchain-bak-content.json.bakr,   r   r-   rg   ri   rj   rl   rm   )r   rB   rC   rR   rS   rQ   rw   rx   r6  r   r   r   r   r   r   r   )r   r#   r   r*   r,   rD   rb   original_textr   r  bak_datar   r   r   r   r   s                   r$   "test_backup_contains_original_dataz-TestBackup.test_backup_contains_original_data*  s+    ?##'" $	
 ,",,.224. *	
  "::


4eAFmg>!!,yI
d <<::h00'0BC #H-::-::::-:::-::::::::::r&   c           	         ddddddt        j                         j                         ddg}d	d
t        j                         j                         dddd|d}|dz  }|j                  t	        j
                  |dd      d       |dz  dz  }|j                  dd       |dz  j                  dd       t        j                  |d      5 }t               }	d|	_
        d|	_        |	|j                  _        t        j                  d      }
|j!                  |
       ddd       |d z  }|j"                  } |       }|sd!d"t%        j&                         v st)        j*                  |      rt)        j,                  |      nd"t)        j,                  |      t)        j,                  |      d#z  }t/        t)        j0                  |            dx}}y# 1 sw Y   xY w)$u6   cmd_next 호출 시에도 .bak 파일이 생성된다.r2   zmemory/tasks/bak-next-test.mdr3   r   z
task-102.1r5   Nr6   zchain-bak-nextr>   rn   u   next 백업 테스트r^   r/  r   zchain-bak-next.jsonFrI   rJ   rM   rN   r   r   Tr   ztask-102.1.mdr   r`   r   r   r   zchain-bak-next.json.bakra   r  rc   )r   rB   rC   rQ   rR   rS   r    r   ry   r   rz   r{   r|   r}   rw   rx   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r,   rD   rb   r   r   r   r   r  r   r   r   s                  r$   test_backup_created_on_nextz&TestBackup.test_backup_created_on_nextN  s    <##'&lln668 $	
 )",,.224, *	
  "77
djjE!LW^_)I5$6		&229w2O\\"l+ 	x#+K%&K"!#K(3HLL%%%l;DKK	  99         x   x             	 	s   AGGN)r   r   r   r   r  r  r  r   r&   r$   r  r    s    4!B";H(!r&   r  c                   ^    e Zd ZdZdedededefdZd Zd Z	d	 Z
d
 Zd Zd Zd Zd Zd Zy)	TestChecku5   cmd_check() 서브커맨드 테스트 (읽기 전용)r*   r+   r,   r	   c           	          |dt        j                         j                         dddd|d}|d| dz  }|j                  t	        j
                  |d	d
      d       |S )u+   테스트용 체인 파일을 생성한다.r>   rn   u   check 테스트r^   r/  r   rG   rH   FrI   rJ   rM   rN   r   )r   r*   r+   r,   rD   r   s         r$   r   zTestCheck._setup_chain  sj     !",,.224& *	
 fXJe44

4eAFQXYr&   c           
      >   ddddddt        j                         j                         ddd	d
dddddddg}| j                  |d|       t	        j
                  d      }|j                  |       |j                         }t        j                  |j                        }|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}}
|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}}
|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)ub   체인 소속이고 마지막이 아닌 경우: in_chain=true, is_last=false, next_task_id 반환.r2   memory/tasks/dispatch-436.2.mdr3   r   
task-436.2r5   Nr6   rI   zmemory/tasks/dispatch-436.3.mdr[   r4   z
task-436.3zremotion-migrationr   in_chainTrt   rv   rj   rl   rm   is_lastFr+   rg   ri   next_task_idr   rB   rC   r   rw   rx   	cmd_checkr   rR   r   r   r   r   r   r   r   r   r#   r   r*   r   r,   r   r   r   r   r   r   r   r   s                 r$   test_check_in_chain_not_lastz&TestCheck.test_check_in_chain_not_last  s    =##'&lln668 $	 =##'" $	
, 	*&:EB!!,7
T$$&HLL)j!)T)!T))))!T)))!)))T)))))))i )E) E)))) E))) )))E)))))))j!9%99!%99999!%9999!999%99999999n%55%5555%555%5555555555r&   c           
         ddddddt        j                         j                         t        j                         j                         ddd	d
dddt        j                         j                         ddg}| j                  |d|       t	        j
                  d      }|j                  |       |j                         }t        j                  |j                        }|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}}
|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}}
|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)uX   체인 소속이고 마지막인 경우: in_chain=true, is_last=true, next_task_id=null.r2   zmemory/tasks/dispatch-436.1.mdr3   r
  z
task-436.1r5   r6   rI   r  r[   r   r  Nzremotion-migration-lastr   r  Trt   rv   rj   rl   rm   r  r+   rg   ri   r  r  r  s                 r$   test_check_in_chain_is_lastz%TestCheck.test_check_in_chain_is_last  s    =# '&lln668 ( 8 8 :	 =##'&lln668 $	
, 	*&?G!!,7
T$$&HLL)j!)T)!T))))!T)))!)))T)))))))i (D( D(((( D((( (((D(((((((j!>%>>!%>>>>>!%>>>>!>>>%>>>>>>>>n%--%----%---%----------r&   c                    t        j                  d      }|j                  |       |j                         }t	        j
                  |j                        }|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}
}	|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}
}	|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}
}	|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
)uU   어떤 체인에도 없는 task_id: in_chain=false, chain_id=null, next_task_id=null.ztask-nonexistent-9999r   r  Frt   rv   rj   rl   rm   Nr  r+   r  )rw   rx   r  r   rR   r   r   r   r   r   r   r   r  s                r$   test_check_not_in_chainz!TestCheck.test_check_not_in_chain  s   !!*AB
T$$&HLL)j!*U*!U****!U***!***U*******i )E) E)))) E))) )))E)))))))j!)T)!T))))!T)))!)))T)))))))n%--%----%---%----------r&   c           
      <   ddddddt        j                         j                         ddd	d
dddddddg}| j                  |d|       t	        j
                  d      }|j                  |       |j                         }t        j                  |j                        }|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}}
|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}}
|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)uN   다음 task가 pending이고 task_id=None인 경우: next_task_id=null 반환.r2   zmemory/tasks/dispatch-500.1.mdr3   r   z
task-500.1r5   Nr6   rI   zmemory/tasks/dispatch-500.2.mdr[   r4   zpending-no-id-chainr   r  Trt   rv   rj   rl   rm   r  Fr+   rg   ri   r  r  r  s                 r$   )test_check_next_task_id_none_when_pendingz3TestCheck.test_check_next_task_id_none_when_pending  s    =##'&lln668 $	 =##" $	
, 	*&;UC!!,7
T$$&HLL)j!)T)!T))))!T)))!)))T)))))))i )E) E)))) E))) )))E)))))))j!:%::!%:::::!%::::!:::%::::::::n%--%----%---%----------r&   c                    ddddddddd	dg}t        j                  d
t        j                  |      ddd      }t	        j
                  |d      5 }t               }d|_        d|_        ||j                  _
        |j                  |       ddd       |dz  }t        j                  |j                  d            }	|	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   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   AxY w)uB   create 시 tasks JSON의 task_id가 체인 파일에 보존된다.r  r2   zmemory/tasks/test.mdr3   r:   r7   r8   r9   z
task-100.2rI   zmemory/tasks/test2.mdr[   zpreserve-id-testu   task_id 보존 테스트r>   r^   r_   r`   r   z{"cron_id": "cron-preserve"}Nzchain-preserve-id-test.jsonrM   rN   r,   r:   rg   ri   rj   rl   rm   )rw   rx   rR   rS   r   ry   r   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   )r   r#   r   r*   r,   r   r   r   rb   rD   r   r   r   r   r   s                  r$   test_create_preserves_task_idz'TestCheck.test_create_preserves_task_id  s    %q?U_jk$q?V`kl
 !!'**U#,
 \\"l+ 	 x#+K%&K"!?K(3HLL%MM$	   "??
zz*...@AG}Q	*:l:*l::::*l:::*:::l:::::::G}Q	*:l:*l::::*l:::*:::l:::::::	  	 s   ;GGc                    ddddddddd	dg}t        j                  d
t        j                  |      ddd      }t	        j
                  |d      5 }t               }d|_        d|_        ||j                  _
        |j                  |       ddd       |j                          t        j                  d      }	|j                  |	       |j                         }
t        j                  |
j                        }|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}}|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}}|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# 1 sw Y   oxY w)!uI   create로 생성한 체인에서 task_id로 check 매칭이 성공한다.z
task-200.1r2   zmemory/tasks/t200-1.mdr3   r  z
task-200.2rI   zmemory/tasks/t200-2.mdr[   zcheck-find-testu   check 매칭 테스트r>   r^   r_   r`   r   z{"cron_id": "cron-find"}Nr   r  Trt   rv   rj   rl   rm   r  Fr+   rg   ri   r  )rw   rx   rR   rS   r   ry   r   rz   r{   r|   r}   r~   r   r  r   r   r   r   r   r   r   )r   r#   r   r*   r   r,   args_creater   r   
args_checkr   r   r   r   r   r   r   s                    r$   !test_check_finds_chain_by_task_idz+TestCheck.test_check_finds_chain_by_task_id   s=    %q?Walm$q?Walm
 ((&**U#*
 \\"l+ 	'x#+K%&K"!;K(3HLL%MM+&	' 	''=

Z $$&HLL)j!)T)!T))))!T)))!)))T)))))))i )E) E)))) E))) )))E)))))))j!6%66!%66666!%6666!666%66666666n%55%5555%555%5555555555#	' 	's   ;K::Lc           
         ddddddt        j                         j                         ddd	d
dddddddg}| j                  |d|       t	        j
                  d      }|dz  dz  }|j                  dd       |dz  j                  dd       |j                  |       |j                         }t        j                  |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}
|
|	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   }
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   cmd_next의 dispatch action 출력에 task_id 필드가 포함된다.r2   zmemory/tasks/task-566.1.mdr3   r   z
task-566.1r5   Nr6   rI   zmemory/tasks/task-566.2.mdr4   z
task-566.2zchain-566-testr   r   r   Tr   ztask-566.1.mdr   rM   rN   r   r   rg   ri   rj   rl   rm   r:   r   r   r   r   u2   dispatch action 출력에 task_id 필드가 없음z
>assert %(py5)sr   )r   rB   rC   r   rw   rx   r    rQ   r   r   rR   r   r   r   r   r   r   r   r   r   r   r$  r   r#   r   r*   r   r,   r   r   r   r   r   r   r   r   r   r   r   s                    r$   $test_next_includes_task_id_in_outputz.TestCheck.test_next_includes_task_id_in_outputA  s    9##'&lln668 $	 9##'" $	
, 	*&6>!!,7)I5$6		&223Cg2V
D$$&HLL)h-:-:----:------:-------XyF"XXXyFXXXyXXXXXXFXXXFXXXX$XXXXXXXi 0L0 L0000 L000 000L0000000r&   c           
         ddddddt        j                         j                         ddd	d
dddddddg}| j                  |d|       t	        j
                  d      }|dz  dz  }|j                  dd       |dz  j                  dd       |j                  |       |j                         }t        j                  |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}
|
|	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}|
|u }|slt        j                  d$|fd%|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            dx}
x}}y)&uW   다음 pending task의 task_id가 None이면 출력의 task_id도 None이어야 한다.r2   zmemory/tasks/task-567.1.mdr3   r   z
task-567.1r5   Nr6   rI   zmemory/tasks/task-567.2.mdr4   zchain-567-testr   r   r   Tr   ztask-567.1.mdr   rM   rN   r   r   rg   ri   rj   rl   rm   r:   r   r   r   r   r   r   rt   rv   )r   rB   rC   r   rw   rx   r    rQ   r   r   rR   r   r   r   r   r   r   r   r   r   r   r  s                    r$   -test_next_task_id_none_when_pending_has_no_idz7TestCheck.test_next_task_id_none_when_pending_has_no_idi  s    9##'&lln668 $	 9##" $	
, 	*&6>!!,7)I5$6		&223Cg2V
D$$&HLL)h-:-:----:------:-------"yF""""yF"""y""""""F"""F"""""""i (D( D(((( D((( (((D(((((((r&   c                 @   ddddddddd	dg}t        j                  d
t        j                  |      ddd      }t	        j
                  |d      5 }t               }d|_        d|_        ||j                  _
        |j                  |       ddd       t        j                  dd      }	|j                  |	       |j                          |dz  dz  }
|
j                  dd       |
dz  j                  dd       t        j                  d      }|j!                  |       |j                         }t        j"                  |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}}|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'z  }t        j"                  |j1                  d            }t3        d( |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}||u}|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   xY w)/uH   create로 생성한 체인에서 task_id로 next 진행이 성공한다.
task-300.1r2   zmemory/tasks/t300-1.mdr3   r  z
task-300.2rI   zmemory/tasks/t300-2.mdr[   znext-advance-testu   next 진행 테스트r>   r^   r_   r`   r   z{"cron_id": "cron-advance"}Nr   r4  r   r   Tr   ztask-300.1.mdr   rM   rN   r   r   r   rg   ri   rj   rl   rm   r8   r9   zchain-next-advance-test.jsonc              3   J   K   | ]  }|j                  d       dk(  s|  yw)r:   r  Nr   r   s     r$   r   z:TestCheck.test_next_advances_by_task_id.<locals>.<genexpr>  s      VqQUU95E5UVr   r,   r-   r
  r=   r  r  )rw   rx   rR   rS   r   ry   r   rz   r{   r|   r}   r~   r6  r   r    rQ   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r,   r  r   r   args_updater   	args_nextr   r   r   r   r   r   r   rb   rD   r  s                         r$   test_next_advances_by_task_idz'TestCheck.test_next_advances_by_task_id  s&    %q?Walm$q?Walm
 (((**U#)
 \\"l+ 	'x#+K%&K"!>K(3HLL%MM+&	' ((iP
k" )I5$6		&223Cg2V &&|<	
I$$&HLL)h-:-:----:------:-------k">&>>"&>>>>>"&>>>>">>>&>>>>>>>>f~,,~,,,,~,,,~,,,,,,,,,,  "@@
zz*...@AVDMVV	",f,"f,,,,"f,,,",,,f,,,,,,,(44(4444(444(4444444444C	' 	's   ;PPN)r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r  r  r   r&   r$   r  r  ~  sY    ?t s 4 D  !6F!.F
.!.F;46B&1P%)N/5r&   r  c                   h    e Zd ZdZ	 	 	 ddedededededefdZd	 Zd
 Z	d Z
d Zd Zd Zd Zd Zy)TestF12RetryPhaseu-   F12: completion-promise retry phase 테스트r*   r   r:   r   report_contentr	   c           
         dddd|dt        j                         j                         d|d	dd	dd
dddddg}ddt        j                         j                         dddd|d}|dz  }|j                  t	        j
                  |dd      d       |dz  dz  }	|	j                  dd       |	| dz  j                  |d       |S )u;   F12 테스트용 체인 + 보고서를 생성하는 헬퍼.r2   zmemory/tasks/task-f12-1.mdr[   r   r5   N	r7   r8   r9   r-   r:   r;   r<   r=   r   rI   zmemory/tasks/task-f12-2.mdr4   
task-f12.2r6   chain-f12-testr>   rn   u   F12 retry 테스트r^   zcron-f12r   chain-f12-test.jsonFrJ   rM   rN   r   r   Tr   r   )r   rB   rC   rQ   rR   rS   r    )
r   r*   r   r:   r   r  r,   rD   r   r   s
             r$   _setup_chain_with_retryz)TestF12RetryPhase._setup_chain_with_retry  s     9##"&lln668 $*
 9##'" $	
0 )",,.224* *	
 11

4eAFQXY)I5$6	'#	&22>G2Tr&   c                 R   | j                  ||d       t        j                  d      }|j                  |       |j	                         }t        j                  |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}
}	t        j                  |dz  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}
}	t        d |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)uK   retry_count=0인 상태에서 QC FAIL → action=dispatch, retry_attempt=1.r   r   
task-f12.1r   r   r   rg   ri   rj   rl   rm   Nr   r2   r  rM   rN   r-   rn   c              3   J   K   | ]  }|j                  d       dk(  s|  ywr:   r  Nr   r   s     r$   r   zCTestF12RetryPhase.test_f12_first_qc_fail_retries.<locals>.<genexpr>        XquuY7G<7W1Xr   r,   r   r   r  rw   rx   r   r   rR   r   r   r   r   r   r   r   r   r   r   r#   r   r*   r   r   r   r   r   r   r   r   r   rD   r   s                  r$   test_f12_first_qc_fail_retriesz0TestF12RetryPhase.test_f12_first_qc_fail_retries  s$   $$Zq$I!!,7
D$$&HLL)h-:-:----:------:-------o&+!+&!++++&!+++&+++!+++++++ zz:(==HHRYHZ[H~))~))))~)))~)))))))))) Xd7mXX8$1	1$	1111$	111$111	1111111=).Q.)Q....)Q...)...Q.......r&   c                 R   | j                  ||d       t        j                  d      }|j                  |       |j	                         }t        j                  |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}
}	t        j                  |dz  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}
}	t        d |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)uK   retry_count=1인 상태에서 QC FAIL → action=dispatch, retry_attempt=2.r2   r  r  r   r   r   rg   ri   rj   rl   rm   Nr   rI   r  rM   rN   r-   rn   c              3   J   K   | ]  }|j                  d       dk(  s|  ywr  r   r   s     r$   r   zDTestF12RetryPhase.test_f12_second_qc_fail_retries.<locals>.<genexpr>#  r  r   r,   r   r  r  s                  r$   test_f12_second_qc_fail_retriesz1TestF12RetryPhase.test_f12_second_qc_fail_retries  s   $$Zq$I!!,7
D$$&HLL)h-:-:----:------:-------o&+!+&!++++&!+++&+++!+++++++ zz:(==HHRYHZ[H~))~))))~)))~)))))))))) Xd7mXX=).Q.)Q....)Q...)...Q.......r&   c                    | j                  ||d       t        j                  d      }|j                  |       |j	                         }t        j                  |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}
}	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        j                  |dz  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z  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)!uM   retry_count=2(=MAX_RETRY)인 상태에서 QC FAIL → circuit breaker 발동.rI   r  r  r   r   r  rg   ri   rj   rl   rm   Ncircuit_breakerr  r   r  escalation_filer   r   r   r   r   r  rM   rN   r-   r   escalationstask-f12.1_escalation.jsonu*   escalation 파일이 생성되어야 함: r  rc   )r  rw   rx   r   r   rR   r   r   r   r   r   r   r   r   r   r   r   r   r$  )r   r#   r   r*   r   r   r   r   r   r   r   r   r   r   r   rD   r  r   s                     r$   %test_f12_circuit_breaker_on_max_retryz7TestF12RetryPhase.test_f12_circuit_breaker_on_max_retry&  sS   $$Zq$I!!,7
D$$&HLL)h,9,9,,,,9,,,,,,9,,,,,,, 4F8$44 $44444 $4444 444$44444444 * F**** F*** ******F***F******* zz:(==HHRYHZ[H~**~****~***~********** #X-=@\\%%g%'g'gg+UVeUf)ggggggggggggg%ggg'ggggggr&   c                 L   | j                  ||d       t        j                  d      }|j                  |       |j	                          |dz  dz  dz  }|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}||
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}}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   }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}}y)!u;   circuit breaker 발동 후 escalation 파일 내용 검증.rI   r  r  r   r   r  r  ra   r  rc   NrM   rN   r:   r   r   contentr   r   r   r+   triggered_atr  	max_retrytotal_attemptsr   rg   ri   rj   rl   rm   r  )r  rw   rx   r   r   r   r   r   r   r   r   r   r   rR   r   r   r   )r   r#   r   r*   r   r   r  r   r   r   r  r   r   r   r   r   s                   r$    test_f12_escalation_file_contentz2TestF12RetryPhase.test_f12_escalation_file_content;  s:   $$Zq$I!!,7
D"X-=@\\%%'%''''''''''''''%''''''''''**_666HI#yG####yG###y######G###G#######$zW$$$$zW$$$z$$$$$$W$$$W$$$$$$$(~((((~(((~(((((((((((((((("x7""""x7"""x""""""7"""7"""""""%{g%%%%{g%%%{%%%%%%g%%%g%%%%%%%*7****7*********7***7*******"x7""""x7"""x""""""7"""7"""""""y!1\1!\1111!\111!111\1111111z"6&66"&66666"&6666"666&66666666{#(q(#q((((#q(((#(((q(((((((r&   c                    |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   MAX_RETRY == 2 확인.rI   rg   )z1%(py2)s
{%(py2)s = %(py0)s.MAX_RETRY
} == %(py5)sr#   )rd   re   r   zassert %(py7)srq   N)		MAX_RETRYr   r   r   r   r   r   r   r   )	r   r#   r   r*   r   r\  r   r   r   s	            r$    test_f12_max_retry_constant_is_2z2TestF12RetryPhase.test_f12_max_retry_constant_is_2T  so    || q |q    |q      r   r   |   q       r&   c                 $   | j                  ||dd       t        j                  d      }|j                  |       |j	                         }t        j                  |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}
}	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)uR   retry_count=1인 상태에서 QC PASS → action=dispatch (다음 task로 진행).r2   u'   작업 완료. 모든 테스트 통과.)r   r  r  r   r   r   rg   ri   rj   rl   rm   Nr:   r  r   not inz%(py1)s not in %(py3)sr   r   r   r   )r  rw   rx   r   r   rR   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   r   r   r   r   r   r   r   r   r   s                  r$    test_f12_qc_pass_no_retry_neededz2TestF12RetryPhase.test_f12_qc_pass_no_retry_neededX  s\   $$D	 	% 	
 !!,7
D$$&HLL)h-:-:----:------:-------i 0L0 L0000 L000 000L0000000,f,,,,f,,,,,,,,,f,,,f,,,,,,,r&   c                    | j                  ||d       t        j                  d      }t        j                         j                         }|j                  |       |j                          t        j                  |dz  j                  d            }t        d |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}
|	|
u }|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}}
|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)uU   retry 후 target_task의 status=running, completed_at=None, started_at 갱신 확인.r   r  r  r   r  rM   rN   c              3   J   K   | ]  }|j                  d       dk(  s|  ywr  r   r   s     r$   r   zFTestF12RetryPhase.test_f12_retry_resets_task_status.<locals>.<genexpr>u  r  r   r,   r-   r   rg   ri   rj   rl   rm   Nr=   rt   rv   r<   r  r  rX  )z%(py1)s >= %(py3)sbeforer   r   r   )r  rw   rx   r   rB   rC   r   r   rR   r   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r   r  rD   r   r   r   r   r   r   r   r   s                   r$   !test_f12_retry_resets_task_statusz3TestF12RetryPhase.test_f12_retry_resets_task_statusk  s   $$Zq$I!!,7))+
Dzz:(==HHRYHZ[Xd7mXX8$1	1$	1111$	111$111	1111111>*2d2*d2222*d222*222d2222222<(44(4444(444(4444444444<(2(F2222(F222(222222F222F2222222r&   c           
      |   ddddddt        j                         j                         ddd		d
ddddddddg}ddt        j                         j                         dddd|d}|dz  }|j                  t	        j
                  |dd
      d       |dz  dz  }|j                  dd       |dz  j                  d d       t        j                  d!      }	|j                  |	       |j                         }
t	        j                  |
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}}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/d0|iz  }t!        t        j"                  |            dx}}y)1uI   gate=none인 task는 QC FAIL이어도 retry 없이 다음 task로 진행.r2   z"memory/tasks/task-f12-gate-none.mdr[   r   ztask-f12-gate.1r\   Nr   r  rI   z$memory/tasks/task-f12-gate-none-2.mdr4   ztask-f12-gate.2r6   zchain-f12-gate-noner>   rn   u   gate=none 테스트r^   zcron-f12-gater   zchain-f12-gate-none.jsonFrJ   rM   rN   r   r   Tr   ztask-f12-gate.1.mdu   QC FAIL: 오류 발견.r   r   r   rg   ri   rj   rl   rm   r:   r   r  r  r   r   r   r   )r   rB   rC   rQ   rR   rS   r    rw   rx   r   r   r   r   r   r   r   r   r   r   r   r   )r   r#   r   r*   r   r,   rD   rb   r   r   r   r   r   r   r   r   r   r   r   s                      r$   !test_f12_gate_none_bypasses_retryz3TestF12RetryPhase.test_f12_gate_none_bypasses_retry}  s4    A##,&lln668 $ 
 C##," $	
0 .",,.224* /	
  "<<
djjE!LW^_ )I5$6	+	+778Q\c7d!!*;<
D$$&HLL)h-:-:----:------:-------i 5$55 $55555 $5555 555$55555555,f,,,,f,,,,,,,,,f,,,f,,,,,,,r&   N)r  r   u   QC FAIL: 타입 에러 발견.)r   r   r   r   r   r   intr  r  r  r  r  r  r  r  r  r   r&   r$   r  r    sy    7 $>11 1 	1
 1 1 
1f/*/(h*)2!-&3$3-r&   r  c                   "    e Zd ZdZd Zd Zd Zy)TestCircuitBreakerIntegrationu6   circuit_breaker 모듈 통합 테스트 (task-1651.1).c                    t        |      }|dz  dz  }|j                  ddd       |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}}t        j                  |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}
}|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)ug   _trigger_circuit_breaker가 circuit_breaker 모듈의 _write_escalation_file을 호출하는지 검증.r   r  ztask-test-1zchain-test-1r   ztask-test-1_escalation.jsonu+   escalation 파일이 생성되어야 한다r  r  rc   Nr:   rg   ri   rj   rl   rm   r+   r   
escalationr  r  r   r  )r%   _trigger_circuit_breakerr   r   r$  r   r   r   r   r   r   rR   r   r   r   )r   r   r#   escalations_dirr  r   r   r   rD   r   r   r   s               r$   (test_trigger_uses_circuit_breaker_modulezFTestCircuitBreakerIntegration.test_trigger_uses_circuit_breaker_module  s    *"X-=
##M>1E),II%%V%'V'VV)VVVVVVVVVVVVV%VVV'VVVVVVzz/3356I/-/-////-//////-///////J1>1>1111>111111>1111111H~--~----~---~----------,d8n,{n,,,,{n,,,{,,,n,,,,,,,r&   c                    t        |      }|dz  dz  }t        |dd      }t        |dd       	 |j                  ddd       t        |d|       |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}}t        j                  |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# t        |d|       w xY w)u`   circuit_breaker 모듈 미사용 시 fallback으로 escalation 파일이 생성되는지 검증.r   r  _CB_AVAILABLEFztask-fallback-1z
chain-fb-1r   ztask-fallback-1_escalation.jsonu:   fallback으로 escalation 파일이 생성되어야 한다r  r  rc   Nr:   rg   ri   rj   rl   rm   r+   )r%   getattrsetattrr  r   r   r$  r   r   r   r   r   r   rR   r   r   r   )r   r   r#   r  original_cbr  r   r   r   rD   r   r   r   s                r$   )test_trigger_fallback_when_cb_unavailablezGTestCircuitBreakerIntegration.test_trigger_fallback_when_cb_unavailable  s    *"X-= b/59OU+	6''(9<KB5),MM%%e%'e'ee)eeeeeeeeeeeee%eee'eeeeeezz/3356I3"33"33333"3333333"33333333J/</<////<//////</////// B5s   H4 4Ic                 `   ddl }ddlm} |j                  |       |j                  }|dz  |_        	 |j                  ddddd	       ||_        t        |dz  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#        j$                  |d   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}}|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# ||_        w xY w)uN   _write_escalation_file이 **extra 필드를 payload에 포함하는지 검증.r   Nr  
test_extraztest reasonr   zchain-extracustom_value)contextr  error_countr+   custom_fieldz*_escalation.jsonr2   rg   rS  ro   filesrT  rU  r  r+   ri   rj   rl   rm   r  r  r  )	importlibutils.circuit_breakerr  reloadESCALATIONS_DIR_write_escalation_filer   globro   r   r   r   r   r   r   r   r   rR   r   r   )r   r   r  cb_modoriginal_dirr  r   r   r\  r   r]  rD   r   r   r   s                  r$   'test_write_escalation_file_extra_fieldszETestCircuitBreakerIntegration.test_write_escalation_file_extra_fields  st   .  --!)M!9		2))$$&+ *  &2F" h.445HIJ5zQzQzQss55zQzz%(,,./J0=0=0000=000000=0000000N#5~5#~5555#~555#555~5555555I.,.,....,......,.......M"'a'"a''''"a'''"'''a''''''' &2F"s   N$ $	N-N)r   r   r   r   r  r  r   r   r&   r$   r  r    s    @-0&(r&   r  )z
test-chainNrn   r^   r]   r/  ))r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   rw   rR   r   r   typesr   r   pathlibr   unittest.mockr   r   r   
ModuleTyper%   fixturer#   r*   r   r   r  dictrE   rU   rW   r   r-  rD  rj  rz  r  r  r  r  r   r&   r$   <module>r     s      	 
  (  * $ 5+;+; 8 ) )
   !##-!!$;! ! 	!
 ! Dj! 
!H
T 
 
$ 
$u( u(zTB TBxS( S(vh h`E, E,ZH, H,`p! p!pA5 A5R
i- i-b@( @(r&   