181 lines
6.3 KiB
Go
181 lines
6.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"sub2api-cn-relay-manager/internal/store/sqlite"
|
|
)
|
|
|
|
func TestAPIListPortalLogicalGroups(t *testing.T) {
|
|
handler := NewAPIHandler("secret-token", ActionSet{
|
|
ListPortalLogicalGroups: func(_ context.Context) ([]PortalLogicalGroupInfo, error) {
|
|
return []PortalLogicalGroupInfo{{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
RouteCount: 2,
|
|
ActiveRouteCount: 1,
|
|
PublicModels: []PortalLogicalGroupModel{
|
|
{PublicModel: "gpt-5.4", Status: "active"},
|
|
},
|
|
}}, nil
|
|
},
|
|
})
|
|
|
|
request := httptestRequest(t, "GET", "/api/portal/logical-groups", nil, "")
|
|
response := httptestRecorder(handler, request)
|
|
assertStatusCode(t, response, 200)
|
|
var listPayload struct {
|
|
LogicalGroups []PortalLogicalGroupInfo `json:"logical_groups"`
|
|
}
|
|
if err := json.Unmarshal(response.Body().Bytes(), &listPayload); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
if len(listPayload.LogicalGroups) != 1 || listPayload.LogicalGroups[0].LogicalGroupID != "gpt-shared" || listPayload.LogicalGroups[0].RouteCount != 2 {
|
|
t.Fatalf("portal logical groups payload = %+v", listPayload)
|
|
}
|
|
}
|
|
|
|
func TestAPIGetPortalLogicalGroupModels(t *testing.T) {
|
|
handler := NewAPIHandler("secret-token", ActionSet{
|
|
ListPortalLogicalGroupModels: func(_ context.Context, logicalGroupID string) ([]PortalLogicalGroupModel, error) {
|
|
if logicalGroupID != "gpt-shared" {
|
|
t.Fatalf("logicalGroupID = %q, want gpt-shared", logicalGroupID)
|
|
}
|
|
return []PortalLogicalGroupModel{{PublicModel: "gpt-5.4", Status: "active"}}, nil
|
|
},
|
|
})
|
|
|
|
request := httptestRequest(t, "GET", "/api/portal/logical-groups/gpt-shared/models", nil, "")
|
|
response := httptestRecorder(handler, request)
|
|
assertStatusCode(t, response, 200)
|
|
var modelsPayload struct {
|
|
Models []PortalLogicalGroupModel `json:"models"`
|
|
}
|
|
if err := json.Unmarshal(response.Body().Bytes(), &modelsPayload); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
if len(modelsPayload.Models) != 1 || modelsPayload.Models[0].PublicModel != "gpt-5.4" {
|
|
t.Fatalf("portal models payload = %+v", modelsPayload)
|
|
}
|
|
}
|
|
|
|
func TestNewActionSetPortalLogicalGroups(t *testing.T) {
|
|
dbPath := filepath.Join(t.TempDir(), "portal-logical-groups.db")
|
|
dsn := "file:" + filepath.ToSlash(dbPath) + "?_busy_timeout=5000"
|
|
actions := NewActionSet(dsn)
|
|
ctx := context.Background()
|
|
|
|
store, err := sqlite.Open(ctx, dsn)
|
|
if err != nil {
|
|
t.Fatalf("sqlite.Open() error = %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
if _, err := store.LogicalGroups().Create(ctx, sqlite.LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
Description: "Public GPT product",
|
|
RoutePolicy: "priority",
|
|
StickyMode: "conversation_preferred",
|
|
ConversationTTLSeconds: 7200,
|
|
UserModelTTLSeconds: 1800,
|
|
FailoverThreshold: 2,
|
|
CooldownSeconds: 600,
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create(active) error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroups().Create(ctx, sqlite.LogicalGroup{
|
|
LogicalGroupID: "disabled-group",
|
|
DisplayName: "Disabled Group",
|
|
Status: "disabled",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create(disabled) error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupModels().Create(ctx, sqlite.LogicalGroupModel{
|
|
LogicalGroupID: "gpt-shared",
|
|
PublicModel: "gpt-5.4",
|
|
Status: "active",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupModels().Create(active) error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupModels().Create(ctx, sqlite.LogicalGroupModel{
|
|
LogicalGroupID: "gpt-shared",
|
|
PublicModel: "gpt-hidden",
|
|
Status: "disabled",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupModels().Create(disabled) error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupRoutes().Create(ctx, sqlite.LogicalGroupRoute{
|
|
RouteID: "asxs",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "ASXS",
|
|
Status: "active",
|
|
Priority: 10,
|
|
Weight: 100,
|
|
ShadowGroupID: "9",
|
|
ShadowHostID: "remote43",
|
|
UpstreamBaseURLHint: "https://api.asxs.top/v1",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRoutes().Create(active) error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupRoutes().Create(ctx, sqlite.LogicalGroupRoute{
|
|
RouteID: "backup",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "Backup",
|
|
Status: "cooldown",
|
|
Priority: 20,
|
|
Weight: 100,
|
|
ShadowGroupID: "9",
|
|
ShadowHostID: "remote43",
|
|
UpstreamBaseURLHint: "https://backup.example.com",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRoutes().Create(cooldown) error = %v", err)
|
|
}
|
|
|
|
listed, err := actions.ListPortalLogicalGroups(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListPortalLogicalGroups() error = %v", err)
|
|
}
|
|
if len(listed) != 1 || listed[0].LogicalGroupID != "gpt-shared" {
|
|
t.Fatalf("ListPortalLogicalGroups() = %+v", listed)
|
|
}
|
|
if listed[0].RouteCount != 2 || listed[0].ActiveRouteCount != 1 {
|
|
t.Fatalf("ListPortalLogicalGroups() route counts = %+v", listed[0])
|
|
}
|
|
if len(listed[0].PublicModels) != 1 || listed[0].PublicModels[0].PublicModel != "gpt-5.4" {
|
|
t.Fatalf("ListPortalLogicalGroups() public models = %+v", listed[0].PublicModels)
|
|
}
|
|
|
|
group, err := actions.GetPortalLogicalGroup(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("GetPortalLogicalGroup() error = %v", err)
|
|
}
|
|
if group.DisplayName != "GPT Shared" || group.RoutePolicy != "priority" {
|
|
t.Fatalf("GetPortalLogicalGroup() = %+v", group)
|
|
}
|
|
|
|
models, err := actions.ListPortalLogicalGroupModels(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListPortalLogicalGroupModels() error = %v", err)
|
|
}
|
|
if len(models) != 1 || models[0].PublicModel != "gpt-5.4" {
|
|
t.Fatalf("ListPortalLogicalGroupModels() = %+v", models)
|
|
}
|
|
|
|
request := httptestRequest(t, "GET", "/api/portal/logical-groups/gpt-shared", nil, "")
|
|
response := httptestRecorder(NewAPIHandler("secret-token", actions), request)
|
|
assertStatusCode(t, response, 200)
|
|
var payload map[string]PortalLogicalGroupInfo
|
|
if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
if payload["logical_group"].LogicalGroupID != "gpt-shared" {
|
|
t.Fatalf("portal logical group payload = %+v", payload)
|
|
}
|
|
}
|