152 lines
5.2 KiB
Go
152 lines
5.2 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/basketikun/infinite-canvas/model"
|
|
)
|
|
|
|
func TestFetchAdminChannelModelsParsesOpenAIModels(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/models" {
|
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"data":[{"id":"z-model"},{"id":"a-model"},{"id":""}]}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
models, err := fetchAdminChannelModels(model.ModelChannel{
|
|
BaseURL: server.URL,
|
|
APIKey: "test-key",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("fetchAdminChannelModels returned error: %v", err)
|
|
}
|
|
if want := []string{"a-model", "z-model"}; !reflect.DeepEqual(models, want) {
|
|
t.Fatalf("models = %#v, want %#v", models, want)
|
|
}
|
|
}
|
|
|
|
func TestFetchAdminChannelModelsReportsArkPlanModelsUnsupported(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/api/plan/v3/models" {
|
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
}
|
|
http.NotFound(w, r)
|
|
}))
|
|
defer server.Close()
|
|
|
|
_, err := fetchAdminChannelModels(model.ModelChannel{
|
|
BaseURL: server.URL + "/api/plan/v3/contents/generations/tasks",
|
|
APIKey: "test-key",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected unsupported /models error")
|
|
}
|
|
if !strings.Contains(err.Error(), "Agent Plan 未提供 OpenAI /models") {
|
|
t.Fatalf("error = %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestBuildModelChannelURLNormalizesArkPlanTaskPath(t *testing.T) {
|
|
got := BuildModelChannelURL(model.ModelChannel{BaseURL: "https://ark.cn-beijing.volces.com/api/plan/v3/contents/generations/tasks?debug=1"}, "/models")
|
|
want := "https://ark.cn-beijing.volces.com/api/plan/v3/models"
|
|
if got != want {
|
|
t.Fatalf("BuildModelChannelURL = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestBuildModelChannelURLAcceptsVersionedSub2APIBaseURL(t *testing.T) {
|
|
got := BuildModelChannelURL(model.ModelChannel{BaseURL: "https://sub2api.example.com/v1"}, "/images/generations")
|
|
want := "https://sub2api.example.com/v1/images/generations"
|
|
if got != want {
|
|
t.Fatalf("BuildModelChannelURL = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestNormalizePrivateSettingDefaultsSub2APIImageResponseFormatToURL(t *testing.T) {
|
|
setting := normalizePrivateSetting(model.PrivateSetting{Channels: []model.ModelChannel{{Compatibility: "sub2api"}}})
|
|
if got := setting.Channels[0].RequestOptions.ImageResponseFormat; got != "url" {
|
|
t.Fatalf("image response format = %q, want url", got)
|
|
}
|
|
}
|
|
|
|
func TestAdminTestChannelModelUsesImageGenerationProbeForImageModels(t *testing.T) {
|
|
var gotPath string
|
|
var gotPayload map[string]any
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotPath = r.URL.Path
|
|
if err := json.NewDecoder(r.Body).Decode(&gotPayload); err != nil {
|
|
t.Fatalf("Decode request body: %v", err)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"data":[{"url":"https://example.com/image.png"}]}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
result, err := testAdminChannelModel(model.ModelChannel{
|
|
Compatibility: "sub2api",
|
|
BaseURL: server.URL + "/v1",
|
|
APIKey: "test-key",
|
|
}, "gpt-image-2")
|
|
if err != nil {
|
|
t.Fatalf("testAdminChannelModel returned error: %v", err)
|
|
}
|
|
if result != "ok" {
|
|
t.Fatalf("result = %q, want ok", result)
|
|
}
|
|
if gotPath != "/v1/images/generations" {
|
|
t.Fatalf("path = %q, want /v1/images/generations", gotPath)
|
|
}
|
|
if gotPayload["model"] != "gpt-image-2" {
|
|
t.Fatalf("model = %#v", gotPayload["model"])
|
|
}
|
|
if gotPayload["response_format"] != "url" {
|
|
t.Fatalf("response_format = %#v, want url", gotPayload["response_format"])
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSettingsPublishesEnabledChannelModelsAndRepairsDefaults(t *testing.T) {
|
|
settings := normalizeSettings(model.Settings{
|
|
Public: model.PublicSetting{
|
|
ModelChannel: model.PublicModelChannelSetting{
|
|
AvailableModels: []string{"grok-imagine-video", "disabled-model"},
|
|
DefaultModel: "grok-imagine-video",
|
|
DefaultTextModel: "missing-text",
|
|
DefaultImageModel: "missing-image",
|
|
DefaultVideoModel: "missing-video",
|
|
},
|
|
},
|
|
Private: model.PrivateSetting{
|
|
Channels: []model.ModelChannel{
|
|
{Enabled: true, Models: []string{"gpt-5.5", "doubao-seedream-5.0-lite", "doubao-seedance-2.0-fast", "gpt-5.5"}},
|
|
{Enabled: false, Models: []string{"disabled-model"}},
|
|
},
|
|
},
|
|
})
|
|
|
|
channel := settings.Public.ModelChannel
|
|
wantModels := []string{"gpt-5.5", "doubao-seedream-5.0-lite", "doubao-seedance-2.0-fast"}
|
|
if !reflect.DeepEqual(channel.AvailableModels, wantModels) {
|
|
t.Fatalf("available models = %#v, want %#v", channel.AvailableModels, wantModels)
|
|
}
|
|
if channel.DefaultModel != "gpt-5.5" {
|
|
t.Fatalf("default model = %q, want text model", channel.DefaultModel)
|
|
}
|
|
if channel.DefaultTextModel != "gpt-5.5" {
|
|
t.Fatalf("default text model = %q, want text model", channel.DefaultTextModel)
|
|
}
|
|
if channel.DefaultImageModel != "doubao-seedream-5.0-lite" {
|
|
t.Fatalf("default image model = %q, want seedream", channel.DefaultImageModel)
|
|
}
|
|
if channel.DefaultVideoModel != "doubao-seedance-2.0-fast" {
|
|
t.Fatalf("default video model = %q, want seedance", channel.DefaultVideoModel)
|
|
}
|
|
}
|