11package cache
22
33import (
4+ "os"
45 "testing"
56)
67
7- // testStore returns a Store built from representative sample data.
8+ // testStore builds a Store from representative sample data.
89func testStore () * Store {
910 corps := []* CorpInfo {
1011 {CorpCode : "00126380" , CorpName : "삼성전자" , StockCode : "005930" },
1112 {CorpCode : "01153956" , CorpName : "컬리" },
1213 {CorpCode : "01494172" , CorpName : "컬리넥스트마일" },
14+ {CorpCode : "01713402" , CorpName : "컬리페이" },
1315 {CorpCode : "01547845" , CorpName : "당근마켓" },
1416 {CorpCode : "01717824" , CorpName : "당근페이" },
1517 {CorpCode : "01138364" , CorpName : "더핑크퐁컴퍼니" , StockCode : "403850" },
1618 {CorpCode : "00293886" , CorpName : "카카오" },
1719 {CorpCode : "00000002" , CorpName : "카카오뱅크" },
1820 {CorpCode : "00000003" , CorpName : "카카오페이" },
1921 {CorpCode : "01154811" , CorpName : "주식회사 오늘의집" },
20- // 법인 형태어 노이즈 테스트용: "주식회사"를 포함하는 무관한 회사
21- {CorpCode : "99999999" , CorpName : "두성에스비텍주식회사(구:두성공업주식회사)" },
22+ // "마켓컬리" 검색 시 노이즈가 될 수 있는 실제 데이터와 유사한 회사들
23+ {CorpCode : "N001" , CorpName : "게이트마켓" },
24+ {CorpCode : "N002" , CorpName : "지마켓" },
25+ {CorpCode : "N003" , CorpName : "알루마켓" },
26+ {CorpCode : "N004" , CorpName : "비즈마켓" },
27+ {CorpCode : "N005" , CorpName : "올인마켓" },
28+ {CorpCode : "N006" , CorpName : "레어마켓" },
29+ {CorpCode : "N007" , CorpName : "마켓비" },
30+ {CorpCode : "N008" , CorpName : "와마켓" },
31+ {CorpCode : "N009" , CorpName : "맥쿼리IMM마켓뉴트럴혼합형사모펀드" },
32+ {CorpCode : "N010" , CorpName : "다이와증권캐피탈마켓서울지점" },
33+ {CorpCode : "N011" , CorpName : "에이치앤디마켓플레이스" },
34+ {CorpCode : "N012" , CorpName : "코리아마켓팅" },
35+ // "주식회사 오늘의집" 검색 시 법인 형태어 노이즈
36+ {CorpCode : "N099" , CorpName : "두성에스비텍주식회사(구:두성공업주식회사)" },
2237 }
2338 return buildStore (corps )
2439}
@@ -68,7 +83,7 @@ func TestSearch_Substring_당근(t *testing.T) {
6883}
6984
7085func TestSearch_Substring_카카오 (t * testing.T ) {
71- // "카카오" 이름 완전일치가 존재하므로 정확히 1건만 반환 (substring 전에 리턴)
86+ // "카카오" 이름 완전일치가 존재하므로 1건만 반환 (substring 전에 리턴)
7287 s := testStore ()
7388 results := s .Search ("카카오" )
7489 if len (results ) != 1 || results [0 ].CorpName != "카카오" {
@@ -77,11 +92,11 @@ func TestSearch_Substring_카카오(t *testing.T) {
7792}
7893
7994func TestSearch_Substring_카카오계열 (t * testing.T ) {
80- // "카카오뱅" 처럼 완전일치 없는 쿼리는 substring 으로 카카오뱅크를 찾음
95+ // 완전일치 없는 쿼리는 substring으로 찾음
8196 s := testStore ()
8297 results := s .Search ("카카오뱅" )
8398 if len (results ) != 1 || results [0 ].CorpName != "카카오뱅크" {
84- t .Fatalf ("'카카오뱅' substring 검색: 카카오뱅크 기대, got %v" , corpNames (results ))
99+ t .Fatalf ("'카카오뱅' substring 검색 실패: got %v" , corpNames (results ))
85100 }
86101}
87102
@@ -93,46 +108,34 @@ func TestSearch_Substring_핑크퐁(t *testing.T) {
93108 }
94109}
95110
96- // --- fuzzy tests ---
97-
98- func TestSearch_Fuzzy_마켓컬리 (t * testing.T ) {
111+ func TestSearch_Substring_오늘의집 (t * testing.T ) {
112+ // "오늘의집"은 "주식회사 오늘의집"의 substring
99113 s := testStore ()
100- results := s .Search ("마켓컬리" )
101- if len (results ) == 0 {
102- t .Fatal ("'마켓컬리' fuzzy 검색: 결과 없음 (기대: '컬리' 포함)" )
103- }
104- found := false
105- for _ , r := range results {
106- if r .CorpName == "컬리" {
107- found = true
108- }
109- }
110- if ! found {
111- t .Fatalf ("'마켓컬리' fuzzy 결과에 '컬리' 없음: got %v" , corpNames (results ))
114+ results := s .Search ("오늘의집" )
115+ if len (results ) != 1 || results [0 ].CorpName != "주식회사 오늘의집" {
116+ t .Fatalf ("'오늘의집' substring 검색 실패: got %v" , corpNames (results ))
112117 }
113- t .Logf ("'마켓컬리' fuzzy 결과: %v" , corpNames (results ))
114118}
115119
116- func TestSearch_Fuzzy_오늘의집 (t * testing.T ) {
117- // "오늘의집" → substring 으로도 찾힘
120+ // --- fuzzy tests ---
121+
122+ // TestSearch_Fuzzy_마켓컬리_컬리상위랭크 는 핵심 케이스:
123+ // "마켓컬리" 검색 시 마켓XX 회사들이 다수 있어도 "컬리"가 1위여야 한다.
124+ // (Dice 계수 기반: 컬리=0.5, 마켓4글자회사=0.333)
125+ func TestSearch_Fuzzy_마켓컬리_컬리상위랭크 (t * testing.T ) {
118126 s := testStore ()
119- results := s .Search ("오늘의집 " )
127+ results := s .Search ("마켓컬리 " )
120128 if len (results ) == 0 {
121- t .Fatal ("'오늘의집' 검색: 결과 없음" )
122- }
123- found := false
124- for _ , r := range results {
125- if r .CorpName == "주식회사 오늘의집" {
126- found = true
127- }
129+ t .Fatal ("'마켓컬리' fuzzy 검색: 결과 없음" )
128130 }
129- if ! found {
130- t .Fatalf ("'오늘의집' 결과에 '주식회사 오늘의집' 없음 : got %v" , corpNames (results ))
131+ if results [ 0 ]. CorpName != "컬리" {
132+ t .Fatalf ("'마켓컬리' fuzzy 1위는 '컬리'여야 함 : got %v" , corpNames (results ))
131133 }
134+ t .Logf ("'마켓컬리' fuzzy 결과: %v" , corpNames (results ))
132135}
133136
137+ // TestSearch_Fuzzy_법인형태어_노이즈제거: "주식회사"만 공유하는 무관한 회사가 나오면 안 됨
134138func TestSearch_Fuzzy_법인형태어_노이즈제거 (t * testing.T ) {
135- // "주식회사 오늘의집" 검색 시 "주식회사"를 공유하는 무관한 회사가 나오면 안 됨
136139 s := testStore ()
137140 results := s .Search ("주식회사 오늘의집" )
138141 for _ , r := range results {
@@ -143,6 +146,58 @@ func TestSearch_Fuzzy_법인형태어_노이즈제거(t *testing.T) {
143146 t .Logf ("'주식회사 오늘의집' 검색 결과: %v" , corpNames (results ))
144147}
145148
149+ func TestSearch_NoResult_완전엉뚱한검색어 (t * testing.T ) {
150+ s := testStore ()
151+ results := s .Search ("xyzxyz없는기업명zyx" )
152+ // 완전히 무관한 검색어는 결과가 없어야 함 (있어도 오탐이지만 경고만)
153+ if len (results ) != 0 {
154+ t .Logf ("경고: 무관한 검색어에 fuzzy 결과 %d건: %v" , len (results ), corpNames (results ))
155+ }
156+ }
157+
158+ // --- bigramSim unit tests ---
159+
160+ func TestBigramSim_완전동일 (t * testing.T ) {
161+ // 동일 문자열은 1.0
162+ score := bigramSim ("삼성전자" , "삼성전자" )
163+ if score != 1.0 {
164+ t .Fatalf ("동일 문자열 bigramSim: 1.0 기대, got %f" , score )
165+ }
166+ }
167+
168+ func TestBigramSim_Dice_짧은타겟이_높은점수 (t * testing.T ) {
169+ // Dice 계수 검증: 짧은 타겟("컬리")이 긴 타겟("컬리넥스트마일")보다 높은 점수여야 함
170+ // "마켓컬리" (3 bigrams) vs "컬리" (1 bigram): 2*1/(3+1)=0.500
171+ // "마켓컬리" (3 bigrams) vs "컬리넥스트마일" (6 bigrams): 2*1/(3+6)=0.222
172+ short := bigramSim ("마켓컬리" , "컬리" )
173+ long := bigramSim ("마켓컬리" , "컬리넥스트마일" )
174+ if short <= long {
175+ t .Fatalf ("'컬리' score(%f) > '컬리넥스트마일' score(%f) 이어야 함" , short , long )
176+ }
177+ if short < 0.3 {
178+ t .Fatalf ("'마켓컬리' vs '컬리': threshold 0.3 이상 기대, got %f" , short )
179+ }
180+ }
181+
182+ func TestBigramSim_긴노이즈_필터링 (t * testing.T ) {
183+ // 긴 회사명은 Dice로 낮아져서 threshold 이하가 되어야 함
184+ // "마켓컬리" vs "맥쿼리IMM마켓뉴트럴혼합형사모펀드": 겹치는 bigram 1개, 타겟이 매우 길어 Dice<0.3
185+ score := bigramSim ("마켓컬리" , "맥쿼리imm마켓뉴트럴혼합형사모펀드" )
186+ if score >= 0.3 {
187+ t .Fatalf ("긴 노이즈 회사명은 threshold 0.3 미만이어야 함, got %f" , score )
188+ }
189+ }
190+
191+ func TestBigramSim_단일문자 (t * testing.T ) {
192+ // 단일 rune은 bigram 없음 → 0
193+ score := bigramSim ("가" , "가나다라" )
194+ if score != 0 {
195+ t .Fatalf ("단일 문자 bigramSim: 0 기대, got %f" , score )
196+ }
197+ }
198+
199+ // --- stripLegalForm tests ---
200+
146201func TestStripLegalForm (t * testing.T ) {
147202 cases := []struct { in , want string }{
148203 {"주식회사 오늘의집" , "오늘의집" },
@@ -159,45 +214,86 @@ func TestStripLegalForm(t *testing.T) {
159214 }
160215}
161216
162- func TestSearch_NoResult_완전엉뚱한검색어 (t * testing.T ) {
163- s := testStore ()
164- results := s .Search ("xyzxyz없는기업명zyx" )
165- if len (results ) != 0 {
166- t .Logf ("'xyzxyz없는기업명zyx' 검색 결과 (fuzzy 허용): %v" , corpNames (results ))
217+ // --- integration tests (실제 캐시 데이터 기반) ---
218+
219+ // realStore 는 실제 캐시가 없으면 nil 반환.
220+ func realStore (t * testing.T ) * Store {
221+ t .Helper ()
222+ path , err := CorpCodePath ()
223+ if err != nil {
224+ t .Skip ("캐시 경로 확인 불가:" , err )
167225 }
226+ if _ , err := os .Stat (path ); os .IsNotExist (err ) {
227+ t .Skip ("캐시 파일 없음 — dartcli cache refresh 후 재시도" )
228+ }
229+ store , err := loadFromDisk (path )
230+ if err != nil {
231+ t .Skip ("캐시 로드 실패:" , err )
232+ }
233+ return store
168234}
169235
170- // --- bigramSim unit tests ---
236+ func TestIntegration_마켓컬리_컬리반환 (t * testing.T ) {
237+ s := realStore (t )
238+ results := s .Search ("마켓컬리" )
239+ if len (results ) == 0 {
240+ t .Fatal ("'마켓컬리' 검색: 결과 없음 (기대: 컬리 포함)" )
241+ }
242+ // "컬리"가 결과에 포함되어야 함
243+ found := false
244+ for _ , r := range results {
245+ if r .CorpName == "컬리" {
246+ found = true
247+ }
248+ }
249+ if ! found {
250+ t .Fatalf ("'마켓컬리' 결과에 '컬리' 없음: got %v" , corpNames (results ))
251+ }
252+ // "컬리"가 1등이어야 함 (Dice 계수로 짧은 매칭이 우선)
253+ if results [0 ].CorpName != "컬리" {
254+ t .Errorf ("'마켓컬리' 1위 기대='컬리', got='%s' (전체: %v)" , results [0 ].CorpName , corpNames (results ))
255+ }
256+ t .Logf ("결과: %v" , corpNames (results ))
257+ }
171258
172- func TestBigramSim_완전동일 (t * testing.T ) {
173- score := bigramSim ("삼성전자" , "삼성전자" )
174- if score != 1.0 {
175- t .Fatalf ("동일 문자열 bigramSim: 1.0 기대, got %f" , score )
259+ func TestIntegration_삼성전자 (t * testing.T ) {
260+ s := realStore (t )
261+ results := s .Search ("삼성전자" )
262+ if len (results ) != 1 || results [0 ].CorpName != "삼성전자" {
263+ t .Fatalf ("'삼성전자' 검색 실패: got %v" , corpNames (results ))
176264 }
177265}
178266
179- func TestBigramSim_부분겹침 (t * testing.T ) {
180- // "마켓컬리" bigrams: [마켓, 켓컬, 컬리]
181- // "컬리" bigrams: [컬리] → 1 match / 3 query bigrams = 0.333
182- score := bigramSim ("마켓컬리" , "컬리" )
183- if score < 0.3 {
184- t .Fatalf ("'마켓컬리' vs '컬리': 0.3 이상 기대, got %f" , score )
267+ func TestIntegration_당근 (t * testing.T ) {
268+ s := realStore (t )
269+ results := s .Search ("당근" )
270+ names := corpNames (results )
271+ foundMarket := false
272+ for _ , n := range names {
273+ if n == "당근마켓" {
274+ foundMarket = true
275+ }
276+ }
277+ if ! foundMarket {
278+ t .Fatalf ("'당근' 검색에 '당근마켓' 없음: got %v" , names )
185279 }
186280}
187281
188- func TestBigramSim_핑크퐁 (t * testing.T ) {
189- // "핑크퐁" bigrams: [핑크, 크퐁]
190- // "더핑크퐁컴퍼니" bigrams: [더핑, 핑크, 크퐁, 퐁컴, 컴퍼, 퍼니] → 2 match / 2 = 1.0
191- score := bigramSim ("핑크퐁" , "더핑크퐁컴퍼니" )
192- if score != 1.0 {
193- t .Fatalf ("'핑크퐁' vs '더핑크퐁컴퍼니': 1.0 기대, got %f" , score )
282+ func TestIntegration_핑크퐁 (t * testing.T ) {
283+ s := realStore (t )
284+ results := s .Search ("핑크퐁" )
285+ if len (results ) == 0 {
286+ t .Fatal ("'핑크퐁' 검색: 결과 없음" )
287+ }
288+ if results [0 ].CorpName != "더핑크퐁컴퍼니" {
289+ t .Fatalf ("'핑크퐁' 1위 기대='더핑크퐁컴퍼니', got='%s'" , results [0 ].CorpName )
194290 }
195291}
196292
197- func TestBigramSim_단일문자 (t * testing.T ) {
198- // 단일 rune은 bigram 없음 → 0
199- score := bigramSim ( "가" , "가나다라 " )
200- if score != 0 {
201- t .Fatalf ("단일 문자 bigramSim: 0 기대, got %f " , score )
293+ func TestIntegration_종목코드_삼성전자 (t * testing.T ) {
294+ s := realStore ( t )
295+ results := s . Search ( "005930 " )
296+ if len ( results ) != 1 || results [ 0 ]. CorpName != "삼성전자" {
297+ t .Fatalf ("종목코드 '005930' 검색 실패: got %v " , corpNames ( results ) )
202298 }
203299}
0 commit comments