Files
dingtalk-workspace-cli/internal/app/skill_setup_publication_race_test.go

86 lines
2.6 KiB
Go

package app
import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/upgrade"
)
func TestCrossPlatformCoverageSkillSetupRollbackRetainsConcurrentReplacement(t *testing.T) {
home := t.TempDir()
base := filepath.Join(home, ".agents", "skills")
first := filepath.Join(base, "dingtalk-first")
second := filepath.Join(base, "dingtalk-second")
writeSkillSetupFile(t, first, "old first")
writeSkillSetupFile(t, second, "old second")
backups, err := backupSkillSetupTarget(home, []skillSetupBackup{{Path: first}, {Path: second}}, io.Discard)
if err != nil {
t.Fatal(err)
}
stageRoot := filepath.Join(base, ".stage")
stagedFirst := filepath.Join(stageRoot, "dingtalk-first")
stagedSecond := filepath.Join(stageRoot, "dingtalk-second")
writeSkillSetupFile(t, stagedFirst, "new first")
writeSkillSetupFile(t, stagedSecond, "new second")
failure := errors.New("injected second setup publication failure")
originalPublish := skillSetupPublishPath
calls := 0
testseam.Swap(t, &skillSetupPublishPath, func(staged, destination string) (upgrade.SkillPathPublication, error) {
calls++
if calls == 2 {
if err := os.RemoveAll(first); err != nil {
t.Fatal(err)
}
writeSkillSetupFile(t, first, "concurrent")
return upgrade.SkillPathPublication{}, failure
}
return originalPublish(staged, destination)
})
err = publishSkillSetupTarget([]skillSetupStagedDir{
{staged: stagedFirst, dest: first},
{staged: stagedSecond, dest: second},
}, backups)
if !errors.Is(err, failure) || !strings.Contains(err.Error(), "拒绝删除非本事务") {
t.Fatalf("setup transaction error = %v", err)
}
assertSkillSetupFile(t, first, "concurrent")
assertSkillSetupFile(t, second, "old second")
var firstBackup string
for _, item := range backups {
if item.original == first {
firstBackup = item.backup
break
}
}
if firstBackup == "" {
t.Fatal("first backup was not recorded")
}
assertSkillSetupFile(t, firstBackup, "old first")
}
func writeSkillSetupFile(t *testing.T, dir, content string) {
t.Helper()
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte(content), 0o644); err != nil {
t.Fatal(err)
}
}
func assertSkillSetupFile(t *testing.T, dir, want string) {
t.Helper()
content, err := os.ReadFile(filepath.Join(dir, "SKILL.md"))
if err != nil || string(content) != want {
t.Fatalf("Skill content at %s = %q, %v; want %q", dir, content, err, want)
}
}