
    i                        d Z ddlZddlmc mZ ddlZddl	Z	ddl
Z
ddlZddlmZ ddlZ ee
j                  j!                  dd            Zedz  Zd Z e       Zej*                  d        Zej*                  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! G d d      Z"y) u  
test_tech_debt.py

scripts/tech-debt.py 단위 테스트 (아르고스 작성)

테스트 항목:
- load_data(): 기존 파일 로드 / 파일 없을 때 기본 구조 반환
- save_data(): 파일 저장, 통계 업데이트, last_updated 갱신, 디렉토리 자동 생성
- generate_id(): TD-001 형식 ID 생성, 최대값+1, 갭 처리
- add_item(): 항목 추가, 필드 검증, ID 증가, 퍼시스턴스
- list_items(): 전체/상태별/심각도별 조회, 정렬 검증
- resolve_item(): 해결 처리, None 반환 케이스, 퍼시스턴스
- show_item(): 단건 조회, 존재하지 않는 ID
- show_stats(): 통계 구조, 항목 추가/해결 후 통계 반영
- print_item_table(): 테이블 출력, 빈 목록 메시지
- print_item_detail(): 상세 출력, 해결된 항목 출력
- main(): CLI 서브커맨드 동작 검증

격리: monkeypatch로 _mod.DATA_FILE을 tmp_path로 교체하여 파일시스템 완전 격리
    N)PathWORKSPACE_ROOTz/home/jay/workspacezscripts/tech-debt.pyc                      t         j                  j                  dt              } t         j                  j	                  |       }| j
                  j                  |       |S )N	tech_debt)	importlibutilspec_from_file_location_SCRIPT_PATHmodule_from_specloaderexec_module)specmodules     +/home/jay/workspace/tests/test_tech_debt.py_load_moduler   &   sC    >>11+|LD^^,,T2FKKF#M    c                 D    | dz  dz  }|j                  t        d|       |S )uH   DATA_FILE을 tmp_path 아래의 임시 파일로 교체하는 픽스처.memorytech-debt.json	DATA_FILE)setattr_mod)tmp_pathmonkeypatchtmp_data_files      r   	data_filer   5   s,     x'*::Mk=9r   c                  >    ddddddddgd	d
d
d	gdd	dddddddddS )u&   기본 샘플 데이터 구조 반환.1.0TD-001u'   레거시 인증 시스템 리팩토링highopenu=   JWT 토큰 검증 로직이 여러 곳에 중복되어 있음authsecurityz2026-03-02 04:05:58N	idtitleseveritystatusdescriptiontags
created_atresolved_atresolution_note
2026-03-02   r   )totalr!   resolved	high_openmedium_openlow_open)createdlast_updatedstatsversionitemsmetadata r<   r   r   sample_datar=   =   sa      B" ^,3##'

 $1 
 r   c                   $    e Zd Zd Zd Zd Zd Zy)TestLoadDatac                    |j                   j                  dd       |j                  t        j                  |d      d       t
        j                         }|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|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}}y)u2   기존 파일이 있으면 내용을 로드한다.T)parentsexist_okF)ensure_asciiutf-8encodingr9   r   ==z%(py1)s == %(py4)spy1py4assert %(py6)spy6Nr:   r/   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenpy0py2rL   py7assert %(py9)spy9r   r%   r   )parentmkdir
