fix(skills): 技能文件包 - 修复旧数据迁移回填元数据超限
This commit is contained in:
1 parent
c8a91fd2e9
commit
9d9cec8e2e
3 files changed
+77
-1
No files matched your search
@@ -118,7 +118,9 @@ func (s *Service) EnsureSkillPackages() error {
|
|||||||
}
|
}
|
||||||
for index := range skills {
|
for index := range skills {
|
||||||
skill := &skills[index]
|
skill := &skills[index]
|
||||||
archive, err := archiveFromMarkdown([]byte(skill.Instruction), skill.Name, skill.Description)
|
// Legacy fields are only fallbacks for package metadata. Bound them at
|
||||||
|
// this migration boundary without rewriting source fields or relaxing uploads.
|
||||||
|
archive, err := archiveFromMarkdown([]byte(skill.Instruction), truncateSkillMetadata(skill.Name, 80), truncateSkillMetadata(skill.Description, 500))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("迁移技能 %s 文件包失败: %w", skill.ID, err)
|
return fmt.Errorf("迁移技能 %s 文件包失败: %w", skill.ID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package skills
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -27,6 +28,73 @@ func TestArchiveFromMarkdownInfersMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReportedBuiltinSkillPackage(t *testing.T) {
|
||||||
|
var definitions []builtinSkillDefinition
|
||||||
|
if err := json.Unmarshal(builtinSkillsJSON, &definitions); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, skill := range definitions {
|
||||||
|
if skill.SkillID != "16000000000049" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
archive, err := archiveFromMarkdown([]byte(skill.Instruction), skill.SkillName, skill.Description)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len([]rune(archive.Metadata.Description)) != 500 || string(archive.Files["SKILL.md"]) != skill.Instruction {
|
||||||
|
t.Fatal("reported skill must have bounded metadata and unchanged instruction")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal("reported builtin skill is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnsureSkillPackagesBoundsLegacyFallbackMetadata(t *testing.T) {
|
||||||
|
for _, source := range []int{3, skillSourceUser} {
|
||||||
|
db, err := gorm.Open(sqlite.Open("file:"+kernel.NewID()+"?mode=memory&cache=shared"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { _ = sqlDB.Close() })
|
||||||
|
if err := db.AutoMigrate(&model.Skill{}, &model.SkillVersion{}, &model.SkillFile{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
svc := New(repository.New(db), t.TempDir(), nil)
|
||||||
|
skill := model.Skill{ID: kernel.NewID(), Name: strings.Repeat("名", 81), Description: strings.Repeat("文", 503), Instruction: "## 旧技能\n", Status: skillStatusEnabled, Source: source}
|
||||||
|
if err := db.Create(&skill).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for range 2 {
|
||||||
|
if err := svc.EnsureSkillPackages(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertSkillVersionCount(t, db, skill.ID, 1)
|
||||||
|
var saved model.Skill
|
||||||
|
if err := db.First(&saved, "id = ?", skill.ID).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if saved.Name != skill.Name || saved.Description != skill.Description || saved.Instruction != skill.Instruction {
|
||||||
|
t.Fatal("migration changed original skill fields")
|
||||||
|
}
|
||||||
|
version, err := svc.repo.SkillVersion(saved.CurrentVersionID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err := svc.readSkillArchiveEntry(version, "SKILL.md")
|
||||||
|
if err != nil || string(body) != skill.Instruction {
|
||||||
|
t.Fatalf("original instruction not preserved: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := archiveFromMarkdown([]byte("## 旧技能\n"), strings.Repeat("名", 81), strings.Repeat("文", 503)); err == nil {
|
||||||
|
t.Fatal("non-migration archive input must still reject oversized fallback metadata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestArchiveFromMarkdownTruncatesInferredDescriptionWithinLimit(t *testing.T) {
|
func TestArchiveFromMarkdownTruncatesInferredDescriptionWithinLimit(t *testing.T) {
|
||||||
longDescription := bytes.Repeat([]byte("描述内容。"), 140)
|
longDescription := bytes.Repeat([]byte("描述内容。"), 140)
|
||||||
data := append([]byte("# 风格库四级匹配序\n\n"), longDescription...)
|
data := append([]byte("# 风格库四级匹配序\n\n"), longDescription...)
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
|
|||||||
|
|
||||||
# 待测试
|
# 待测试
|
||||||
|
|
||||||
|
## 技能文件包迁移元数据长度
|
||||||
|
|
||||||
|
- [x] 启动迁移对旧技能名称、简介的回填元数据按字符数限制长度,省略号计入上限;不改写原始名称、简介和正文,不放宽非迁移入口的校验。
|
||||||
|
- [x] 新增回归先复现旧回填路径的长度错误,修复后技能域全量测试通过;覆盖内置与用户旧技能、重复初始化不重复建版本、原始正文和字段保留,以及 `16000000000049` 内置技能包。
|
||||||
|
- [ ] 在报错用户的实际部署核对后端镜像与构建提交,并用包含修复的新产物验证启动迁移。本地通过不代表用户环境已修复;本轮未发布镜像、未操作生产数据库。
|
||||||
|
|
||||||
## 模型失败原因在画布卡片展示
|
## 模型失败原因在画布卡片展示
|
||||||
|
|
||||||
- [x] 后端 Provider、错误分类和请求日志专项回归通过;本地模拟上游验证 HTTP 400 与 HTTP 200 业务失败均保留安全原因,覆盖图片响应和通用 JSON 响应。前端 4 项专项测试通过,覆盖具体原因、HTTP 状态提示、审核元数据及 URL 隐藏。
|
- [x] 后端 Provider、错误分类和请求日志专项回归通过;本地模拟上游验证 HTTP 400 与 HTTP 200 业务失败均保留安全原因,覆盖图片响应和通用 JSON 响应。前端 4 项专项测试通过,覆盖具体原因、HTTP 状态提示、审核元数据及 URL 隐藏。
|
||||||
|
|||||||
Reference in new issue
Block a user