
    i]                       d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	 ddl
Z
	 ddlmZmZmZ 	 	 d	 	 	 	 	 ddZd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e
j4                  j6                   G d d             Zy# e$ r	 dZdZdZY w xY w)u   
Lightpanda 크롤러 래퍼 테스트

단위 테스트: mock 사용 (네트워크 불필요)
통합 테스트: @pytest.mark.integration — 실제 lightpanda 서버(ws://127.0.0.1:9333) 필요
    )annotationsN)fields)	AsyncMock	MagicMockpatch)
CrawlErrorCrawlResultLightpandaCrawlerc                &   t               }t        |       |_        t        |      |_        t        t        d            |_        t               |_        d|_        t        d      |_        t        d      |_        t        g       |_	        |S )u$   playwright Page 모의 객체 생성return_value   )statushttps://example.com	Test PageN)
r   r   titlecontentgotocloseurlevaluatequery_selectorquery_selector_all)r   r   pages      L/home/jay/workspace/.worktrees/task-2116-dev1/tools/tests/test_lightpanda.py_make_mock_pager      so    
 ;D.DJ'2DLy'<=DIDJ$DH;7DM#6D'R8DK    c                    | 
t               } t               }t        |       |_        t               |_        t        d      |_        |S )u'   playwright Browser 모의 객체 생성r   T)r   r   r   new_pager   is_connected)r   browsers     r   _make_mock_browserr"   /   s>    | kG d3GKGM$$7GNr   c                  ,    e Zd ZddZddZddZddZy)TestLightpandaCrawlerInitc                T    t         J d       t               }|j                  dk(  sJ y)u;   CDP endpoint 기본값이 ws://127.0.0.1:9333 인지 확인Nu   LightpandaCrawler import 실패zws://127.0.0.1:9333r
   cdp_endpointselfcrawlers     r   test_default_cdp_endpointz3TestLightpandaCrawlerInit.test_default_cdp_endpoint@   s1     ,O.OO,#%##'<<<<r   c                N    t         J t        d      }|j                  dk(  sJ y)u$   사용자 지정 CDP endpoint 설정Nzws://localhost:9999)r'   r&   r(   s     r   test_custom_cdp_endpointz2TestLightpandaCrawlerInit.test_custom_cdp_endpointF   s.     ,,,#1FG##'<<<<r   c                J    t         J t               }|j                  dk(  sJ y)u   기본 타임아웃 30000msNi0u  )r
   
timeout_msr(   s     r   test_default_timeoutz.TestLightpandaCrawlerInit.test_default_timeoutL   s*     ,,,#%!!U***r   c                J    t         J t               }|j                  dk(  sJ y)u)   Chrome fallback endpoint 기본값 확인Nzws://127.0.0.1:9222)r
   chrome_endpointr(   s     r   test_chrome_fallback_endpointz7TestLightpandaCrawlerInit.test_chrome_fallback_endpointR   s+     ,,,#%&&*????r   NreturnNone)__name__
__module____qualname__r+   r-   r0   r3    r   r   r$   r$   ?   s    ==+@r   r$   c                  $    e Zd ZddZddZddZy)TestCrawlResultDataclassc                    t         J d       t        t               D ch c]  }|j                   }}h d}|j                  |      sJ d||z
          yc c}w )u>   CrawlResult 에 필수 필드가 모두 존재하는지 확인Nu   CrawlResult import 실패>	   r   htmlmetatextlinksr   enginer   
