
    Rit              	          d Z ddlZddlZddlZddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlZej                  j                  d e ee      j                   j                                ddlZd!deded	ed
efdZd!ded	ed
efdZ G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d       Zy)"u  Tests for file-cleanup.py - TDD approach (RED phase first).

파일 자동 정리 스크립트의 4가지 정리 카테고리, 실행 모드, 안전장치를
테스트합니다. 모든 테스트는 tmp_path fixture를 사용하여 실제 시스템
파일을 건드리지 않습니다.
    N)datetime)Path)Any)patchpathdayscontentreturnc                     | j                   j                  dd       | j                  |       t        j                         |dz  dz  z
  }t	        j
                  | ||f       | S )u?   파일을 생성하고 mtime을 days일 전으로 설정한다.Tparentsexist_ok   i  )parentmkdir
write_texttimeosutime)r   r   r	   old_times       P/home/jay/workspace/.worktrees/task-2117-dev1/scripts/tests/test_file_cleanup.py_make_old_filer      sV    KKdT2OOGyy{dRi$./HHHTHh'(K    c                 b    | j                   j                  dd       | j                  |       | S )u,   최근 파일을 생성한다 (mtime = now).Tr   )r   r   r   )r   r	   s     r   _make_recent_filer   %   s*    KKdT2OOGKr   c                       e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZdeddfdZdeddfdZdeddfdZdeddfdZy)TestCokacDirCleanupuK   cokacdir 업로드 파일 정리 테스트 (30일 초과 미디어 파일).tmp_pathr
   Nc                     |dz  dz  dz  }t        |dz  d      }t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        |      |v sJ yc c}w )	u@   31일 된 PDF 파일은 삭제 후보로 감지되어야 한다.	.cokacdir	workspaceproj1z
report.pdf   r   r   Nr   fc find_cokacdir_cleanup_candidatesstr)selfr   cokac_wsold_pdf
candidatescpathss          r   test_old_pdf_is_candidatez-TestCokacDirCleanup.test_old_pdf_is_candidate4   sp    k)K7'A L!8rB88K9OR]9]^
$./q6//7|u$$$ 0   Ac                     |dz  dz  dz  }t        |dz  d      }t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        |      |v sJ yc c}w )	u@   31일 된 PNG 파일은 삭제 후보로 감지되어야 한다.r    r!   r"   z	image.png#   r$   r   Nr%   )r)   r   r*   old_pngr,   r-   r.   s          r   test_old_png_is_candidatez-TestCokacDirCleanup.test_old_png_is_candidate>   sp    k)K7'A K!7bA88K9OR]9]^
$./q6//7|u$$$ 0r0   c                     |dz  dz  dz  }t        |dz  d      }t        |dz  d      }t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        |      |v sJ t        |      |v sJ y	c c}w )
uE   31일 된 JPG/JPEG 파일은 삭제 후보로 감지되어야 한다.r    r!   r"   z	photo.jpg(   r$   z	scan.jpegr   Nr%   )r)   r   r*   old_jpgold_jpegr,   r-   r.   s           r   $test_old_jpg_and_jpeg_are_candidatesz8TestCokacDirCleanup.test_old_jpg_and_jpeg_are_candidatesH   s    k)K7'A K!7bA!(["8rB88K9OR]9]^
$./q6//7|u$$$8}%%% 0s   A8c                     |dz  dz  dz  }t        |dz        }t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uF   30일 이내 미디어 파일은 삭제 후보가 아니어야 한다.r    r!   r"   z
recent.pdfr   N)r   r&   r'   r(   )r)   r   r*   
recent_pdfr,   r-   r.   s          r   "test_recent_media_is_not_candidatez6TestCokacDirCleanup.test_recent_media_is_not_candidateT   sn    k)K7'A&x,'>?
88K9OR]9]^
$./q6//:e+++ 0s   Ac                     |dz  dz  dz  }t        |dz  d      }t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )	uR   정확히 30일 된 파일은 삭제 후보가 아니다 (30일 초과만 대상).r    r!   r"   z
border.pdf   r$   r   Nr%   )r)   r   r*   
border_pdfr,   r-   r.   s          r   "test_exactly_30_days_not_candidatez6TestCokacDirCleanup.test_exactly_30_days_not_candidate^   sp    k)K7'A#H|$;"E
88K9OR]9]^
$./q6//:e+++ 0r0   c                     |dz  dz  dz  }t        |dz  d       t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        d |D              rJ y	c c}w )
uB   CLAUDE.md는 오래되어도 삭제 후보가 아니어야 한다.r    r!   r"   	CLAUDE.md<   r$   r   c              3   $   K   | ]  }d |v  
 yw)rB   N .0ps     r   	<genexpr>zBTestCokacDirCleanup.test_claude_md_is_protected.<locals>.<genexpr>p   s     7A{a'7   Nr   r&   r'   anyr)   r   r*   r,   r-   r.   s         r   test_claude_md_is_protectedz/TestCokacDirCleanup.test_claude_md_is_protectedh   ss    k)K7'Ax+-B788K9OR]9]^
