
    i`=              
          d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
Z
ddlZddlZe	j                  j                  dej                  j                  ej                  j                  ej                  j!                  e                         ddlZg dZg 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   keyword_cluster.py 유닛 테스트 (TDD - RED→GREEN).

샘플 데이터 기반 테스트: 보험 도메인 검색어 클러스터링 기능 검증.
    N)   보험료 계산u   보험료 할인   월납 보험료   보험 비용u   납입 방법   보험 종류   보험이란u
   보험 뜻   보험 기초u   보험 설명   보험 가입   가입 절차   청약 서류u   보험 심사u   가입 방법   보험 추천   보험 비교u   보험 순위   보험 후기u   보험 평판   연금 보험   변액 보험   저축 보험u   보험 수익률u   적금 보험)r   r   r	   r   r   r   c                   $    e Zd Zd Zd Zd Zd Zy)TestTokenizeKoreanc                 ~   t        j                  d      }g d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
u6   공백 기반 토큰화가 정상 동작해야 한다.u   보험료 계산 방법)	   보험료u   계산u   방법==z%(py0)s == %(py3)stokenspy0py3assert %(py5)spy5N
kctokenize_korean
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationselfr   @py_assert2@py_assert1@py_format4@py_format6s         E/home/jay/workspace/tools/geo-analytics/tests/test_keyword_cluster.pytest_basic_tokenizationz*TestTokenizeKorean.test_basic_tokenizationH   so    ##$=>::v:::::v:::::::v:::v:::::::::::    c                 |   t        j                  d      }dg}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u2   단어 하나도 리스트로 반환해야 한다.   보험r   r   r   r   r   r   Nr   r*   s         r0   test_single_wordz#TestTokenizeKorean.test_single_wordM   sm    ##H-"#v####v######v###v##########r2   c                 z   t        j                  d      }g }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u6   빈 문자열은 빈 리스트를 반환해야 한다. r   r   r   r   r   r   Nr   r*   s         r0   test_empty_stringz$TestTokenizeKorean.test_empty_stringR   sj    ##B'v|vvvr2   c                    t        j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	x}}d
}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	x}}y	)u2   여러 공백도 올바르게 처리해야 한다.u   보험  종류  비교r4   inz%(py1)s in %(py3)sr   py1r   r   r   Nu   종류u   비교)
r    r!   r"   r#   r'   r$   r%   r&   r(   r)   )r+   r   @py_assert0r,   r.   r/   s         r0   test_extra_spacesz$TestTokenizeKorean.test_extra_spacesW   s   ##$<=!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!r2   N)__name__
__module____qualname__r1   r5   r8   r@    r2   r0   r   r   G   s    ;
$

"r2   r   c                   0    e Zd Zd Zd Zd Zd Zd Zd Zy)TestLoadKeywordsFromCsvc                    |dz  }|j                  dd       t        j                  t        |            }g d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}}y)uM   헤더 없이 키워드만 있는 CSV를 읽어야 한다 (샘플 데이터).zsample_queries.csvu-   보험료 계산
보험 종류
보험 가입
utf-8encoding)r   r   r	   r   r   keywordsr   r   r   N
write_textr    load_keywords_from_csvstrr"   r#   r$   r%   r&   r'   r(   r)   r+   tmp_pathcsv_filerK   r,   r-   r.   r/   s           r0   test_load_single_column_csvz3TestLoadKeywordsFromCsv.test_load_single_column_csve   s    22NY`a,,S];QQxQQQQQxQQQQQQQxQQQxQQQQQQQQQQQr2   c                    |dz  }t        t        |      ddd      5 }t        j                  |ddg      }|j	                          |j                  d	d
ddddg       ddd       t        j                  t        |            }d	}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   sxY w)uK   'query' 컬럼 헤더가 있는 CSV를 읽어야 한다 (샘플 데이터).zsample_ga4_queries.csvwr7   rH   newlinerJ   querysessions
fieldnamesr   100)rX   rY   r   80Nr:   r<   rK   r=   r   r   openrO   csv
DictWriterwriteheader	writerowsr    rN   r"   r#   r'   r$   r%   r&   r(   r)   
r+   rQ   rR   fwriterrK   r?   r,   r.   r/   s
             r0   &test_load_csv_with_header_query_columnz>TestLoadKeywordsFromCsv.test_load_csv_with_header_query_columnl   sE   66#h-b7C 	q^^A7J2GHF ,%@)t< 	 ,,S];!-!X----!X---!------X---X-------*(****(*********(***(*******	 	s   AGGc                 j   |dz  }t        t        |      ddd      5 }t        j                  |ddg      }|j	                          |j                  d	d
