Files
ragflow/internal/service/model_service_test.go
bitloi d69518ea42 fix(go): guard custom base URL driver creation (#15030)
### What problem does this PR solve?

Closes #15029.

Some custom `base_url` paths in `ModelProviderService` call
`NewInstance(newURL)` and then immediately invoke methods on the
returned driver. Several real Go model drivers still return `nil` from
`NewInstance`, so those paths can panic instead of returning a normal
error.

This PR:

- centralizes custom base URL driver creation in `model_service.go`
- skips request-local driver creation when `base_url` is blank or
whitespace
- preserves the existing region key behavior when building the
request-local base URL map
- returns a clear error when the provider driver is missing or
`NewInstance` returns `nil`
- routes list/check/task and active model paths through the guarded
helper
- adds focused unit coverage for empty-region preservation, regional
base URLs, blank base URLs, nil drivers, and nil `NewInstance` results

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

### Test plan

- [x] `git diff --check upstream/main...HEAD`
- [x] `/root/go/bin/gofmt -w internal/service/model_service.go
internal/service/model_service_test.go`
- [x] `GOPATH=/root/gopath GOTOOLCHAIN=local /root/go/bin/go test
./internal/service -run TestNewModelDriverForBaseURL -count=1 -vet=off`
- [x] `GOPATH=/root/gopath GOTOOLCHAIN=local /root/go/bin/go build
./internal/service/... ./internal/entity/models/...`

Note: the same targeted `go test` command without `-vet=off` is
currently blocked by an existing unrelated vet finding in
`internal/service/llm.go:355` (`non-constant format string in call to
fmt.Errorf`).
2026-05-20 14:58:20 +08:00

130 lines
3.6 KiB
Go

package service
import (
"strings"
"testing"
modelModule "ragflow/internal/entity/models"
)
type stubModelDriver struct {
modelModule.ModelDriver
newInstance func(map[string]string) modelModule.ModelDriver
}
var _ modelModule.ModelDriver = (*stubModelDriver)(nil)
func (s *stubModelDriver) NewInstance(baseURL map[string]string) modelModule.ModelDriver {
if s.newInstance != nil {
return s.newInstance(baseURL)
}
return s
}
func (s *stubModelDriver) Name() string {
return "stub"
}
func TestNewModelDriverForBaseURLPreservesEmptyRegion(t *testing.T) {
expected := &stubModelDriver{}
var gotBaseURL map[string]string
driver := &stubModelDriver{
newInstance: func(baseURL map[string]string) modelModule.ModelDriver {
gotBaseURL = baseURL
return expected
},
}
got, err := newModelDriverForBaseURL(driver, "stub", "", "http://localhost:1234")
if err != nil {
t.Fatalf("newModelDriverForBaseURL returned error: %v", err)
}
if got != expected {
t.Fatalf("expected returned driver %p, got %p", expected, got)
}
if gotBaseURL[""] != "http://localhost:1234" {
t.Fatalf("expected empty-region base URL, got %#v", gotBaseURL)
}
if _, ok := gotBaseURL["default"]; ok {
t.Fatalf("unexpected default region key in base URL map: %#v", gotBaseURL)
}
}
func TestNewModelDriverForBaseURLUsesProvidedRegion(t *testing.T) {
var gotBaseURL map[string]string
driver := &stubModelDriver{
newInstance: func(baseURL map[string]string) modelModule.ModelDriver {
gotBaseURL = baseURL
return &stubModelDriver{}
},
}
_, err := newModelDriverForBaseURL(driver, "stub", "cn-hangzhou", "http://localhost:5678")
if err != nil {
t.Fatalf("newModelDriverForBaseURL returned error: %v", err)
}
if gotBaseURL["cn-hangzhou"] != "http://localhost:5678" {
t.Fatalf("expected regional base URL, got %#v", gotBaseURL)
}
if _, ok := gotBaseURL["default"]; ok {
t.Fatalf("unexpected default region key in base URL map: %#v", gotBaseURL)
}
}
func TestNewModelDriverForBaseURLSkipsEmptyBaseURL(t *testing.T) {
for _, baseURL := range []string{"", " "} {
t.Run(baseURL, func(t *testing.T) {
called := false
driver := &stubModelDriver{
newInstance: func(map[string]string) modelModule.ModelDriver {
called = true
return nil
},
}
got, err := newModelDriverForBaseURL(driver, "deepseek", "default", baseURL)
if err != nil {
t.Fatalf("newModelDriverForBaseURL returned error: %v", err)
}
if got != driver {
t.Fatalf("expected original driver %p, got %p", driver, got)
}
if called {
t.Fatal("expected empty base URL to skip NewInstance")
}
})
}
}
func TestNewModelDriverForBaseURLRejectsNilInstance(t *testing.T) {
driver := &stubModelDriver{
newInstance: func(map[string]string) modelModule.ModelDriver {
return nil
},
}
got, err := newModelDriverForBaseURL(driver, "deepseek", "default", "http://localhost:1234")
if err == nil {
t.Fatal("expected nil NewInstance result to return an error")
}
if got != nil {
t.Fatalf("expected nil driver on error, got %T", got)
}
if !strings.Contains(err.Error(), "deepseek") || !strings.Contains(err.Error(), "custom base_url") {
t.Fatalf("expected provider-specific custom base_url error, got %v", err)
}
}
func TestNewModelDriverForBaseURLRejectsNilDriver(t *testing.T) {
got, err := newModelDriverForBaseURL(nil, "deepseek", "default", "http://localhost:1234")
if err == nil {
t.Fatal("expected nil driver to return an error")
}
if got != nil {
t.Fatalf("expected nil driver on error, got %T", got)
}
if !strings.Contains(err.Error(), "driver not found") {
t.Fatalf("expected driver not found error, got %v", err)
}
}