$./q6//777777 0   Ac                     |dz  dz  dz  }t        |dz  d       t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        d |D              rJ y	c c}w )
uD   *.py 파일은 오래되어도 삭제 후보가 아니어야 한다.r    r!   r"   z	helper.pyrC   r$   r   c              3   $   K   | ]  }d |v  
 yw)z.pyNrE   rF   s     r   rI   zDTestCokacDirCleanup.test_py_scripts_are_protected.<locals>.<genexpr>z        1auz1rJ   NrK   rM   s         r   test_py_scripts_are_protectedz1TestCokacDirCleanup.test_py_scripts_are_protectedr   ss    k)K7'Ax+-B788K9OR]9]^
$./q6//1511111 0rO   c                     |dz  dz  dz  }t        |dz  d       t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        d |D              rJ y	c c}w )
uD   *.sh 파일은 오래되어도 삭제 후보가 아니어야 한다.r    r!   r"   zsetup.shrC   r$   r   c              3   $   K   | ]  }d |v  
 yw)z.shNrE   rF   s     r   rI   zDTestCokacDirCleanup.test_sh_scripts_are_protected.<locals>.<genexpr>   rR   rJ   NrK   rM   s         r   test_sh_scripts_are_protectedz1TestCokacDirCleanup.test_sh_scripts_are_protected|   ss    k)K7'Ax*,2688K9OR]9]^
$./q6//1511111 0rO   c                    |dz  dz  dz  }t        |dz  d       t        |dz  d       t        j                  |dz  dz        }|D cg c]  }|d   	 }}t        d	 |D              rJ t        d
 |D              rJ yc c}w )uC   PDF/PNG/JPG/JPEG가 아닌 파일은 포함되지 않아야 한다.r    r!   r"   zdata.csvrC   r$   z	notes.txtr   c              3   $   K   | ]  }d |v  
 yw)z.csvNrE   rF   s     r   rI   zHTestCokacDirCleanup.test_non_media_files_not_included.<locals>.<genexpr>        2qv{2rJ   c              3   $   K   | ]  }d |v  
 yw)z.txtNrE   rF   s     r   rI   zHTestCokacDirCleanup.test_non_media_files_not_included.<locals>.<genexpr>   rY   rJ   NrK   rM   s         r   !test_non_media_files_not_includedz5TestCokacDirCleanup.test_non_media_files_not_included   s    k)K7'Ax*,26x+-B788K9OR]9]^
