309 lines
9.7 KiB
Go
309 lines
9.7 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
|
store := openTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
id, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
Description: "shared group",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Create() error = %v", err)
|
|
}
|
|
if id <= 0 {
|
|
t.Fatalf("Create() id = %d, want positive", id)
|
|
}
|
|
|
|
group, err := store.LogicalGroups().GetByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("GetByLogicalGroupID() error = %v", err)
|
|
}
|
|
if group.RoutePolicy != defaultLogicalGroupRoutePolicy {
|
|
t.Fatalf("RoutePolicy = %q, want %q", group.RoutePolicy, defaultLogicalGroupRoutePolicy)
|
|
}
|
|
if group.StickyMode != defaultLogicalGroupStickyMode {
|
|
t.Fatalf("StickyMode = %q, want %q", group.StickyMode, defaultLogicalGroupStickyMode)
|
|
}
|
|
|
|
if err := store.LogicalGroups().UpdateByLogicalGroupID(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared Updated",
|
|
Status: "paused",
|
|
Description: "updated",
|
|
RoutePolicy: "priority",
|
|
StickyMode: "user_preferred",
|
|
ConversationTTLSeconds: 3600,
|
|
UserModelTTLSeconds: 900,
|
|
FailoverThreshold: 3,
|
|
CooldownSeconds: 120,
|
|
}); err != nil {
|
|
t.Fatalf("UpdateByLogicalGroupID() error = %v", err)
|
|
}
|
|
|
|
updated, err := store.LogicalGroups().GetByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("GetByLogicalGroupID(updated) error = %v", err)
|
|
}
|
|
if updated.DisplayName != "GPT Shared Updated" || updated.Status != "paused" {
|
|
t.Fatalf("updated group = %+v, want updated fields", updated)
|
|
}
|
|
|
|
if err := store.LogicalGroups().DeleteByLogicalGroupID(ctx, "gpt-shared"); err != nil {
|
|
t.Fatalf("DeleteByLogicalGroupID() error = %v", err)
|
|
}
|
|
_, err = store.LogicalGroups().GetByLogicalGroupID(ctx, "gpt-shared")
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("GetByLogicalGroupID() after delete error = %v, want sql.ErrNoRows", err)
|
|
}
|
|
}
|
|
|
|
func TestLogicalGroupsRepoList(t *testing.T) {
|
|
store := openTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
for _, group := range []LogicalGroup{
|
|
{LogicalGroupID: "group-a", DisplayName: "Group A", Status: "active"},
|
|
{LogicalGroupID: "group-b", DisplayName: "Group B", Status: "active"},
|
|
} {
|
|
if _, err := store.LogicalGroups().Create(ctx, group); err != nil {
|
|
t.Fatalf("Create(%q) error = %v", group.LogicalGroupID, err)
|
|
}
|
|
}
|
|
|
|
groups, err := store.LogicalGroups().List(ctx)
|
|
if err != nil {
|
|
t.Fatalf("List() error = %v", err)
|
|
}
|
|
if len(groups) != 2 {
|
|
t.Fatalf("List() len = %d, want 2", len(groups))
|
|
}
|
|
if groups[0].LogicalGroupID != "group-a" || groups[1].LogicalGroupID != "group-b" {
|
|
t.Fatalf("List() = %+v, want insertion order", groups)
|
|
}
|
|
}
|
|
|
|
func TestLogicalGroupModelsRepoCreateListDelete(t *testing.T) {
|
|
store := openTestDBWithFK(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create() error = %v", err)
|
|
}
|
|
|
|
if _, err := store.LogicalGroupModels().Create(ctx, LogicalGroupModel{
|
|
LogicalGroupID: "gpt-shared",
|
|
PublicModel: "gpt-5.4",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupModels().Create() error = %v", err)
|
|
}
|
|
|
|
models, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListByLogicalGroupID() error = %v", err)
|
|
}
|
|
if len(models) != 1 || models[0].PublicModel != "gpt-5.4" {
|
|
t.Fatalf("ListByLogicalGroupID() = %+v, want gpt-5.4", models)
|
|
}
|
|
|
|
if err := store.LogicalGroupModels().DeleteByLogicalGroupIDAndModel(ctx, "gpt-shared", "gpt-5.4"); err != nil {
|
|
t.Fatalf("DeleteByLogicalGroupIDAndModel() error = %v", err)
|
|
}
|
|
models, err = store.LogicalGroupModels().ListByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListByLogicalGroupID() after delete error = %v", err)
|
|
}
|
|
if len(models) != 0 {
|
|
t.Fatalf("ListByLogicalGroupID() after delete len = %d, want 0", len(models))
|
|
}
|
|
}
|
|
|
|
func TestLogicalGroupRoutesRepoCreateGetListUpdateDelete(t *testing.T) {
|
|
store := openTestDBWithFK(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create() error = %v", err)
|
|
}
|
|
|
|
if _, err := store.LogicalGroupRoutes().Create(ctx, LogicalGroupRoute{
|
|
RouteID: "asxs",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "ASXS",
|
|
Status: "active",
|
|
Priority: 10,
|
|
ShadowGroupID: "gpt-shared__asxs",
|
|
ShadowHostID: "remote43",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRoutes().Create() error = %v", err)
|
|
}
|
|
|
|
route, err := store.LogicalGroupRoutes().GetByRouteID(ctx, "asxs")
|
|
if err != nil {
|
|
t.Fatalf("GetByRouteID() error = %v", err)
|
|
}
|
|
if route.Weight != 100 {
|
|
t.Fatalf("Weight = %d, want 100", route.Weight)
|
|
}
|
|
|
|
routes, err := store.LogicalGroupRoutes().ListByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListByLogicalGroupID() error = %v", err)
|
|
}
|
|
if len(routes) != 1 || routes[0].RouteID != "asxs" {
|
|
t.Fatalf("ListByLogicalGroupID() = %+v, want route asxs", routes)
|
|
}
|
|
|
|
if err := store.LogicalGroupRoutes().UpdateByRouteID(ctx, LogicalGroupRoute{
|
|
RouteID: "asxs",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "ASXS Updated",
|
|
Status: "degraded",
|
|
Priority: 20,
|
|
Weight: 80,
|
|
ShadowGroupID: "gpt-shared__asxs",
|
|
ShadowHostID: "remote43",
|
|
UpstreamBaseURLHint: "https://api.asxs.top/v1",
|
|
CooldownUntil: "2026-05-28T16:00:00Z",
|
|
}); err != nil {
|
|
t.Fatalf("UpdateByRouteID() error = %v", err)
|
|
}
|
|
|
|
updated, err := store.LogicalGroupRoutes().GetByRouteID(ctx, "asxs")
|
|
if err != nil {
|
|
t.Fatalf("GetByRouteID(updated) error = %v", err)
|
|
}
|
|
if updated.Name != "ASXS Updated" || updated.Status != "degraded" || updated.Weight != 80 {
|
|
t.Fatalf("updated route = %+v, want updated fields", updated)
|
|
}
|
|
|
|
if err := store.LogicalGroupRoutes().DeleteByRouteID(ctx, "asxs"); err != nil {
|
|
t.Fatalf("DeleteByRouteID() error = %v", err)
|
|
}
|
|
_, err = store.LogicalGroupRoutes().GetByRouteID(ctx, "asxs")
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("GetByRouteID() after delete error = %v, want sql.ErrNoRows", err)
|
|
}
|
|
}
|
|
|
|
func TestLogicalGroupRouteModelsRepoCreateList(t *testing.T) {
|
|
store := openTestDBWithFK(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create() error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupRoutes().Create(ctx, LogicalGroupRoute{
|
|
RouteID: "codex2api",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "Codex2API",
|
|
Status: "active",
|
|
Priority: 20,
|
|
ShadowGroupID: "gpt-shared__codex2api",
|
|
ShadowHostID: "remote43",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRoutes().Create() error = %v", err)
|
|
}
|
|
|
|
if _, err := store.LogicalGroupRouteModels().Create(ctx, LogicalGroupRouteModel{
|
|
RouteID: "codex2api",
|
|
PublicModel: "gpt-5.4",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRouteModels().Create() error = %v", err)
|
|
}
|
|
|
|
models, err := store.LogicalGroupRouteModels().ListByRouteID(ctx, "codex2api")
|
|
if err != nil {
|
|
t.Fatalf("ListByRouteID() error = %v", err)
|
|
}
|
|
if len(models) != 1 {
|
|
t.Fatalf("ListByRouteID() len = %d, want 1", len(models))
|
|
}
|
|
if models[0].ShadowModel != "gpt-5.4" {
|
|
t.Fatalf("ShadowModel = %q, want default public model", models[0].ShadowModel)
|
|
}
|
|
}
|
|
|
|
func TestLogicalGroupReposEnforceForeignKeysAndCascadeDelete(t *testing.T) {
|
|
store := openTestDBWithFK(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := store.LogicalGroupModels().Create(ctx, LogicalGroupModel{
|
|
LogicalGroupID: "missing-group",
|
|
PublicModel: "gpt-5.4",
|
|
}); err == nil {
|
|
t.Fatal("LogicalGroupModels().Create() error = nil, want foreign key failure")
|
|
}
|
|
|
|
if _, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
|
LogicalGroupID: "gpt-shared",
|
|
DisplayName: "GPT Shared",
|
|
Status: "active",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroups().Create() error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupModels().Create(ctx, LogicalGroupModel{
|
|
LogicalGroupID: "gpt-shared",
|
|
PublicModel: "gpt-5.4",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupModels().Create() error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupRoutes().Create(ctx, LogicalGroupRoute{
|
|
RouteID: "asxs",
|
|
LogicalGroupID: "gpt-shared",
|
|
Name: "ASXS",
|
|
Status: "active",
|
|
Priority: 10,
|
|
ShadowGroupID: "gpt-shared__asxs",
|
|
ShadowHostID: "remote43",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRoutes().Create() error = %v", err)
|
|
}
|
|
if _, err := store.LogicalGroupRouteModels().Create(ctx, LogicalGroupRouteModel{
|
|
RouteID: "asxs",
|
|
PublicModel: "gpt-5.4",
|
|
}); err != nil {
|
|
t.Fatalf("LogicalGroupRouteModels().Create() error = %v", err)
|
|
}
|
|
|
|
if err := store.LogicalGroups().DeleteByLogicalGroupID(ctx, "gpt-shared"); err != nil {
|
|
t.Fatalf("DeleteByLogicalGroupID() error = %v", err)
|
|
}
|
|
|
|
models, err := store.LogicalGroupModels().ListByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListByLogicalGroupID() after cascade error = %v", err)
|
|
}
|
|
if len(models) != 0 {
|
|
t.Fatalf("models after cascade len = %d, want 0", len(models))
|
|
}
|
|
routes, err := store.LogicalGroupRoutes().ListByLogicalGroupID(ctx, "gpt-shared")
|
|
if err != nil {
|
|
t.Fatalf("ListByLogicalGroupID(routes) after cascade error = %v", err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatalf("routes after cascade len = %d, want 0", len(routes))
|
|
}
|
|
}
|