Files
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

245 lines
7.5 KiB
Go

package services
import (
"errors"
"os"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
)
func createSkillTestSymlink(t *testing.T, target, link string) {
t.Helper()
if err := os.Symlink(target, link); err != nil {
// Windows requires Developer Mode or an elevated symlink privilege.
if runtime.GOOS == "windows" && errors.Is(err, syscall.Errno(1314)) {
t.Skipf("symlink privilege unavailable: %v", err)
}
t.Fatal(err)
}
}
func TestBuiltinSkillsInstall(t *testing.T) {
s := NewSkillService()
project := t.TempDir()
ids := []string{}
for _, skill := range s.List() {
ids = append(ids, skill.ID)
}
results, err := s.Install(ids, "project", project, false)
if err != nil {
t.Fatal(err)
}
if len(results) != 5 {
t.Fatalf("results: %d", len(results))
}
for _, result := range results {
if result.Status != "installed" {
t.Fatalf("%+v", result)
}
expected := filepath.Join(project, ".agents", "skills", result.ID)
if result.Path != expected {
t.Fatalf("path: %s", result.Path)
}
content, err := os.ReadFile(filepath.Join(expected, "SKILL.md"))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(content), "name: "+result.ID) {
t.Fatal("missing frontmatter")
}
if _, err := os.Stat(filepath.Join(expected, "references", "mcp-workflow.md")); err != nil {
t.Fatal(err)
}
}
// Reinstallation must preserve an edited entrypoint and all custom files.
entrypoint := filepath.Join(results[0].Path, "SKILL.md")
if err := os.WriteFile(entrypoint, []byte("user edits"), 0644); err != nil {
t.Fatal(err)
}
results, err = s.Install(ids, "project", project, false)
if err != nil {
t.Fatal(err)
}
for _, result := range results {
if result.Status != "skipped" {
t.Fatalf("%+v", result)
}
}
content, err := os.ReadFile(entrypoint)
if err != nil || string(content) != "user edits" {
t.Fatalf("user edits replaced: %v", err)
}
}
func TestBuiltinSkillsRejectInvalidRequest(t *testing.T) {
s := NewSkillService()
root := filepath.Join(t.TempDir(), "new")
for _, ids := range [][]string{nil, {"../../escape"}, {"pvfine-quests", "unknown"}, {"pvfine-quests", "pvfine-quests"}} {
if _, err := s.Install(ids, "custom", root, false); err == nil {
t.Fatalf("accepted %v", ids)
}
}
if _, err := os.Stat(root); !os.IsNotExist(err) {
t.Fatal("invalid request created directories")
}
for _, target := range []string{"project", "custom", "unknown"} {
if _, err := s.ResolveDirectory(target, "relative"); err == nil {
t.Fatalf("accepted %s", target)
}
}
}
func TestBuiltinSkillsFileAndSymlinkConflict(t *testing.T) {
for _, conflict := range []string{"file", "symlink"} {
t.Run(conflict, func(t *testing.T) {
s := NewSkillService()
root, external := t.TempDir(), t.TempDir()
destination := filepath.Join(root, "pvfine-quests")
if conflict == "file" {
if err := os.WriteFile(destination, []byte("keep"), 0644); err != nil {
t.Fatal(err)
}
} else {
createSkillTestSymlink(t, external, destination)
}
results, err := s.Install([]string{"pvfine-quests", "pvfine-upgrade"}, "custom", root, false)
if err != nil {
t.Fatal(err)
}
if results[0].Status != "skipped" || results[1].Status != "installed" {
t.Fatalf("%+v", results)
}
if conflict == "file" {
content, err := os.ReadFile(destination)
if err != nil || string(content) != "keep" {
t.Fatalf("conflicting file changed: %v", err)
}
}
entries, err := os.ReadDir(external)
if err != nil || len(entries) != 0 {
t.Fatalf("external directory changed: %v", err)
}
})
}
}
func TestBuiltinSkillDefaultDirectories(t *testing.T) {
s := NewSkillService()
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
for target, tool := range map[string]string{"global": ".agents", "claude": ".claude"} {
got, err := s.ResolveDirectory(target, "")
if err != nil || got != filepath.Join(home, tool, "skills") {
t.Fatalf("%s: %s %v", target, got, err)
}
}
}
func TestBuiltinSkillsOverwritePreservesExtraFiles(t *testing.T) {
s := NewSkillService()
root := t.TempDir()
id := "pvfine-quests"
if _, err := s.Install([]string{id}, "custom", root, false); err != nil {
t.Fatal(err)
}
destination := filepath.Join(root, id)
for _, relative := range []string{"SKILL.md", "references/mcp-workflow.md", "references/custom.md"} {
if err := os.WriteFile(filepath.Join(destination, filepath.FromSlash(relative)), []byte("local content"), 0644); err != nil {
t.Fatal(err)
}
}
results, err := s.Install([]string{id, "pvfine-upgrade"}, "custom", root, true)
if err != nil {
t.Fatal(err)
}
if results[0].Status != "updated" || results[1].Status != "installed" {
t.Fatalf("%+v", results)
}
for _, relative := range []string{"SKILL.md", "references/mcp-workflow.md"} {
expected, err := builtinSkills.ReadFile("builtin_skills/" + id + "/" + relative)
if err != nil {
t.Fatal(err)
}
got, err := os.ReadFile(filepath.Join(destination, filepath.FromSlash(relative)))
if err != nil || string(got) != string(expected) {
t.Fatalf("not updated: %s %v", relative, err)
}
}
extra, err := os.ReadFile(filepath.Join(destination, "references", "custom.md"))
if err != nil || string(extra) != "local content" {
t.Fatalf("extra file changed: %v", err)
}
}
func TestBuiltinSkillsOverwriteRejectsConflictsBeforeWriting(t *testing.T) {
for _, conflict := range []string{"root-symlink", "root-file", "reference-symlink", "reference-directory", "reference-parent-symlink"} {
t.Run(conflict, func(t *testing.T) {
s := NewSkillService()
root, external := t.TempDir(), t.TempDir()
id := "pvfine-quests"
destination := filepath.Join(root, id)
if err := os.WriteFile(filepath.Join(external, "marker"), []byte("keep"), 0644); err != nil {
t.Fatal(err)
}
if conflict == "root-symlink" {
createSkillTestSymlink(t, external, destination)
} else if conflict == "root-file" {
if err := os.WriteFile(destination, []byte("keep"), 0644); err != nil {
t.Fatal(err)
}
} else {
if _, err := s.Install([]string{id}, "custom", root, false); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(destination, "SKILL.md"), []byte("local entrypoint"), 0644); err != nil {
t.Fatal(err)
}
reference := filepath.Join(destination, "references", "mcp-workflow.md")
if err := os.Remove(reference); err != nil {
t.Fatal(err)
}
switch conflict {
case "reference-symlink":
createSkillTestSymlink(t, filepath.Join(external, "marker"), reference)
case "reference-directory":
if err := os.Mkdir(reference, 0755); err != nil {
t.Fatal(err)
}
case "reference-parent-symlink":
parent := filepath.Dir(reference)
if err := os.Remove(parent); err != nil {
t.Fatal(err)
}
createSkillTestSymlink(t, external, parent)
}
}
results, err := s.Install([]string{id}, "custom", root, true)
if err != nil {
t.Fatal(err)
}
if results[0].Status != "failed" {
t.Fatalf("%+v", results)
}
if conflict != "root-symlink" && conflict != "root-file" {
entry, err := os.ReadFile(filepath.Join(destination, "SKILL.md"))
if err != nil || string(entry) != "local entrypoint" {
t.Fatalf("entrypoint changed on failed preflight: %v", err)
}
}
marker, err := os.ReadFile(filepath.Join(external, "marker"))
if err != nil || string(marker) != "keep" {
t.Fatalf("external file changed: %v", err)
}
entries, err := os.ReadDir(external)
if err != nil || len(entries) != 1 {
t.Fatalf("external directory changed: %v", err)
}
})
}
}