$./q6//2E22222E22222 0s   Bc                 L    |dz  dz  }t        j                  |      }|g k(  sJ y)uC   존재하지 않는 workspace는 빈 목록을 반환해야 한다.r    r!   N)r&   r'   r)   r   missingr,   s       r   (test_nonexistent_workspace_returns_emptyz<TestCokacDirCleanup.test_nonexistent_workspace_returns_empty   s/    [(;688A
Rr   c                     |dz  dz  dz  }t        |dz  d       t        j                  |dz  dz        }t        |      dk(  sJ |d   }d	|v sJ d
|v sJ d|v sJ d|v sJ |d   dk\  sJ y)uU   각 후보 항목은 path, size_bytes, days_old, category 필드를 가져야 한다.r    r!   r"   doc.pdf-   r$      r   r   
size_bytesdays_oldcategoryN)r   r&   r'   len)r)   r   r*   r,   r-   s        r   "test_candidate_has_required_fieldsz6TestCokacDirCleanup.test_candidate_has_required_fields   s    k)K7'Ax)+"588K9OR]9]^
:!###qM{{q   QQ}"""r   )__name__
__module____qualname____doc__r   r/   r4   r9   r<   r@   rN   rS   rV   r[   r_   rh   rE   r   r   r   r   1   s    U%$ %4 %%$ %4 %
&T 
&d 
&,4 ,D ,,4 ,D ,8D 8T 82d 2t 22d 2t 2
3$ 
34 
3   $  #4 #D #r   r   c                   `    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	y)
TestDoneClearCleanupu3   memory/events/*.done.clear 파일 정리 테스트.r   r
   Nc                     |dz  dz  }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |v sJ yc c}w )uH   31일 된 .done.clear 파일은 삭제 후보로 감지되어야 한다.memoryeventsztask-2025.done.clearr#   r$   r   Nr   r&   find_done_clear_candidatesr(   )r)   r   
events_dirold_filer,   r-   r.   s          r    test_old_done_clear_is_candidatez5TestDoneClearCleanup.test_old_done_clear_is_candidate   sa    (83
!*/E"EBO22:>
$./q6//8}%%% 0   Ac                     |dz  dz  }t        |dz        }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uH   30일 이내 .done.clear 파일은 삭제 후보가 아니어야 한다.rp   rq   zrecent.done.clearr   N)r   r&   rs   r(   )r)   r   rt   recentr,   r-   r.   s          r   $test_recent_done_clear_not_candidatez9TestDoneClearCleanup.test_recent_done_clear_not_candidate   s_    (83
":0C#CD22:>
$./q6//6{%''' 0   Ac                 l    |dz  dz  }t        |dz  d       t        j                  |      }|g k(  sJ y)u?   *.done.clear가 아닌 파일은 포함되지 않아야 한다.rp   rq   znotes.mdrC   r$   N)r   r&   rs   )r)   r   rt   r,   s       r   &test_non_done_clear_files_not_includedz;TestDoneClearCleanup.test_non_done_clear_files_not_included   s>    (83
zJ.R822:>
Rr   c                 L    |dz  dz  }t        j                  |      }|g k(  sJ y)uM   존재하지 않는 events 디렉토리는 빈 목록을 반환해야 한다.rp   rq   N)r&   rs   r]   s       r   )test_nonexistent_events_dir_returns_emptyz>TestDoneClearCleanup.test_nonexistent_events_dir_returns_empty   s/    X%0227;
Rr   c                 &   |dz  dz  }t        |dz  d      }t        |dz  d      }t        |dz  d	      }t        j                  |      }|D cg c]  }|d
   	 }}t        |      |v sJ t        |      |v sJ t        |      |v sJ yc c}w )uL   여러 개의 오래된 .done.clear 파일이 모두 포함되어야 한다.rp   rq   za.done.clearr2   r$   zb.done.clearr6   zc.done.clearrb   r   Nrr   )	r)   r   rt   f1f2f3r,   r-   r.   s	            r   $test_multiple_old_files_all_includedz9TestDoneClearCleanup.test_multiple_old_files_all_included   s    (83
J7bAJ7bAJ7bA22:>
$./q6//2w%2w%2w% 0s   B)
ri   rj   rk   rl   r   rv   rz   r}   r   r   rE   r   r   rn   rn      sd    =& &$ &(T (d ( t     $  4   T  d  r   rn   c                   `    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	y)
TestDispatchCleanupuB   memory/tasks/dispatch-*.md 파일 정리 테스트 (90일 초과).r   r
   Nc                     |dz  dz  }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |v sJ yc c}w )uJ   91일 된 dispatch-*.md 파일은 삭제 후보로 감지되어야 한다.rp   taskszdispatch-2024-001.md[   r$   r   Nr   r&   find_dispatch_candidatesr(   )r)   r   	tasks_dirold_dispatchr,   r-   r.   s          r   test_old_dispatch_is_candidatez2TestDispatchCleanup.test_old_dispatch_is_candidate   sb    x''1	%i2H&HrR00;
$./q6//< E))) 0rw   c                     |dz  dz  }t        |dz        }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uJ   90일 이내 dispatch-*.md 파일은 삭제 후보가 아니어야 한다.rp   r   zdispatch-recent.mdr   N)r   r&   r   r(   )r)   r   r   ry   r,   r-   r.   s          r   "test_recent_dispatch_not_candidatez6TestDispatchCleanup.test_recent_dispatch_not_candidate   s_    x''1	"9/C#CD00;
$./q6//6{%''' 0r{   c                     |dz  dz  }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uR   정확히 90일 된 파일은 삭제 후보가 아니다 (90일 초과만 대상).rp   r   zdispatch-border.mdZ   r$   r   Nr   )r)   r   r   borderr,   r-   r.   s          r   "test_exactly_90_days_not_candidatez6TestDispatchCleanup.test_exactly_90_days_not_candidate  sa    x''1		,@ @rJ00;
$./q6//6{%''' 0rw   c                     |dz  dz  }t        |dz  d       t        |dz  d       t        j                  |      }|g k(  sJ y)uI   dispatch-로 시작하지 않는 파일은 포함되지 않아야 한다.rp   r   ztask-notes.mdd   r$   zother-dispatch.mdN)r   r&   r   )r)   r   r   r,   s       r   !test_non_dispatch_md_not_includedz5TestDispatchCleanup.test_non_dispatch_md_not_included  sN    x''1	y?2=y#66SA00;
Rr   c                 L    |dz  dz  }t        j                  |      }|g k(  sJ y)uL   존재하지 않는 tasks 디렉토리는 빈 목록을 반환해야 한다.rp   r   N)r&   r   r]   s       r   (test_nonexistent_tasks_dir_returns_emptyz<TestDispatchCleanup.test_nonexistent_tasks_dir_returns_empty  s/    X%/009
Rr   )
ri   rj   rk   rl   r   r   r   r   r   r   rE   r   r   r   r      sd    L*t * *(4 (D ((4 (D ( $  4     $  r   r   c                   p    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
y)TestLogCleanupu<   workspace/logs/*.log 파일 정리 테스트 (60일 초과).r   r
   Nc                     |dz  }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |v sJ yc c}w )uA   61일 된 .log 파일은 삭제 후보로 감지되어야 한다.logsapp.log=   r$   r   Nr   r&   find_log_candidatesr(   )r)   r   logs_dirold_logr,   r-   r.   s          r   test_old_log_is_candidatez(TestLogCleanup.test_old_log_is_candidate'  s[    f$ I!5B?++H5
$./q6//7|u$$$ 0   Ac                     |dz  }t        |dz        }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uA   60일 이내 .log 파일은 삭제 후보가 아니어야 한다.r   zcurrent.logr   N)r   r&   r   r(   )r)   r   r   ry   r,   r-   r.   s          r   test_recent_log_not_candidatez,TestLogCleanup.test_recent_log_not_candidate1  sY    f$"8m#;<++H5
$./q6//6{%''' 0s   Ac                     |dz  }t        |dz  d      }t        j                         }t        j                  |||f       t	        j
                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uP   오늘 수정된 로그 파일은 스킵되어야 한다 (활성 로그 보존).r   z
active.logF   r$   r   N)r   r   r   r   r&   r   r(   )r)   r   r   
active_lognowr,   r-   r.   s           r   test_today_modified_log_skippedz.TestLogCleanup.test_today_modified_log_skipped;  sx    f$#H|$;"E
iik
c3Z(++H5
$./q6//:e+++ 0s   A9c                 f    |dz  }t        |dz  d       t        j                  |      }|g k(  sJ y)u8   *.log가 아닌 파일은 포함되지 않아야 한다.r   z	debug.txtr   r$   N)r   r&   r   )r)   r   r   r,   s       r   test_non_log_files_not_includedz.TestLogCleanup.test_non_log_files_not_includedI  s9    f$x+-C8++H5
Rr   c                     |dz  }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ yc c}w )uR   정확히 60일 된 파일은 삭제 후보가 아니다 (60일 초과만 대상).r   z
border.logrC   r$   r   Nr   )r)   r   r   r   r,   r-   r.   s          r   "test_exactly_60_days_not_candidatez1TestLogCleanup.test_exactly_60_days_not_candidateR  s[    f$< 7bA++H5
$./q6//6{%''' 0r   c                 F    |dz  }t        j                  |      }|g k(  sJ y)uK   존재하지 않는 logs 디렉토리는 빈 목록을 반환해야 한다.r   N)r&   r   r]   s       r   'test_nonexistent_logs_dir_returns_emptyz6TestLogCleanup.test_nonexistent_logs_dir_returns_empty\  s*    V#++G4
Rr   )ri   rj   rk   rl   r   r   r   r   r   r   r   rE   r   r   r   r   $  sw    F%$ %4 %(d (t (, , ,     (4 (D (     r   r   c                       e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZdeddfdZy)TestSafetyGuardsu7   삭제 금지 대상 보호 및 안전장치 테스트.r   r
   Nc                     g d}|D ]*  }||z  }|j                  dd       t        |dz  d       , t        j                  |      }|D ]%  }||z  dz  }|j	                  |      rJ | d        y	)
u^   보호된 디렉토리 내 파일은 어떤 카테고리에서도 대상이 되면 안 된다.)zmemory/reportszmemory/researchzmemory/specszmemory/meetingszmemory/plansTr   zimportant.mdr   r$   base_dir should be protectedN)r   r   r&   SafetyCheckeris_protected)r)   r   protected_dirsdrH   checker	test_paths          r   "test_protected_dirs_never_targetedz3TestSafetyGuards.test_protected_dirs_never_targetedm  s    
   	9A1AGGD4G01~-C8	9
 ""H5 	OA 1~5I''	2Nqc9M4NN2	Or   c                     g d}t        j                  |      }|D ]-  }||z  }t        |       |j                  |      r%J | d        y)uK   CLAUDE.md, MEMORY.md, .env, .env.keys는 절대 삭제 대상이 아니다.)rB   z	MEMORY.mdz.envz	.env.keysr   r   Nr&   r   r   r   )r)   r   protected_namesr   namer   s         r   test_protected_filenamesz)TestSafetyGuards.test_protected_filenames  sZ    I""H5# 	RD 4Ii(''	2Qtf<P4QQ2	Rr   c                     t        j                  |      }|dz  dz  }|dz  dz  dz  }|j                  |      sJ |j                  |      sJ y)u9   .git, .worktrees 관련 파일은 보호되어야 한다.r   z.gitconfigz
.worktreesbranchzfile.pyNr&   r   r   )r)   r   r   git_pathworktree_paths        r   test_git_files_protectedz)TestSafetyGuards.test_git_files_protected  s]    ""H5f$x/ </(:YF##H---##M222r   c                 r    t        j                  |      }|dz  dz  dz  dz  }|j                  |      sJ y)uG   /home/jay/projects/ 내 파일은 절대 건드리지 않아야 한다.r   projectsmyappsrczmain.pyNr   )r)   r   r   projects_paths       r   test_projects_dir_protectedz,TestSafetyGuards.test_projects_dir_protected  s?    ""H5 :-7%?)K##M222r   c                     |dz  }t        |dz  d      }t        j                  |      }t        j                  |d      }|j	                         sJ |d   dk(  sJ |d	   du sJ y
)uJ   dry-run 모드에서는 파일이 실제로 삭제되지 않아야 한다.r   old.logr   r$   Tdry_rundeleted_countr   r   Nr   r&   r   execute_cleanupexists)r)   r   r   r   r,   results         r   test_dry_run_does_not_deletez-TestSafetyGuards.test_dry_run_does_not_delete  sw    f$ I!5B?++H5
##J= ~~o&!+++i D(((r   c                    |dz  }t        |dz  d      }t        |dz  d      }t        j                  |      }t        j                  |d      }|j	                         rJ |j	                         rJ |d	   d
k(  sJ |d   du sJ y)uE   --execute 모드에서는 파일이 실제로 삭제되어야 한다.r   zold1.logr   r$   zold2.logA   Fr   r      r   Nr   )r)   r   r   old_log1old_log2r,   r   s          r   test_execute_mode_deletes_filesz0TestSafetyGuards.test_execute_mode_deletes_files  s    f$!(Z"7bA!(Z"7bA++H5
##J>??$$$??$$$o&!+++i E)))r   c                     |dz  }t        |dz  dd       t        |dz  dd       t        |d	z  d
d       t        j                  |      }t        j                  |d      }t	        |      dk  sJ y)u[   각 카테고리에서 최소 1개 파일은 보존되어야 한다 (전체 삭제 방지).r   a.logr   aaar   r	   b.logr   bbbzc.logr   cccrc   keepr   N)r   r&   r   apply_minimum_keeprg   )r)   r   r   r,   safe_candidatess        r   "test_minimum_keep_one_per_categoryz3TestSafetyGuards.test_minimum_keep_one_per_category  sv    f$x')EBx')EBx')EB++H5
//
C ?#q(((r   c                 @    t        j                  g d      }|g k(  sJ y)uR   후보가 없는 경우 최소 보존 규칙은 빈 목록을 반환해야 한다.rc   r   N)r&   r   r)   r   r   s      r   "test_minimum_keep_empty_candidatesz3TestSafetyGuards.test_minimum_keep_empty_candidates  s     &&r2||r   )ri   rj   rk   rl   r   r   r   r   r   r   r   r   r   rE   r   r   r   r   j  s    AO4 OD O&R R$ R3 3$ 33D 3T 3)T )d )* * *)4 )D )4 D r   r   c                   0    e Zd ZdZdeddfdZdeddfdZy)TestCleanupLogu   삭제 로그 기록 테스트.r   r
   Nc                     |dz  }|dz  }t        |dz  d       t        j                  |      }t        j                  |d|       |j	                         sJ |j                         }d|v sJ y)	uN   --execute 모드에서 삭제 후 로그 파일에 기록이 남아야 한다.r   file-cleanup.logr   r   r$   Fr   cleanup_log_pathN)r   r&   r   r   r   	read_text)r)   r   r   cleanup_logr,   r	   s         r   #test_cleanup_log_written_on_executez2TestCleanupLog.test_cleanup_log_written_on_execute  sv    f$!33x)+"5++H5

:u{S!!###'')G###r   c                     |dz  }|dz  }t        |dz  d       t        j                  |      }t        j                  |d|       |j	                         rJ y)	uG   dry-run 모드에서는 로그 파일이 생성되지 않아야 한다.r   r   r   r   r$   Tr   Nr   )r)   r   r   r   r,   s        r   'test_cleanup_log_not_written_on_dry_runz6TestCleanupLog.test_cleanup_log_not_written_on_dry_run  s]    f$!33x)+"5++H5

:tkR%%''''r   )ri   rj   rk   rl   r   r   r   rE   r   r   r   r     s+    )$D $T $	( 	( 	(r   r   c                   P    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZy)	TestReportModeu   --report 모드 테스트.r   r
   Nc                 T    t        j                  |      }d|v sJ d|v sJ d|v sJ y)uB   report 함수는 필수 키를 갖는 dict를 반환해야 한다.r   
disk_usagecleanup_candidatestotal_reclaimable_mbN)r&   generate_reportr   s      r   +test_report_returns_dict_with_required_keysz:TestReportMode.test_report_returns_dict_with_required_keys  s<    ##X6v%%%#v---%///r   c                     t        j                  |      }|d   }d|v sJ d|v sJ d|v sJ t        |d   t        t        f      sJ y)u@   disk_usage 섹션에는 total_mb와 free_mb가 있어야 한다.r   r   total_mbfree_mbused_mbN)r&   r  
isinstanceintfloat)r)   r   r   dus       r   )test_report_disk_usage_has_total_and_freez8TestReportMode.test_report_disk_usage_has_total_and_free  sZ    ##X6L!RBB"Z.3,777r   c                     |dz  }t        |dz  dd       t        |dz  dd       t        j                  ||	      }|d
   dk\  sJ y)uN   total_reclaimable_mb는 삭제 가능 파일들의 크기 합계여야 한다.r   r   r      AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr   r   r      BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB)r   r   r  r   N)r   r&   r  )r)   r   r   r   s       r   ,test_report_reclaimable_is_sum_of_candidatesz;TestReportMode.test_report_reclaimable_is_sum_of_candidates  sT    f$x')JGx')JG##XI,-222r   c                 ^    t        j                  |      }|d   }t        |t              sJ y)u@   cleanup_candidates는 카테고리별로 분류되어야 한다.r   r   N)r&   r  r  dict)r)   r   r   r,   s       r   *test_report_cleanup_candidates_by_categoryz9TestReportMode.test_report_cleanup_candidates_by_category  s-    ##X601
*d+++r   )	ri   rj   rk   rl   r   r  r  r  r  rE   r   r   r   r     sQ    $0D 0T 08$ 84 83T 3d 3,4 ,D ,r   r   c                   p    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
y)TestOrganizeModeu   --organize 모드 테스트.r   r
   Nc                     |dz  dz  dz  }t        |dz        }t        j                  |dz  dz  |dz  |dz        }|D cg c]  }|d   	 }}t        |      |v sJ y	c c}w )
uB   cokacdir workspace에서 이동 대상 파일을 찾아야 한다.r    r!   r"   zdocument.pdfuploadsr   cokacdir_workspaceuploads_baseprojects_basesourceN)r   r&   find_organize_candidatesr(   )r)   r   r*   pdf_filemovesmsourcess          r   $test_organize_find_files_in_cokacdirz5TestOrganizeMode.test_organize_find_files_in_cokacdir  s    k)K7'A$X%>?++'+5C!I-"Z/
 )..11X;..8}''' /s   A c                 N   |dz  dz  dz  }t        |dz         t        j                  |dz  dz  |dz  |dz        }t        |      dk\  sJ |d	   }d|d
   v sJ t	        |d
         }|j
                  j                  t        j                         j                  d      k(  sJ y)uP   프로젝트명이 없는 파일은 uploads/YYYY-MM/ 으로 이동해야 한다.r    r!   r"   zrandom-doc.pdfr  r   r  rc   r   destinationz%Y-%mN)
r   r&   r  rg   r   r   r   r   r   strftime)r)   r   r*   r  r   dests         r   )test_organize_moves_to_uploads_by_defaultz:TestOrganizeMode.test_organize_moves_to_uploads_by_default)  s    k)K7'A(%556++'+5C!I-"Z/
 5zQ!HAm,,,,Am$%{{8<<>#:#:7#CCCCr   c                     |dz  dz  dz  }t        |dz        }t        j                  |dz  dz  |dz  |dz        }t        j                  |d	       |j	                         sJ y
)uJ   dry-run 모드에서는 파일이 실제로 이동되지 않아야 한다.r    r!   r"   ra   r  r   r  Tr   Nr   r&   r  execute_organizer   r)   r   r*   r  r  s        r   #test_organize_dry_run_does_not_movez4TestOrganizeMode.test_organize_dry_run_does_not_move;  sv    k)K7'A$X	%9:++'+5C!I-"Z/

 	E40    r   c                     |dz  dz  dz  }t        |dz        }t        j                  |dz  dz  |dz  |dz        }t        j                  |d	       |j	                         rJ y
)uC   execute 모드에서는 파일이 실제로 이동되어야 한다.r    r!   r"   ra   r  r   r  Fr   Nr)  r+  s        r   !test_organize_execute_moves_filesz2TestOrganizeMode.test_organize_execute_moves_filesJ  sy    k)K7'A$X	%9:++'+5C!I-"Z/

 	E51 ??$$$$r   c                 Z    t        j                  |dz  dz  |dz  |dz        }|g k(  sJ y)uJ   존재하지 않는 workspace는 빈 이동 목록을 반환해야 한다.r    r!   r  r   r  N)r&   r  )r)   r   r  s      r   1test_organize_nonexistent_workspace_returns_emptyzBTestOrganizeMode.test_organize_nonexistent_workspace_returns_emptyY  s=    ++'+5C!I-"Z/

 {{r   c                     |dz  dz  dz  }t        |dz         t        j                  |dz  dz  |dz  |dz        }t        |      dk\  sJ |d	   }d
|v sJ d|v sJ d|v sJ y)uL   각 이동 후보는 source, destination, reason 필드를 가져야 한다.r    r!   r"   ra   r  r   r  rc   r   r  r$  reasonN)r   r&   r  rg   )r)   r   r*   r  r   s        r   +test_organize_candidate_has_required_fieldsz<TestOrganizeMode.test_organize_candidate_has_required_fieldsb  s    k)K7'A(Y./++'+5C!I-"Z/
 5zQ!H1}}!!!1}}r   )ri   rj   rk   rl   r   r"  r'  r,  r.  r0  r3  rE   r   r   r  r    s{    &(T (d (D$ D4 D$!D !T !%$ %4 %$ SW D T r   r  c                   @    e Zd ZdZdeddfdZdeddfdZdeddfdZy)TestAllCandidatesu&   전체 정리 후보 수집 테스트.r   r
   Nc                     t        j                  |dz  dz  |dz  dz  |dz  dz  |dz        }d|v sJ d	|v sJ d
|v sJ d|v sJ y)uN   collect_all_candidates는 카테고리별 딕셔너리를 반환해야 한다.r    r!   rp   rq   r   r   r  rt   r   r   cokacdir
done_cleardispatchNr&   collect_all_candidatesr   s      r   (test_collect_all_candidates_returns_dictz:TestAllCandidates.test_collect_all_candidates_returns_dict|  s{    **'+5C(*X5)G3&	
 V###v%%%V###r   c                     t        j                  |dz  dz  |dz  dz  |dz  dz  |dz        }|d   g k(  sJ |d	   g k(  sJ |d
   g k(  sJ |d   g k(  sJ y)uW   모든 디렉토리가 비어 있으면 각 카테고리가 빈 목록이어야 한다.r    r!   rp   rq   r   r   r7  r8  r9  r:  Nr;  r   s      r   &test_collect_all_candidates_empty_dirsz8TestAllCandidates.test_collect_all_candidates_empty_dirs  s    **'+5C(*X5)G3&	
 j!R'''l#r)))j!R'''f~###r   c                    |dz  dz  dz  }|dz  dz  }|dz  dz  }|dz  }t        |dz  d	
       t        |dz  d	
       t        |dz  d
       t        |dz  d
       t        j                  |dz  dz  |||      }t        |d         dk\  sJ t        |d         dk\  sJ t        |d         dk\  sJ t        |d         dk\  sJ y)uG   여러 카테고리에 후보가 있을 때 모두 수집해야 한다.r    r!   p1rp   rq   r   r   zold.pdfr#   r$   zevent.done.clearzdispatch-001.mdr   z
system.logr   r7  r8  rc   r9  r:  N)r   r&   r<  rg   )r)   r   r*   rt   r   r   r   s          r   /test_collect_all_candidates_multiple_categorieszATestAllCandidates.test_collect_all_candidates_multiple_categories  s   k)K7$>(83
x''1	f$x)+"5z$66R@y#442>x,.R8**'+5C!	
 6*%&!+++6,'(A---6*%&!+++6&>"a'''r   )ri   rj   rk   rl   r   r=  r?  rB  rE   r   r   r5  r5  y  s?    0   $  $t $ $( (QU (r   r5  c                   0    e Zd ZdZdeddfdZdeddfdZy)TestSafetyCheckerIntegrationuP   SafetyChecker가 실제 후보 수집과 통합되어 동작하는지 테스트.r   r
   Nc                     |dz  }t        |dz  d      }t        |dz  d      }t        j                  |      }|D cg c]  }|d   	 }}t        |      |vsJ t        |      |v sJ yc c}w )	uF   file-cleanup.log 자체는 로그 후보에서 제외되어야 한다.r   r   r   r$   r   r   r   Nr   )r)   r   r   r   old_app_logr,   r-   r.   s           r   0test_protected_path_excluded_from_log_candidateszMTestSafetyCheckerIntegration.test_protected_path_excluded_from_log_candidates  s    f$$X0B%BL$X	%9C++H5
$./q6//;u,,,;5(((	 0s   A,c                     t        j                  |      }|dz  dz  }t        |       |j                  |      du sJ y)u>   일반 파일은 is_protected가 False를 반환해야 한다.r   r   r   FNr   )r)   r   r   normal_files       r   $test_safety_checker_is_not_protectedzATestSafetyCheckerIntegration.test_safety_checker_is_not_protected  sB    ""H5')3+&##K0E999r   )ri   rj   rk   rl   r   rG  rJ  rE   r   r   rD  rD    s,    Z) )RV ):T :d :r   rD  )data) rl   r   sysr   r   pathlibr   typingr   unittest.mockr   pytestr   insertr(   __file__r   file_cleanupr&   r	  r   r   r   rn   r   r   r   r   r   r  r5  rD  rE   r   r   <module>rT     s    
 
       3tH~,,334 5  S 3 D D 3 D v# v#|4  4 x1  1 r>  > LY YB( (B$, $,XY YB5( 5(z: :r   