elapsed_msu   누락 필드: )r	   r   nameissubset)r)   ffield_namesrequireds       r   test_required_fields_existz3TestCrawlResultDataclass.test_required_fields_exist_   s`    &C(CC&'-k':;!qvv;;f  -YKAW@X/YY- <s   Ac                    t         J t        dddddgddid	d
d	      }|j                  dk(  sJ |j                  dk(  sJ t        |j                  t
              sJ t        |j                  t              sJ y)u   CrawlResult 인스턴스 생성Nr   ExamplezHello worldz<html></html>zhttps://example.com/pagedescriptiontestr   g      Y@
lightpanda	r   r   r@   r>   rA   r?   r   rC   rB   )r	   r   rB   
isinstancerA   listr?   dict)r)   results     r   test_instantiationz+TestCrawlResultDataclass.test_instantiationf   s    &&&% -.(

 zz2222}},,,&,,---&++t,,,r   c                l    t         J dD ]'  }t        ddddg i dd|	      }|j                  |k(  r'J  y)u8   engine 필드는 'lightpanda' 또는 'chrome' 중 하나NrN   chromezhttps://x.com r   g        rO   )r	   rB   )r)   rB   rs      r   test_engine_field_valuesz1TestCrawlResultDataclass.test_engine_field_valuesy   sV    &&&. 	&F#
A 88v%%%	&r   Nr4   )r7   r8   r9   rI   rT   rZ   r:   r   r   r<   r<   ^   s    Z-&&r   r<   c                     e Zd Zej                  d        Zej                  j                  dd       Zej                  j                  dd       Z	ej                  j                  dd       Z
ej                  j                  dd       Zy)TestFetchUnitc              #  :  K   t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _        |||f ddd       y# 1 sw Y   yxY ww)z playwright connect_over_cdp mock)tools.lightpanda_crawler.async_playwrightr   FN	r   r   r   
__aenter__	__aexit__r   r"   chromiumconnect_over_cdpr)   mock_pwmock_instancer   r!   s        r   mock_playwright_connectz%TestFetchUnit.mock_playwright_connect   s      >? 		)7%KM.7].SG  +-6E-JG  *"$D(.G6?W6UM""37D((		) 		) 		)   BA8B	BBBc                   K   t         J |\  }}}t               4 d{   }|j                  d       d{   }ddd      d{    t        t              sJ y7 @7 )7 # 1 d{  7  sw Y   +xY ww)u2   fetch() 가 CrawlResult 를 반환하는지 확인Nr   )r
   fetchrP   r	   r)   rg   _r!   r   r*   rS   s          r   test_fetch_returns_crawl_resultz-TestFetchUnit.test_fetch_returns_crawl_result   s      !,,,27D$& 	@ 	@'"==)>??F	@ 	@ &+...	@?	@ 	@ 	@ 	@sS   A;A A;A&A"A&A;A$	A;"A&$A;&A8,A/-A84A;c                F  K   t         J |\  }}}t               4 d{   }|j                  d       d{   }ddd      d{    t        d      sJ t        |d      sJ t        |d      sJ t        |d      sJ y7 f7 O7 A# 1 d{  7  sw Y   QxY ww)uB   fetch() 결과에 title/text/html/links 필드가 있는지 확인Nr   r   r@   r>   rA   )r
   rj   hasattrrk   s          r   %test_fetch_result_has_required_fieldsz3TestFetchUnit.test_fetch_result_has_required_fields   s      !,,,27D$& 	@ 	@'"==)>??F	@ 	@ vw'''vv&&&vv&&&vw'''	@?	@ 	@ 	@ 	@sS   B!BB!BBBB!B
	>B!B
B!BBBB!c                   K   t         J |\  }}}t               4 d{   }|j                  d       d{   }ddd      d{    j                  dk(  sJ y7 ?7 (7 # 1 d{  7  sw Y   *xY ww)u6   fetch() 결과에 요청 URL이 저장되는지 확인Nr   )r
   rj   r   rk   s          r   test_fetch_url_stored_correctlyz-TestFetchUnit.test_fetch_url_stored_correctly   s      !,,,27D$& 	@ 	@'"==)>??F	@ 	@ zz2222	@?	@ 	@ 	@ 	@S   A:AA:A%A!A%A:A#	A:!A%#A:%A7+A.,A73A:c                   K   t         J |\  }}}t               4 d{   }|j                  d       d{   }ddd      d{    j                  dk\  sJ y7 ?7 (7 # 1 d{  7  sw Y   *xY ww)u*   fetch() elapsed_ms 가 양수인지 확인Nr   r   )r
   rj   rC   rk   s          r   test_fetch_elapsed_ms_positivez,TestFetchUnit.test_fetch_elapsed_ms_positive   s      !,,,27D$& 	@ 	@'"==)>??F	@ 	@   A%%%	@?	@ 	@ 	@ 	@rs   Nr4   )r7   r8   r9   pytestfixturerg   markasynciorm   rp   rr   ru   r:   r   r   r\   r\      s    ^^) ) [[/ / [[( ( [[3 3 [[& &r   r\   c                  R   e Zd Zej                  d        Zej                  j                  dd       Zej                  j                  dd       Z	ej                  j                  dd       Z
ej                  j                  dd       Zej                  j                  dd       Zy)	TestFetchManyUnitc              #  :  K   t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _        |||f d d d        y # 1 sw Y   y xY ww)Nr^   r   Fr_   rd   s        r   rg   z)TestFetchManyUnit.mock_playwright_connect   s     >? 		)7%KM.7].SG  +-6E-JG  *"$D(.G6?W6UM""37D((		) 		) 		)rh   c                   K   t         J g d}t               4 d{   }|j                  |       d{   }ddd      d{    t        t              sJ y7 @7 )7 # 1 d{  7  sw Y   +xY ww)u4   fetch_many() 가 리스트를 반환하는지 확인Nr   https://example.orgzhttps://example.net)r
   
fetch_manyrP   rQ   r)   rg   urlsr*   resultss        r   test_fetch_many_returns_listz.TestFetchManyUnit.test_fetch_many_returns_list   sy      !,,,T$& 	5 	5'#..t44G	5 	5 '4(((	54	5 	5 	5 	5sS   A9AA9A$A A$A9A"A9 A$"A9$A6*A-+A62A9c                   K   t         J g d}t               4 d{   }|j                  |       d{   }ddd      d{    t              dk(  sJ y7 >7 '7 # 1 d{  7  sw Y   )xY ww)uC   fetch_many() 결과 수가 입력 URL 수와 일치하는지 확인Nr~      r
   r   lenr   s        r   #test_fetch_many_count_matches_inputz5TestFetchManyUnit.test_fetch_many_count_matches_input   s{      !,,,T$& 	5 	5'#..t44G	5 	5 7|q   	54	5 	5 	5 	5sS   A7AA7A"AA"A7A A7A" A7"A4(A+)A40A7c                   K   t         J g d}t               4 d{   }|j                  |       d{   }ddd      d{    t        d D              sJ y7 B7 +7 # 1 d{  7  sw Y   -xY ww)uC   fetch_many() 결과가 모두 CrawlResult 인스턴스인지 확인Nr~   c              3  <   K   | ]  }t        |t                y wNrP   r	   .0rY   s     r   	<genexpr>zFTestFetchManyUnit.test_fetch_many_all_crawl_results.<locals>.<genexpr>       ?!:a-?   )r
   r   allr   s        r   !test_fetch_many_all_crawl_resultsz3TestFetchManyUnit.test_fetch_many_all_crawl_results   s}      !,,,T$& 	5 	5'#..t44G	5 	5 ?w????	54	5 	5 	5 	5sS   A;A A;A&A"A&A;A$A;"A&$A;&A8,A/-A84A;c                   K   t         J t               4 d{   }|j                  g        d{   }ddd      d{    g k(  sJ y7 57 7 # 1 d{  7  sw Y    xY ww)u:   fetch_many() 빈 리스트 입력 시 빈 리스트 반환N)r
   r   )r)   rg   r*   r   s       r   test_fetch_many_empty_listz,TestFetchManyUnit.test_fetch_many_empty_list  sp      !,,,$& 	3 	3'#..r22G	3 	3 "}}	32	3 	3 	3 	3sS   A*AA*AAAA*AA*AA*A'AA'#A*c                   K   t         J ddg}t               4 d{   }|j                  |d       d{   }ddd      d{    t              dk(  sJ y7 @7 '7 # 1 d{  7  sw Y   )xY ww)u?   fetch_many() concurrency 파라미터가 수용되는지 확인Nr   r      concurrency   r   r   s        r   !test_fetch_many_concurrency_paramz3TestFetchManyUnit.test_fetch_many_concurrency_param  s      !,,,%'<=$& 	D 	D'#..t.CCG	D 	D 7|q   	DC	D 	D 	D 	DsS   A9AA9A$A A$A9A"	A9 A$"A9$A6*A-+A62A9Nr4   )r7   r8   r9   rv   rw   rg   rx   ry   r   r   r   r   r   r:   r   r   r{   r{      s    ^^
) 
) [[) ) [[! ! [[@ @ [[  [[! !r   r{   c                      e Zd Zej                  d        Zej                  j                  dd       Zej                  j                  dd       Z	y)TestEvaluateUnitc              #  \  K   t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        d      |_        t        |      }t        |      |j                  _	        |||f d d d        y # 1 sw Y   y xY ww)Nr^   r   FExample Domain)
r   r   r   r`   ra   r   r   r"   rb   rc   rd   s        r   rg   z(TestEvaluateUnit.mock_playwright_connect  s     >? 
	)7%KM.7].SG  +-6E-JG  *"$D%3CDDM(.G6?W6UM""37D((
	) 
	) 
	)s   B,B	B 	B, B)%B,c                   K   t         J |\  }}}t               4 d{   }|j                  dd       d{   }ddd      d{    dk(  sJ y7 67 7 # 1 d{  7  sw Y    xY ww)u9   evaluate() 가 JS 실행 결과를 반환하는지 확인Nr   document.titler   )r
   r   rk   s          r   test_evaluate_returns_valuez,TestEvaluateUnit.test_evaluate_returns_value-  s      !,,,27D$& 	U 	U'"++,ACSTTF	U 	U ))))	UT	U 	U 	U 	UsS   A1AA1AAAA1	A
A1AA1A."A%#A.*A1c                  K   t         J |\  }}}t               4 d{   }|j                  dd       d{    ddd      d{    |j                  j                          y7 I7 17 ## 1 d{  7  sw Y   3xY ww)u7   evaluate() 가 page.evaluate 를 호출하는지 확인Nr   r   )r
   r   assert_called_once)r)   rg   rl   r!   r   r*   s         r   !test_evaluate_calls_page_evaluatez2TestEvaluateUnit.test_evaluate_calls_page_evaluate8  s      !,,,27D$& 	L 	L'""#8:JKKK	L 	L 	((*	LK	L 	L 	L 	LsS   BA)BA/A+A/B	A-
 B+A/-B/B5A86B=BNr4   )
