
    (<i                        d Z ddlZddlZddlZddlZddlmZ ddl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     E/home/jay/workspace/.worktrees/task-2057-dev2/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(  sJ t        |d	         d
k(  sJ |d	   d   d   dk(  sJ y)u2   기존 파일이 있으면 내용을 로드한다.T)parentsexist_okF)ensure_asciiutf-8encodingr9   r   r:   r/   r   r%   r   N)parentmkdir
write_textjsondumpsr   	load_datalen)selfr   r=   results       r   test_load_existing_dataz$TestLoadData.test_load_existing_datad   s    td;JJ{7' 	 	
 !i E)))6'?#q(((gq!$'8333r   c                     |j                         rJ t        j                         }t        |t              sJ d|v sJ y)u2   파일이 없으면 기본 구조를 반환한다.r9   N)existsr   rL   
isinstancedictrN   r   rO   s      r   test_load_missing_filez#TestLoadData.test_load_missing_fileq   s>    ##%%%!&$'''F"""r   c                 v    t         j                         }d|v sJ t        |d   t              sJ |d   g k(  sJ y)u<   기본 구조에 items 키가 있고 리스트여야 한다.r:   N)r   rL   rS   listrU   s      r   test_default_has_items_listz(TestLoadData.test_default_has_items_listz   sB    !&   &/4000g"$$$r   c                 b    t         j                         }d|v sJ t        |d   t              sJ y)u2   기본 구조에 metadata 키가 있어야 한다.r;   N)r   rL   rS   rT   rU   s      r   test_default_has_metadataz&TestLoadData.test_default_has_metadata   s1    !V###&,d333r   N)__name__
__module____qualname__rP   rV   rY   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                 z    t         j                         }t         j                  |       |j                         sJ y)u3   save_data()를 호출하면 파일이 생성된다.N)r   rL   	save_datarR   rN   r   datas      r   test_save_creates_filez#TestSaveData.test_save_creates_file   s,    ~~t!!!r   c                     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(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ 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         r/   r2   r3   r4   r   N)r   rb   )rN   r   rd   r7   s       r   test_save_updates_statsz$TestSaveData.test_save_updates_stats   s      #- &$	 #/ ($	 #, %(	( %1"E-
2 	tZ )W~"""V}!!!Z A%%%[!Q&&&]#q(((Z A%%%r   c                     t         j                         }d}||d   d<   t         j                  |       |d   d   |k7  sJ |d   d   }t        |      dk(  sJ |d   dk(  r|d   dk(  r|d   d	k(  sJ y
)uA   save_data() 후 last_updated가 현재 시각으로 갱신된다.z1970-01-01 00:00:00r;   r6         -   
    N)r   rL   rb   rM   )rN   r   rd   old_timestamptss        r   test_save_updates_last_updatedz+TestSaveData.test_save_updates_last_updated   s    ~~-+8Z(tJ/=@@@*n-2w"}}!u|1B3>>r   c                     |dz  dz  dz  dz  }|j                  t        d|       dg ddd	d
}t        j                  |       |j                         sJ y)u;   부모 디렉토리가 없어도 자동으로 생성한다.deepnesteddirr   r   r   r.   rm   rn   r8   N)r   r   rb   rR   )rN   r   r   nested_pathrd   s        r   test_save_creates_parent_dirsz*TestSaveData.test_save_creates_parent_dirs   se    '(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	       t        d         d
k(  sJ |d   d   d   dk(  sJ y	# 1 sw Y   -xY w)u;   저장된 데이터가 실제로 디스크에 반영된다.r:   r   zDisk persist testrl   r!   rg   rD   rE   Nr/   r   r&   )r   rL   appendrb   r!   rJ   loadrM   )rN   r   rd   floadeds        r   test_save_persists_to_diskz'TestSaveData.test_save_persists_to_disk   s    ~~W,! 		
 	t )g. 	"!YYq\F	" 6'?#q(((gq!'*.AAAA		" 	"s   BBN)r\   r]   r^   re   rq   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                 :    t         j                         }|dk(  sJ y)u1   items가 비어 있으면 TD-001을 반환한다.r   N)r   generate_idrU   s      r   test_first_id_is_td_001z&TestGenerateId.test_first_id_is_td_001   s    !!#!!!r   c                     t         j                         }|d   j                  ddddd       t         j                  |       t         j	                         }|dk(  sJ y)	u+   TD-001이 있으면 TD-002를 반환한다.r:   r   Existingrl   r!   rg   rh   Nr   rL   r   rb   r   )rN   r   rd   rO   s       r   test_increments_from_existingz,TestGenerateId.test_increments_from_existing   sV    ~~WjevV	
 	t!!#!!!r   c                     t         j                         }dD ]"  }|d   j                  d|dd| ddd       $ t         j                  |       t         j	                         }|d	k(  sJ y
)u2   여러 항목 중 최대 번호+1을 반환한다.)r/   ro   rv   r:   TD-03dItem rl   r!   rg   zTD-008Nr   rN   r   rd   irO   s        r   test_finds_max_idz TestGenerateId.test_finds_max_id  sx    ~~ 	AM  #w-$QC[ %$		 	t!!#!!!r   c                     t         j                         }dD ]"  }|d   j                  d|dd| ddd       $ t         j                  |       t         j	                         }|d	k(  sJ y
)uJ   TD-001, TD-005가 있으면 TD-006을 반환한다 (갭 무시, 최대+1).)r/      r:   r   r   r   rl   r!   rg   zTD-006Nr   r   s        r   test_handles_gap_in_idsz&TestGenerateId.test_handles_gap_in_ids  sx    ~~ 	AM  #w-$QC[ %$		 	t!!#!!!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                 T    t         j                  dd      }t        |t              sJ y)u+   add_item()은 딕셔너리를 반환한다.	Test itemr    N)r   add_itemrS   rT   rU   s      r   test_add_item_returns_dictz&TestAddItem.test_add_item_returns_dict0  s"    {F3&$'''r   c                 D    t         j                  dd      }|d   dk(  sJ y)u2   새로 추가된 항목의 status는 'open'이다.zStatus testri   r(   r!   Nr   r   rN   r   items      r   test_add_item_status_openz%TestAddItem.test_add_item_status_open6  s$    }}]H5H~'''r   c                     t         j                  dd       t         j                         }t        |d         dk(  sJ |d   d   d   dk(  sJ y)u+   추가된 항목이 파일에 저장된다.zPersist testrl   r:   r/   r   r&   N)r   r   rL   rM   rc   s      r   test_add_item_persistedz#TestAddItem.test_add_item_persisted<  sO    ne,~~4=!Q&&&G}Q(N:::r   c                 P    t         j                  ddddg      }|d   ddgk(  sJ y)u$   태그가 올바르게 저장된다.zTagged itemr    r"   r#   )r*   r*   Nr   r   s      r   test_add_item_with_tagsz#TestAddItem.test_add_item_with_tagsD  s2    }}]F&*9M}NF|
3333r   c                 L    d}t         j                  dd|      }|d   |k(  sJ y)u$   설명이 올바르게 저장된다.u   JWT 토큰 검증 로직 중복z	Desc testri   r)   r)   Nr   )rN   r   descr   s       r   test_add_item_with_descriptionz*TestAddItem.test_add_item_with_descriptionJ  s.    0}}[(}EM"d***r   c                     t         j                  dd      }t         j                  dd      }t         j                  dd      }|d   dk(  sJ |d   d	k(  sJ |d   d
k(  sJ y)u8   여러 번 추가하면 ID가 순서대로 증가한다.Firstr    Secondrl   Thirdri   r%   r   rh   rj   Nr   )rN   r   item1item2item3s        r   test_add_item_increments_idz'TestAddItem.test_add_item_increments_idQ  sh    gv.h.gx0T{h&&&T{h&&&T{h&&&r   c                 D    t         j                  dd      }|d   g k(  sJ y)u?   태그를 지정하지 않으면 빈 리스트가 저장된다.No tagsrl   r*   Nr   r   s      r    test_add_item_default_tags_emptyz,TestAddItem.test_add_item_default_tags_empty[  s$    }}Y.F|r!!!r   c                 j    t         j                  ddddg      }g d}|D ]  }||v rJ d|         y)	u9   반환된 항목에 필수 필드가 모두 포함된다.zFull fieldsr    r   t1)r)   r*   r$   u   필드 누락: Nr   )rN   r   r   required_fieldsfields        r   )test_add_item_all_required_fields_presentz5TestAddItem.test_add_item_all_required_fields_presenta  sJ    }}]FdV}T

 % 	<ED=;OE7";;=	<r   c                 >    t         j                  dd      }|d   J y)u(   새 항목의 resolved_at은 None이다.New itemrl   r,   Nr   r   s      r   !test_add_item_resolved_at_is_nonez-TestAddItem.test_add_item_resolved_at_is_nones  s#    }}Z/M"***r   c                 >    t         j                  dd      }|d   J y)u,   새 항목의 resolution_note는 None이다.r   rl   r-   Nr   r   s      r   %test_add_item_resolution_note_is_nonez1TestAddItem.test_add_item_resolution_note_is_noney  s$    }}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 openri   zLow openrl   zHigh to resolver%   u   해결 완료Nr   r   resolve_itemr   s      r   setup_itemszTestListItems.setup_items  sS     	k6*mX.j%(}}.7$t*o6r   c                 L    t         j                         }t        |      dk(  sJ y)u,   필터 없이 전체 항목을 반환한다.rt   N)r   
list_itemsrM   rU   s      r   test_list_all_itemsz!TestListItems.test_list_all_items  s     "6{ar   c                 x    t         j                  d      }t        d |D              sJ t        |      dk(  sJ 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+?   ro   Nr   r   allrM   rU   s      r   test_list_filter_by_status_openz-TestListItems.test_list_filter_by_status_open  s8    /?????6{ar   c                 x    t         j                  d      }t        d |D              sJ t        |      dk(  sJ y)uD   status='resolved' 필터 적용 시 resolved 항목만 반환한다.r1   r   c              3   ,   K   | ]  }|d    dk(    yw)r(   r1   Nr<   r   s     r   r   zDTestListItems.test_list_filter_by_status_resolved.<locals>.<genexpr>  s     CD4>Z/Cr   r/   Nr   rU   s      r   #test_list_filter_by_status_resolvedz1TestListItems.test_list_filter_by_status_resolved  s8    
3CFCCCC6{ar   c                     t         j                  d      }t         j                  d      }t         j                  d      }t        d |D              sJ t        d |D              sJ t        d |D              sJ y)	uC   severity 필터 적용 시 해당 심각도 항목만 반환한다.r    )r'   ri   rl   c              3   ,   K   | ]  }|d    dk(    yw)r'   r    Nr<   r   s     r   r   z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     F$4
#v-Fr   c              3   ,   K   | ]  }|d    dk(    yw)r'   ri   Nr<   r   s     r   r   z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     JD4
#x/Jr   c              3   ,   K   | ]  }|d    dk(    yw)r'   rl   Nr<   r   s     r   r   z=TestListItems.test_list_filter_by_severity.<locals>.<genexpr>  s     D4
#u,Dr   Nr   r   r   )rN   r   result_highresult_medium
result_lows        r   test_list_filter_by_severityz*TestListItems.test_list_filter_by_severity  sn    oovo6:__e_4
F+FFFFJMJJJJDDDDDr   c                     t         j                  d      }dddd}|D cg c]
  }||d       }}|t        |      k(  sJ d       y	c c}w )
uE   open 항목 내에서 high → medium → low 순으로 정렬된다.r!   r   r   r/   rp   )r    ri   rl   r'   u1   심각도 순 정렬이 올바르지 않습니다N)r   r   sorted)rN   r   rO   severity_orderr   
severitiess         r   test_list_sorted_by_severityz*TestListItems.test_list_sorted_by_severity  sZ    /"#q;CIJ4nT*%56J
JVJ//d1dd/ Ks   Ac                 x    |dz  dz  }|j                  t        d|       t        j                         }|g k(  sJ y)u2   항목이 없으면 빈 리스트를 반환한다.emptyr   r   N)r   r   r   )rN   r   r   empty_data_filerO   s        r   test_list_emptyzTestListItems.test_list_empty  s<    "W,/??D+?"||r   c                 $   t         j                         }t        |      D cg c]  \  }}|d   dk(  s| }}}t        |      D cg c]  \  }}|d   dk(  s| }}}|r"|rt        |      t	        |      k  sJ d       yyyc c}}w c c}}w )uJ   정렬 결과에서 open 항목이 resolved 항목보다 앞에 나온다.r(   r!   r1   u?   open 항목이 resolved 항목보다 먼저 나와야 합니다N)r   r   	enumeratemaxmin)rN   r   rO   r   r   open_indicesresolved_indicess          r   #test_list_resolved_items_after_openz1TestListItems.test_list_resolved_items_after_open  s    ")26):Wgad8nPV>VWW&v.
!T$x.J2NA
 
 ,|$s (  QPQ  -< X
s   BB	BBc                 Z    t         j                  dd      }t        d |D              sJ 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   r   z;TestListItems.test_list_combined_filters.<locals>.<genexpr>  s0      
HLDNf$Cj)9V)CC
s   Nr   rU   s      r   test_list_combined_filtersz(TestListItems.test_list_combined_filters  s3    @ 
PV
 
 	
 
r   N)r\   r]   r^   pytestfixturer   r   r   r   r   r   r   r   r   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    t         j                  dd      }t         j                  |d         }|d   dk(  sJ y)u8   resolve_item() 후 status가 'resolved'로 변경된다.
To resolver    r%   r(   r1   Nr   rN   r   r   rO   s       r   test_resolve_sets_statusz(TestResolveItem.test_resolve_sets_status  s9    }}\62""4:.h:---r   c                     t         j                  dd      }t         j                  |d         }|d   J |d   }t        |      dk(  sJ |d   dk(  r|d	   dk(  r|d
   dk(  sJ y)uB   resolve_item() 후 resolved_at에 타임스탬프가 설정된다.r   r    r%   r,   Nrs   rt   ru   rv   rw   rx   )r   r   r   rM   )rN   r   r   rO   rz   s        r   test_resolve_sets_resolved_atz-TestResolveItem.test_resolve_sets_resolved_at  sx    }}\62""4:.m$000M"2w"}}!u|1B3>>r   c                 |    t         j                  dd      }d}t         j                  |d   |      }|d   |k(  sJ y)u-   resolution_note가 올바르게 저장된다.z	Note testri      리팩토링 완료r%   r-   r-   Nr   )rN   r   r   noterO   s        r   test_resolve_sets_notez&TestResolveItem.test_resolve_sets_note  sD    }}[(3$""4:t"D'(D000r   c                 6    t         j                  d      }|J y)u=   존재하지 않는 ID로 resolve하면 None을 반환한다.TD-999N)r   r   rU   s      r   %test_resolve_nonexistent_returns_nonez5TestResolveItem.test_resolve_nonexistent_returns_none  s    ""8,~~r   c                     t         j                  dd      }t         j                  |d          t         j                  |d         }|J y)uE   이미 해결된 항목을 다시 resolve하면 None을 반환한다.zAlready resolvedrl   r%   Nr   r   s       r   *test_resolve_already_resolved_returns_nonez:TestResolveItem.test_resolve_already_resolved_returns_none  sC    }}/7$t*%""4:.~~r   c                     t         j                  dd      t         j                  d   d       t         j                         }t	        fd|d   D        d      }|J |d	   d
k(  sJ |d   dk(  sJ y)u(   해결 상태가 파일에 저장된다.zPersist resolver    r%   u   완료r  c              3   :   K   | ]  }|d    d    k(  s|  yw)r%   Nr<   )r   r   r   s     r   r   z8TestResolveItem.test_resolve_persists.<locals>.<genexpr>  s      ?14DJ)>Q?s   r:   Nr(   r1   r-   )r   r   r   rL   next)rN   r   rd   
saved_itemr   s       @r   test_resolve_persistsz%TestResolveItem.test_resolve_persists  s    }}.7$t*h? ~~?W?

 %%%(#z111+,888r   c                 n    t         j                  dd      }t         j                  |d         }|d   J y)u8   note 없이 resolve하면 resolution_note가 None이다.No note resolveri   r%   r-   Nr   r   s       r   test_resolve_without_notez)TestResolveItem.test_resolve_without_note  s9    }}.9""4:.'(000r   N)
r\   r]   r^   r   r   r  r  r	  r  r  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         }|J |d   |d   k(  sJ |d   dk(  sJ y)uN   존재하는 ID로 show_item() 호출 시 항목 딕셔너리를 반환한다.z	Show testr    r%   Nr&   r   r   	show_item)rN   r   addedrO   s       r   test_show_existing_itemz$TestShowItem.test_show_existing_item   sX    k62d,!!!d|uT{***g+---r   c                 6    t         j                  d      }|J y)uF   존재하지 않는 ID로 show_item() 호출 시 None을 반환한다.r  N)r   r  rU   s      r   "test_show_nonexistent_returns_nonez/TestShowItem.test_show_nonexistent_returns_none)  s    )~~r   c                     t         j                  dd       t         j                  dd      }t         j                  dd       t         j                  |d         }|d   dk(  sJ |d	   dk(  sJ y
)u3   여러 항목 중 올바른 항목을 반환한다.r   r    r   ri   r   rl   r%   r&   r'   Nr  )rN   r   secondrO   s       r   test_show_returns_correct_itemz+TestShowItem.test_show_returns_correct_item/  se    gv&x2gu%t-g(***j!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 rJ d|         y)uE   show_stats()가 올바른 키를 가진 딕셔너리를 반환한다.)r0   r!   r1   r2   r3   r4   r6   u   통계 키 누락: Nr   
show_stats)rN   r   rO   required_keyskeys        r   test_stats_structurez"TestShowStats.test_stats_structureA  s?    "
 ! 	>C&==$7u"===	>r   c                 (   t         j                  dd       t         j                  dd       t         j                  dd       t         j                         }|d   dk(  sJ |d	   dk(  sJ |d
   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y)u6   항목 추가 후 통계가 올바르게 반영된다.	High itemr    Medium itemri   zLow itemrl   r0   ro   r!   r1   r   r2   r/   r3   r4   N)r   r   r"  rN   r   r7   s      r   test_stats_after_addz"TestShowStats.test_stats_after_addQ  s    k6*mX.j%(
 !W~"""V}!!!Z A%%%[!Q&&&]#q(((Z A%%%r   c                    t         j                  dd      }t         j                  dd       t         j                  |d          t         j                         }|d   dk(  sJ |d   d	k(  sJ |d
   d	k(  sJ |d   dk(  sJ |d   d	k(  sJ y)u6   항목 해결 후 통계가 올바르게 반영된다.r'  r    r(  ri   r%   r0   rp   r!   r/   r1   r2   r   r3   N)r   r   r   r"  )rN   r   r   r7   s       r   test_stats_after_resolvez&TestShowStats.test_stats_after_resolvec  s    }}[&1mX.$t*%!W~"""V}!!!Z A%%%[!Q&&&]#q(((r   c                 h    t         j                         }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y)u1   데이터가 없을 때 통계는 모두 0이다.r0   r   r!   r1   Nr!  r)  s      r   test_stats_empty_dataz#TestShowStats.test_stats_empty_dataq  sG     !W~"""V}!!!Z 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                     dddddg}t         j                  |       |j                         }d|j                  v sJ d|j                  v sJ y)u9   항목이 있을 때 테이블 형식으로 출력한다.r   r   r    r!   rg   Nr   print_item_table
readouterrout)rN   capsysr   r:   captureds        r   test_print_with_itemsz(TestPrintItemTable.test_print_with_items  s^     $" 	
 	e$$$&8<<'''hll***r   c                 n    t         j                  g        |j                         }d|j                  v sJ y)uR   항목이 없으면 '등록된 항목이 없습니다.' 메시지를 출력한다.u!   등록된 항목이 없습니다.Nr2  )rN   r6  r7  s      r   test_print_emptyz#TestPrintItemTable.test_print_empty  s0    b!$$&2hllBBBr   c                     dddddg}t         j                  |       |j                         }d|j                  v sJ d|j                  v sJ d|j                  v sJ y	)
u<   테이블 헤더(ID, 상태, 심각도 등)가 출력된다.r   zHeader testri   r!   rg   IDu   상태u	   심각도Nr2  rN   r6  r:   r7  s       r   test_print_table_headerz*TestPrintItemTable.test_print_table_header  sp     &$ 	
 	e$$$&x||###8<<'''hll***r   c                     ddddddddd	dg}t         j                  |       |j                         }d|j                  v sJ d|j                  v sJ d|j                  v sJ d|j                  v sJ y
)u%   여러 항목이 모두 출력된다.r   r   r    r!   rg   rh   r   rl   r1   Nr2  r=  s       r   test_print_multiple_itemsz,TestPrintItemTable.test_print_multiple_items  s      " 	 !!$	
 	e$$$&8<<'''8<<'''(,,&&&8<<'''r   N)r\   r]   r^   r8  r:  r>  r@  r<   r   r   r0  r0    s    + C+"(r   r0  c                   $    e Zd Zd Zd Zd Zd Zy)TestPrintItemDetailc           
      
   dddddddgdd	d	d
	}t         j                  |       |j                         }d|j                  v sJ d|j                  v sJ d|j                  v sJ d|j                  v sJ d|j                  v sJ y	)u%   항목 상세 정보가 출력된다.r   zDetail testr    r!   u   상세 설명backendrefactorz2026-03-02 10:00:00Nr$   r   print_item_detailr4  r5  rN   r6  r   r7  s       r   test_print_detailz%TestPrintItemDetail.test_print_detail  s     "*
+/#

 	t$$$&8<<''',,,(,,...HLL((($444r   c           
          dddddg dddd		}t         j                  |       |j                         }d|j                  v sJ d|j                  v sJ y)
uC   해결된 항목은 resolved_at과 resolution_note가 출력된다.rh   rk   ri   r1   Nz2026-03-01 09:00:00z2026-03-02 15:00:00r  r$   rF  rH  s       r   test_print_resolved_detailz.TestPrintItemDetail.test_print_resolved_detail  sh     $  /04

 	t$$$&$444$444r   c           
          dddddg dddd	}t         j                  |       |j                         }d|j                  v sJ y)u/   설명이 없어도 오류 없이 출력된다.rj   zNo descrl   r!   N2026-03-02 08:00:00r$   rF  rH  s       r   %test_print_detail_without_descriptionz9TestPrintItemDetail.test_print_detail_without_description  sV     /#

 	t$$$&8<<'''r   c           
          dddddg dddd	}t         j                  |       |j                         }d|j                  v sJ y)	u/   태그가 없어도 오류 없이 출력된다.zTD-004r   rl   r!   u   설명 있음rM  Nr$   rF  rH  s       r   test_print_detail_without_tagsz2TestPrintItemDetail.test_print_detail_without_tags  sV     */#

 	t$$$&8<<'''r   N)r\   r]   r^   rI  rK  rN  rP  r<   r   r   rB  rB    s    5,5&(&(r   rB  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 sJ d|j
                  v sJ t        j                         }t        |d         dk(  sJ |d   d   d   dk(  sJ y	)
uE   add 서브커맨드가 항목을 추가하고 결과를 출력한다.argv)tech-debt.pyadd--titleCLI Add Test
--severityr    rX  r   r:   r/   r   r&   N)r   sysr   mainr4  r5  rL   rM   )rN   r   r   r6  r7  rd   s         r   test_add_commandzTestCLIMain.test_add_command  s    	
 			$$&---8<<''' ~~4=!Q&&&G}Q(N:::r   c                     t         j                  dd       |j                  t        dddg       t         j	                          |j                         }d|j                  v sJ y)u9   list 서브커맨드가 항목 테이블을 출력한다.zList CLI itemri   rT  rU  rX   Nr   r   r   rZ  r[  r4  r5  rN   r   r   r6  r7  s        r   test_list_commandzTestCLIMain.test_list_command7  sW    ox0V$	
 			$$&(,,...r   c                 F   t         j                  dd       t         j                  dd      }t         j                  |d          |j                  t        dg d       t         j                          |j                         }d|j                  v sJ d|j                  vsJ y)	u>   list 서브커맨드에 status 필터를 적용할 수 있다.z	Open itemr    rk   rl   r%   rT  )rU  rX   z--statusr!   N)r   r   r   r   rZ  r[  r4  r5  )rN   r   r   r6  r   r7  s         r   test_list_command_with_filtersz*TestCLIMain.test_list_command_with_filtersF  s    k6*}}_e4$t*%8	
 			$$&hll***hll222r   c           	      4   t         j                  dd      }|j                  t        ddd|d   ddg       t         j	                          |j                         }|d   |j                  v sJ t         j                  |d         }|d	   d
k(  sJ |d   dk(  sJ y)u9   resolve 서브커맨드가 항목을 해결 처리한다.zResolve CLI testr    rT  rU  resolver%   z--noteu   완료 처리r(   r1   r-   N)r   r   r   rZ  r[  r4  r5  r  )rN   r   r   r6  r  r7  r1   s          r   test_resolve_commandz TestCLIMain.test_resolve_commandY  s    0&9d
	
 			$$&T{hll*** >>%+.!Z///)*o===r   c                     t         j                  ddd      }|j                  t        ddd|d   g       t         j	                          |j                         }d|j                  v sJ d|j                  v sJ y	)
u6   show 서브커맨드가 항목 상세를 출력한다.zShow CLI testri   u   상세 내용r   rT  rU  showr%   Nr^  )rN   r   r   r6  r  r7  s         r   test_show_commandzTestCLIMain.test_show_commands  su    ox_UVU4[1	
 			$$&(,,...(,,...r   c                 6   t         j                  dd       t         j                  dd       |j                  t        dddg       t         j	                          |j                         }d|j                  v sJ d	|j                  v sJ d
|j                  v sJ y)u0   stats 서브커맨드가 통계를 출력한다.zStats item 1r    zStats item 2ri   rT  rU  r7   u
   총 항목u	   미해결u	   해결됨Nr^  r_  s        r   test_stats_commandzTestCLIMain.test_stats_command  s    nf-nh/W%	
 			$$&x||+++hll***hll***r   c                     |j                  t        ddg       t        j                  t              5 }t
        j                          ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)u@   서브커맨드 없이 실행하면 sys.exit(1)이 발생한다.rT  rU  Nr/   	r   rZ  r   raises
SystemExitr   r[  valuecode)rN   r   r   exc_infos       r   test_no_command_exitsz!TestCLIMain.test_no_command_exits  s]    C.)9:]]:& 	(IIK	 ~~""a'''	 	s   A++A4c                     |j                  t        dg d       t        j                  t              5 }t
        j                          ddd       j                  j                  dk(  sJ y# 1 sw Y   %xY w)uF   존재하지 않는 ID로 show 호출 시 sys.exit(1)이 발생한다.rT  )rU  rg  r  Nr/   rl  )rN   r   r   r6  rq  s        r   test_show_nonexistent_exitsz'TestCLIMain.test_show_nonexistent_exits  sa    .	
 ]]:& 	(IIK	 ~~""a'''	 	s   A,,A5c                     |j                  t        dg d       t        j                          t        j	                         }|d   d   }|d   dk(  sJ |d   dd	gk(  sJ y
)uH   add 서브커맨드에서 태그와 설명이 올바르게 처리된다.rT  )rU  rV  rW  zFull Add TestrY  rl   z--descFull descriptionz--tagsr"   r#   r:   r   r)   rv  r*   r"   r#   N)r   rZ  r   r[  rL   )rN   r   r   r6  rd   r   s         r   #test_add_command_with_tags_and_descz/TestCLIMain.test_add_command_with_tags_and_desc  sk    	
$ 			~~G}QM"&8888F|
3333r   c                     t         j                  dd      }|j                  t        ddd|d   g       t         j	                          t         j                  |d         }|d   dk(  sJ |d	   J y
)uF   resolve 서브커맨드에서 note 없이 해결 처리할 수 있다.r  ri   rT  rU  rd  r%   r(   r1   r-   N)r   r   r   rZ  r[  r  )rN   r   r   r6  r  r1   s         r   !test_resolve_command_without_notez-TestCLIMain.test_resolve_command_without_note  sw    /:Yd4	
 			>>%+.!Z///)*222r   N)r\   r]   r^   r\  r`  rb  re  rh  rj  rr  rt  rw  ry  r<   r   r   rR  rR    s4    ;4/3&>4/ +$((463r   rR  )__doc__importlib.utilr   rJ   osrZ  pathlibr   r   environget
_WORKSPACEr
   r   r   r   r   r=   r?   r`   r   r   r   r   r  r  r0  rB  rR  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   