
    %<iu/                         d Z ddlZddlZddlmZmZ ddlZdZej                  de	fd       Z
 G d d      Z G d	 d
      Z G d d      ZdefdZdefdZ G d d      Zy)uJ   
TDD 테스트: doc_parser.py
RED → GREEN → REFACTOR 순서로 구현
    N)	MagicMockpatchuj   /home/jay/.cokacdir/workspace/autoset/금융소비자_보호에_관한_법률법률제21065호20260102.pdfreturnc                  n    t        t        d      5 } | j                         cddd       S # 1 sw Y   yxY w)u/   실제 PDF 파일을 바이트로 읽어 반환rbN)openTEST_PDF_PATHread)fs    K/home/jay/workspace/.worktrees/task-2057-dev2/libs/tests/test_doc_parser.py	pdf_bytesr      s.     
mT	" avvx  s   +4c                       e Zd ZddZddZy)TestParseResultNc                     ddl m}  |dddgddgdd	ggd
gddi      }|j                  dk(  sJ |j                  ddgk(  sJ t	        |j
                        dk(  sJ |j                  d   dk(  sJ y)uF   ParseResult 데이터클래스를 기본값으로 생성할 수 있다r   ParseResulthellopage1page2AB12)headersrows
page_count   textpagestablesmetadata   N)
doc_parserr   r   r    lenr!   r"   selfr   results      r   test_parse_result_instantiationz/TestParseResult.test_parse_result_instantiation   s    *G$!$c
c3ZLAB"A&	
 {{g%%%||11116==!Q&&&|,111    c                     ddl m}  |dg g i       }t        |j                  t              sJ t        |j
                  t              sJ t        |j                  t              sJ y)uK   ParseResult 필드 기본값(빈 컨테이너)으로도 생성 가능하다r   r    r   N)r$   r   
isinstancer    listr!   r"   dictr&   s      r    test_parse_result_default_fieldsz0TestParseResult.test_parse_result_default_fields*   sQ    *"BrBG&,,---&--...&//4000r*   r   N)__name__
__module____qualname__r)   r0    r*   r   r   r      s    21r*   r   c                   T    e 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	dZy)
TestParsePdfr   r   Nc                 @    ddl m}m}  ||      }t        ||      sJ y)uF   parse_pdf가 유효한 PDF bytes를 받아 ParseResult를 반환한다r   )r   	parse_pdfN)r$   r   r9   r-   )r'   r   r   r9   r(   s        r   #test_parse_pdf_returns_parse_resultz0TestParsePdf.test_parse_pdf_returns_parse_result8   s    59%&+...r*   c                     ddl m}  ||      }t        |j                  t              sJ t        |j                        dkD  sJ y)u/   parse_pdf 결과의 text가 비어있지 않다r   r9   N)r$   r9   r-   r   strr%   r'   r   r9   r(   s       r   test_parse_pdf_text_not_emptyz*TestParsePdf.test_parse_pdf_text_not_empty?   s:    (9%&++s+++6;;!###r*   c                     ddl m}  ||      }t        |j                  t              sJ t        |j                        dkD  sJ y)u@   parse_pdf 결과의 pages가 리스트이고 비어있지 않다r   r<   N)r$   r9   r-   r    r.   r%   r>   s       r   %test_parse_pdf_pages_is_nonempty_listz2TestParsePdf.test_parse_pdf_pages_is_nonempty_listG   s:    (9%&,,---6<< 1$$$r*   c                     ddl m}  ||      }d|j                  v sJ t        |j                  d   t              sJ |j                  d   dkD  sJ y)u8   parse_pdf 결과의 metadata에 page_count 키가 있다r   r<   r   N)r$   r9   r"   r-   intr>   s       r   &test_parse_pdf_metadata_has_page_countz3TestParsePdf.test_parse_pdf_metadata_has_page_countO   sQ    (9%v...&//,7===|,q000r*   c                 |    ddl m} t        j                  t              5   |d       ddd       y# 1 sw Y   yxY w)u7   잘못된 bytes를 넣었을 때 예외가 발생한다r   r<   s$   this is not a valid pdf file contentN)r$   r9   pytestraises	Exception)r'   r9   s     r   -test_parse_pdf_invalid_bytes_raises_exceptionz:TestParsePdf.test_parse_pdf_invalid_bytes_raises_exceptionX   s/    (]]9% 	?=>	? 	? 	?s   	2;r1   )	r2   r3   r4   bytesr:   r?   rA   rD   rI   r5   r*   r   r7   r7   7   sS    /U /t /$u $ $%u % %1 1$ 1?r*   r7   c                   4    e Zd ZdeddfdZddZdeddfdZy)TestParseDocumentr   r   Nc                 v    ddl m}m}  ||d      }t        ||      sJ t	        |j
                        dkD  sJ y)uY   parse_document가 .pdf 파일명으로 포맷을 감지하여 ParseResult를 반환한다r   r   parse_documentzdocument.pdfN)r$   r   rO   r-   r%   r   r'   r   r   rO   r(   s        r   +test_parse_document_detects_pdf_by_filenamez=TestParseDocument.test_parse_document_detects_pdf_by_filenamed   s8    :	>:&+...6;;!###r*   c                     ddl m} t        j                  t        t
        f      5   |dd       ddd       y# 1 sw Y   yxY w)u:   지원하지 않는 확장자는 예외를 발생시킨다r   )rO   s
   some byteszfile.xyzN)r$   rO   rF   rG   
