diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cedc62daf9..3bd18a065b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -141,24 +141,25 @@ jobs: sudo docker rm -f -v "${BUILDER_CONTAINER}" fi -# - name: Prepare test resources -# run: | -# RESOURCE_REPO=https://github.com/infiniflow/resource.git -# RESOURCE_REF=549feaaf998954d65b668667f009125bc84a9c5e -# rm -rf /tmp/resource -# git clone "${RESOURCE_REPO}" /tmp/resource -# git -C /tmp/resource checkout "${RESOURCE_REF}" -# sudo mkdir -p /usr/share/infinity -# sudo ln -sf /tmp/resource /usr/share/infinity/resource -# mkdir -p resource -# ln -sf /tmp/resource/wordnet resource/wordnet -# -# - name: Test Go packages -# run: | -# set -euo pipefail -# packages=$(go list ./internal/... | grep -vE '/storage(/|$)') -# CGO_ENABLED=1 GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} \ -# go test -count=1 ${packages} + - name: Prepare test resources + run: | + RESOURCE_REPO=https://github.com/infiniflow/resource.git + RESOURCE_REF=549feaaf998954d65b668667f009125bc84a9c5e + RESOURCE_PATH="/tmp/resource-${GITHUB_RUN_ID}" + if [ -d "${RESOURCE_PATH}" ]; then rm -rf "${RESOURCE_PATH}"; fi + git clone "${RESOURCE_REPO}" "${RESOURCE_PATH}" + git -C "${RESOURCE_PATH}" checkout "${RESOURCE_REF}" + sudo mkdir -p /usr/share/infinity + sudo ln -sf "${RESOURCE_PATH}" /usr/share/infinity/resource + mkdir -p resource + ln -sf "${RESOURCE_PATH}/wordnet" resource/wordnet + + - name: Test Go packages + run: | + set -euo pipefail + packages=$(go list ./internal/... | grep -vE '/storage(/|$)|/cli\b') + CGO_ENABLED=1 GOPROXY=${GOPROXY:-https://goproxy.cn,https://proxy.golang.org,direct} \ + go test -count=1 ${packages} - name: Build ragflow:nightly run: | diff --git a/internal/entity/models/302ai_test.go b/internal/entity/models/302ai_test.go index 472415f5da..e711433c78 100644 --- a/internal/entity/models/302ai_test.go +++ b/internal/entity/models/302ai_test.go @@ -181,7 +181,7 @@ func TestAI302ListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if got := strings.Join(models, ","); got != "gpt-5,jina-embeddings-v3" { + if got := joinModelNames(models, ","); got != "gpt-5,jina-embeddings-v3" { t.Errorf("models=%q", got) } } diff --git a/internal/entity/models/anthropic_test.go b/internal/entity/models/anthropic_test.go index e2ad0768d2..56846f16ad 100644 --- a/internal/entity/models/anthropic_test.go +++ b/internal/entity/models/anthropic_test.go @@ -297,7 +297,7 @@ func TestAnthropicListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "claude-sonnet-4-5-20250929,claude-haiku-4-5-20251001" { + if joinModelNames(models, ",") != "claude-sonnet-4-5-20250929,claude-haiku-4-5-20251001" { t.Errorf("models=%v", models) } if err := m.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/astraflow_test.go b/internal/entity/models/astraflow_test.go index d948aa6d2e..6c1bbbe335 100644 --- a/internal/entity/models/astraflow_test.go +++ b/internal/entity/models/astraflow_test.go @@ -374,7 +374,7 @@ func TestAstraflowListModelsHappyPath(t *testing.T) { t.Fatalf("ListModels: %v", err) } want := []string{"claude-opus-4-7", "gpt-5.4", "Qwen/Qwen3-Max"} - if strings.Join(models, ",") != strings.Join(want, ",") { + if joinModelNames(models, ",") != strings.Join(want, ",") { t.Errorf("models=%v, want %v", models, want) } } diff --git a/internal/entity/models/avian_test.go b/internal/entity/models/avian_test.go index 3ff25220e7..464c357c51 100644 --- a/internal/entity/models/avian_test.go +++ b/internal/entity/models/avian_test.go @@ -303,7 +303,7 @@ func TestAvianListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "deepseek/deepseek-v3.2,moonshotai/kimi-k2.5" { + if joinModelNames(models, ",") != "deepseek/deepseek-v3.2,moonshotai/kimi-k2.5" { t.Errorf("models=%v", models) } if err := newAvianForTest(srv.URL).CheckConnection(cfg); err != nil { diff --git a/internal/entity/models/bedrock_test.go b/internal/entity/models/bedrock_test.go index b92edee5c0..13124461bb 100644 --- a/internal/entity/models/bedrock_test.go +++ b/internal/entity/models/bedrock_test.go @@ -438,8 +438,8 @@ func TestBedrockListModelsParsesCatalog(t *testing.T) { t.Fatalf("len(got)=%d want %d (%v)", len(got), len(want), got) } for i := range want { - if got[i] != want[i] { - t.Errorf("got[%d]=%q want %q", i, got[i], want[i]) + if got[i].Name != want[i] { + t.Errorf("got[%d]=%s want %q", i, got[i].Name, want[i]) } } } diff --git a/internal/entity/models/cometapi_test.go b/internal/entity/models/cometapi_test.go index 64d1b311ff..d084388775 100644 --- a/internal/entity/models/cometapi_test.go +++ b/internal/entity/models/cometapi_test.go @@ -447,7 +447,7 @@ func TestCometAPIListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if len(ids) != 3 || ids[0] != "gpt-5" || ids[2] != "text-embedding-3-small" { + if len(ids) != 3 || ids[0].Name != "gpt-5" || ids[2].Name != "text-embedding-3-small" { t.Errorf("ids=%v, want [gpt-5 gpt-4o-mini text-embedding-3-small]", ids) } } @@ -463,7 +463,7 @@ func TestCometAPIListModelsAllowsNilAPIConfig(t *testing.T) { if err != nil { t.Fatalf("ListModels(nil): %v", err) } - if len(ids) != 1 || ids[0] != "gpt-5" { + if len(ids) != 1 || ids[0].Name != "gpt-5" { t.Errorf("ids=%v want [gpt-5]", ids) } } diff --git a/internal/entity/models/google_test.go b/internal/entity/models/google_test.go index fc70c52fcc..1fe62b9f78 100644 --- a/internal/entity/models/google_test.go +++ b/internal/entity/models/google_test.go @@ -13,7 +13,7 @@ import ( var googleListModelsMu sync.Mutex -func withGoogleListModelsStub(t *testing.T, fn func(context.Context, *genai.ClientConfig) ([]string, error)) { +func withGoogleListModelsStub(t *testing.T, fn func(context.Context, *genai.ClientConfig) ([]ListModelResponse, error)) { t.Helper() googleListModelsMu.Lock() @@ -54,7 +54,7 @@ func TestGoogleModelListModelsRequiresAPIKey(t *testing.T) { } calls := 0 - withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]ListModelResponse, error) { calls++ return nil, nil }) @@ -83,9 +83,9 @@ func TestGoogleModelListModelsReturnsModelNames(t *testing.T) { model := &GoogleModel{} apiKey := "test-api-key" configuredAPIKey := " " + apiKey + " " - expected := []string{"models/gemini-2.5-flash", "models/gemini-2.5-pro"} + expected := []ListModelResponse{{Name: "models/gemini-2.5-flash"}, {Name: "models/gemini-2.5-pro"}} - withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]ListModelResponse, error) { if config.APIKey != apiKey { t.Fatalf("expected API key %q, got %q", apiKey, config.APIKey) } @@ -107,7 +107,7 @@ func TestGoogleModelCheckConnectionUsesListModels(t *testing.T) { apiKey := "test-api-key" calls := 0 - withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]ListModelResponse, error) { calls++ if config.APIKey != apiKey { t.Fatalf("expected API key %q, got %q", apiKey, config.APIKey) @@ -115,7 +115,7 @@ func TestGoogleModelCheckConnectionUsesListModels(t *testing.T) { if config.HTTPOptions.BaseURL != customBaseURL { t.Fatalf("expected base URL %q, got %q", customBaseURL, config.HTTPOptions.BaseURL) } - return []string{"models/gemini-2.5-flash"}, nil + return []ListModelResponse{{Name: "models/gemini-2.5-flash"}}, nil }) if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { @@ -130,7 +130,7 @@ func TestGoogleModelCheckConnectionRequiresAPIKey(t *testing.T) { model := &GoogleModel{} calls := 0 - withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]ListModelResponse, error) { calls++ return nil, nil }) @@ -182,7 +182,7 @@ func TestGoogleModelCheckConnectionReturnsListModelsError(t *testing.T) { apiKey := "test-api-key" listErr := errors.New("list models failed") - withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(context.Context, *genai.ClientConfig) ([]ListModelResponse, error) { return nil, listErr }) @@ -313,11 +313,11 @@ func TestGoogleModelListModelsPassesBaseURL(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { model := NewGoogleModel(tc.baseURL, URLSuffix{}) - withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]string, error) { + withGoogleListModelsStub(t, func(_ context.Context, config *genai.ClientConfig) ([]ListModelResponse, error) { if config.HTTPOptions.BaseURL != tc.expectedBaseURL { t.Fatalf("expected base URL %q, got %q", tc.expectedBaseURL, config.HTTPOptions.BaseURL) } - return []string{"models/gemini-2.5-flash"}, nil + return []ListModelResponse{{Name: "models/gemini-2.5-flash"}}, nil }) if _, err := model.ListModels(&APIConfig{ApiKey: &apiKey, Region: tc.region}); err != nil { @@ -345,7 +345,7 @@ func TestCollectGoogleModelNamesPaginates(t *testing.T) { t.Fatalf("expected no error, got %v", err) } - expectedModels := []string{"models/gemini-2.5-flash", "models/gemini-2.5-pro"} + expectedModels := []ListModelResponse{{Name: "models/gemini-2.5-flash"}, {Name: "models/gemini-2.5-pro"}} if !reflect.DeepEqual(models, expectedModels) { t.Fatalf("expected models %v, got %v", expectedModels, models) } diff --git a/internal/entity/models/gpustack_test.go b/internal/entity/models/gpustack_test.go index 02f5ae5a43..af05ae5bd9 100644 --- a/internal/entity/models/gpustack_test.go +++ b/internal/entity/models/gpustack_test.go @@ -438,7 +438,7 @@ func TestGPUStackListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "qwen3-8b,qwen3-32b" { + if joinModelNames(models, ",") != "qwen3-8b,qwen3-32b" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/groq_test.go b/internal/entity/models/groq_test.go index 984bd95b08..01e61a320e 100644 --- a/internal/entity/models/groq_test.go +++ b/internal/entity/models/groq_test.go @@ -306,7 +306,7 @@ func TestGroqListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "llama-3.3-70b-versatile,openai/gpt-oss-120b" { + if joinModelNames(models, ",") != "llama-3.3-70b-versatile,openai/gpt-oss-120b" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/hunyuan_test.go b/internal/entity/models/hunyuan_test.go index 1c7aaf4770..5bc42795d9 100644 --- a/internal/entity/models/hunyuan_test.go +++ b/internal/entity/models/hunyuan_test.go @@ -378,7 +378,7 @@ func TestHunyuanListModelsHappyPath(t *testing.T) { t.Fatalf("ListModels: %v", err) } want := []string{"hunyuan-pro", "hunyuan-standard", "hunyuan-standard-256K"} - if strings.Join(models, ",") != strings.Join(want, ",") { + if joinModelNames(models, ",") != strings.Join(want, ",") { t.Errorf("models=%v, want %v", models, want) } } diff --git a/internal/entity/models/jiekouai_test.go b/internal/entity/models/jiekouai_test.go index b95360268b..2e325eec6e 100644 --- a/internal/entity/models/jiekouai_test.go +++ b/internal/entity/models/jiekouai_test.go @@ -178,7 +178,7 @@ func TestJieKouAIListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if got := strings.Join(models, ","); got != "gpt-5,text-embedding-3-large" { + if got := joinModelNames(models, ","); got != "gpt-5,text-embedding-3-large" { t.Errorf("models=%q", got) } } diff --git a/internal/entity/models/longcat_test.go b/internal/entity/models/longcat_test.go index d36a2867b2..9591d6fa93 100644 --- a/internal/entity/models/longcat_test.go +++ b/internal/entity/models/longcat_test.go @@ -463,7 +463,7 @@ func TestLongCatListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if got := strings.Join(models, ","); got != "LongCat-Flash-Chat,LongCat-Flash-Thinking-2601" { + if got := joinModelNames(models, ","); got != "LongCat-Flash-Chat,LongCat-Flash-Thinking-2601" { t.Errorf("models=%q", got) } if err := newLongCatForTest(srv.URL).CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/minimax_test.go b/internal/entity/models/minimax_test.go index 6093a713cd..d3dd6c1b71 100644 --- a/internal/entity/models/minimax_test.go +++ b/internal/entity/models/minimax_test.go @@ -245,7 +245,7 @@ func TestMinimaxListModelsUsesBodylessGet(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if got := strings.Join(models, ","); got != "MiniMax-M3,minimax-m2.7" { + if got := joinModelNames(models, ","); got != "MiniMax-M3,minimax-m2.7" { t.Errorf("models=%q", got) } } diff --git a/internal/entity/models/mistral_test.go b/internal/entity/models/mistral_test.go index 8be9d89935..6e9a07312b 100644 --- a/internal/entity/models/mistral_test.go +++ b/internal/entity/models/mistral_test.go @@ -339,7 +339,7 @@ func TestMistralListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if len(ids) != 3 || ids[0] != "mistral-large-latest" || ids[2] != "mistral-embed" { + if len(ids) != 3 || ids[0].Name != "mistral-large-latest" || ids[2].Name != "mistral-embed" { t.Errorf("ids=%v, want [mistral-large-latest mistral-small-latest mistral-embed]", ids) } } diff --git a/internal/entity/models/model.go b/internal/entity/models/model.go index b915eb68ca..26b2dc696a 100644 --- a/internal/entity/models/model.go +++ b/internal/entity/models/model.go @@ -292,9 +292,10 @@ func InitProviderManager(dirPath string) error { return fmt.Errorf("no JSON files found in directory %s", dirPath) } - // Read the file + // Read the file. Use a repo-root-relative path so that go test + // (which sets CWD to the package directory) can still find it. var data []byte - data, err = os.ReadFile("conf/all_models.json") + data, err = os.ReadFile(filepath.Join(findRepoRoot(), "conf", "all_models.json")) if err != nil { return fmt.Errorf("error reading file 'conf/all_models.json': %w", err) } @@ -700,6 +701,23 @@ func modelHasFeature(features Features, featureType string) bool { } } +// findRepoRoot walks up from CWD until it finds the repo root (marked by +// conf/all_models.json). This makes tests work regardless of the Go test +// binary's CWD (which is set to the package directory by go test). +func findRepoRoot() string { + dir, err := os.Getwd() + if err != nil { + return "." + } + for dir != "/" && dir != "" { + if _, err := os.Stat(filepath.Join(dir, "conf", "all_models.json")); err == nil { + return dir + } + dir = filepath.Dir(dir) + } + return "." +} + // Helper: Find provider by name func (pm *ProviderManager) FindProvider(name string) *Provider { for i := range pm.Providers { diff --git a/internal/entity/models/model_test.go b/internal/entity/models/model_test.go index f4dac06e96..77475c6eaf 100644 --- a/internal/entity/models/model_test.go +++ b/internal/entity/models/model_test.go @@ -22,12 +22,23 @@ import ( "strings" "testing" ) +// joinModelNames extracts model names from a ListModelResponse slice and +// joins them with sep, for use in test assertions. +func joinModelNames(models []ListModelResponse, sep string) string { + names := make([]string, len(models)) + for i, m := range models { + names[i] = m.Name + } + return strings.Join(names, sep) +} + + func readProviderConfig(t *testing.T, fileName string) []byte { t.Helper() for _, candidate := range []string{ - filepath.Join("..", "..", "conf", "models", fileName), + filepath.Join("..", "..", "..", "conf", "models", fileName), filepath.Join("conf", "models", fileName), } { data, err := os.ReadFile(candidate) @@ -140,6 +151,8 @@ func TestProviderConfigsLoadURLSuffixKeys(t *testing.T) { t.Fatalf("InitProviderManager: %v", err) } + pm := GetProviderManager() + cohere := pm.FindProvider("CoHere") if cohere == nil { t.Fatal("CoHere provider not found") @@ -180,7 +193,7 @@ func TestProviderConfigRejectsUnknownURLSuffixKey(t *testing.T) { t.Fatalf("write config: %v", err) } - _, err := InitProviderManager(dir) + err := InitProviderManager(dir) if err == nil { t.Fatal("InitProviderManager succeeded with unknown url_suffix key") } @@ -203,6 +216,8 @@ func TestPPIOProviderConfigLoadsIntoProviderManager(t *testing.T) { t.Fatalf("InitProviderManager: %v", err) } + pm := GetProviderManager() + provider := pm.FindProvider("ppio") if provider == nil { t.Fatal("PPIO provider not found") @@ -252,22 +267,22 @@ func TestPPIOProviderConfigLoadsIntoProviderManager(t *testing.T) { if err != nil { t.Fatalf("GetModelByName: %v", err) } - if model.MaxTokens != 64000 { - t.Errorf("deepseek/deepseek-r1 max_tokens=%d", model.MaxTokens) + if *model.MaxTokens != 64000 { + t.Errorf("deepseek/deepseek-r1 max_tokens=%d", *model.MaxTokens) } model, err = pm.GetModelByName("ppio", "deepseek/deepseek-v4-pro") if err != nil { t.Fatalf("GetModelByName v4 pro: %v", err) } - if model.MaxTokens != 1048576 { - t.Errorf("deepseek/deepseek-v4-pro max_tokens=%d", model.MaxTokens) + if *model.MaxTokens != 1048576 { + t.Errorf("deepseek/deepseek-v4-pro max_tokens=%d", *model.MaxTokens) } model, err = pm.GetModelByName("ppio", "deepseek/deepseek-v4-flash") if err != nil { t.Fatalf("GetModelByName v4 flash: %v", err) } - if model.MaxTokens != 1048576 { - t.Errorf("deepseek/deepseek-v4-flash max_tokens=%d", model.MaxTokens) + if *model.MaxTokens != 1048576 { + t.Errorf("deepseek/deepseek-v4-flash max_tokens=%d", *model.MaxTokens) } resp := pm.SearchByType("chat") @@ -290,6 +305,8 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { t.Fatalf("InitProviderManager: %v", err) } + pm := GetProviderManager() + provider := pm.FindProvider("SiliconFlow") if provider == nil { t.Fatal("SiliconFlow provider not found") @@ -314,8 +331,8 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { if err != nil { t.Fatalf("GetModelByName DeepSeek-V4-Pro: %v", err) } - if deepSeekV4Pro.MaxTokens != 1048576 { - t.Errorf("DeepSeek-V4-Pro max_tokens=%d", deepSeekV4Pro.MaxTokens) + if *deepSeekV4Pro.MaxTokens != 1048576 { + t.Errorf("DeepSeek-V4-Pro max_tokens=%d", *deepSeekV4Pro.MaxTokens) } if !deepSeekV4Pro.ModelTypeMap["chat"] { t.Errorf("DeepSeek-V4-Pro model types=%v, want chat", deepSeekV4Pro.ModelTypes) @@ -325,8 +342,8 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { if err != nil { t.Fatalf("GetModelByName Kimi-K2.6: %v", err) } - if kimiK26.MaxTokens != 262144 { - t.Errorf("Kimi-K2.6 max_tokens=%d", kimiK26.MaxTokens) + if *kimiK26.MaxTokens != 262144 { + t.Errorf("Kimi-K2.6 max_tokens=%d", *kimiK26.MaxTokens) } if !kimiK26.ModelTypeMap["chat"] || !kimiK26.ModelTypeMap["vision"] { t.Errorf("Kimi-K2.6 model types=%v, want chat+vision", kimiK26.ModelTypes) @@ -336,7 +353,7 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { if err != nil { t.Fatalf("GetModelByName GLM-5.1: %v", err) } - if glm51.MaxTokens != 204800 { - t.Errorf("GLM-5.1 max_tokens=%d", glm51.MaxTokens) + if *glm51.MaxTokens != 204800 { + t.Errorf("GLM-5.1 max_tokens=%d", *glm51.MaxTokens) } } diff --git a/internal/entity/models/modelscope_test.go b/internal/entity/models/modelscope_test.go index 3333469d0a..535e9eaabf 100644 --- a/internal/entity/models/modelscope_test.go +++ b/internal/entity/models/modelscope_test.go @@ -308,7 +308,7 @@ func TestModelScopeListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "Qwen/Qwen2.5-7B-Instruct,Qwen/Qwen3-8B" { + if joinModelNames(models, ",") != "Qwen/Qwen2.5-7B-Instruct,Qwen/Qwen3-8B" { t.Errorf("models=%v", models) } if err := m.CheckConnection(apiConfig); err != nil { diff --git a/internal/entity/models/moonshot_test.go b/internal/entity/models/moonshot_test.go index f80013086b..d8eb5f7a72 100644 --- a/internal/entity/models/moonshot_test.go +++ b/internal/entity/models/moonshot_test.go @@ -244,7 +244,7 @@ func TestMoonshotListModelsUsesBodylessGet(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if got := strings.Join(models, ","); got != "kimi-k2.6,moonshot-v1-8k" { + if got := joinModelNames(models, ","); got != "kimi-k2.6,moonshot-v1-8k" { t.Errorf("models=%q", got) } } diff --git a/internal/entity/models/perplexity_test.go b/internal/entity/models/perplexity_test.go index 39640d2457..a219c9770c 100644 --- a/internal/entity/models/perplexity_test.go +++ b/internal/entity/models/perplexity_test.go @@ -259,7 +259,7 @@ func TestPerplexityListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "sonar,sonar-pro,pplx-embed-v1-0.6b" { + if joinModelNames(models, ",") != "sonar,sonar-pro,pplx-embed-v1-0.6b" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { @@ -281,7 +281,7 @@ func TestPerplexityListModelsAcceptsBareArray(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "sonar,sonar-pro" { + if joinModelNames(models, ",") != "sonar,sonar-pro" { t.Errorf("models=%v", models) } } diff --git a/internal/entity/models/ppio_test.go b/internal/entity/models/ppio_test.go index 34bfbc7b45..f9e6ff256d 100644 --- a/internal/entity/models/ppio_test.go +++ b/internal/entity/models/ppio_test.go @@ -376,7 +376,7 @@ func TestPPIOListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "deepseek/deepseek-r1,qwen/qwen-2.5-72b-instruct" { + if joinModelNames(models, ",") != "deepseek/deepseek-r1,qwen/qwen-2.5-72b-instruct" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/replicate_test.go b/internal/entity/models/replicate_test.go index d9bd64e498..50701e8983 100644 --- a/internal/entity/models/replicate_test.go +++ b/internal/entity/models/replicate_test.go @@ -299,7 +299,7 @@ func TestReplicateListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "meta/meta-llama-3-70b-instruct,replicate/hello-world" { + if joinModelNames(models, ",") != "meta/meta-llama-3-70b-instruct,replicate/hello-world" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/togetherai_test.go b/internal/entity/models/togetherai_test.go index cfaaa562d9..336bbb1d5e 100644 --- a/internal/entity/models/togetherai_test.go +++ b/internal/entity/models/togetherai_test.go @@ -255,7 +255,7 @@ func TestTogetherAIListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "openai/gpt-oss-20b,meta-llama/Llama-3.3-70B-Instruct-Turbo" { + if joinModelNames(models, ",") != "openai/gpt-oss-20b,meta-llama/Llama-3.3-70B-Instruct-Turbo" { t.Errorf("models=%v", models) } if err := model.CheckConnection(&APIConfig{ApiKey: &apiKey}); err != nil { diff --git a/internal/entity/models/tokenhub_test.go b/internal/entity/models/tokenhub_test.go index d01c246e82..3a17a1a4d0 100644 --- a/internal/entity/models/tokenhub_test.go +++ b/internal/entity/models/tokenhub_test.go @@ -305,7 +305,7 @@ func TestTokenHubListModelsHappyPathSkipsMalformedItems(t *testing.T) { t.Fatalf("ListModels: %v", err) } want := []string{"gpt-4o-mini", "gpt-4o"} - if strings.Join(models, ",") != strings.Join(want, ",") { + if joinModelNames(models, ",") != strings.Join(want, ",") { t.Fatalf("models=%v, want %v", models, want) } } diff --git a/internal/entity/models/tokenpony_test.go b/internal/entity/models/tokenpony_test.go index 73f1240e0e..82724ff54c 100644 --- a/internal/entity/models/tokenpony_test.go +++ b/internal/entity/models/tokenpony_test.go @@ -374,7 +374,7 @@ func TestTokenPonyListModelsHappyPath(t *testing.T) { t.Fatalf("ListModels: %v", err) } want := []string{"qwen3-32b", "deepseek-v3-0324", "qwen3-coder-480b"} - if strings.Join(models, ",") != strings.Join(want, ",") { + if joinModelNames(models, ",") != strings.Join(want, ",") { t.Errorf("models=%v, want %v", models, want) } } diff --git a/internal/entity/models/volcengine_test.go b/internal/entity/models/volcengine_test.go index 08da61b04f..c7870d4ad4 100644 --- a/internal/entity/models/volcengine_test.go +++ b/internal/entity/models/volcengine_test.go @@ -81,7 +81,7 @@ func TestVolcEngineListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "doubao-seed-2-0-pro-260215@volcengine,doubao-embedding-vision-251215" { + if joinModelNames(models, ",") != "doubao-seed-2-0-pro-260215@volcengine,doubao-embedding-vision-251215" { t.Errorf("models=%v", models) } } diff --git a/internal/entity/models/xai_test.go b/internal/entity/models/xai_test.go index 40a538c014..44cc3fde04 100644 --- a/internal/entity/models/xai_test.go +++ b/internal/entity/models/xai_test.go @@ -73,7 +73,7 @@ func TestXAIListModelsHappyPath(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "grok-4,grok-3-mini" { + if joinModelNames(models, ",") != "grok-4,grok-3-mini" { t.Fatalf("models=%v", models) } } diff --git a/internal/entity/models/xinference_test.go b/internal/entity/models/xinference_test.go index 4e03bdf644..5b2ae5f7d9 100644 --- a/internal/entity/models/xinference_test.go +++ b/internal/entity/models/xinference_test.go @@ -266,7 +266,7 @@ func TestXinferenceListModelsAndCheckConnection(t *testing.T) { if err != nil { t.Fatalf("ListModels: %v", err) } - if strings.Join(models, ",") != "qwen2.5-instruct,custom-chat" { + if joinModelNames(models, ",") != "qwen2.5-instruct,custom-chat" { t.Errorf("models=%v", models) } if err := x.CheckConnection(apiConfig); err != nil {