dg       ddd       t        j                  t        |            }d	}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   xY w)uP   'searchTerm' 컬럼 헤더가 있는 CSV를 읽어야 한다 (샘플 데이터).zsample_searchterm.csvrU   r7   rH   rV   
searchTermcountrZ   r   50)ri   rj   Nr:   r<   rK   r=   r   r   r^   rd   s
             r0   $test_load_csv_with_searchterm_columnz<TestLoadKeywordsFromCsv.test_load_csv_with_searchterm_columnz   s    55#h-b7C 	q^^A<2IJF .> 	 ,,S];*(****(*********(***(*******	 	s   ?D))D2c                     t        j                  t              5  t        j                  d       ddd       y# 1 sw Y   yxY w)uJ   존재하지 않는 파일은 FileNotFoundError를 발생시켜야 한다.z/nonexistent/path/queries.csvN)pytestraisesFileNotFoundErrorr    rN   r+   s    r0   !test_load_nonexistent_file_raisesz9TestLoadKeywordsFromCsv.test_load_nonexistent_file_raises   s7    ]],- 	G%%&EF	G 	G 	Gs	   9Ac                    |dz  }|j                  dd       t        j                  t        |            }g }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            dx}}y)u7   빈 CSV 파일은 빈 리스트를 반환해야 한다.z	empty.csvr7   rH   rI   r   r   rK   r   r   r   NrL   rP   s           r0   !test_empty_csv_returns_empty_listz9TestLoadKeywordsFromCsv.test_empty_csv_returns_empty_list   s    k)B1,,S];x2~x2xx2r2   c                 l   |dz  }|j                  dd       t        j                  t        |            }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}y)u>   중복 키워드는 제거되어야 한다 (샘플 데이터).zsample_dup.csvu0   보험료 계산
보험료 계산
보험 종류
rH   rI   r      r   )zK%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.count
}(%(py4)s)
} == %(py9)srK   )r   py2py4py6py9assert %(py11)spy11N)rM   r    rN   rO   rj   r"   r#   r$   r%   r&   r'   r(   r)   )r+   rQ   rR   rK   r-   @py_assert3@py_assert5@py_assert8@py_assert7@py_format10@py_format12s              r0   test_deduplicationz*TestLoadKeywordsFromCsv.test_deduplication   s    ..AG 	 	
 ,,S];~~606~016Q61Q66661Q666666x666x666~66606661666Q66666666r2   N)	rA   rB   rC   rS   rg   rl   rr   rt   r   rD   r2   r0   rF   rF   d   s"    R+
