//go:build llm_script package main import ( "context" "database/sql" "path/filepath" "strings" "testing" ) func TestLoadIntradaySearchRecordsFromFixture(t *testing.T) { cfg := intradayProviderConfig{ Mode: "fixture", Fixture: filepath.Join("testdata", "intraday_discovery_search_sample.json"), } records, err := loadIntradaySearchRecords(cfg, "2026-05-25", []string{"OpenAI pricing release"}) if err != nil { t.Fatalf("loadIntradaySearchRecords 返回错误: %v", err) } if len(records) != 2 { t.Fatalf("搜索样例条数错误: got=%d", len(records)) } if records[0].URL == "" || records[0].Provider == "" { t.Fatalf("搜索样例未保留 URL/provider: %+v", records[0]) } } func TestLoadIntradayLLMRecordsFromFixture(t *testing.T) { cfg := intradayProviderConfig{ Mode: "fixture", Fixture: filepath.Join("testdata", "intraday_discovery_llm_sample.json"), } records, err := loadIntradayLLMRecords(cfg, "2026-05-25", nil) if err != nil { t.Fatalf("loadIntradayLLMRecords 返回错误: %v", err) } if len(records) != 2 { t.Fatalf("LLM 样例条数错误: got=%d", len(records)) } if records[0].EventType != "official_release" { t.Fatalf("LLM 事件类型错误: %+v", records[0]) } } func TestNormalizeIntradayCandidatesDedupesEquivalentEvents(t *testing.T) { searchRecords := []intradaySearchRecord{{ Title: "OpenAI announces GPT-5.6 preview pricing update", Summary: "Search summary", URL: "https://openai.example.com/news/gpt-5-6-pricing", Provider: "OpenAI", PublishedAt: "2026-05-25", }} llmRecords := []intradayLLMRecord{ { EventType: "official_release", ProviderName: "OpenAI", ModelName: "GPT-5.6", ProviderCountry: "US", Title: "GPT-5.6 preview pricing update", Summary: "First summary", CandidateURLs: []string{"https://openai.example.com/news/gpt-5-6-pricing"}, }, { EventType: "official_release", ProviderName: "OpenAI", ModelName: "GPT 5.6", ProviderCountry: "US", Title: "OpenAI GPT 5.6 preview pricing update", Summary: "Second summary", CandidateURLs: []string{"https://openai.example.com/news/gpt-5-6-pricing"}, }, } candidates := normalizeIntradayCandidates("2026-05-25", searchRecords, llmRecords) if len(candidates) != 1 { t.Fatalf("期望去重后只剩 1 条候选, got=%d", len(candidates)) } if candidates[0].DiscoverySource != "web_search+llm" { t.Fatalf("期望 discovery source 合并, got=%q", candidates[0].DiscoverySource) } } func TestNormalizeIntradayCandidatesDropsOutdatedSearchMatches(t *testing.T) { searchRecords := []intradaySearchRecord{{ Title: "Old DeepSeek pricing article", Summary: "Yesterday record", URL: "https://deepseek.example.com/pricing", Provider: "DeepSeek", PublishedAt: "2026-05-24", }} llmRecords := []intradayLLMRecord{{ EventType: "price_cut", ProviderName: "DeepSeek", ModelName: "DeepSeek-V4-Flash", ProviderCountry: "CN", Title: "DeepSeek V4 Flash price cut", Summary: "Should be dropped because search evidence is stale", CandidateURLs: []string{"https://deepseek.example.com/pricing"}, }} candidates := normalizeIntradayCandidates("2026-05-25", searchRecords, llmRecords) if len(candidates) != 0 { t.Fatalf("旧闻搜索结果不应进入候选池, got=%d", len(candidates)) } } func TestNormalizeIntradayCandidatesDropsURLlessRecords(t *testing.T) { llmRecords := []intradayLLMRecord{{ EventType: "promo_campaign", ProviderName: "DeepSeek", ModelName: "DeepSeek-V4-Flash", Title: "No URL candidate", Summary: "Should be dropped", }} candidates := normalizeIntradayCandidates("2026-05-25", nil, llmRecords) if len(candidates) != 0 { t.Fatalf("无 URL 候选应被丢弃, got=%d", len(candidates)) } } func TestSearchRecordMatchesLocalizedBingDate(t *testing.T) { record := intradaySearchRecord{PublishedAt: "周一, 25 5月 2026 14:08:00 GMT"} if !searchRecordMatchesDate(record, "2026-05-25") { t.Fatal("应识别本地化 Bing pubDate 为当天") } } func TestValidateIntradayProviderConfigRequiresCommandOrURLOrFixture(t *testing.T) { if err := validateIntradayProviderConfig("search", intradayProviderConfig{Mode: "command_json"}); err == nil { t.Fatal("缺少 command 时应报错") } if err := validateIntradayProviderConfig("llm", intradayProviderConfig{Mode: "http_json"}); err == nil { t.Fatal("缺少 url 时应报错") } if err := validateIntradayProviderConfig("search", intradayProviderConfig{Mode: "fixture", Fixture: "fixture.json"}); err != nil { t.Fatalf("fixture provider 不应报错: %v", err) } } func TestBuildIntradayNormalizedKeyUsesProviderModelAndDate(t *testing.T) { key := buildIntradayNormalizedKey(intradayNewsCandidate{ CandidateDate: "2026-05-25", EventType: "official_release", ProviderName: "OpenAI", ModelName: "GPT-5.6", }) if !strings.Contains(key, "2026-05-25") || !strings.Contains(key, "openai") || !strings.Contains(key, "gpt-5-6") { t.Fatalf("normalized key 不符合预期: %q", key) } } func TestUpsertIntradayCandidatesRequiresDB(t *testing.T) { var db *sql.DB err := upsertIntradayCandidates(context.Background(), db, nil) if err == nil { t.Fatal("nil db 时应报错") } }