Files
ttstd-infinite-canvas/backend/internal/app/channel_presentation_test.go

164 lines
6.1 KiB
Go

package app
import (
"encoding/json"
"reflect"
"strings"
"testing"
"time"
"infinite-canvas/backend/internal/model"
)
func TestChannelPresentationAliasAndPublicOrder(t *testing.T) {
svc, db := newChannelModelTestService(t)
svc.dataDir = t.TempDir()
admin := &model.User{ID: "admin", Role: model.UserRoleAdmin}
for i, id := range []string{"first", "second"} {
channel := model.ModelChannel{ID: id, Name: "内部" + id, Scope: model.ChannelScopeSystem, Enabled: true, BaseURL: "https://dead.invalid/v1", ModelsJSON: `[]`, CreatedAt: time.Unix(int64(i+1), 0)}
if err := db.Create(&channel).Error; err != nil {
t.Fatal(err)
}
cm := model.ChannelModel{
ID: id + "-model", ChannelID: id, ModelKey: id, Enabled: true, PriceConfigured: true, Capability: "image",
CapabilityConfigJSON: mustEncodeModelCapabilityConfig(t, DefaultModelCapabilityConfigForModel("", id)),
}
if err := db.Create(&cm).Error; err != nil {
t.Fatal(err)
}
}
alias := " 创作精选 "
updated, err := svc.UpdateSystemChannel(admin, "second", ChannelRequest{PublicAlias: &alias})
if err != nil {
t.Fatal(err)
}
if updated.Name != "内部second" || updated.PublicAlias != "创作精选" {
t.Fatalf("admin alias: %#v", updated)
}
if _, err := svc.UpdateSystemChannel(admin, "first", ChannelRequest{SortOrder: intPtr(20)}); err != nil {
t.Fatal(err)
}
adminRows, total, err := svc.repo.AdminSystemChannels("", "all", 1, 0)
if err != nil || total != 2 || len(adminRows) != 1 || adminRows[0].ID != "second" {
t.Fatalf("pagination before sort: %v %v %v", adminRows, total, err)
}
search, _, err := svc.repo.AdminSystemChannels("精选", "all", 10, 0)
if err != nil || len(search) != 1 {
t.Fatalf("alias search: %v %v", search, err)
}
catalog, err := svc.publicSystemChannelCatalog(nil)
if err != nil {
t.Fatal(err)
}
if len(catalog) != 2 || catalog[0].ID != "second" || catalog[0].Name != "创作精选" || catalog[0].DisplayName != "创作精选" {
t.Fatalf("public catalog: %#v", catalog)
}
legacy, err := svc.PublicSystemChannels()
if err != nil {
t.Fatal(err)
}
if legacy[0].Name != "创作精选" {
t.Fatalf("public session: %#v", legacy)
}
encoded, _ := json.Marshal([]any{catalog[0], legacy[0]})
if strings.Contains(string(encoded), "内部second") {
t.Fatal("internal channel name leaked into public payload")
}
if _, err := svc.UpdateSystemChannel(admin, "second", ChannelRequest{SortOrder: intPtr(5)}); err != nil {
t.Fatal(err)
}
stored, _ := svc.repo.AdminSystemChannel("second")
if stored.PublicAlias != "创作精选" {
t.Fatal("partial sort patch cleared alias")
}
alias = " "
if _, err := svc.UpdateSystemChannel(admin, "second", ChannelRequest{PublicAlias: &alias, SortOrder: intPtr(0)}); err != nil {
t.Fatal(err)
}
catalog, err = svc.publicSystemChannelCatalog(nil)
if err != nil || catalog[0].Name != "内部second" {
t.Fatalf("empty alias fallback: %v %v", catalog, err)
}
for _, invalid := range []int{-1, 1000000} {
if _, err := svc.UpdateSystemChannel(admin, "second", ChannelRequest{SortOrder: &invalid}); err == nil {
t.Fatal("invalid sort accepted")
}
}
alias = strings.Repeat("字", 81)
if _, err := svc.UpdateSystemChannel(admin, "second", ChannelRequest{PublicAlias: &alias}); err == nil {
t.Fatal("long alias accepted")
}
}
func TestChannelModelSortPreservesConfigurationAndOwnership(t *testing.T) {
svc, db := newChannelModelTestService(t)
admin := &model.User{ID: "admin", Role: model.UserRoleAdmin}
for _, id := range []string{"channel", "other"} {
if err := db.Create(&model.ModelChannel{ID: id, Scope: model.ChannelScopeSystem, Enabled: true, Name: id, ModelsJSON: `[]`}).Error; err != nil {
t.Fatal(err)
}
}
for i, id := range []string{"z", "a", "b"} {
cm := model.ChannelModel{
ID: id, ChannelID: "channel", ModelKey: id, ProviderModelKey: "provider-" + id, Enabled: true,
Capability: "image", PriceConfigured: true, UnitPriceMicrocredits: 123, PriceVersion: 7, CapabilityVersion: 4,
CapabilityConfigJSON: mustEncodeModelCapabilityConfig(t, DefaultModelCapabilityConfigForModel("", id)),
CreatedAt: time.Unix(int64(i), 0),
}
if err := db.Create(&cm).Error; err != nil {
t.Fatal(err)
}
}
var before model.ChannelModel
if err := db.First(&before, "id = ?", "z").Error; err != nil {
t.Fatal(err)
}
if err := svc.UpdateAdminChannelModelSort(admin, "channel", "z", ChannelModelSortRequest{SortOrder: intPtr(10)}); err != nil {
t.Fatal(err)
}
var after model.ChannelModel
if err := db.First(&after, "id = ?", "z").Error; err != nil {
t.Fatal(err)
}
after.SortOrder, after.UpdatedAt = before.SortOrder, before.UpdatedAt
if !reflect.DeepEqual(before, after) {
t.Fatal("sort changed model business configuration")
}
rows, err := svc.repo.ChannelModels("channel", true)
if err != nil || len(rows) != 3 || rows[0].ID != "a" || rows[1].ID != "b" || rows[2].ID != "z" {
t.Fatalf("sort and stable tie: %v %v", rows, err)
}
channel, _ := svc.repo.AdminSystemChannel("channel")
if channel.ModelsJSON != `["a","b","z"]` {
t.Fatalf("legacy models order: %s", channel.ModelsJSON)
}
catalog, err := svc.publicSystemChannelCatalog(nil)
if err != nil || len(catalog) != 1 || catalog[0].Models[2].ModelKey != "z" {
t.Fatalf("public model order: %v %v", catalog, err)
}
for _, tc := range []struct {
actor *model.User
channel, id string
value *int
}{
{nil, "channel", "z", intPtr(1)},
{&model.User{ID: "user"}, "channel", "z", intPtr(1)},
{admin, "other", "z", intPtr(1)},
{admin, "channel", "missing", intPtr(1)},
{admin, "channel", "z", nil},
{admin, "channel", "z", intPtr(-1)},
{admin, "channel", "z", intPtr(1000000)},
} {
if err := svc.UpdateAdminChannelModelSort(tc.actor, tc.channel, tc.id, ChannelModelSortRequest{SortOrder: tc.value}); err == nil {
t.Fatalf("invalid update accepted: %#v", tc)
}
}
if err := svc.UpdateAdminChannelModelSort(admin, "channel", "z", ChannelModelSortRequest{SortOrder: intPtr(0)}); err != nil {
t.Fatal(err)
}
rows, _ = svc.repo.ChannelModels("channel", true)
if rows[0].ID != "z" {
t.Fatal("resetting to zero did not restore creation order")
}
}