mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 23:41:12 +08:00
### What problem does this PR solve? Closes #15199. The add-custom-model endpoint is routed through `/api/v1/providers/:provider_name/instances/:instance_name/models`, but the handler previously trusted `provider_name` and `instance_name` from the JSON body instead of the path target. A request could therefore hit one provider/instance URL while operating on a different body provider/instance. The same handler only rejected `model_types` when the slice was nil. An empty array passed validation and reached `ModelProviderService.AddCustomModel`, where `request.ModelTypes[0]` could panic. This PR makes the path provider/instance authoritative, rejects mismatched body values, rejects missing or empty `model_types`, and adds a service-level guard so direct service callers cannot hit the same panic path. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"ragflow/internal/service"
|
|
)
|
|
|
|
func TestPrepareAddCustomModelRequestUsesPathTarget(t *testing.T) {
|
|
req := service.AddCustomModelRequest{
|
|
ModelName: "custom-chat",
|
|
ModelTypes: []string{"chat"},
|
|
}
|
|
|
|
if err := prepareAddCustomModelRequest(&req, "openai", "default"); err != nil {
|
|
t.Fatalf("prepareAddCustomModelRequest returned error: %v", err)
|
|
}
|
|
if req.ProviderName != "openai" {
|
|
t.Fatalf("expected provider name from path, got %q", req.ProviderName)
|
|
}
|
|
if req.InstanceName != "default" {
|
|
t.Fatalf("expected instance name from path, got %q", req.InstanceName)
|
|
}
|
|
}
|
|
|
|
func TestPrepareAddCustomModelRequestAcceptsCaseInsensitivePathMatch(t *testing.T) {
|
|
req := service.AddCustomModelRequest{
|
|
ProviderName: "openai",
|
|
InstanceName: "default",
|
|
ModelName: "custom-chat",
|
|
ModelTypes: []string{"chat"},
|
|
}
|
|
|
|
if err := prepareAddCustomModelRequest(&req, "OpenAI", "Default"); err != nil {
|
|
t.Fatalf("prepareAddCustomModelRequest returned error: %v", err)
|
|
}
|
|
if req.ProviderName != "OpenAI" {
|
|
t.Fatalf("expected provider name from path, got %q", req.ProviderName)
|
|
}
|
|
if req.InstanceName != "Default" {
|
|
t.Fatalf("expected instance name from path, got %q", req.InstanceName)
|
|
}
|
|
}
|
|
|
|
func TestPrepareAddCustomModelRequestRejectsPathMismatches(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req service.AddCustomModelRequest
|
|
expectedErr string
|
|
}{
|
|
{
|
|
name: "provider",
|
|
req: service.AddCustomModelRequest{
|
|
ProviderName: "deepseek",
|
|
InstanceName: "default",
|
|
ModelName: "custom-chat",
|
|
ModelTypes: []string{"chat"},
|
|
},
|
|
expectedErr: "Provider name does not match path",
|
|
},
|
|
{
|
|
name: "instance",
|
|
req: service.AddCustomModelRequest{
|
|
ProviderName: "openai",
|
|
InstanceName: "other",
|
|
ModelName: "custom-chat",
|
|
ModelTypes: []string{"chat"},
|
|
},
|
|
expectedErr: "Instance name does not match path",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := prepareAddCustomModelRequest(&tt.req, "openai", "default")
|
|
if err == nil {
|
|
t.Fatal("expected mismatch error")
|
|
}
|
|
if err.Error() != tt.expectedErr {
|
|
t.Fatalf("expected %q, got %q", tt.expectedErr, err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPrepareAddCustomModelRequestRejectsEmptyModelTypes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
modelTypes []string
|
|
}{
|
|
{name: "nil"},
|
|
{name: "empty", modelTypes: []string{}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := service.AddCustomModelRequest{
|
|
ModelName: "custom-chat",
|
|
ModelTypes: tt.modelTypes,
|
|
}
|
|
|
|
err := prepareAddCustomModelRequest(&req, "openai", "default")
|
|
if err == nil {
|
|
t.Fatal("expected empty model_types to return an error")
|
|
}
|
|
if err.Error() != "Model type is required" {
|
|
t.Fatalf("expected model type error, got %q", err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|