Files
ttstd-infinite-canvas/backend/internal/tools/tools_integration_test.go
ddCat 9148ceab66 feat(canvas): 画布工作区 - 整合侧栏与风格预设并修复提示词回填
选择性整合 #547,保留主分支节点与工具条设计。新增默认折叠的节点、资产、任务和历史面板,风格与预设保留在节点内,移除重复入口。

补齐工具权限、提示词解析、资源引用保护及数据库迁移,修复提示词清空和连续编辑后的生成配置同步。

验证:前端全量 1925 项测试、构建、类型检查及 lint 通过;后端 go test ./... 通过。真实模型、PostgreSQL 实库与登录态页面验收仍待进行。

Refs #547

Co-authored-by: bensharp <bensharp@qq.com>
2026-09-20 16:40:08 +08:00

101 lines
3.3 KiB
Go

package tools_test
import (
"infinite-canvas/backend/internal/database"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/repository"
"infinite-canvas/backend/internal/tools"
"path/filepath"
"strings"
"testing"
)
func TestToolsVisibilityTokensSeedAndReferences(t *testing.T) {
db, err := database.Open(database.Config{Driver: "sqlite", DSN: filepath.Join(t.TempDir(), "tools.db")})
if err != nil {
t.Fatal(err)
}
sqlDB, _ := db.DB()
defer sqlDB.Close()
if err = database.MigrateSchema(db); err != nil {
t.Fatal(err)
}
repo := repository.New(db)
svc := tools.New(repo)
for range 2 {
if err := tools.EnsureBuiltinTools(repo); err != nil {
t.Fatal(err)
}
}
list, err := svc.List("alice", tools.ToolListRequest{PageSize: 80})
if err != nil || list.TotalCount != 87 || !list.HasMore {
t.Fatalf("seed/pagination: %+v %v", list, err)
}
custom, err := svc.Create("alice", tools.ToolMutationRequest{Type: "style", Label: "private", Prompt: "private style"})
if err != nil || custom.ID <= 87 {
t.Fatalf("custom: %+v %v", custom, err)
}
if _, err := svc.Detail("bob", custom.ID); err == nil {
t.Fatal("private tool leaked")
}
if _, err := svc.SetFavorite("bob", custom.ID, true); err == nil {
t.Fatal("private favorite accepted")
}
if err := svc.Delete("bob", custom.ID); err == nil {
t.Fatal("cross-user delete accepted")
}
if _, err := svc.SetFavorite("alice", custom.ID, true); err != nil {
t.Fatal(err)
}
if _, err := svc.SetFavorite("alice", custom.ID, true); err != nil {
t.Fatal(err)
}
for _, scope := range []string{"", "public", "favorites", "custom"} {
got, err := svc.List("bob", tools.ToolListRequest{Scope: scope, Search: "private"})
if err != nil || got.TotalCount != 0 {
t.Fatalf("scope %q leaked: %+v %v", scope, got, err)
}
}
valid := "scene @[tool:style:1:label:Palette]"
expanded, err := svc.ResolveToolMentionTokens("alice", "image", valid)
if err != nil || strings.Contains(expanded, "@[tool:") {
t.Fatalf("expand: %s %v", expanded, err)
}
for _, token := range []string{"@[tool:style:0:x:X]", "@[tool:effect:1:x:X]", "@[tool:style:999999:x:X]", "@[tool:bogus:1:x:X]", "@[tool:style:1:broken]"} {
if _, err := svc.ResolveToolMentionTokens("alice", "image", token); err == nil {
t.Fatalf("accepted %s", token)
}
}
if _, err := svc.ResolveToolMentionTokens("alice", "video", valid); err == nil {
t.Fatal("wrong mode accepted")
}
if _, err := svc.ResolveToolMentionTokens("", "image", valid); err == nil {
t.Fatal("anonymous accepted")
}
if err := db.Model(&model.Tool{}).Where("id = ?", 1).Update("enabled", false).Error; err != nil {
t.Fatal(err)
}
if _, err := svc.ResolveToolMentionTokens("alice", "image", valid); err == nil {
t.Fatal("disabled accepted")
}
if err := svc.Delete("alice", custom.ID); err != nil {
t.Fatal(err)
}
var count int64
db.Model(&model.ToolFavorite{}).Where("tool_id = ?", custom.ID).Count(&count)
if count != 0 {
t.Fatal("orphan favorites")
}
if err := db.Model(&model.Tool{}).Where("id = ?", 1).Updates(map[string]any{"source": "user", "owner_id": "alice", "label": "keep"}).Error; err != nil {
t.Fatal(err)
}
if err := tools.EnsureBuiltinTools(repo); err == nil {
t.Fatal("seed collision accepted")
}
var preserved model.Tool
db.First(&preserved, 1)
if preserved.Label != "keep" {
t.Fatal("user tool overwritten")
}
}