+G
7r2   rF   c                   6    e Zd Zd Zd Zd Zd Zd Zd Zd Z	y)	TestAssignClusterLabelc                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uQ   보험료/비용 키워드는 COST 라벨을 받아야 한다 (샘플 데이터).)r   r   r   COSTr   r   labelr   r   r   N
r    assign_cluster_labelr"   r#   r$   r%   r&   r'   r(   r)   r+   r   r,   r-   r.   r/   s         r0   test_cost_cluster_labelz.TestAssignClusterLabel.test_cost_cluster_label   sk    ''(abuuuur2   c                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uV   보험 종류/뜻 키워드는 LEARNING 라벨을 받아야 한다 (샘플 데이터).)r   r   r   LEARNINGr   r   r   r   r   r   Nr   r   s         r0   test_learning_cluster_labelz2TestAssignClusterLabel.test_learning_cluster_label   sl    ''(Z[""u
""""u
""""""u"""u"""
"""""""r2   c                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uQ   가입/절차 키워드는 PROCESS 라벨을 받아야 한다 (샘플 데이터).)r	   r
   r   PROCESSr   r   r   r   r   r   Nr   r   s         r0   test_process_cluster_labelz1TestAssignClusterLabel.test_process_cluster_label   sl    ''([\!!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!r2   c                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uO   추천/비교 키워드는 TRUST 라벨을 받아야 한다 (샘플 데이터).)r   r   r   TRUSTr   r   r   r   r   r   Nr   r   s         r0   test_trust_cluster_labelz/TestAssignClusterLabel.test_trust_cluster_label   sl    ''([\uuuur2   c                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uT   연금/투자 키워드는 INVESTMENT 라벨을 받아야 한다 (샘플 데이터).)r   r   r   
INVESTMENTr   r   r   r   r   r   Nr   r   s         r0   test_investment_cluster_labelz4TestAssignClusterLabel.test_investment_cluster_label   sl    ''([\$$u$$$$u$$$$$$u$$$u$$$$$$$$$$r2   c                 ~   t        j                  g d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	)
uG   프리셋과 무관한 키워드는 UNKNOWN 라벨을 받아야 한다.)zxyz abczfoo barztest 123UNKNOWNr   r   r   r   r   r   Nr   r   s         r0   test_unknown_cluster_labelz1TestAssignClusterLabel.test_unknown_cluster_label   sl    ''(JK!!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!r2   c                 z   t        j                  g       }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u:   빈 키워드 리스트는 UNKNOWN을 반환해야 한다.r   r   r   r   r   r   r   Nr   r   s         r0   #test_empty_keywords_returns_unknownz:TestAssignClusterLabel.test_empty_keywords_returns_unknown   sk    ''+!!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!r2   N)
rA   rB   rC   r   r   r   r   r   r   r   rD   r2   r0   r   r      s%    
#
"
 
%
"
"r2   r   c                   N    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)TestClusterKeywordsc                    t        j                  t        d      }d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}d}||v }|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }dd	|iz  }t        t        j                  |            d
x}}y
)uL   결과 딕셔너리는 필수 키를 포함해야 한다 (샘플 데이터).   
n_clustersclustersr:   r<   resultr=   r   r   Ntotal_keywordssilhouette_score)r    cluster_keywordsSAMPLE_KEYWORDSr"   r#   r'   r$   r%   r&   r(   r)   )r+   r   r?   r,   r.   r/   s         r0   $test_returns_dict_with_required_keysz8TestClusterKeywords.test_returns_dict_with_required_keys   s%   $$_C#zV####zV###z######V###V#######)6))))6)))))))))6)))6)))))))!+!V++++!V+++!++++++V+++V+++++++r2   c                    t        j                  t        d      }|d   }t        |      }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}x}}y
)u[   요청한 클러스터 수만큼 클러스터가 생성되어야 한다 (샘플 데이터).r   r   r   r   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenr   rw   rx   py7assert %(py9)srz   N)r    r   r   r   r"   r#   r$   r%   r&   r'   r(   r)   r+   r   r-   r}   @py_assert6r~   @py_format8r   s           r0   test_cluster_countz&TestClusterKeywords.test_cluster_count   s    $$_C*%+s%&+!+&!++++&!++++++s+++s+++%+++&+++!+++++++r2   c                 x   t        j                  t        d      }|d   }t        t              }||k(  }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            dx}x}}y)uQ   total_keywords가 입력 키워드 수와 일치해야 한다 (샘플 데이터).r   r   r   r   z0%(py1)s == %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}r   r   r>   r   rx   ry   assert %(py8)spy8N)r    r   r   r   r"   r#   r'   r$   r%   r&   r(   r)   )r+   r   r?   r~   r,   @py_format7@py_format9s          r0   test_total_keywords_countz-TestClusterKeywords.test_total_keywords_count   s    $$_C&'?3+??'+?????'+????'??????3???3????????????+????????r2   c           
      "   t        j                  t        d      }h d}|d   D ]g  }|j                  }|j                  } |       } ||      }|s2t        j                  d|j                  d       d||j	                         z
         dz   d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}x}x}}j y)uP   각 클러스터는 필수 필드를 모두 가져야 한다 (샘플 데이터).r   r   >   idsizer   rK   representative_keywordpillar_document_suggestionr   u   클러스터 r   u   에 필드 누락: z}
>assert %(py9)s
{%(py9)s = %(py2)s
{%(py2)s = %(py0)s.issubset
}(%(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.keys
}()
})
}required_fieldscluster)r   rw   r   r   r   rz   N)r    r   r   issubsetkeysr"   _format_assertmsggetr$   r%   r&   r'   r(   r)   )	r+   r   r   r   r-   @py_assert4r   r   r   s	            r0   %test_each_cluster_has_required_fieldsz9TestClusterKeywords.test_each_cluster_has_required_fields   s   $$_C
 j) 	G"++ GLL LN +N; ;    D 122E"W\\^346  v   #   I #   I ,   v   -4   I -4   I -9   I -;   I <      	r2   c                    t        j                  t        d      }t        d |d   D              }|d   }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}}y)ua   모든 클러스터의 키워드 합이 total_keywords와 일치해야 한다 (샘플 데이터).r   r   c              3   &   K   | ]	  }|d      yw)r   NrD   ).0cs     r0   	<genexpr>zMTestClusterKeywords.test_cluster_keywords_sum_equals_total.<locals>.<genexpr>   s     :!AfI:s   r   r   r   r   totalr   r   r   N)r    r   r   sumr"   r#   r$   r%   r&   r'   r(   r)   )r+   r   r   r,   r-   r.   r/   s          r0   &test_cluster_keywords_sum_equals_totalz:TestClusterKeywords.test_cluster_keywords_sum_equals_total   s    $$_C:vj'9::/00u00000u0000000u000u00000000000r2   c                    t        j                  t        d      }d}| }|d   }||k  }d}||k  }|r|st        j                  d||fd|||f      t        j
                  |      t        j
                  |      t        j
                  |      dz  }dd	|iz  }	t        t        j                  |	            d
x}x}x}x}x}}y
)uF   silhouette_score는 -1에서 1 사이여야 한다 (샘플 데이터).r   r   g      ?r   )<=r   )z-%(py1)s <= %(py6)sz%(py6)s <= %(py8)s)r>   ry   r   zassert %(py10)spy10Nr    r   r   r"   r#   r'   r(   r)   )
r+   r   r?   r,   r~   r}   r   r   r   @py_format11s
             r0   test_silhouette_score_rangez/TestClusterKeywords.test_silhouette_score_range   s    $$_C8t8v018t188S81S88888t1S8888881888S88888888r2   c                    ddl m} t        |j                               dhz  }t	        j
                  t        d      }|d   D ]  }|d   }||v }|st        j                  d|fd	||f      t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t        t        j                  |            dx}} y)uS   클러스터 라벨은 프리셋 또는 UNKNOWN이어야 한다 (샘플 데이터).r   )INSURANCE_CLUSTER_PRESETSr   r   r   r   r   r:   r<   valid_labelsr=   r   r   N)configr   setr   r    r   r   r"   r#   r'   r$   r%   r&   r(   r)   )	r+   r   r   r   r   r?   r,   r.   r/   s	            r0   %test_label_is_valid_preset_or_unknownz9TestClusterKeywords.test_label_is_valid_preset_or_unknown   s    4499;<	{J$$_Cj) 	4G7#3#|3333#|333#333333|333|3333333	4r2   c                 R   t        j                  t        d      }|d   D ]  }|d   }|d   }||v }|slt        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}} y)ue   representative_keyword는 해당 클러스터의 keywords 안에 있어야 한다 (샘플 데이터).r   r   r   r   rK   r:   )z%(py1)s in %(py4)s)r>   rx   zassert %(py6)sry   Nr   )r+   r   r   r?   r}   r,   @py_format5r   s           r0   &test_representative_keyword_in_clusterz:TestClusterKeywords.test_representative_keyword_in_cluster   s    $$_Cj) 	LG34K
8KK48KKKKK48KKKK4KKK8KKKKKKKK	Lr2   c                 L   t        j                  t        d      }|d   }t        |      }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}x}}|d   }t        t              }||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }
dd|
iz  }t        t	        j                  |            d
x}x}	}y
)uB   소규모 샘플 데이터도 클러스터링 가능해야 한다.   r   r   r   r   r   r   r   rz   Nr   r   SAMPLE_KEYWORDS_SMALLr   r   r   )r    r   r   r   r"   r#   r$   r%   r&   r'   r(   r)   )r+   r   r-   r}   r   r~   r   r   r?   r,   r   r   s               r0   test_small_datasetz&TestClusterKeywords.test_small_dataset  s9   $$%:qI*%+s%&+!+&!++++&!++++++s+++s+++%+++&+++!+++++++&'E3/D+EE'+EEEEE'+EEEE'EEEEEE3EEE3EEEEEE/DEEE/DEEE+EEEEEEEEr2   c           	      6   ddg}t        j                  |d      }|d   }t        |      }t        |      }||k  }|sSt        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}}y)uJ   클러스터 수가 키워드 수보다 클 경우 조정되어야 한다.r   r   r   r   r   )r   )zN%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} <= %(py9)s
{%(py9)s = %(py6)s(%(py7)s)
}r   rK   )r   rw   rx   ry   r   rz   r{   r|   N)r    r   r   r"   r#   r$   r%   r&   r'   r(   r)   )	r+   rK   r   r-   r}   r   r~   r   r   s	            r0   *test_cluster_count_capped_at_keyword_countz>TestClusterKeywords.test_cluster_count_capped_at_keyword_count	  s    1$$X!<*%7s%&7#h-7&-7777&-777777s777s777%777&777777#777#777777h777h777-7777777r2   c                    t        j                  t        d      }t        j                  |d      }t        j
                  |      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndd	z  }d
d|iz  }t        t        j                  |            dx}}y)u@   결과는 JSON 직렬화 가능해야 한다 (샘플 데이터).r   r   F)ensure_asciir   r:   r<   parsedr=   r   r   N)r    r   r   jsondumpsloadsr"   r#   r'   r$   r%   r&   r(   r)   )r+   r   json_strr   r?   r,   r.   r/   s           r0   test_json_serializablez*TestClusterKeywords.test_json_serializable  s    $$_C::f59H%#zV####zV###z######V###V#######r2   N)rA   rB   rC   r   r   r   r   r   r   r   r   r   r   r   rD   r2   r0   r   r      s<    ,,
@
19
4LF8$r2   r   c                   $    e Zd Zd Zd Zd Zd Zy)TestEdgeCasesc                 (   	 t        j                  dgd      }|d   }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}x}}y# t        $ r Y yw xY w)uO   키워드 1개는 ValueError 또는 단일 클러스터를 반환해야 한다.r   rv   r   r   r   r   r   r   r   rz   N)r    r   r   r"   r#   r$   r%   r&   r'   r(   r)   
ValueErrorr   s           r0   %test_single_keyword_raises_or_returnsz3TestEdgeCases.test_single_keyword_raises_or_returns  s    	((+1EFj)/3)*/a/*a////*a//////3///3///)///*///a/////// 		s   DD 	DDc                     t        j                  t              5  t        j                  g d       ddd       y# 1 sw Y   yxY w)u@   빈 키워드 리스트는 ValueError를 발생시켜야 한다.r   r   N)rn   ro   r   r    r   rq   s    r0   &test_empty_keywords_raises_value_errorz4TestEdgeCases.test_empty_keywords_raises_value_error%  s3    ]]:& 	2q1	2 	2 	2s	   ;Ac                    dgdz  dgdz  z   }t        j                  |d      }d}||v }|st        j                  d|fd||f      t        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}y)uI   중복 키워드 입력도 처리 가능해야 한다 (샘플 데이터).r   
   r      r   r   r:   r<   r   r=   r   r   N)
r    r   r"   r#   r'   r$   r%   r&   r(   r)   )r+   rK   r   r?   r,   r.   r/   s          r0   test_duplicate_keywords_handledz-TestEdgeCases.test_duplicate_keywords_handled*  s    &'",/@2/EE$$X!<#zV####zV###z######V###V#######r2   c                 @   t        j                  t        d      }|d   D ]  }|d   }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d	x}} y	)
uK   pillar_document_suggestion은 문자열이어야 한다 (샘플 데이터).r   r   r   r   z5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstancerO   )r   rw   r   r   N)r    r   r   r   rO   r$   r%   r"   r&   r'   r(   r)   )r+   r   r   r-   r   r/   s         r0   )test_pillar_document_suggestion_is_stringz7TestEdgeCases.test_pillar_document_suggestion_is_string0  s    $$_Cj) 	JG%&BCI:CSIIIIIIII:III:IIICIIIIIISIIISIIIIIIIIII	Jr2   N)rA   rB   rC   r   r   r   r   rD   r2   r0   r   r     s    2
$Jr2   r   c                       e Zd Zd Zd Zd Zy)TestBuildReportc                    t        j                  t        d      }t        |dz        }t        j                  ||       t
        j                  }|j                  } ||      }|sddt        j                         v st        j                  t
              rt        j                  t
              ndt        j                  |      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        t        j                  |            dx}x}}y)	uF   build_report는 JSON 파일을 생성해야 한다 (샘플 데이터).r   r   sample_report.jsonzbassert %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.exists
}(%(py5)s)
}osoutput_path)r   rw   rx   r   r   N)r    r   r   rO   build_reportr  pathexistsr$   r%   r"   r&   r'   r(   r)   )r+   rQ   cluster_resultr  r-   r}   r   r   s           r0   "test_build_report_writes_json_filez2TestBuildReport.test_build_report_writes_json_file=  s    ,,_K(%99:
4ww*w~~*~k********r***r***w***~******k***k**********r2   c                    t        j                  t        d      }t        |dz        }t        j                  ||       t        |d      5 }t        j                  |      }ddd       d}|v }|st        j                  d|fd	||f      t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }	t        t        j                  |	            dx}}d}||v }|st        j                  d|fd	||f      t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }	t        t        j                  |	            dx}}d}||v }|st        j                  d|fd	||f      t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }	t        t        j                  |	            dx}}y# 1 sw Y   xY w)uF   생성된 파일은 유효한 JSON이어야 한다 (샘플 데이터).r   r   r   rH   rI   Nr   r:   r<   r   r=   r   r   r   r   )r    r   r   rO   r  r_   r   loadr"   r#   r'   r$   r%   r&   r(   r)   )
r+   rQ   r  r  re   r   r?   r,   r.   r/   s
             r0   $test_build_report_content_valid_jsonz4TestBuildReport.test_build_report_content_valid_jsonD  sq   ,,_K(%99:
4+0 	"AYYq\F	"#zV####zV###z######V###V#######)6))))6)))))))))6)))6)))))))!+!V++++!V+++!++++++V+++V+++++++		" 	"s   II%c                 R   t        j                  t        d      }t        j                  |d      }g }d}||u }|}|st	        |t
              }|}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }	|j                  |	       |sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t
              rt        j                  t
              ndt        j                        dz  }
|j                  |
       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)u`   output_path가 None이면 파일 생성 없이 결과를 반환해야 한다 (샘플 데이터).r   r   N)r  )is)z%(py2)s is %(py5)sr   )rw   r   z%(py7)sr   z2%(py13)s
{%(py13)s = %(py9)s(%(py10)s, %(py11)s)
}r   dict)rz   r   r|   py13rv   zassert %(py16)spy16)r    r   r   r  r   r  r"   r#   r$   r%   r&   r'   append_format_boolopr(   r)   )r+   r  r   r-   r   r}   r?   @py_assert12r/   r   @py_format14@py_format15@py_format17s                r0   +test_build_report_returns_none_when_no_pathz;TestBuildReport.test_build_report_returns_none_when_no_pathO  s   ,,_KTB99v~9FD!99!9999v999999v999v9999999999999999999999F999F999999D999D999!99999999999999r2   N)rA   rB   rC   r  r
  r  rD   r2   r0   r   r   <  s    +	,:r2   r   c                       e Zd Zd Zd Zy)TestGa4Optionalc                 Z   ddl m}  |       rt        j                  d        |       }| }|syddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        t        j                  |            dx}}y)uF   GA4 미설정 시 is_ga4_configured()는 False를 반환해야 한다.r   is_ga4_configuredB   GA4가 설정된 환경에서는 이 테스트를 스킵합니다.z)assert not %(py2)s
{%(py2)s = %(py0)s()
}r  )r   rw   N)r   r  rn   skipr$   r%   r"   r&   r'   r(   r)   )r+   r  r-   r}   r.   s        r0   %test_ga4_not_configured_returns_falsez5TestGa4Optional.test_ga4_not_configured_returns_false\  sk    , KK\]$&&&&&&&&&&&$&&&$&&&&&&&&&&r2   c                     ddl m}  |       rt        j                  d       t        j                  t
              5  t        j                  dd       ddd       y# 1 sw Y   yxY w)uO   GA4 미설정 시 fetch_ga4_keywords는 RuntimeError를 발생시켜야 한다.r   r  r  123456   )property_iddaysN)r   r  rn   r  ro   RuntimeErrorr    fetch_ga4_keywords)r+   r  s     r0   2test_fetch_ga4_keywords_raises_when_not_configuredzBTestGa4Optional.test_fetch_ga4_keywords_raises_when_not_configurede  sM    ,KK\]]]<( 	A!!hR@	A 	A 	As   AA&N)rA   rB   rC   r  r&  rD   r2   r0   r  r  [  s    'Ar2   r  )__doc__builtinsr$   _pytest.assertion.rewrite	assertionrewriter"   r   r  systempfiler`   rn   r  insertdirnameabspath__file__keyword_clusterr    r   r   r   rF   r   r   r   r   r  rD   r2   r0   <module>r3     s   
   	 
  
  277??277??277??83L#MN O B " ":57 57z"" ""TI$ I$bJ J@: :>A Ar2   