ValueErrorrH   )r'   rO   s     r   0test_parse_document_unsupported_extension_raiseszBTestParseDocument.test_parse_document_unsupported_extension_raisesl   s5    -]]J	23 	6=*5	6 	6 	6s	   
9Ac                 B    ddl m}m}  ||d      }t        ||      sJ y)uB   확장자 대소문자 구분 없이 포맷 감지가 동작한다r   rN   zdocument.PDFN)r$   r   rO   r-   rP   s        r   .test_parse_document_case_insensitive_extensionz@TestParseDocument.test_parse_document_case_insensitive_extensions   s!    :	>:&+...r*   r1   )r2   r3   r4   rJ   rQ   rT   rV   r5   r*   r   rL   rL   c   s.    $U $t $6/ /RV /r*   rL   c                  8    dddddddg dddddd	dg d
ddgdS )u>   opendataloader-pdf JSON 출력을 흉내 내는 dict를 반환ztest.pdfr#   Nheading)r   r   d   rY   u   테스트 PDF 텍스트)typeidpage numberbounding boxzheading levelcontent	paragraphr   )r   r   rY   2   u   페이지1 텍스트)rZ   r[   r\   r]   r^   )z	file nameznumber of pagesauthortitlekidsr5   r5   r*   r   _make_mock_odl_outputrd   ~   sN       "  0!"4 $  /1
 r*   output_datac                       fd}|S )uP   opendataloader_pdf.convert()의 side_effect: JSON 파일을 output_dir에 생성c                 <   t        j                  |      j                  dd       t        | t              r| n| d   }t        j                  |      j
                  dz   }t        j                  |      |z  j                  t        j                  d             y )NT)parentsexist_okr   z.jsonF)ensure_ascii)	pathlibPathmkdirr-   r=   stem
write_textjsondumps)
input_path
output_dirkwargssrcfnamere   s        r   side_effectz._mock_convert_side_effect.<locals>.side_effect   sv    Z &&td&C&z37jZ]S!&&0	j	!E	)55JJ{7	
r*   r5   )re   rw   s   ` r   _mock_convert_side_effectrx      s    
 r*   c                   t   e Zd Zdej                  dej                  ddfdZdej                  dej                  ddfdZdej                  dej                  ddfdZ	dej                  dej                  ddfdZ
dej                  dej                  ddfd	Zdej                  dej                  ddfd
Zy)TestParseCachetmp_pathmonkeypatchr   Nc                    ddl }ddl m} |j                  |d|dz         t               }t	        d      5 }t        |      |_        d} ||      } ||      }	ddd       j                  	j                  k(  sJ |j                  |	j                  k(  sJ |j                  |	j                  k(  sJ j                  dk(  sJ y# 1 sw Y   lxY w)	u`   같은 bytes로 parse_pdf 2회 호출 시 같은 결과 반환, convert 호출은 1회만 발생r   Nr<   	CACHE_DIRparse_cacheopendataloader_pdf.converts   %PDF-1.4 fake pdf contentr#   )r$   r9   setattrrd   r   rx   rw   r   r    r!   
call_count)
r'   r{   r|   r$   r9   re   mock_convert
file_bytesresult1result2s
             r   "test_cache_hit_returns_same_resultz1TestParseCache.test_cache_hit_returns_same_result   s     	(JX5MN+-/0 	,L'@'ML$5J