write_textjsondumpsr   	load_data
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanationrP   @py_builtinslocals_should_repr_global_name)selfr   r=   result@py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_assert1@py_assert6@py_assert5@py_format8@py_format10s                 r   test_load_existing_dataz$TestLoadData.test_load_existing_datad   s~   td;JJ{7' 	 	
 !i )E) E)))) E))) )))E)))))))'?(s?#(q(#q((((#q((((((s(((s(((?(((#(((q(((((((gq!$'383'83333'8333'33383333333r   c                    |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}}t        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}d
}||v }	|	st        j                  d|	fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }
dd|
iz  }t        t        j                  |            dx}}	y)u2   파일이 없으면 기본 구조를 반환한다.zEassert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r   rR   rS   rL   N5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancerf   dictrR   rK   rS   rL   r9   inz%(py1)s in %(py3)srK   py3assert %(py5)spy5)existsrb   rc   r]   rd   r_   r`   ra   r   r\   ru   rv   r^   )re   r   rl   rh   rn   @py_format6rf   rj   rg   ri   @py_format4s              r   test_load_missing_filez#TestLoadData.test_load_missing_fileq   sV   ##%#%%%%%%%%%%%9%%%9%%%#%%%%%%%%%%!&$''''''''z'''z''''''&'''&''''''$'''$''''''''''"yF""""yF"""y""""""F"""F"""""""r   c                 h   t         j                         }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}|d   }t        |t              }|sd	d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            dx}}|d   }g }	||	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<   기본 구조에 items 키가 있고 리스트여야 한다.r:   rx   rz   rf   r{   r}   r~   N5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}ru   listrR   rS   r|   r~   rG   rI   rJ   rM   rN   )r   r\   r]   r^   r_   rb   rc   rd   r`   ra   ru   r   )re   r   rf   rg   ri   r   r   rl   @py_assert4rh   rj   rk   s               r   test_default_has_items_listz(TestLoadData.test_default_has_items_listz   s2   ! w&    w&   w      &   &        /0z/400000000z000z000/000000400040000000000g$"$"$$$$"$$$$$$"$$$$$$$r   c                 h   t         j                         }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}|d   }t        |t              }|sd	d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            dx}}y)u2   기본 구조에 metadata 키가 있어야 한다.r;   rx   rz   rf   r{   r}   r~   Nr   ru   rv   r   )r   r\   r]   r^   r_   rb   rc   rd   r`   ra   ru   rv   )	re   r   rf   rg   ri   r   r   rl   r   s	            r   test_default_has_metadataz&TestLoadData.test_default_has_metadata   s    !#zV####zV###z######V###V####### ,3z,d33333333z333z333,333333d333d3333333333r   N)__name__
__module____qualname__rq   r   r   r   r<   r   r   r?   r?   c   s    4#%4r   r?   c                   *    e Zd Zd Zd Zd Zd Zd Zy)TestSaveDatac                    t         j                         }t         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}}y)u3   save_data()를 호출하면 파일이 생성된다.Aassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r   rs   N)r   r\   	save_datar   rb   rc   r]   rd   r_   r`   ra   )re   r   datarl   rh   rj   s         r   test_save_creates_filez#TestSaveData.test_save_creates_file   sx    ~~t!!!!!!!!!y!!!y!!!!!!!!!!!!!r   c                 t   ddddddddd	ddd
ddddgdddd}t         j                  |       |d   d   }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|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}}y)"u<   save_data() 후 metadata.stats가 올바르게 계산된다.r   r   zHigh open itemr    r!   r%   r&   r'   r(   TD-002zMedium open itemmediumTD-003Resolved itemlowr1   r.    r5   r6   r8   r;   r7   r0      rG   rI   rJ   rM   rN   N   r/   r2   r3   r4   r   )r   r   r]   r^   r_   r`   ra   )	re   r   r   r7   rg   rh   ri   rj   rk   s	            r   test_save_updates_statsz$TestSaveData.test_save_updates_stats   sW     #- &$	 #/ ($	 #, %(	( %1"E-
2 	tZ )W~""~""""~"""~""""""""""V}!!}!!!!}!!!}!!!!!!!!!!Z %A% A%%%% A%%% %%%A%%%%%%%[!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&]#(q(#q((((#q(((#(((q(((((((Z %A% A%%%% A%%% %%%A%%%%%%%r   c                 B   t         j                         }d}||d   d<   t         j                  |       |d   d   }||k7  }|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   }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}
}	g }|d   }d}	||	k(  }
|
}|
r|d   }d}||k(  }|}|r|d   }d}||k(  }|}|s_t        j                  d|
fd||	f      t        j
                  |      t        j
                  |	      dz  }dd|iz  }|j                  |       |
rt        j                  dfdf      t        j
                  |      t        j
                  |      dz  }dd|iz  }|j                  |       |r_t        j                  dfdf      t        j
                  |      t        j
                  |      d z  }d!d"|iz  }|j                  |       t        j                  |d#      i z  }d$d%|iz  }t        t        j                  |            d
x}x}x}x}
x}	x}x}x}x}x}}y
)&uA   save_data() 후 last_updated가 현재 시각으로 갱신된다.z1970-01-01 00:00:00r;   r6   )!=)z%(py1)s != %(py3)sold_timestampr{   r}   r~   N   rG   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)srP   tsrR   rK   r|   rN   assert %(py8)spy8   -   
    z%(py3)s == %(py6)sr|   rN   %(py8)sz%(py11)s == %(py14)spy11py14%(py16)spy16z%(py19)s == %(py22)spy19py22%(py24)spy24r   assert %(py27)spy27)r   r\   r   r]   r^   r_   rb   rc   rd   r`   ra   rP   append_format_boolop)re   r   r   r   rg   ri   r   r   r   rn   r   rk   @py_format9rl   @py_assert10@py_assert13@py_assert12@py_assert18@py_assert21@py_assert20@py_format15@py_format17@py_format23@py_format25@py_format26@py_format28s                             r   test_save_updates_last_updatedz+TestSaveData.test_save_updates_last_updated   s$   ~~-+8Z(tJ/@/=@@@@/=@@@/@@@@@@=@@@=@@@@@@@*n-2w"w"}w"ss22w">r!u>>u|>1>>>B>3>3>>>>u>>>u>>>>>>>>>>>>>>>>>>>>>>>3>>>>>>3>>>>>>>>>>>>>>>r   c                    |dz  dz  dz  dz  }|j                  t        d|       dg ddd	d
}t        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}}y)u;   부모 디렉토리가 없어도 자동으로 생성한다.deepnesteddirr   r   r   r.   r   r   r8   r   nested_pathrs   N)r   r   r   r   rb   rc   r]   rd   r_   r`   ra   )re   r   r   r   r   rl   rh   rj   s           r   test_save_creates_parent_dirsz*TestSaveData.test_save_creates_parent_dirs   s    '(2U:=MMD+{; $0"E

 	t!!#!########{###{###!##########r   c                    t         j                         }|d   j                  ddddd       t         j                  |       t	        |d      5 }t        j                  |      }d	d	d	       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}}y	# 1 sw Y   {xY w)u;   저장된 데이터가 실제로 디스크에 반영된다.r:   r   zDisk persist testr   r!   r   rD   rE   Nr/   rG   rO   rP   rQ   rU   rV   r   r&   rI   rJ   rM   rN   )r   r\   r   r   r!   rZ   loadrP   r]   r^   rb   rc   rd   r_   r`   ra   )re   r   r   floadedrl   rh   rm   rn   ro   rp   rg   ri   rj   rk   s                  r   test_save_persists_to_diskz'TestSaveData.test_save_persists_to_disk   sU   ~~W,! 		
 	t )g. 	"!YYq\F	" '?(s?#(q(#q((((#q((((((s(((s(((?(((#(((q(((((((gq!'*A.AA*.AAAAA*.AAAA*AAA.AAAAAAAA		" 	"s   GG)N)r   r   r   r   r   r   r   r   r<   r   r   r   r      s    "#&J?$Br   r   c                   $    e Zd Zd Zd Zd Zd Zy)TestGenerateIdc                 x   t         j                         }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u1   items가 비어 있으면 TD-001을 반환한다.r   rG   z%(py0)s == %(py3)srf   rR   r|   r}   r~   N)
r   generate_idr]   r^   rb   rc   rd   r_   r`   ra   re   r   rf   ri   rl   r   r   s          r   test_first_id_is_td_001z&TestGenerateId.test_first_id_is_td_001   si    !!#!!v!!!!v!!!!!!v!!!v!!!!!!!!!!r   c                    t         j                         }|d   j                  ddddd       t         j                  |       t         j	                         }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)u+   TD-001이 있으면 TD-002를 반환한다.r:   r   Existingr   r!   r   r   rG   r   rf   r   r}   r~   Nr   r\   r   r   r   r]   r^   rb   rc   rd   r_   r`   ra   )re   r   r   rf   ri   rl   r   r   s           r   test_increments_from_existingz,TestGenerateId.test_increments_from_existing   s    ~~WjevV	
 	t!!#!!v!!!!v!!!!!!v!!!v!!!!!!!!!!r   c                    t         j                         }dD ]"  }|d   j                  d|dd| ddd       $ t         j                  |       t         j	                         }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)u2   여러 항목 중 최대 번호+1을 반환한다.)r/   r   r   r:   TD-03dItem r   r!   r   zTD-008rG   r   rf   r   r}   r~   Nr   	re   r   r   irf   ri   rl   r   r   s	            r   test_finds_max_idz TestGenerateId.test_finds_max_id  s    ~~ 	AM  #w-$QC[ %$		 	t!!#!!v!!!!v!!!!!!v!!!v!!!!!!!!!!r   c                    t         j                         }dD ]"  }|d   j                  d|dd| ddd       $ t         j                  |       t         j	                         }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)uJ   TD-001, TD-005가 있으면 TD-006을 반환한다 (갭 무시, 최대+1).)r/      r:   r   r   r   r   r!   r   zTD-006rG   r   rf   r   r}   r~   Nr   r   s	            r   test_handles_gap_in_idsz&TestGenerateId.test_handles_gap_in_ids  s    ~~ 	AM  #w-$QC[ %$		 	t!!#!!v!!!!v!!!!!!v!!!v!!!!!!!!!!r   N)r   r   r   r   r   r   r   r<   r   r   r   r      s    "
""$"r   r   c                   H    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zy)TestAddItemc                 p   t         j                  dd      }t        |t              }|sddt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d}y)	u+   add_item()은 딕셔너리를 반환한다.	Test itemr    rt   ru   rf   rv   rw   N)r   add_itemru   rv   rb   rc   r]   rd   r_   r`   ra   )re   r   rf   rh   rj   s        r   test_add_item_returns_dictz&TestAddItem.test_add_item_returns_dict0  s    {F3&$''''''''z'''z''''''&'''&''''''$'''$''''''''''r   c                 0   t         j                  dd      }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}x}}y
)u2   새로 추가된 항목의 status는 'open'이다.zStatus testr   r(   r!   rG   rI   rJ   rM   rN   Nr   r   r]   r^   r_   r`   ra   re   r   itemrg   rh   ri   rj   rk   s           r   test_add_item_status_openz%TestAddItem.test_add_item_status_open6  s`    }}]H5H~''~''''~'''~''''''''''r   c                 8   t         j                  dd       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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}
}y)u+   추가된 항목이 파일에 저장된다.zPersist testr   r:   r/   rG   rO   rP   rQ   rU   rV   Nr   r&   rI   rJ   rM   rN   )r   r   r\   rP   r]   r^   rb   rc   rd   r_   r`   ra   )re   r   r   rl   rh   rm   rn   ro   rp   rg   ri   rj   rk   s                r   test_add_item_persistedz#TestAddItem.test_add_item_persisted<  s    ne,~~=&s=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&=&&&!&&&Q&&&&&&&G}Q(:N:(N::::(N:::(:::N:::::::r   c                 <   t         j                  ddddg      }|d   }ddg}||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$   태그가 올바르게 저장된다.zTagged itemr    r"   r#   )r*   r*   rG   rI   rJ   rM   rN   Nr   r   s           r   test_add_item_with_tagsz#TestAddItem.test_add_item_with_tagsD  sq    }}]F&*9M}NF|3
33|33333|3333|33333333333r   c                    d}t         j                  dd|      }|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)u$   설명이 올바르게 저장된다.u   JWT 토큰 검증 로직 중복z	Desc testr   r)   r)   rG   z%(py1)s == %(py3)sdescr{   r}   r~   N)
r   r   r]   r^   r_   rb   rc   rd   r`   ra   )re   r   r  r   rg   ri   r   r   s           r   test_add_item_with_descriptionz*TestAddItem.test_add_item_with_descriptionJ  s|    0}}[(}EM"*"d****"d***"******d***d*******r   c                    t         j                  dd      }t         j                  dd      }t         j                  dd      }|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|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)u8   여러 번 추가하면 ID가 순서대로 증가한다.Firstr    Secondr   Thirdr   r%   r   rG   rI   rJ   rM   rN   Nr   r   r   )
re   r   item1item2item3rg   rh   ri   rj   rk   s
             r   test_add_item_increments_idz'TestAddItem.test_add_item_increments_idQ  s   gv.h.gx0T{&h&{h&&&&{h&&&{&&&h&&&&&&&T{&h&{h&&&&{h&&&{&&&h&&&&&&&T{&h&{h&&&&{h&&&{&&&h&&&&&&&r   c                 0   t         j                  dd      }|d   }g }||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?   태그를 지정하지 않으면 빈 리스트가 저장된다.No tagsr   r*   rG   rI   rJ   rM   rN   Nr   r   s           r    test_add_item_default_tags_emptyz,TestAddItem.test_add_item_default_tags_empty[  s`    }}Y.F|!r!|r!!!!|r!!!|!!!r!!!!!!!r   c                    t         j                  ddddg      }g d}|D ]  }||v }|st        j                  d|fd||f      d	t	        j
                         v st        j                  |      rt        j                  |      nd	d
t	        j
                         v st        j                  |      rt        j                  |      nd
dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            d} y)u9   반환된 항목에 필수 필드가 모두 포함된다.zFull fieldsr    r  t1)r)   r*   r$   rx   z%(py0)s in %(py2)sfieldr   rR   rS   u   필드 누락: 
>assert %(py4)srL   N)r   r   r]   r^   rb   rc   rd   r_   _format_assertmsgr`   ra   )re   r   r   required_fieldsr  rl   @py_format3rj   s           r   )test_add_item_all_required_fields_presentz5TestAddItem.test_add_item_all_required_fields_presenta  s    }}]FdV}T

 % 	<ED=;;;5D;;;;;;5;;;5;;;;;;D;;;D;;;;OE7";;;;;;;	<r   c                 .   t         j                  dd      }|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(   새 항목의 resolved_at은 None이다.New itemr   r,   Nisz%(py1)s is %(py4)srJ   rM   rN   r   r   s           r   !test_add_item_resolved_at_is_nonez-TestAddItem.test_add_item_resolved_at_is_nones  sd    }}Z/M"*d*"d****"d***"***d*******r   c                 .   t         j                  dd      }|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,   새 항목의 resolution_note는 None이다.r  r   r-   Nr  r  rJ   rM   rN   r   r   s           r   %test_add_item_resolution_note_is_nonez1TestAddItem.test_add_item_resolution_note_is_noney  se    }}Z/%&.$.&$....&$...&...$.......r   N)r   r   r   r   r   r   r   r  r  r  r  r  r!  r<   r   r   r   r   /  s4    ((;4+'"<$+/r   r   c                   n    e Zd Z ej                  d      d        Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)TestListItemsT)autousec                     t         j                  dd       t         j                  dd       t         j                  dd       t         j                  dd      }t         j                  |d   d	       y
)u6   각 테스트 전에 다양한 항목을 추가한다.z	High openr    zMedium openr   zLow openr   zHigh to resolver%   u   해결 완료N)r   r   resolve_item)re   r   r   s      r   setup_itemszTestListItems.setup_items  sS     	k6*mX.j%(}}.7$t*o6r   c                 L   t         j                         }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	)
u,   필터 없이 전체 항목을 반환한다.r   rG   r   rP   rf   r   r   r   N)r   
list_itemsrP   r]   r^   rb   rc   rd   r_   r`   ra   )re   r   rf   ri   rn   r   rk   r   s           r   test_list_all_itemsz!TestListItems.test_list_all_items  s    "6{a{a{ass66{ar   c                    t         j                  d      }d |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}t        |      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u<   status='open' 필터 적용 시 open 항목만 반환한다.r!   r(   c              3   ,   K   | ]  }|d    dk(    yw)r(   r!   Nr<   .0r   s     r   	<genexpr>z@TestListItems.test_list_filter_by_status_open.<locals>.<genexpr>  s     ?4>V+?   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allrs   Nr   rG   r   rP   rf   r   r   r   r   r)  r3  rb   rc   r]   rd   r_   r`   ra   rP   r^   re   r   rf   rl   rh   rj   ri   rn   r   rk   r   s              r   test_list_filter_by_status_openz-TestListItems.test_list_filter_by_status_open  s    /???s?????????s???s??????????????6{a{a{ass66{ar   c                    t         j                  d      }d |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}t        |      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)uD   status='resolved' 필터 적용 시 resolved 항목만 반환한다.r1   r,  c              3   ,   K   | ]  }|d    dk(    yw)r(   r1   Nr<   r.  s     r   r0  zDTestListItems.test_list_filter_by_status_resolved.<locals>.<genexpr>  s     CD4>Z/Cr1  r2  r3  rs   Nr/   rG   r   rP   rf   r   r   r   r4  r5  s              r   #test_list_filter_by_status_resolvedz1TestListItems.test_list_filter_by_status_resolved  s    
3CFCCsCCCCCCCCCsCCCsCCCCCCCCCCCCCC6{a{a{ass66{ar   c                    t         j                  d      }t         j                  d      }t         j                  d      }d |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            d	x}}d
 |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            d	x}}d |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            d	x}}y	)uC   severity 필터 적용 시 해당 심각도 항목만 반환한다.r    )r'   r   r   c              3   ,   K   | ]  }|d    dk(    yw)r'   r    Nr<   r.  s     r   r0  z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     F$4
#v-Fr1  r2  r3  rs   Nc              3   ,   K   | ]  }|d    dk(    yw)r'   r   Nr<   r.  s     r   r0  z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     JD4
#x/Jr1  c              3   ,   K   | ]  }|d    dk(    yw)r'   r   Nr<   r.  s     r   r0  z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     D4
#u,Dr1  
r   r)  r3  rb   rc   r]   rd   r_   r`   ra   )re   r   result_highresult_medium
result_lowrl   rh   rj   s           r   test_list_filter_by_severityz*TestListItems.test_list_filter_by_severity  sO   oovo6:__e_4
F+FFsFFFFFFFFFsFFFsFFFFFFFFFFFFFFJMJJsJJJJJJJJJsJJJsJJJJJJJJJJJJJJDDDsDDDDDDDDDsDDDsDDDDDDDDDDDDDDr   c                    t         j                  d      }dddd}|D cg c]
  }||d       }}t        |      }||k(  }|s9t        j                  d|fd	||f      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d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }t        j                  d      dz   d|iz  }	t        t        j                  |	            dx}}yc c}w )uE   open 항목 내에서 high → medium → low 순으로 정렬된다.r!   r,  r   r/   r   )r    r   r   r'   rG   )z0%(py0)s == %(py5)s
{%(py5)s = %(py2)s(%(py3)s)
}
severitiessortedr   u1   심각도 순 정렬이 올바르지 않습니다z
>assert %(py7)srT   N)r   r)  rE  r]   r^   rb   rc   rd   r_   r  r`   ra   )
re   r   rf   severity_orderr   rD  r   rl   r   ro   s
             r   test_list_sorted_by_severityz*TestListItems.test_list_sorted_by_severity  s    /"#q;CIJ4nT*%56J
J#J/dz//dddz/ddddddzdddzddddddVdddVddddddJdddJddd/ddd1ddddddd Ks   Fc                    |dz  dz  }|j                  t        d|       t        j                         }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}}y
)u2   항목이 없으면 빈 리스트를 반환한다.emptyr   r   rG   r   rf   r   r}   r~   N)r   r   r)  r]   r^   rb   rc   rd   r_   r`   ra   )	re   r   r   empty_data_filerf   ri   rl   r   r   s	            r   test_list_emptyzTestListItems.test_list_empty  s    "W,/??D+?"v|vvvr   c           	      `   t         j                         }t        |      D cg c]  \  }}|d   dk(  s| }}}t        |      D cg c]  \  }}|d   dk(  s| }}}|r|rt        |      }t	        |      }||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                  |      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                  |      d
z  }
t        j                  d      dz   d|
iz  }t        t        j                  |            dx}x}	}yyyc c}}w c c}}w )uJ   정렬 결과에서 open 항목이 resolved 항목보다 앞에 나온다.r(   r!   r1   )<)zM%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} < %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
}maxopen_indicesminresolved_indices)rR   rK   r|   r~   rN   r   u?   open 항목이 resolved 항목보다 먼저 나와야 합니다z
>assert %(py10)spy10N)r   r)  	enumeraterN  rP  r]   r^   rb   rc   rd   r_   r  r`   ra   )re   r   rf   r   r   rO  rQ  ri   @py_assert7r   r   @py_format11s               r   #test_list_resolved_items_after_openz1TestListItems.test_list_resolved_items_after_open  s-   ")26):Wgad8nPV>VWW&v.
!T$x.J2NA
 
 ,|$ Qs ( Q$ (  Q Q?P?PQ$ ( Q QJP&Q Q8P8P  Q QGPy  Q QJP&Q Q8P8P $ Q QGPy $ Q QGPy % Q QJP&Q Q8P8P (+ Q QGPy (+ Q QJP&Q Q8P8P !Q QGPy !Q QGPy( Q Q?P?PPQ Q Q=P=PQ Q Q -< X
s   H$H$	H*H*c                    t         j                  dd      }d |D        }t        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)	u<   status와 severity 필터를 동시에 적용할 수 있다.r!   r    )r(   r'   c              3   @   K   | ]  }|d    dk(  xr |d   dk(    yw)r(   r!   r'   r    Nr<   r.  s     r   r0  z;TestListItems.test_list_combined_filters.<locals>.<genexpr>  s0      
HLDNf$Cj)9V)CC
s   r2  r3  rs   Nr>  )re   r   rf   rl   rh   rj   s         r   test_list_combined_filtersz(TestListItems.test_list_combined_filters  s    @
PV
 	
s 
 
 	
 
 	
	6	
 	
   	
 	
 		  	
 	
 		
 	
 	
 		
 	
 	
 	
 	
 	
r   N)r   r   r   pytestfixturer'  r*  r6  r9  rB  rG  rK  rV  rY  r<   r   r   r#  r#    sJ    V^^D!7 "7   EeQ
r   r#  c                   6    e Zd Zd Zd Zd Zd Zd Zd Zd Z	y)	TestResolveItemc                 `   t         j                  dd      }t         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)u8   resolve_item() 후 status가 'resolved'로 변경된다.
To resolver    r%   r(   r1   rG   rI   rJ   rM   rN   Nr   r   r&  r]   r^   r_   r`   ra   	re   r   r   rf   rg   rh   ri   rj   rk   s	            r   test_resolve_sets_statusz(TestResolveItem.test_resolve_sets_status  sx    }}\62""4:.h-:-:----:------:-------r   c                    t         j                  dd      }t         j                  |d         }|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   }	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}}
g }|	d   }d}
||
k(  }|}|r|	d   }d}||k(  }|}|r|	d   }d}||k(  }|}|s_t        j                  d|fd||
f      t        j
                  |      t        j
                  |
      dz  }dd|iz  }|j                  |       |rt        j                  dfdf      t        j
                  |      t        j
                  |      dz  }dd|iz  }|j                  |       |r_t        j                  dfdf      t        j
                  |      t        j
                  |      d z  }d!d"|iz  }|j                  |       t        j                  |d#      i z  }d$d%|iz  }t        t        j                  |            dx}x}x}x}x}
x}x}x}x}x}}y)&uB   resolve_item() 후 resolved_at에 타임스탬프가 설정된다.r_  r    r%   r,   Nis not)z%(py1)s is not %(py4)srJ   rM   rN   r   rG   r   rP   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`   ra   rP   rb   rc   rd   r   r   )re   r   r   rf   rg   rh   ri   rj   rk   r   rn   r   r   rl   r   r   r   r   r   r   r   r   r   r   r   r   s                             r   test_resolve_sets_resolved_atz-TestResolveItem.test_resolve_sets_resolved_at  s   }}\62""4:.m$0D0$D0000$D000$000D0000000M"2w"w"}w"ss22w">r!u>>u|>1>>>B>3>3>>>>u>>>u>>>>>>>>>>>>>>>>>>>>>>>3>>>>>>3>>>>>>>>>>>>>>>r   c                    t         j                  dd      }d}t         j                  |d   |      }|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)u-   resolution_note가 올바르게 저장된다.z	Note testr      리팩토링 완료r%   r-   r-   rG   r  noter{   r}   r~   N)r   r   r&  r]   r^   r_   rb   rc   rd   r`   ra   )	re   r   r   rj  rf   rg   ri   r   r   s	            r   test_resolve_sets_notez&TestResolveItem.test_resolve_sets_note  s    }}[(3$""4:t"D'(0(D0000(D000(000000D000D0000000r   c                 x   t         j                  d      }d}||u }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u=   존재하지 않는 ID로 resolve하면 None을 반환한다.TD-999Nr  z%(py0)s is %(py3)srf   r   r}   r~   )
r   r&  r]   r^   rb   rc   rd   r_   r`   ra   r   s          r   %test_resolve_nonexistent_returns_nonez5TestResolveItem.test_resolve_nonexistent_returns_none  sj    ""8,v~vvvr   c                    t         j                  dd      }t         j                  |d          t         j                  |d         }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}}y)uE   이미 해결된 항목을 다시 resolve하면 None을 반환한다.zAlready resolvedr   r%   Nr  rn  rf   r   r}   r~   )r   r   r&  r]   r^   rb   rc   rd   r_   r`   ra   )re   r   r   rf   ri   rl   r   r   s           r   *test_resolve_already_resolved_returns_nonez:TestResolveItem.test_resolve_already_resolved_returns_none  s    }}/7$t*%""4:.v~vvvr   c                    t         j                  dd      t         j                  d   d       t         j                         }t	        fd|d   D        d      }d}||u}|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}|d   }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(   해결 상태가 파일에 저장된다.zPersist resolver    r%   u   완료ri  c              3   :   K   | ]  }|d    d    k(  s|  yw)r%   Nr<   )r/  r   r   s     r   r0  z8TestResolveItem.test_resolve_persists.<locals>.<genexpr>  s      ?14DJ)>Q?s   r:   Nrd  z%(py0)s is not %(py3)s
saved_itemr   r}   r~   r(   r1   rG   rI   rJ   rM   rN   r-   )r   r   r&  r\   nextr]   r^   rb   rc   rd   r_   r`   ra   )re   r   r   ru  ri   rl   r   r   rg   rh   rj   rk   r   s               @r   test_resolve_persistsz%TestResolveItem.test_resolve_persists  sQ   }}.7$t*h? ~~?W?

 "&%z%%%%z%%%%%%z%%%z%%%%%%%%%%(#1z1#z1111#z111#111z1111111+,88,8888,888,8888888888r   c                 ^   t         j                  dd      }t         j                  |d         }|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)u8   note 없이 resolve하면 resolution_note가 None이다.No note resolver   r%   r-   Nr  r  rJ   rM   rN   r`  ra  s	            r   test_resolve_without_notez)TestResolveItem.test_resolve_without_note  sz    }}.9""4:.'(0D0(D0000(D000(000D0000000r   N)
r   r   r   rb  rf  rk  ro  rq  rw  rz  r<   r   r   r]  r]    s%    .	?191r   r]  c                       e Zd Zd Zd Zd Zy)TestShowItemc                    t         j                  dd      }t         j                  |d         }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}}|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)uN   존재하는 ID로 show_item() 호출 시 항목 딕셔너리를 반환한다.z	Show testr    r%   Nrd  rt  rf   r   r}   r~   rG   rI   rJ   rM   rN   r&   )r   r   	show_itemr]   r^   rb   rc   rd   r_   r`   ra   )re   r   addedrf   ri   rl   r   r   rg   rh   rj   rk   s               r   test_show_existing_itemz$TestShowItem.test_show_existing_item   s   k62d,!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!d|*uT{*|{****|{***|***{*******g-+-+----+------+-------r   c                 x   t         j                  d      }d}||u }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	uF   존재하지 않는 ID로 show_item() 호출 시 None을 반환한다.rm  Nr  rn  rf   r   r}   r~   )
r   r~  r]   r^   rb   rc   rd   r_   r`   ra   r   s          r   "test_show_nonexistent_returns_nonez/TestShowItem.test_show_nonexistent_returns_none)  sh    )v~vvvr   c                    t         j                  dd       t         j                  dd      }t         j                  dd       t         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}}y)u3   여러 항목 중 올바른 항목을 반환한다.r  r    r  r   r  r   r%   r&   rG   rI   rJ   rM   rN   Nr'   )r   r   r~  r]   r^   r_   r`   ra   )	re   r   secondrf   rg   rh   ri   rj   rk   s	            r   test_show_returns_correct_itemz+TestShowItem.test_show_returns_correct_item/  s    gv&x2gu%t-g*(*(****(******(*******j!-X-!X----!X---!---X-------r   N)r   r   r   r  r  r  r<   r   r   r|  r|    s    .	.r   r|  c                   $    e Zd Zd Zd Zd Zd Zy)TestShowStatsc                    t         j                         }g d}|D ]  }||v }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
} y
)uE   show_stats()가 올바른 키를 가진 딕셔너리를 반환한다.)r0   r!   r1   r2   r3   r4   r6   rx   r  keyrf   r  u   통계 키 누락: r  rL   N)r   
show_statsr]   r^   rb   rc   rd   r_   r  r`   ra   )re   r   rf   required_keysr  rl   r  rj   s           r   test_stats_structurez"TestShowStats.test_stats_structureA  s    "
 ! 	>C&====3&======3===3======&===&====$7u"=======	>r   c                    t         j                  dd       t         j                  dd       t         j                  dd       t         j                         }|d   }d}||k(  }|slt        j                  d	|fd
||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|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}}y)u6   항목 추가 후 통계가 올바르게 반영된다.	High itemr    Medium itemr   zLow itemr   r0   r   rG   rI   rJ   rM   rN   Nr!   r1   r   r2   r/   r3   r4   )r   r   r  r]   r^   r_   r`   ra   re   r   r7   rg   rh   ri   rj   rk   s           r   test_stats_after_addz"TestShowStats.test_stats_after_addQ  s   k6*mX.j%(
 !W~""~""""~"""~""""""""""V}!!}!!!!}!!!}!!!!!!!!!!Z %A% A%%%% A%%% %%%A%%%%%%%[!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&]#(q(#q((((#q(((#(((q(((((((Z %A% A%%%% A%%% %%%A%%%%%%%r   c                    t         j                  dd      }t         j                  dd       t         j                  |d          t         j                         }|d   }d}||k(  }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}|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)u6   항목 해결 후 통계가 올바르게 반영된다.r  r    r  r   r%   r0   r   rG   rI   rJ   rM   rN   Nr!   r/   r1   r2   r   r3   )	r   r   r&  r  r]   r^   r_   r`   ra   )	re   r   r   r7   rg   rh   ri   rj   rk   s	            r   test_stats_after_resolvez&TestShowStats.test_stats_after_resolvec  s   }}[&1mX.$t*%!W~""~""""~"""~""""""""""V}!!}!!!!}!!!}!!!!!!!!!!Z %A% A%%%% A%%% %%%A%%%%%%%[!&Q&!Q&&&&!Q&&&!&&&Q&&&&&&&]#(q(#q((((#q(((#(((q(((((((r   c                 ,   t         j                         }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d	   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d
   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u1   데이터가 없을 때 통계는 모두 0이다.r0   r   rG   rI   rJ   rM   rN   Nr!   r1   )r   r  r]   r^   r_   r`   ra   r  s           r   test_stats_empty_dataz#TestShowStats.test_stats_empty_dataq  s     !W~""~""""~"""~""""""""""V}!!}!!!!}!!!}!!!!!!!!!!Z %A% A%%%% A%%% %%%A%%%%%%%r   N)r   r   r   r  r  r  r  r<   r   r   r  r  @  s    > &$)&r   r  c                   $    e Zd Zd Zd Zd Zd Zy)TestPrintItemTablec                 z   dddddg}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}y)u9   항목이 있을 때 테이블 형식으로 출력한다.r   r   r    r!   r   rx   z+%(py1)s in %(py5)s
{%(py5)s = %(py3)s.out
}capturedrK   r|   r~   assert %(py7)srT   Nr   print_item_table
readouterroutr]   r^   r_   rb   rc   rd   r`   ra   )
re   capsysr   r:   r  rg   r   ri   r   ro   s
             r   test_print_with_itemsz(TestPrintItemTable.test_print_with_items  s    $" 	
 	e$$$&'8<<'x<''''x<'''x''''''8'''8'''<'''''''*hll*{l****{l***{******h***h***l*******r   c                    t         j                  g        |j                         }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}y)	uR   항목이 없으면 '등록된 항목이 없습니다.' 메시지를 출력한다.u!   등록된 항목이 없습니다.rx   r  r  r  r  rT   Nr  )re   r  r  rg   r   ri   r   ro   s           r   test_print_emptyz#TestPrintItemTable.test_print_empty  s    b!$$&2BhllB2lBBBB2lBBB2BBBBBBhBBBhBBBlBBBBBBBr   c                    dddddg}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}y)u<   테이블 헤더(ID, 상태, 심각도 등)가 출력된다.r   zHeader testr   r!   r   IDrx   r  r  r  r  rT   Nu   상태u	   심각도r  	re   r  r:   r  rg   r   ri   r   ro   s	            r   test_print_table_headerz*TestPrintItemTable.test_print_table_header  s    &$ 	
 	e$$$&#x||#t|####t|###t######x###x###|#######'8<<'x<''''x<'''x''''''8'''8'''<'''''''*hll*{l****{l***{******h***h***l*******r   c                    ddddddddd	dg}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}y)u%   여러 항목이 모두 출력된다.r   r  r    r!   r   r   r  r   r1   rx   r  r  r  r  rT   Nr  r  s	            r   test_print_multiple_itemsz,TestPrintItemTable.test_print_multiple_items  s     " 	 !!$	
 	e$$$&'8<<'x<''''x<'''x''''''8'''8'''<''''''''8<<'x<''''x<'''x''''''8'''8'''<'''''''&(,,&w,&&&&w,&&&w&&&&&&(&&&(&&&,&&&&&&&'8<<'x<''''x<'''x''''''8'''8'''<'''''''r   N)r   r   r   r  r  r  r  r<   r   r   r  r    s    + C+"(r   r  c                   $    e Zd Zd Zd Zd Zd Zy)TestPrintItemDetailc           
      0   dddddddgdd	d	d
	}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d|fd||f      t	        j                  |      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}x}}y	)u%   항목 상세 정보가 출력된다.r   zDetail testr    r!   u   상세 설명backendrefactorz2026-03-02 10:00:00Nr$   rx   r  r  r  r  rT   r   print_item_detailr  r  r]   r^   r_   rb   rc   rd   r`   ra   	re   r  r   r  rg   r   ri   r   ro   s	            r   test_print_detailz%TestPrintItemDetail.test_print_detail  sm    "*
+/#

 	t$$$&'8<<'x<''''x<'''x''''''8'''8'''<''''''',,},,,,},,,},,,,,,,,,,,,,,,,,,,.(,,.,....,.........(...(...,.......(HLL(yL((((yL(((y((((((H(((H(((L((((((($44$4444$444$4444444444444444444r   c           
         dddddg dddd		}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}d}|j                  }||v }|st	        j
                  d
|fd||f      t	        j                  |      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}x}}y)uC   해결된 항목은 resolved_at과 resolution_note가 출력된다.r   r   r   r1   Nz2026-03-01 09:00:00z2026-03-02 15:00:00rh  r$   rx   r  r  r  r  rT   r  r  s	            r   test_print_resolved_detailz.TestPrintItemDetail.test_print_resolved_detail  s$    $  /04

 	t$$$&$44$4444$444$4444444444444444444$44$4444$444$4444444444444444444r   c           
         dddddg dddd	}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d|fd	||f      t	        j                  |      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}x}}y)u/   설명이 없어도 오류 없이 출력된다.r   zNo descr   r!   N2026-03-02 08:00:00r$   rx   r  r  r  r  rT   r  r  s	            r   %test_print_detail_without_descriptionz9TestPrintItemDetail.test_print_detail_without_description  s     /#

 	t$$$&'8<<'x<''''x<'''x''''''8'''8'''<'''''''r   c           
         dddddg dddd	}t         j                  |       |j                         }d}|j                  }||v }|st	        j
                  d	|fd
||f      t	        j                  |      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}x}}y)u/   태그가 없어도 오류 없이 출력된다.zTD-004r  r   r!   u   설명 있음r  Nr$   rx   r  r  r  r  rT   r  r  s	            r   test_print_detail_without_tagsz2TestPrintItemDetail.test_print_detail_without_tags  s     */#

 	t$$$&'8<<'x<''''x<'''x''''''8'''8'''<'''''''r   N)r   r   r   r  r  r  r  r<   r   r   r  r    s    5,5&(&(r   r  c                   H    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zy)TestCLIMainc                    |j                  t        dg d       t        j                          |j	                         }d}|j
                  }||v }|st        j                  d|fd||f      t        j                  |      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}x}}d}|j
                  }||v }|st        j                  d|fd||f      t        j                  |      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}x}}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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}}y
)uE   add 서브커맨드가 항목을 추가하고 결과를 출력한다.argv)tech-debt.pyadd--titleCLI Add Test
--severityr    r  rx   r  r  r  r  rT   Nr   r:   r/   rG   rO   rP   rQ   rU   rV   r   r&   rI   rJ   rM   rN   )r   sysr   mainr  r  r]   r^   r_   rb   rc   rd   r`   ra   r\   rP   )re   r   r   r  r  rg   r   ri   r   ro   r   rl   rh   rm   rn   rp   rj   rk   s                     r   test_add_commandzTestCLIMain.test_add_command  s   	
 			$$&--~----~---~-------------------'8<<'x<''''x<'''x''''''8'''8'''<''''''' ~~=&s=!&Q&!Q&&&&!Q&&&&&&s&&&s&&&=&&&!&&&Q&&&&&&&G}Q(:N:(N::::(N:::(:::N:::::::r   c                 8   t         j                  dd       |j                  t        dddg       t         j	                          |j                         }d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      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}x}}y)u9   list 서브커맨드가 항목 테이블을 출력한다.zList CLI itemr   r  r  r   rx   r  r  r  r  rT   Nr   r   r   r  r  r  r  r]   r^   r_   rb   rc   rd   r`   ra   
re   r   r   r  r  rg   r   ri   r   ro   s
             r   test_list_commandzTestCLIMain.test_list_command7  s    ox0V$	
 			$$&.(,,.,....,.........(...(...,.......r   c                 "   t         j                  dd       t         j                  dd      }t         j                  |d          |j                  t        dg d       t         j                          |j                         }d}|j                  }||v }|st        j                  d|fd	||f      t        j                  |      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}x}}d}|j                  }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y)u>   list 서브커맨드에 status 필터를 적용할 수 있다.z	Open itemr    r   r   r%   r  )r  r   z--statusr!   rx   r  r  r  r  rT   N)not in)z/%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.out
})r   r   r&  r   r  r  r  r  r]   r^   r_   rb   rc   rd   r`   ra   )re   r   r   r  r   r  rg   r   ri   r   ro   s              r   test_list_command_with_filtersz*TestCLIMain.test_list_command_with_filtersF  s<   k6*}}_e4$t*%8	
 			$$&*hll*{l****{l***{******h***h***l*******2hll2l2222l222222222h222h222l2222222r   c           	      z   t         j                  dd      }|j                  t        ddd|d   ddg       t         j	                          |j                         }|d   }|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}t         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}}y)u9   resolve 서브커맨드가 항목을 해결 처리한다.zResolve CLI testr    r  r  resolver%   z--noteu   완료 처리rx   r  r  r  r  rT   Nr(   r1   rG   rI   rJ   rM   rN   r-   )r   r   r   r  r  r  r  r]   r^   r_   rb   rc   rd   r`   ra   r~  )re   r   r   r  r  r  rg   r   ri   r   ro   r1   rh   rj   rk   s                  r   test_resolve_commandz TestCLIMain.test_resolve_commandY  s   0&9d
	
 			$$&T{*hll*{l****{l***{******h***h***l******* >>%+.!/Z/!Z////!Z///!///Z///////)*=o=*o====*o===*===o=======r   c                    t         j                  ddd      }|j                  t        ddd|d   g       t         j	                          |j                         }d}|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}d}|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}y)u6   show 서브커맨드가 항목 상세를 출력한다.zShow CLI testr   u   상세 내용r  r  r  showr%   rx   r  r  r  r  rT   Nr  )re   r   r   r  r  r  rg   r   ri   r   ro   s              r   test_show_commandzTestCLIMain.test_show_commands  s+   ox_UVU4[1	
 			$$&.(,,.,....,.........(...(...,........(,,.,....,.........(...(...,.......r   c                    t         j                  dd       t         j                  dd       |j                  t        dddg       t         j	                          |j                         }d}|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}d}|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}d}|j                  }||v }|st        j                  d	|fd
||f      t        j                  |      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}x}}y)u0   stats 서브커맨드가 통계를 출력한다.zStats item 1r    zStats item 2r   r  r  r7   u
   총 항목rx   r  r  r  r  rT   Nu	   미해결u	   해결됨r  r  s
             r   test_stats_commandzTestCLIMain.test_stats_command  s   nf-nh/W%	
 			$$&+x||+||++++||+++|++++++x+++x+++|+++++++*hll*{l****{l***{******h***h***l********hll*{l****{l***{******h***h***l*******r   c                    |j                  t        ddg       t        j                  t              5 }t
        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)u@   서브커맨드 없이 실행하면 sys.exit(1)이 발생한다.r  r  Nr/   rG   zG%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.value
}.code
} == %(py7)sexc_inforQ   rU   rV   r   r  rZ  raises
SystemExitr   r  valuecoder]   r^   rb   rc   rd   r_   r`   ra   )
re   r   r   r  rl   rh   rm   rn   ro   rp   s
             r   test_no_command_exitsz!TestCLIMain.test_no_command_exits  s    C.)9:]]:& 	(IIK	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	 	s   D::Ec                    |j                  t        dg d       t        j                  t              5 }t
        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)uF   존재하지 않는 ID로 show 호출 시 sys.exit(1)이 발생한다.r  )r  r  rm  Nr/   rG   r  r  rQ   rU   rV   r  )re   r   r   r  r  rl   rh   rm   rn   ro   rp   s              r   test_show_nonexistent_exitsz'TestCLIMain.test_show_nonexistent_exits  s    .	
 ]]:& 	(IIK	 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	 	s   D;;Ec                    |j                  t        dg d       t        j                          t        j	                         }|d   d   }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }	d
d|	iz  }
t        t        j                  |
            dx}x}}|d   }ddg}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }	d
d|	iz  }
t        t        j                  |
            dx}x}}y)uH   add 서브커맨드에서 태그와 설명이 올바르게 처리된다.r  )r  r  r  zFull Add Testr  r   z--descFull descriptionz--tagsr"   r#   r:   r   r)   r  rG   rI   rJ   rM   rN   Nr*   r"   r#   )
r   r  r   r  r\   r]   r^   r_   r`   ra   )re   r   r   r  r   r   rg   rh   ri   rj   rk   s              r   #test_add_command_with_tags_and_descz/TestCLIMain.test_add_command_with_tags_and_desc  s    	
$ 			~~G}QM"8&88"&88888"&8888"888&88888888F|3
33|33333|3333|33333333333r   c                    t         j                  dd      }|j                  t        ddd|d   g       t         j	                          t         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}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)uF   resolve 서브커맨드에서 note 없이 해결 처리할 수 있다.ry  r   r  r  r  r%   r(   r1   rG   rI   rJ   rM   rN   Nr-   r  r  )r   r   r   r  r  r~  r]   r^   r_   r`   ra   )re   r   r   r  r  r1   rg   rh   ri   rj   rk   s              r   !test_resolve_command_without_notez-TestCLIMain.test_resolve_command_without_note  s    /:Yd4	
 			>>%+.!/Z/!Z////!Z///!///Z///////)*2d2*d2222*d222*222d2222222r   N)r   r   r   r  r  r  r  r  r  r  r  r  r  r<   r   r   r  r    s4    ;4/3&>4/ +$((463r   r  )#__doc__builtinsrb   _pytest.assertion.rewrite	assertionrewriter]   importlib.utilr   rZ   osr  pathlibr   rZ  environget
_WORKSPACEr
   r   r   r[  r   r=   r?   r   r   r   r#  r]  r|  r  r  r  r  r<   r   r   <module>r     s  *     	 
   "**..!13HIJ
22 ~    J$4 $4X[B [BF5" 5"zN/ N/lM
 M
j=1 =1J. .B8& 8&@?( ?(NM( M(jx3 x3r   