//go:build llm_script package main import ( "bytes" "path/filepath" "strings" "testing" ) func TestBuildPlanCatalogRows(t *testing.T) { envelope, err := loadPlanCatalogSeeds([]string{ filepath.Join("..", "seeds", "plan_catalog_inventory_seed.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_cn_vendors_top20.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_cn_relays_top20plus.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_web_research.json"), }) if err != nil { t.Fatalf("loadPlanCatalogSeeds 失败: %v", err) } rows, err := buildPlanCatalogRows(envelope) if err != nil { t.Fatalf("buildPlanCatalogRows 失败: %v", err) } if len(rows) != 71 { t.Fatalf("期望 71 条基础目录记录,实际 %d", len(rows)) } foundVendorTop20 := false foundRelayTop20Plus := false wantImporterKeys := map[string]string{ "tencent-cloud-token-plan-enterprise-pro": "tencent_catalog", "tencent-cloud-token-plan-enterprise-lite": "tencent_catalog", "tencent-cloud-coding-plan": "tencent_catalog", "aliyun-bailian-token-plan-team": "import_aliyun_subscription.go", "aliyun-bailian-coding-plan": "import_aliyun_subscription.go", "baidu-qianfan-token-benefit-pack": "import_baidu_subscription.go", "baidu-qianfan-coding-plan": "import_baidu_subscription.go", "bytedance-doubao-api-payg": "import_bytedance_pricing.go", "zhipu-glm-coding-plan": "import_zhipu_coding_plan.go", "minimax-token-plan": "import_minimax_subscription.go", "volcengine-ark-coding-plan": "import_bytedance_subscription.go", "huawei-cloud-maas-package-plan": "import_huawei_package.go", "ctyun-token-plan": "import_ctyun_subscription.go", "ctyun-coding-plan": "import_ctyun_subscription.go", "cucloud-aicp-platform": "import_cucloud_catalog.go", "cucloud-ai-app-platform": "import_cucloud_catalog.go", "cucloud-aisp-token-plan-pricing": "import_cucloud_pricing.go", "mobile-cloud-ai-market": "import_mobile_cloud_pricing.go", "aliyun-modelscope-api-inference": "import_catalog_seed_verification.go", "youdao-zhiyun-maas": "import_youdao_pricing.go", "ctyun-model-inference-payg": "import_catalog_seed_verification.go", "360-open-platform": "import_360_pricing.go", "siliconflow-siliconcloud": "import_siliconflow_pricing.go", "ppio-model-api": "import_ppio_pricing.go", "ucloud-umodelverse": "import_ucloud_pricing.go", "anthropic-api-payg": "import_catalog_seed_verification.go", "xai-api-payg": "import_catalog_seed_verification.go", "alibaba-qwen-api-payg": "import_qwen_pricing.go", "tencent-hunyuan-api-payg": "import_hunyuan_pricing.go", "huawei-pangu-api-payg": "import_huawei_maas_pricing.go", "baichuan-api-payg": "import_catalog_seed_verification.go", "01ai-api-payg": "import_catalog_seed_verification.go", "sensenova-api-payg": "import_catalog_seed_verification.go", "xfyun-spark-api-payg": "import_catalog_seed_verification.go", "360-zhinao-api-payg": "import_catalog_seed_verification.go", "youdao-ziyue-api-payg": "import_catalog_seed_verification.go", "modelbest-minicpm-api-payg": "import_catalog_seed_verification.go", "baai-flagopen-api-payg": "import_catalog_seed_verification.go", "skywork-api-payg": "import_catalog_seed_verification.go", "infinigence-api-payg": "import_catalog_seed_verification.go", "qingcloud-coreshub": "import_coreshub_pricing.go", "ksyun-xingliu-platform": "import_catalog_seed_verification.go", "google-gemini-api-payg": "import_catalog_seed_verification.go", "mistral-api-payg": "import_catalog_seed_verification.go", "cohere-api-payg": "import_catalog_seed_verification.go", "openrouter-api-payg": "fetch_openrouter.go", "together-ai-api-payg": "import_catalog_seed_verification.go", "fireworks-ai-api-payg": "import_catalog_seed_verification.go", "deepinfra-api-payg": "import_catalog_seed_verification.go", "groq-api-payg": "import_catalog_seed_verification.go", "replicate-api-payg": "import_catalog_seed_verification.go", "hyperbolic-api-payg": "import_catalog_seed_verification.go", "novita-ai-api-payg": "import_catalog_seed_verification.go", "azure-openai-service-payg": "import_azure_openai_pricing.go", "amazon-bedrock-payg": "import_bedrock_pricing.go", "google-vertex-ai-genai-payg": "import_vertex_pricing.go", "cloudflare-workers-ai-payg": "import_cloudflare_pricing.go", "baseten-inference-payg": "import_catalog_seed_verification.go", "cerebras-inference-payg": "import_catalog_seed_verification.go", "perplexity-agent-api-payg": "import_perplexity_pricing.go", "sambanova-cloud-payg": "import_catalog_seed_verification.go", "jdcloud-joybuilder-payg": "import_catalog_seed_verification.go", } for _, row := range rows { if row.CatalogCode == "zhipu-glm-coding-plan" { if row.CatalogSegment != "vendor_top20" || row.MarketRank != 5 { t.Fatalf("智谱榜单字段错误: segment=%q rank=%d", row.CatalogSegment, row.MarketRank) } foundVendorTop20 = true } if row.CatalogCode == "ctyun-coding-plan" { if row.CatalogSegment != "relay_top20plus" || row.MarketRank != 9 { t.Fatalf("天翼云编码套餐榜单字段错误: segment=%q rank=%d", row.CatalogSegment, row.MarketRank) } foundRelayTop20Plus = true } if wantImporterKey, ok := wantImporterKeys[row.CatalogCode]; ok && row.ImporterKey != wantImporterKey { t.Fatalf("%s importerKey 错误: got=%q want=%q", row.CatalogCode, row.ImporterKey, wantImporterKey) } } if !foundVendorTop20 { t.Fatalf("缺少 vendor_top20 覆盖记录") } if !foundRelayTop20Plus { t.Fatalf("缺少 relay_top20plus 覆盖记录") } } func TestRunPlanCatalogImportDryRunPrintsSummary(t *testing.T) { var out bytes.Buffer err := runPlanCatalogImport(importPlanCatalogConfig{ SeedPaths: strings.Join([]string{ filepath.Join("..", "seeds", "plan_catalog_inventory_seed.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_cn_vendors_top20.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_cn_relays_top20plus.json"), filepath.Join("..", "seeds", "plan_catalog_inventory_seed_web_research.json"), }, ","), DryRun: true, }, nil, &out) if err != nil { t.Fatalf("runPlanCatalogImport 失败: %v", err) } output := out.String() for _, want := range []string{ "source=plan-catalog-import", "rows=71", "coding_plan:7", "package_plan:1", "pay_as_you_go:52", "token_plan:8", "unknown:3", "confirmed:71", "dry_run=true", } { if !strings.Contains(output, want) { t.Fatalf("输出缺少 %q,实际: %q", want, output) } } }