引用行(ref_system_model_id>0)在 DB 只存个人字段(apiKey/enabled/chatModel),
Get 之前返回空配置,前端编辑表单无法补全。新增 mergeReferenceConfigForQuery:
以引用行身份字段为基底,把系统行配置列合入返回(不替换 {apiKey} 占位、
不做 enabled AND 门禁,区别于调用时 mergeReferenceConfig)。
保留引用行自身 id,Update/Delete 仍命中引用行。
171 lines
5.8 KiB
Go
171 lines
5.8 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"model-gateway/model/entity"
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/beans"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
func TestReplacePlaceholder(t *testing.T) {
|
|
if got := replacePlaceholder("Bearer {apiKey}", "{apiKey}", "sk-1"); got != "Bearer sk-1" {
|
|
t.Fatalf("string: %v", got)
|
|
}
|
|
got := replacePlaceholder(map[string]any{
|
|
"url": "https://x.com?key={apiKey}",
|
|
"n": 1,
|
|
"sub": map[string]any{"k": "v-{apiKey}"},
|
|
"list": []any{"a-{apiKey}", 2},
|
|
}, "{apiKey}", "sk-9")
|
|
m, ok := got.(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("not map: %T", got)
|
|
}
|
|
if m["url"] != "https://x.com?key=sk-9" {
|
|
t.Fatalf("url: %v", m["url"])
|
|
}
|
|
if m["n"] != 1 {
|
|
t.Fatalf("n: %v", m["n"])
|
|
}
|
|
if m["sub"].(map[string]any)["k"] != "v-sk-9" {
|
|
t.Fatalf("sub: %v", m["sub"])
|
|
}
|
|
if m["list"].([]any)[0] != "a-sk-9" {
|
|
t.Fatalf("list[0]: %v", m["list"])
|
|
}
|
|
}
|
|
|
|
func TestSubstituteAPIPlaceholder(t *testing.T) {
|
|
m := &entity.ModelManage{
|
|
BaseURL: "https://api.xx.com?key={apiKey}",
|
|
RequestHeadMapping: map[string]string{"Authorization": "Bearer {apiKey}"},
|
|
RequestBodyMapping: map[string]any{"model": "{apiKey}"},
|
|
RequestBusinessFieldMapping: map[string]string{"key": "{apiKey}"},
|
|
AsyncTaskMapping: &entity.AsyncTaskMapping{
|
|
Url: "https://api.xx.com/task/{taskId}?key={apiKey}",
|
|
RequestBodyMapping: map[string]any{"auth": "{apiKey}"},
|
|
},
|
|
}
|
|
substituteAPIPlaceholder(m, "sk-user")
|
|
if m.BaseURL != "https://api.xx.com?key=sk-user" {
|
|
t.Fatalf("baseURL: %s", m.BaseURL)
|
|
}
|
|
if m.RequestHeadMapping["Authorization"] != "Bearer sk-user" {
|
|
t.Fatalf("head: %s", m.RequestHeadMapping["Authorization"])
|
|
}
|
|
if m.RequestBodyMapping["model"] != "sk-user" {
|
|
t.Fatalf("body: %v", m.RequestBodyMapping["model"])
|
|
}
|
|
if m.RequestBusinessFieldMapping["key"] != "sk-user" {
|
|
t.Fatalf("biz: %v", m.RequestBusinessFieldMapping["key"])
|
|
}
|
|
if m.AsyncTaskMapping.Url != "https://api.xx.com/task/{taskId}?key=sk-user" {
|
|
t.Fatalf("async url: %s", m.AsyncTaskMapping.Url)
|
|
}
|
|
if m.AsyncTaskMapping.RequestBodyMapping["auth"] != "sk-user" {
|
|
t.Fatalf("async body: %v", m.AsyncTaskMapping.RequestBodyMapping["auth"])
|
|
}
|
|
}
|
|
|
|
func TestMergeReferenceConfig(t *testing.T) {
|
|
sys := &entity.ModelManage{
|
|
SQLBaseDO: beans.SQLBaseDO{Id: 100}, BaseURL: "https://sys", SystemModel: gconv.PtrBool(true),
|
|
Enabled: gconv.PtrBool(true), RequestHeadMapping: map[string]string{"Authorization": "Bearer {apiKey}"},
|
|
}
|
|
stub := &entity.ModelManage{RefSystemModelId: 100, ApiKey: "sk-user", Enabled: gconv.PtrBool(true), ChatModel: gconv.PtrBool(true)}
|
|
|
|
out := mergeReferenceConfig(stub, sys)
|
|
if out.Id != 100 {
|
|
t.Fatalf("id: %d", out.Id)
|
|
}
|
|
if out.ApiKey != "sk-user" {
|
|
t.Fatalf("apiKey: %s", out.ApiKey)
|
|
}
|
|
if out.ChatModel == nil || !*out.ChatModel {
|
|
t.Fatalf("chatModel should follow stub")
|
|
}
|
|
if out.BaseURL != "https://sys" {
|
|
t.Fatalf("baseURL should follow sys: %s", out.BaseURL)
|
|
}
|
|
|
|
// 系统停用 → 引用失效
|
|
sysOff := &entity.ModelManage{Enabled: gconv.PtrBool(false)}
|
|
if out := mergeReferenceConfig(stub, sysOff); out.Enabled == nil || *out.Enabled {
|
|
t.Fatalf("system disabled should gate reference")
|
|
}
|
|
// 引用行停用 → 停用
|
|
stubOff := &entity.ModelManage{Enabled: gconv.PtrBool(false)}
|
|
if out := mergeReferenceConfig(stubOff, sys); out.Enabled == nil || *out.Enabled {
|
|
t.Fatalf("stub disabled should gate")
|
|
}
|
|
// 引用行未设 enabled → 用系统行
|
|
stubNil := &entity.ModelManage{ApiKey: "sk-user"}
|
|
if out := mergeReferenceConfig(stubNil, sys); out.Enabled == nil || !*out.Enabled {
|
|
t.Fatalf("stub nil enabled should follow sys")
|
|
}
|
|
}
|
|
|
|
func TestMergeReferenceConfigForQuery(t *testing.T) {
|
|
sys := &entity.ModelManage{
|
|
SQLBaseDO: beans.SQLBaseDO{Id: 100, Creator: "admin"}, BaseURL: "https://sys",
|
|
SystemModel: gconv.PtrBool(true), Enabled: gconv.PtrBool(false),
|
|
RequestHeadMapping: map[string]string{"Authorization": "Bearer {apiKey}"},
|
|
TokenMapping: &entity.TokenMapping{PromptTokens: "a"},
|
|
}
|
|
stub := &entity.ModelManage{
|
|
SQLBaseDO: beans.SQLBaseDO{Id: 200, Creator: "user"}, RefSystemModelId: 100,
|
|
SystemModel: gconv.PtrBool(false), Enabled: gconv.PtrBool(true), ApiKey: "sk-user",
|
|
}
|
|
|
|
out := mergeReferenceConfigForQuery(stub, sys)
|
|
// 保留引用行自身身份字段
|
|
if out.Id != 200 {
|
|
t.Fatalf("id should keep stub: %d", out.Id)
|
|
}
|
|
if out.RefSystemModelId != 100 {
|
|
t.Fatalf("refSystemModelId should keep stub: %d", out.RefSystemModelId)
|
|
}
|
|
if out.Creator != "user" {
|
|
t.Fatalf("creator should keep stub: %s", out.Creator)
|
|
}
|
|
if out.SystemModel == nil || *out.SystemModel {
|
|
t.Fatalf("systemModel should keep stub (false)")
|
|
}
|
|
// 配置列合入系统行
|
|
if out.BaseURL != "https://sys" {
|
|
t.Fatalf("baseURL should follow sys: %s", out.BaseURL)
|
|
}
|
|
if out.RequestHeadMapping["Authorization"] != "Bearer {apiKey}" {
|
|
t.Fatalf("head mapping should follow sys template (不替换占位): %v", out.RequestHeadMapping)
|
|
}
|
|
if out.TokenMapping == nil || out.TokenMapping.PromptTokens != "a" {
|
|
t.Fatalf("tokenMapping should follow sys")
|
|
}
|
|
// 个人字段保留引用行(不做系统 enabled 的 AND 门禁)
|
|
if out.ApiKey != "sk-user" {
|
|
t.Fatalf("apiKey should keep stub: %s", out.ApiKey)
|
|
}
|
|
if out.Enabled == nil || !*out.Enabled {
|
|
t.Fatalf("enabled should keep stub (系统停用不在此门禁)")
|
|
}
|
|
}
|
|
|
|
func TestResolveModelConfigNonReference(t *testing.T) {
|
|
m := &entity.ModelManage{SQLBaseDO: beans.SQLBaseDO{Id: 5}, BaseURL: "https://x?key={apiKey}", ApiKey: "sk-sys"}
|
|
out, err := resolveModelConfig(nil, m) // 非引用路径不触 DB,ctx 可为 nil
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if out.BaseURL != "https://x?key=sk-sys" {
|
|
t.Fatalf("baseURL: %s", out.BaseURL)
|
|
}
|
|
if out.Id != 5 {
|
|
t.Fatalf("id: %d", out.Id)
|
|
}
|
|
if out == m {
|
|
t.Fatalf("should return a copy, not mutate input")
|
|
}
|
|
}
|