package app import ( "context" "encoding/json" "net/http" "path/filepath" "testing" "sub2api-cn-relay-manager/internal/store/sqlite" ) func TestAPICreateLogicalGroupReturnsCreated(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ CreateLogicalGroup: func(_ context.Context, req CreateLogicalGroupRequest) (LogicalGroupInfo, error) { if req.LogicalGroupID != "gpt-shared" { t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID) } if req.UsageScenario != "适合统一 GPT 产品入口" { t.Fatalf("UsageScenario = %q, want configured guidance", req.UsageScenario) } if req.VisibilityScope != "login_required" || req.PackageTier != "pro" || req.PurchaseCTALabel != "升级到 Pro" || req.PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" { t.Fatalf("packaging fields = %+v, want configured packaging", req) } return LogicalGroupInfo{ LogicalGroupID: req.LogicalGroupID, DisplayName: req.DisplayName, Status: req.Status, UsageScenario: req.UsageScenario, VisibilityScope: req.VisibilityScope, PackageTier: req.PackageTier, PurchaseCTALabel: req.PurchaseCTALabel, PurchaseCTAURL: req.PurchaseCTAURL, }, nil }, }) request := httptestRequest(t, http.MethodPost, "/api/logical-groups", map[string]any{ "logical_group_id": "gpt-shared", "display_name": "GPT Shared", "status": "active", "usage_scenario": "适合统一 GPT 产品入口", "visibility_scope": "login_required", "package_tier": "pro", "purchase_cta_label": "升级到 Pro", "purchase_cta_url": "https://sub.tksea.top/portal/upgrade/pro", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusCreated) assertJSONContains(t, response.Body().Bytes(), "logical_group.logical_group_id", "gpt-shared") } func TestAPIGetLogicalGroupReturnsAggregatedItem(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ GetLogicalGroup: func(_ context.Context, groupID string) (LogicalGroupInfo, error) { if groupID != "gpt-shared" { t.Fatalf("groupID = %q, want gpt-shared", groupID) } return LogicalGroupInfo{ LogicalGroupID: groupID, DisplayName: "GPT Shared", Status: "active", UsageScenario: "适合统一 GPT 产品入口", Recommendation: "优先使用 gpt-5.4", NextStepHint: "先创建测试 Key", VisibilityScope: "login_required", PackageTier: "pro", PurchaseCTALabel: "升级到 Pro", PurchaseCTAURL: "https://sub.tksea.top/portal/upgrade/pro", Models: []LogicalGroupModelInfo{{PublicModel: "gpt-5.4", Status: "active"}}, Routes: []LogicalGroupRouteInfo{{ RouteID: "asxs", LogicalGroupID: groupID, Name: "ASXS", Status: "active", Models: []LogicalGroupRouteModelInfo{{PublicModel: "gpt-5.4", ShadowModel: "gpt-5.4"}}, }}, }, nil }, }) request := httptestRequest(t, http.MethodGet, "/api/logical-groups/gpt-shared", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) var payload map[string]any if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } group, ok := payload["logical_group"].(map[string]any) if !ok { t.Fatalf("logical_group = %#v, want object", payload["logical_group"]) } models, ok := group["models"].([]any) if !ok || len(models) != 1 { t.Fatalf("models = %#v, want one item", group["models"]) } firstModel, ok := models[0].(map[string]any) if !ok || firstModel["public_model"] != "gpt-5.4" { t.Fatalf("first model = %#v, want public_model gpt-5.4", models[0]) } routes, ok := group["routes"].([]any) if !ok || len(routes) != 1 { t.Fatalf("routes = %#v, want one item", group["routes"]) } firstRoute, ok := routes[0].(map[string]any) if !ok || firstRoute["route_id"] != "asxs" { t.Fatalf("first route = %#v, want route_id asxs", routes[0]) } if group["usage_scenario"] != "适合统一 GPT 产品入口" || group["recommendation"] != "优先使用 gpt-5.4" || group["next_step_hint"] != "先创建测试 Key" { t.Fatalf("group guidance = %#v, want configured guidance fields", group) } if group["visibility_scope"] != "login_required" || group["package_tier"] != "pro" || group["purchase_cta_label"] != "升级到 Pro" || group["purchase_cta_url"] != "https://sub.tksea.top/portal/upgrade/pro" { t.Fatalf("group packaging = %#v, want configured packaging fields", group) } } func TestAPIListLogicalGroupsReturnsRows(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ ListLogicalGroups: func(_ context.Context) ([]LogicalGroupInfo, error) { return []LogicalGroupInfo{{ LogicalGroupID: "gpt-shared", DisplayName: "GPT Shared", Status: "active", UsageScenario: "适合统一 GPT 产品入口", VisibilityScope: "login_required", PackageTier: "pro", PurchaseCTALabel: "升级到 Pro", PurchaseCTAURL: "https://sub.tksea.top/portal/upgrade/pro", }}, nil }, }) request := httptestRequest(t, http.MethodGet, "/api/logical-groups", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) var payload struct { LogicalGroups []LogicalGroupInfo `json:"logical_groups"` } if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } if len(payload.LogicalGroups) != 1 || payload.LogicalGroups[0].LogicalGroupID != "gpt-shared" { t.Fatalf("logical_groups = %+v, want one row gpt-shared", payload.LogicalGroups) } if payload.LogicalGroups[0].VisibilityScope != "login_required" { t.Fatalf("logical_groups[0].visibility_scope = %q, want login_required", payload.LogicalGroups[0].VisibilityScope) } } func TestAPIUpdateLogicalGroupUsesPathID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ UpdateLogicalGroup: func(_ context.Context, req UpdateLogicalGroupRequest) (LogicalGroupInfo, error) { if req.LogicalGroupID != "gpt-shared" { t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID) } if req.DisplayName != "GPT Shared Updated" || req.Status != "paused" { t.Fatalf("request = %+v, want updated display name and status", req) } return LogicalGroupInfo{ LogicalGroupID: req.LogicalGroupID, DisplayName: req.DisplayName, Status: req.Status, Recommendation: req.Recommendation, VisibilityScope: req.VisibilityScope, PackageTier: req.PackageTier, PurchaseCTALabel: req.PurchaseCTALabel, }, nil }, }) request := httptestRequest(t, http.MethodPut, "/api/logical-groups/gpt-shared", map[string]any{ "display_name": "GPT Shared Updated", "status": "paused", "recommendation": "先验证高质量推理链路", "visibility_scope": "entitled_only", "package_tier": "enterprise", "purchase_cta_label": "联系销售升级", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) assertJSONContains(t, response.Body().Bytes(), "logical_group.logical_group_id", "gpt-shared") assertJSONContains(t, response.Body().Bytes(), "logical_group.package_tier", "enterprise") } func TestAPIDeleteLogicalGroupUsesPathID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ DeleteLogicalGroup: func(_ context.Context, groupID string) error { if groupID != "gpt-shared" { t.Fatalf("groupID = %q, want gpt-shared", groupID) } return nil }, }) request := httptestRequest(t, http.MethodDelete, "/api/logical-groups/gpt-shared", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusNoContent) } func TestAPICreateLogicalGroupModelUsesPathGroupID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ CreateLogicalGroupModel: func(_ context.Context, req CreateLogicalGroupModelRequest) (LogicalGroupModelInfo, error) { if req.LogicalGroupID != "gpt-shared" { t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID) } if req.PublicModel != "gpt-5.4" || req.Status != "active" { t.Fatalf("request = %+v, want public_model gpt-5.4 active", req) } return LogicalGroupModelInfo{ PublicModel: req.PublicModel, Status: req.Status, }, nil }, }) request := httptestRequest(t, http.MethodPost, "/api/logical-groups/gpt-shared/models", map[string]any{ "public_model": "gpt-5.4", "status": "active", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusCreated) assertJSONContains(t, response.Body().Bytes(), "logical_group_model.public_model", "gpt-5.4") } func TestAPIListLogicalGroupModelsUsesPathGroupID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ ListLogicalGroupModels: func(_ context.Context, groupID string) ([]LogicalGroupModelInfo, error) { if groupID != "gpt-shared" { t.Fatalf("groupID = %q, want gpt-shared", groupID) } return []LogicalGroupModelInfo{{PublicModel: "gpt-5.4", Status: "active"}}, nil }, }) request := httptestRequest(t, http.MethodGet, "/api/logical-groups/gpt-shared/models", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) var payload struct { Models []LogicalGroupModelInfo `json:"models"` } if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } if len(payload.Models) != 1 || payload.Models[0].PublicModel != "gpt-5.4" { t.Fatalf("models = %+v, want one row gpt-5.4", payload.Models) } } func TestAPIDeleteLogicalGroupModelUsesPathValues(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ DeleteLogicalGroupModel: func(_ context.Context, req DeleteLogicalGroupModelRequest) error { if req.LogicalGroupID != "gpt-shared" || req.PublicModel != "gpt-5.4" { t.Fatalf("request = %+v, want gpt-shared/gpt-5.4", req) } return nil }, }) request := httptestRequest(t, http.MethodDelete, "/api/logical-groups/gpt-shared/models/gpt-5.4", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusNoContent) } func TestAPICreateLogicalGroupRouteUsesPathGroupID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ CreateLogicalGroupRoute: func(_ context.Context, req CreateLogicalGroupRouteRequest) (LogicalGroupRouteInfo, error) { if req.LogicalGroupID != "gpt-shared" { t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID) } return LogicalGroupRouteInfo{ RouteID: req.RouteID, LogicalGroupID: req.LogicalGroupID, Name: req.Name, Status: req.Status, }, nil }, }) request := httptestRequest(t, http.MethodPost, "/api/logical-groups/gpt-shared/routes", map[string]any{ "route_id": "asxs", "name": "ASXS", "status": "active", "priority": 10, "shadow_group_id": "gpt-shared__asxs", "shadow_host_id": "remote43", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusCreated) assertJSONContains(t, response.Body().Bytes(), "route.logical_group_id", "gpt-shared") assertJSONContains(t, response.Body().Bytes(), "route.route_id", "asxs") } func TestAPIListLogicalGroupRoutesUsesPathGroupID(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ ListLogicalGroupRoutes: func(_ context.Context, groupID string) ([]LogicalGroupRouteInfo, error) { if groupID != "gpt-shared" { t.Fatalf("groupID = %q, want gpt-shared", groupID) } return []LogicalGroupRouteInfo{{ RouteID: "asxs", LogicalGroupID: groupID, Name: "ASXS", Status: "active", ShadowGroupID: "gpt-shared__asxs", ShadowHostID: "remote43", }}, nil }, }) request := httptestRequest(t, http.MethodGet, "/api/logical-groups/gpt-shared/routes", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) var payload struct { Routes []LogicalGroupRouteInfo `json:"routes"` } if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } if len(payload.Routes) != 1 || payload.Routes[0].RouteID != "asxs" { t.Fatalf("routes = %+v, want one row asxs", payload.Routes) } if payload.Routes[0].ShadowHostID != "remote43" { t.Fatalf("routes[0].shadow_host_id = %q, want remote43", payload.Routes[0].ShadowHostID) } } func TestAPIUpdateLogicalGroupRouteUsesPathValues(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ UpdateLogicalGroupRoute: func(_ context.Context, req UpdateLogicalGroupRouteRequest) (LogicalGroupRouteInfo, error) { if req.LogicalGroupID != "gpt-shared" || req.RouteID != "asxs" { t.Fatalf("request = %+v, want gpt-shared/asxs", req) } if req.Priority != 20 || req.Status != "degraded" { t.Fatalf("request = %+v, want priority 20 degraded", req) } return LogicalGroupRouteInfo{ RouteID: req.RouteID, LogicalGroupID: req.LogicalGroupID, Name: req.Name, Status: req.Status, Priority: req.Priority, ShadowGroupID: req.ShadowGroupID, ShadowHostID: req.ShadowHostID, }, nil }, }) request := httptestRequest(t, http.MethodPut, "/api/logical-groups/gpt-shared/routes/asxs", map[string]any{ "name": "ASXS Updated", "status": "degraded", "priority": 20, "shadow_group_id": "gpt-shared__asxs", "shadow_host_id": "remote43", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) assertJSONContains(t, response.Body().Bytes(), "route.route_id", "asxs") assertJSONContains(t, response.Body().Bytes(), "route.status", "degraded") } func TestAPIDeleteLogicalGroupRouteUsesPathValues(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ DeleteLogicalGroupRoute: func(_ context.Context, req DeleteLogicalGroupRouteRequest) error { if req.LogicalGroupID != "gpt-shared" || req.RouteID != "asxs" { t.Fatalf("request = %+v, want gpt-shared/asxs", req) } return nil }, }) request := httptestRequest(t, http.MethodDelete, "/api/logical-groups/gpt-shared/routes/asxs", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusNoContent) } func TestAPICreateLogicalGroupRouteModelUsesPathValues(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ CreateLogicalGroupRouteModel: func(_ context.Context, req CreateLogicalGroupRouteModelRequest) (LogicalGroupRouteModelInfo, error) { if req.LogicalGroupID != "gpt-shared" { t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID) } if req.RouteID != "asxs" { t.Fatalf("RouteID = %q, want asxs", req.RouteID) } return LogicalGroupRouteModelInfo{ PublicModel: req.PublicModel, ShadowModel: req.ShadowModel, Status: req.Status, }, nil }, }) request := httptestRequest(t, http.MethodPost, "/api/logical-groups/gpt-shared/routes/asxs/models", map[string]any{ "public_model": "gpt-5.4", "shadow_model": "gpt-5.4", "status": "active", }, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusCreated) assertJSONContains(t, response.Body().Bytes(), "route_model.public_model", "gpt-5.4") } func TestAPIListLogicalGroupRouteModelsUsesPathValues(t *testing.T) { handler := NewAPIHandler("secret-token", ActionSet{ ListLogicalGroupRouteModels: func(_ context.Context, req ListLogicalGroupRouteModelsRequest) ([]LogicalGroupRouteModelInfo, error) { if req.LogicalGroupID != "gpt-shared" || req.RouteID != "asxs" { t.Fatalf("request = %+v, want gpt-shared/asxs", req) } return []LogicalGroupRouteModelInfo{{ PublicModel: "gpt-5.4", ShadowModel: "gpt-5.4", Status: "active", }}, nil }, }) request := httptestRequest(t, http.MethodGet, "/api/logical-groups/gpt-shared/routes/asxs/models", nil, "secret-token") response := httptestRecorder(handler, request) assertStatusCode(t, response, http.StatusOK) var payload struct { RouteModels []LogicalGroupRouteModelInfo `json:"route_models"` } if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } if len(payload.RouteModels) != 1 || payload.RouteModels[0].PublicModel != "gpt-5.4" { t.Fatalf("route_models = %+v, want one row gpt-5.4", payload.RouteModels) } if payload.RouteModels[0].ShadowModel != "gpt-5.4" { t.Fatalf("route_models[0].shadow_model = %q, want gpt-5.4", payload.RouteModels[0].ShadowModel) } } func TestNewActionSetLogicalGroupCRUDFlow(t *testing.T) { dbPath := filepath.Join(t.TempDir(), "logical-groups.db") dsn := "file:" + filepath.ToSlash(dbPath) + "?_busy_timeout=5000" actions := NewActionSet(dsn) ctx := context.Background() createdGroup, err := actions.CreateLogicalGroup(ctx, CreateLogicalGroupRequest{ LogicalGroupID: "gpt-shared", DisplayName: "GPT Shared", Status: "active", UsageScenario: "适合统一 GPT 产品入口", Recommendation: "优先使用 gpt-5.4", NextStepHint: "先创建测试 Key", VisibilityScope: "login_required", PackageTier: "pro", PurchaseCTALabel: "升级到 Pro", PurchaseCTAURL: "https://sub.tksea.top/portal/upgrade/pro", }) if err != nil { t.Fatalf("CreateLogicalGroup() error = %v", err) } if createdGroup.LogicalGroupID != "gpt-shared" { t.Fatalf("CreateLogicalGroup() = %+v, want logical_group_id gpt-shared", createdGroup) } if createdGroup.UsageScenario != "适合统一 GPT 产品入口" || createdGroup.Recommendation != "优先使用 gpt-5.4" || createdGroup.NextStepHint != "先创建测试 Key" { t.Fatalf("CreateLogicalGroup() guidance = %+v, want configured guidance fields", createdGroup) } if createdGroup.VisibilityScope != "login_required" || createdGroup.PackageTier != "pro" || createdGroup.PurchaseCTALabel != "升级到 Pro" || createdGroup.PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" { t.Fatalf("CreateLogicalGroup() packaging = %+v, want configured packaging fields", createdGroup) } if _, err := actions.CreateLogicalGroupModel(ctx, CreateLogicalGroupModelRequest{ LogicalGroupID: "gpt-shared", PublicModel: "gpt-5.4", }); err != nil { t.Fatalf("CreateLogicalGroupModel() error = %v", err) } if _, err := actions.CreateLogicalGroupRoute(ctx, CreateLogicalGroupRouteRequest{ LogicalGroupID: "gpt-shared", RouteID: "asxs", Name: "ASXS", Status: "active", Priority: 10, ShadowGroupID: "gpt-shared__asxs", ShadowHostID: "remote43", }); err != nil { t.Fatalf("CreateLogicalGroupRoute() error = %v", err) } if _, err := actions.CreateLogicalGroupRouteModel(ctx, CreateLogicalGroupRouteModelRequest{ LogicalGroupID: "gpt-shared", RouteID: "asxs", PublicModel: "gpt-5.4", }); err != nil { t.Fatalf("CreateLogicalGroupRouteModel() error = %v", err) } group, err := actions.GetLogicalGroup(ctx, "gpt-shared") if err != nil { t.Fatalf("GetLogicalGroup() error = %v", err) } if len(group.Models) != 1 || group.Models[0].PublicModel != "gpt-5.4" { t.Fatalf("GetLogicalGroup().Models = %+v, want gpt-5.4", group.Models) } if len(group.Routes) != 1 || group.Routes[0].RouteID != "asxs" { t.Fatalf("GetLogicalGroup().Routes = %+v, want route asxs", group.Routes) } if len(group.Routes[0].Models) != 1 || group.Routes[0].Models[0].ShadowModel != "gpt-5.4" { t.Fatalf("GetLogicalGroup().Routes[0].Models = %+v, want shadow gpt-5.4", group.Routes[0].Models) } if group.UsageScenario != "适合统一 GPT 产品入口" || group.Recommendation != "优先使用 gpt-5.4" || group.NextStepHint != "先创建测试 Key" { t.Fatalf("GetLogicalGroup() guidance = %+v, want configured guidance fields", group) } if group.VisibilityScope != "login_required" || group.PackageTier != "pro" || group.PurchaseCTALabel != "升级到 Pro" || group.PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" { t.Fatalf("GetLogicalGroup() packaging = %+v, want configured packaging fields", group) } if _, err := actions.UpdateLogicalGroup(ctx, UpdateLogicalGroupRequest{ LogicalGroupID: "gpt-shared", DisplayName: "GPT Shared Updated", Status: "paused", UsageScenario: "适合升级后的 GPT 产品入口", Recommendation: "先验证高质量推理链路", NextStepHint: "升级后重新申请测试 Key", VisibilityScope: "entitled_only", PackageTier: "enterprise", PurchaseCTALabel: "联系销售升级", PurchaseCTAURL: "https://sub.tksea.top/portal/contact-sales", }); err != nil { t.Fatalf("UpdateLogicalGroup() error = %v", err) } if _, err := actions.UpdateLogicalGroupRoute(ctx, UpdateLogicalGroupRouteRequest{ LogicalGroupID: "gpt-shared", RouteID: "asxs", Name: "ASXS Updated", Status: "degraded", Priority: 20, Weight: 80, ShadowGroupID: "gpt-shared__asxs", ShadowHostID: "remote43", CooldownUntil: "2026-05-28T16:00:00Z", }); err != nil { t.Fatalf("UpdateLogicalGroupRoute() error = %v", err) } groups, err := actions.ListLogicalGroups(ctx) if err != nil { t.Fatalf("ListLogicalGroups() error = %v", err) } if len(groups) != 1 || groups[0].DisplayName != "GPT Shared Updated" { t.Fatalf("ListLogicalGroups() = %+v, want updated group", groups) } if groups[0].UsageScenario != "适合升级后的 GPT 产品入口" || groups[0].Recommendation != "先验证高质量推理链路" || groups[0].NextStepHint != "升级后重新申请测试 Key" { t.Fatalf("ListLogicalGroups() guidance = %+v, want updated guidance fields", groups[0]) } if groups[0].VisibilityScope != "entitled_only" || groups[0].PackageTier != "enterprise" || groups[0].PurchaseCTALabel != "联系销售升级" || groups[0].PurchaseCTAURL != "https://sub.tksea.top/portal/contact-sales" { t.Fatalf("ListLogicalGroups() packaging = %+v, want updated packaging fields", groups[0]) } routeModels, err := actions.ListLogicalGroupRouteModels(ctx, ListLogicalGroupRouteModelsRequest{ LogicalGroupID: "gpt-shared", RouteID: "asxs", }) if err != nil { t.Fatalf("ListLogicalGroupRouteModels() error = %v", err) } if len(routeModels) != 1 || routeModels[0].PublicModel != "gpt-5.4" { t.Fatalf("ListLogicalGroupRouteModels() = %+v, want gpt-5.4", routeModels) } if err := actions.DeleteLogicalGroupRoute(ctx, DeleteLogicalGroupRouteRequest{ LogicalGroupID: "gpt-shared", RouteID: "asxs", }); err != nil { t.Fatalf("DeleteLogicalGroupRoute() error = %v", err) } if err := actions.DeleteLogicalGroupModel(ctx, DeleteLogicalGroupModelRequest{ LogicalGroupID: "gpt-shared", PublicModel: "gpt-5.4", }); err != nil { t.Fatalf("DeleteLogicalGroupModel() error = %v", err) } if err := actions.DeleteLogicalGroup(ctx, "gpt-shared"); err != nil { t.Fatalf("DeleteLogicalGroup() error = %v", err) } store, err := sqlite.Open(ctx, dsn) if err != nil { t.Fatalf("sqlite.Open() error = %v", err) } defer store.Close() remaining, err := store.LogicalGroups().List(ctx) if err != nil { t.Fatalf("LogicalGroups().List() error = %v", err) } if len(remaining) != 0 { t.Fatalf("remaining logical groups = %+v, want empty", remaining) } }