159 lines
5.3 KiB
Go
159 lines
5.3 KiB
Go
//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 时应报错")
|
|
}
|
|
}
|