+G
+G		, ||w||+++}}---~~///&&!+++	, 	,s   #CCc                     ddl }ddl m} |j                  |d|dz         t               }t	        d      5 }t        |      |_         |d        |d       ddd       j                  d	k(  sJ y# 1 sw Y   xY w)
uG   다른 bytes는 다른 캐시 — 각각 convert 호출이 발생한다r   Nr<   r~   r   r   s   %PDF-1.4 content As   %PDF-1.4 content Br   r$   r9   r   rd   r   rx   rw   r   )r'   r{   r|   r$   r9   re   r   s          r   test_cache_miss_different_bytesz.TestParseCache.test_cache_miss_different_bytes   s~     	(JX5MN+-/0 	-L'@'ML$+,+,	- &&!+++	- 	-s   !A11A:c                    ddl }ddl m} |j                  |d|dz         t               }d}t	        d      5 }t        |      |_         ||        ||d	       ddd       j                  d
k(  sJ y# 1 sw Y   xY w)uJ   `use_cache=False`로 호출 시 캐시를 무시하고 다시 파싱한다r   Nr<   r~   r   s   %PDF-1.4 use_cache false testr   F)	use_cacher   r   )r'   r{   r|   r$   r9   re   r   r   s           r   #test_use_cache_false_forces_reparsez2TestParseCache.test_use_cache_false_forces_reparse   s     	(JX5MN+-5
/0 	3L'@'ML$j!jE2	3 &&!+++	3 	3s   #A55A>c                    ddl }ddl m}m} |dz  }|j                  |d|       t	               }t        d      5 }t        |      |_         |d        |d       ddd       |j                         sJ t        |j                  d	            }	t        |	      d
k\  sJ  |        t        |j                  d	            }
t        |
      dk(  sJ y# 1 sw Y   wxY w)u8   `clear_cache()` 후 캐시 디렉토리가 비어 있다r   N)clear_cacher9   r   r~   r   s   %PDF-1.4 clear cache test As   %PDF-1.4 clear cache test B*.jsonr#   )r$   r   r9   r   rd   r   rx   rw   existsr.   globr%   )r'   r{   r|   r$   r   r9   	cache_dirre   r   
json_files	remainings              r   test_clear_cache_removes_allz+TestParseCache.test_clear_cache_removes_all   s     	5},	JY?+-/0 	6L'@'ML$4545	6 !!!)..23
:!### 	12	9~"""	6 	6s   !CCc                 H   ddl }ddl m} |dz  }|j                  |d|       t               }d}t	        d      5 }t        |      |_         ||       ddd       |j                         sJ t        |j                  d            }	t        |	      d	k(  sJ y# 1 sw Y   FxY w)
u1   캐시 파일이 실제 디스크에 생성된다r   Nr<   r   r~   s   %PDF-1.4 disk cache testr   r   r#   )r$   r9   r   rd   r   rx   rw   r   r.   r   r%   )
r'   r{   r|   r$   r9   r   re   r   r   r   s
             r   test_cache_stores_to_diskz(TestParseCache.test_cache_stores_to_disk  s     	(},	JY?+-0
/0 	"L'@'ML$j!	"
 !!!)..23
:!###	" 	"s   BB!c                 4   ddl }ddl m} |j                  |d|dz         t               }d}t	        d      5 }t        |      |_         ||      }ddd       t	        d      5 }	t        |      |	_         ||      }
|	j                          ddd       j                  
j                  k(  sJ |j                  |
j                  k(  sJ |j                  |
j                  k(  sJ |j                  |
j                  k(  sJ y# 1 sw Y   xY w# 1 sw Y   xY w)uX   캐시된 결과와 원본 결과가 동일하다 (text, pages, tables, metadata 비교)r   Nr<   r~   r   s   %PDF-1.4 result match testr   )r$   r9   r   rd   r   rx   rw   assert_not_calledr   r    r!   r"   )r'   r{   r|   r$   r9   re   r   r   originalmock_no_callcacheds              r   #test_cached_result_matches_originalz2TestParseCache.test_cached_result_matches_original  s    	(JX5MN+-2
/0 	-L'@'ML$ ,H	-
 /0 	-L'@'ML$z*F**,		- }}+++~~---&--///  FOO333	- 	-
	- 	-s   D$)DDD)r2   r3   r4   rk   rl   rF   MonkeyPatchr   r   r   r   r   r   r5   r*   r   rz   rz      s    ,,393E3E,	,0,,393E3E,	,&,,393E3E,	,,##393E3E#	#6$$393E3E$	$,44393E3E4	4r*   rz   )__doc__rp   rk   unittest.mockr   r   rF   r	   fixturerJ   r   r   r7   rL   r/   rd   rx   rz   r5   r*   r   <module>r      s   
   *  @ 5  1 1:&? &?X/ /6t 64 "M4 M4r*   