Files
gpt-load/internal/control/group_options_test.go
T

148 lines
5.0 KiB
Go

package control
import (
"context"
"encoding/json"
"errors"
"reflect"
"strings"
"testing"
"gpt-load/internal/channel"
app_errors "gpt-load/internal/platform/errors"
"gpt-load/internal/storage/models"
)
func TestListGroupOptionsReturnsAllGroupsByIDWithExternalModels(t *testing.T) {
fixture := newServiceFixture(t)
createGroupOptionGroup(t, fixture, 20, "later enabled", true,
channel.OpenAICompatible, `{"base_url":"https://later-enabled.example/v1"}`,
`[{"id":" upstream-first ","alias":" public-first "},{"id":" upstream-second ","alias":" "}]`,
)
createGroupOptionGroup(t, fixture, 10, "earlier disabled", false,
channel.Anthropic, `{}`,
`[{"id":" upstream-third ","alias":""},{"id":" upstream-fourth ","alias":" public-fourth "}]`,
)
if err := fixture.db.Create(&models.Credential{
GroupID: 10, Data: "ciphertext-secret", Fingerprint: "hash-secret",
Status: models.CredentialStatusActive,
}).Error; err != nil {
t.Fatalf("create unrelated upstream key: %v", err)
}
options, err := fixture.service.ListGroupOptions(t.Context())
if err != nil {
t.Fatalf("ListGroupOptions() error = %v", err)
}
if len(options) != 2 || options[0].ID != 10 || options[0].Name != "earlier disabled" ||
options[0].ChannelID != channel.Anthropic || string(options[0].Params) != `{}` ||
options[0].Enabled || !reflect.DeepEqual(options[0].Models, []string{"upstream-third", "public-fourth"}) ||
options[1].ID != 20 || options[1].Name != "later enabled" ||
options[1].ChannelID != channel.OpenAICompatible ||
string(options[1].Params) != `{"base_url":"https://later-enabled.example/v1"}` ||
!options[1].Enabled || !reflect.DeepEqual(options[1].Models, []string{"public-first", "upstream-second"}) {
t.Fatalf("ListGroupOptions() = %#v, want terminal channel options", options)
}
encoded, err := json.Marshal(options)
if err != nil {
t.Fatalf("json.Marshal(options) error = %v", err)
}
for _, forbiddenField := range []string{
"upstream_url", "protocols", "key_value", "key_hash", "keyvalue", "keyhash",
} {
if containsJSONToken(encoded, forbiddenField) {
t.Fatalf("options JSON exposes field %q: %s", forbiddenField, encoded)
}
}
for _, forbiddenValue := range []string{"ciphertext-secret", "hash-secret", "plaintext-secret", "secret"} {
if strings.Contains(string(encoded), forbiddenValue) {
t.Fatalf("options JSON exposes %q: %s", forbiddenValue, encoded)
}
}
}
func TestListGroupOptionsFailsClosedForInvalidDataDatabaseAndCancellation(t *testing.T) {
t.Run("invalid models JSON", func(t *testing.T) {
fixture := newServiceFixture(t)
createGroupOptionGroup(t, fixture, 1, "invalid", true, channel.OpenAI, `{}`, `{"not":"an array"}`)
options, err := fixture.service.ListGroupOptions(t.Context())
if options != nil || !errors.Is(err, app_errors.ErrInternalServer) {
t.Fatalf("ListGroupOptions() = %#v, %v; want nil, ErrInternalServer", options, err)
}
})
for name, test := range map[string]struct {
channelID channel.ID
params string
}{
"invalid params shape": {channelID: channel.OpenAICompatible, params: `[]`},
"invalid params": {channelID: channel.OpenAICompatible, params: `{"base_url":"relative"}`},
"unknown channel": {channelID: channel.ID("unknown"), params: `{}`},
} {
t.Run(name, func(t *testing.T) {
fixture := newServiceFixture(t)
createGroupOptionGroup(t, fixture, 1, "invalid", true, test.channelID, test.params, `[]`)
options, err := fixture.service.ListGroupOptions(t.Context())
if options != nil || !errors.Is(err, app_errors.ErrInternalServer) {
t.Fatalf("ListGroupOptions() = %#v, %v; want nil, ErrInternalServer", options, err)
}
})
}
t.Run("database error", func(t *testing.T) {
fixture := newServiceFixture(t)
sqlDB, err := fixture.db.DB()
if err != nil {
t.Fatalf("fixture DB(): %v", err)
}
if err := sqlDB.Close(); err != nil {
t.Fatalf("close fixture DB: %v", err)
}
options, gotErr := fixture.service.ListGroupOptions(t.Context())
if options != nil || !errors.Is(gotErr, app_errors.ErrDatabase) {
t.Fatalf("ListGroupOptions() = %#v, %v; want nil, ErrDatabase", options, gotErr)
}
})
t.Run("context cancellation", func(t *testing.T) {
fixture := newServiceFixture(t)
ctx, cancel := context.WithCancel(t.Context())
cancel()
options, err := fixture.service.ListGroupOptions(ctx)
if options != nil || err != context.Canceled {
t.Fatalf("ListGroupOptions() = %#v, %v; want nil, context.Canceled", options, err)
}
})
}
func createGroupOptionGroup(
t *testing.T,
fixture serviceFixture,
id uint,
name string,
enabled bool,
channelID channel.ID,
rawParams string,
rawModels string,
) {
t.Helper()
group := validControlGroup(name)
group.ID = id
group.ChannelID = string(channelID)
group.Params = models.JSON(rawParams)
group.Models = models.JSON(rawModels)
if err := fixture.db.Create(group).Error; err != nil {
t.Fatalf("create group %q: %v", name, err)
}
if !enabled {
if err := fixture.db.Model(group).Update("enabled", false).Error; err != nil {
t.Fatalf("disable group %q: %v", name, err)
}
}
}