r7   r8   r9   rv   rw   rg   rx   ry   r   r   r:   r   r   r   r     sT    ^^) ) [[* * [[+ +r   r   c                     e Zd Zej                  d        Zej                  j                  dd       Zej                  j                  dd       Z	ej                  j                  dd       Z
ej                  j                  dd       Zy)TestExtractStructuredUnitc              #    K   t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        d      |_        t        d      |_        t               }t        |      |_	        t        ||g      |_
        t        |      }t        |      |j                  _        ||||f d d d        y # 1 sw Y   y xY ww)Nr^   r   FzHello World
attr_value)r   r   r   r`   ra   r   
inner_textget_attributer   r   r   r"   rb   rc   )r)   re   rf   mock_elr   r!   s         r   rg   z1TestExtractStructuredUnit.mock_playwright_connectJ  s     >? 	27%KM.7].SG  +-6E-JG  *kG!*!FG$-<$HG!"$D"+"AD&/gw=O&PD#(.G6?W6UM""37D'11	2 	2 	2s   C,C	C 	C, C)%C,c                  K   t         J |\  }}}}t               4 d{   }|j                  dddi       d{   }ddd      d{    t        t              sJ y7 C7 )7 # 1 d{  7  sw Y   +xY ww)u8   extract_structured() 가 dict 를 반환하는지 확인Nr   r   h1)r
   extract_structuredrP   rR   r)   rg   rl   r!   r   r   r*   rS   s           r   $test_extract_structured_returns_dictz>TestExtractStructuredUnit.test_extract_structured_returns_dict]  s      !,,,$;!7D'$& 	^ 	^'"556KgW[_]]F	^ 	^ &$'''	^]	^ 	^ 	^ 	^sT   A?A$A?A*A&A*A?A(A?&A*(A?*A<0A31A<8A?c                   K   t         J |\  }}}}t               4 d{   }|j                  dddi       d{   }ddd      d{    dv sJ y7 77 7 # 1 d{  7  sw Y   xY ww)u@   extract_structured() 결과에 요청한 키가 있는지 확인Nr   r   r   r
   r   r   s           r   #test_extract_structured_key_presentz=TestExtractStructuredUnit.test_extract_structured_key_presenth  s      !,,,$;!7D'$& 	^ 	^'"556KgW[_]]F	^ 	^ &   	^]	^ 	^ 	^ 	^T   A3AA3AAAA3AA3AA3A0$A'%A0,A3c                   K   t         J |\  }}}}t               4 d{   }|j                  dddi       d{   }ddd      d{    dv sJ y7 77 7 # 1 d{  7  sw Y   xY ww)u;   ::attr(name) 문법으로 속성 추출 가능한지 확인Nr   hrefza::attr(href)r   r   s           r   #test_extract_structured_attr_syntaxz=TestExtractStructuredUnit.test_extract_structured_attr_syntaxs  s      !,,,$;!7D'$& 	h 	h'"556KfVeMfggF	h 	h 	hg	h 	h 	h 	hr   c                   K   t         J |\  }}}}t               4 d{   }|j                  dddi       d{   }ddd      d{    t        j                  d      t              sJ y7 R7 87 *# 1 d{  7  sw Y   :xY ww)u;   복수형 키(links)는 리스트를 반환하는지 확인Nr   rA   a)r
   r   rP   getrQ   r   s           r   /test_extract_structured_plural_key_returns_listzITestExtractStructuredUnit.test_extract_structured_plural_key_returns_list~  s      !,,,$;!7D'$& 	] 	]'"556KgWZ^\\F	] 	] &**W-t444	]\	] 	] 	] 	]sT   BA3BA9A5A9BA7'B5A97B9B?B BBNr4   )r7   r8   r9   rv   rw   rg   rx   ry   r   r   r   r   r:   r   r   r   r   I  s    ^^2 2$ [[( ( [[! ! [[    [[5 5r   r   c                      e Zd Zej                  j
                  dd       Zej                  j
                  dd       Zej                  j
                  dd       Zy)TestChromeFallbackc                  K   t         J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        t        d      |g      |j                  _
        t               4 d{   }|j                  dk(  sJ ddd      d{    ddd       y7 .7 # 1 d{  7  sw Y   xY w# 1 sw Y   yxY ww)uC   Lightpanda 연결 실패 시 Chrome으로 fallback 되는지 확인Nr^   r   FzLightpanda connection refusedside_effectrW   )r
   r   r   r   r`   ra   r   r"   	Exceptionrb   rc   _engine)r)   re   rf   r   chrome_browserr*   s         r   *test_chrome_fallback_on_lightpanda_failurez=TestChromeFallback.test_chrome_fallback_on_lightpanda_failure  s      !,,,>? 	37%KM.7].SG  +-6E-JG  *"$D/5N 7@=>"7M""3 )* 3 3g(2223 3!	3 	3 3 3 3 3 3!	3 	3se   C4B
C( C!C($C6C(CC(	C4C(C(C%	CC%	!C((C1-C4c                  K   t         J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _	        t               4 d{   }|j                  dk(  sJ ddd      d{    ddd       y7 .7 # 1 d{  7  sw Y   xY w# 1 sw Y   yxY ww)uB   Lightpanda 연결 성공 시 engine 이 'lightpanda' 인지 확인Nr^   r   FrN   )r
   r   r   r   r`   ra   r   r"   rb   rc   r   )r)   re   rf   r   
lp_browserr*   s         r   $test_engine_is_lightpanda_on_successz7TestChromeFallback.test_engine_is_lightpanda_on_success  s      !,,,>? 
	77%KM.7].SG  +-6E-JG  *"$D+D1J6?Z6XM""3(* 7 7g,6667 7
	7 
	77 7 7 7 7
	7 
	7se   C)A?CCCC+C6C7C;	C)CCC	CC	CC&"C)c                   K   t         J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _	        t               4 d{   }|j                  d       d{   }|j                  |j                  k(  sJ ddd      d{    ddd       y7 Q7 :7 # 1 d{  7  sw Y   !xY w# 1 sw Y   yxY ww)uE   CrawlResult.engine 이 실제 사용 엔진과 일치하는지 확인Nr^   r   Fr   )r
   r   r   r   r`   ra   r   r"   rb   rc   rj   rB   r   )r)   re   rf   r   r   r*   rS   s          r   (test_crawl_result_engine_matches_crawlerz;TestChromeFallback.test_crawl_result_engine_matches_crawler  s      !,,,>? 	87%KM.7].SG  +-6E-JG  *"$D+D1J6?Z6XM""3(* 8 8g&}}-BCC}}7778 8	8 	88C8 8 8 8	8 	8sw   DA?DC'DC-.C)/C-DC+D	D'D)C-+D-C?	3C64C?	;DDDNr4   )	r7   r8   r9   rv   rx   ry   r   r   r   r:   r   r   r   r     sZ    [[3 3. [[7 7  [[8 8r   r   c                      e Zd ZddZej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Z	y)TestErrorHandlingc                J    t         J d       t        t         t              sJ y)u5   CrawlError 커스텀 예외가 존재하는지 확인Nu   CrawlError import 실패)r   
issubclassr   )r)   s    r   test_crawl_error_existsz)TestErrorHandling.test_crawl_error_exists  s$    %A'AA%*i000r   c           	     
  K   t         J t        J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t        t        d            |j                  _	        t        j                  t              5  t               4 d{   }	 ddd      d{    ddd       ddd       y7 &7 # 1 d{  7  sw Y   'xY w# 1 sw Y   +xY w# 1 sw Y   yxY ww)u7   Lightpanda + Chrome 모두 실패 시 CrawlError 발생Nr^   r   FzConnection refusedr   )r
   r   r   r   r   r`   ra   r   rb   rc   rv   raises)r)   re   rf   r*   s       r   +test_both_endpoints_fail_raises_crawl_errorz=TestErrorHandling.test_both_endpoints_fail_raises_crawl_error  s      !,,,%%%>? 		7%KM.7].SG  +-6E-JG  *6?IVjLk6lM""3z* ,.  ' 		 		     		 		s}   DA>C7C++C
,C+/C1C+<C=C+C7		DC+C+C(CC($C++C4	0C77D <Dc                  K   t         J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _	        t               4 d{   }t        j                  t              5 }|j                  d       d{    ddd       dt        j                        v sdt        |j                        v sJ ddd      d{    ddd       y7 7 W# 1 sw Y   VxY w7 # 1 d{  7  sw Y   -xY w# 1 sw Y   yxY ww)u2   screenshot() 호출 시 NotImplementedError 발생Nr^   r   Fr   zPlaywright+Chromeu   스크린샷)r
   r   r   r   r`   ra   r   r"   rb   rc   rv   r   NotImplementedError
screenshotstrvalue)r)   re   rf   r   r!   r*   exc_infos          r   &test_screenshot_raises_not_implementedz8TestErrorHandling.test_screenshot_raises_not_implemented  s<     !,,,>? 	k7%KM.7].SG  +-6E-JG  *"$D(.G6?W6UM""3(* k kg]]#67 D8!,,-BCCCD*c(...AA^WZ[c[i[iWjEjjjk k	k 	kkCD Dk k k k	k 	ks   EA?EDED03D"D 
	D"8D0ED.E	EE D""D+'D0.E0E	6D97E	>EE
Ec                F  K   t         J t        d      5 }t               }t        |      |j                  _        t        d      |j                  _        t               }t        |      }t        |      |j                  _	        t               4 d{   }t        j                  t              5  |j                  d       d{    ddd       ddd      d{    ddd       y7 X7 '# 1 sw Y   &xY w7 # 1 d{  7  sw Y   -xY w# 1 sw Y   yxY ww)u+   pdf() 호출 시 NotImplementedError 발생Nr^   r   Fr   )r
   r   r   r   r`   ra   r   r"   rb   rc   rv   r   r   pdf)r)   re   rf   r   r!   r*   s         r   test_pdf_raises_not_implementedz1TestErrorHandling.test_pdf_raises_not_implemented  s      !,,,>? 	=7%KM.7].SG  +-6E-JG  *"$D(.G6?W6UM""3(* = =g]]#67 =!++&;<<<== =	= 	==<= == = = =	= 	=s   D!A?DC.DD 3C2C0
	C2D D C>!D%	D!.D0C22C;7D >D D	D	D	DDD!Nr4   )
r7   r8   r9   r   rv   rx   ry   r   r   r   r:   r   r   r   r     sc    1
 [[   [[k k$ [[= =r   r   c                      e Zd Zej                  j
                  dd       Zej                  j
                  dd       Zej                  j
                  dd       Zej                  j
                  dd       Z	y)TestIntegrationc                  K   t         J t               4 d{   }|j                  d       d{   }ddd      d{    t        t              sJ |j                  sJ |j
                  sJ |j                  dk(  sJ t        |j                  t              sJ |j                  dkD  sJ |j                  dv sJ y7 7 7 # 1 d{  7  sw Y   xY ww)uK   실제 example.com 크롤링 — title/text/html/links 필드 존재 확인Nr   r   rV   )r
   rj   rP   r	   r   r>   r   rA   rQ   rC   rB   r)   r*   rS   s      r   test_fetch_example_comz&TestIntegration.test_fetch_example_com  s      !,,,$& 	@ 	@'"==)>??F	@ 	@ &+...|||{{{zz2222&,,---  1$$$}} 8888	@?	@ 	@ 	@ 	@sT   CCCC
CC
CCBCC
C
CCCCc                  K   t         J g d}t               4 d{   }|j                  |d       d{   }ddd      d{    t              dk(  sJ t        d |D              sJ y7 T7 ;7 -# 1 d{  7  sw Y   =xY ww)u/   3개 URL 병렬 크롤링 — 결과 수 확인Nr~   r   r   c              3  <   K   | ]  }t        |t                y wr   r   r   s     r   r   z=TestIntegration.test_fetch_many_three_urls.<locals>.<genexpr>1  r   r   )r
   r   r   r   )r)   r   r*   r   s       r   test_fetch_many_three_urlsz*TestIntegration.test_fetch_many_three_urls$  s      !,,,

 %& 	D 	D'#..t.CCG	D 	D 7|q   ?w????		DC	D 	D 	D 	DsS   BA2BA8A4A8BA6	*B4A86B8B
>B?B
Bc                  K   t         J t               4 d{   }|j                  dd       d{   }ddd      d{    t        t              sJ t	        |      dkD  sJ y7 Q7 97 +# 1 d{  7  sw Y   ;xY ww)u+   evaluate() 로 document.title 반환 확인Nr   r   r   )r
   r   rP   r   r   )r)   r*   r   s      r   test_evaluate_document_titlez,TestIntegration.test_evaluate_document_title3  s      !,,,$& 	T 	T'!**+@BRSSE	T 	T %%%%5zA~~		TS	T 	T 	T 	TsS   BA+BA1A-A1BA/(B-A1/B1B7A:8B?Bc                8  K   t         J t               4 d{   }|j                  dddd       d{   }ddd      d{    t        t              sJ d|v sJ d|v sJ t        |d   t              sJ y7 e7 J7 <# 1 d{  7  sw Y   LxY ww)u+   CSS 셀렉터로 구조화 데이터 추출Nr   r   r   )headingrA   r   rA   )r
   r   rP   rR   rQ   r   s      r   test_extract_structured_cssz+TestIntegration.test_extract_structured_css=  s      !,,,$& 	 	'"55% 3/ F	 	 &$'''F"""&   &/4000		 	 	 	sS   BA?BBBBBB9BBBBBBBNr4   )
r7   r8   r9   rv   rx   ry   r   r   r   r   r:   r   r   r   r     sz    [[9 9 [[@ @ [[  [[1 1r   r   )r   zx<html><head><title>Test Page</title></head><body><h1>Hello</h1><a href='https://example.com/link'>link</a></body></html>)r   r   r   r   r5   r   r   )r   zMagicMock | Noner5   r   )__doc__
__future__r   ry   dataclassesr   unittest.mockr   r   r   rv   tools.lightpanda_crawlerr   r	   r
   ImportErrorr   r"   r$   r<   r\   r{   r   r   r   r   rx   integrationr   r:   r   r   <module>r      s    #   5 5 SS  N " @ @>*& *&d<& <&HB! B!T#+ #+V>5 >5L:8 :8D:= :=D 61 61 61C  KJs   
B1 1B?>B?