yao/registry/manager/common/packer_test.go
Max dae35cfe86 Implement force push option for agent and MCP commands
- Add a `--force` flag to the `push` command in both agent and MCP modules, allowing users to overwrite existing versions by deleting them before pushing.
- Update the `PushOptions` struct to include the `Force` field, ensuring the functionality is integrated into the push logic.
- Enhance E2E tests to validate the behavior of the force push feature, ensuring that existing versions are deleted as expected when the flag is used.
- Introduce tests for packing directories to respect `.yaoignore` rules, improving file exclusion handling during the packaging process.
2026-03-03 10:31:50 +08:00

284 lines
7.5 KiB
Go

package common
import (
"os"
"path/filepath"
"sort"
"testing"
)
func TestPackAndUnpack(t *testing.T) {
// Create source directory
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "package.yao"), []byte(`{"name":"test"}`), 0644)
os.MkdirAll(filepath.Join(srcDir, "prompts"), 0755)
os.WriteFile(filepath.Join(srcDir, "prompts", "main.md"), []byte("You are a test."), 0644)
manifest := &PkgManifest{
Type: TypeAssistant,
Scope: "test",
Name: "demo",
Version: "1.0.0",
}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatalf("PackDir: %v", err)
}
if len(zipData) == 0 {
t.Fatal("expected non-empty zip")
}
// Read manifest from zip
m, err := ReadManifest(zipData)
if err != nil {
t.Fatalf("ReadManifest: %v", err)
}
if m.Type != TypeAssistant || m.Version != "1.0.0" {
t.Errorf("unexpected manifest: %+v", m)
}
// Unpack
destDir := t.TempDir()
files, err := UnpackTo(zipData, destDir)
if err != nil {
t.Fatalf("UnpackTo: %v", err)
}
sort.Strings(files)
if len(files) != 2 {
t.Fatalf("expected 2 files, got %d: %v", len(files), files)
}
if files[0] != "package.yao" || files[1] != "prompts/main.md" {
t.Errorf("unexpected files: %v", files)
}
// Verify content
data, err := os.ReadFile(filepath.Join(destDir, "package.yao"))
if err != nil {
t.Fatal(err)
}
if string(data) != `{"name":"test"}` {
t.Errorf("unexpected content: %s", data)
}
}
func TestPackDirWithExtraFiles(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "main.mcp.yao"), []byte("{}"), 0644)
// Create an extra file in a separate location
extraDir := t.TempDir()
os.MkdirAll(filepath.Join(extraDir, "scripts", "yao"), 0755)
scriptPath := filepath.Join(extraDir, "scripts", "yao", "rag.ts")
os.WriteFile(scriptPath, []byte("export function Search() {}"), 0644)
manifest := &PkgManifest{
Type: TypeMCP,
Scope: "yao",
Name: "rag-tools",
Version: "1.0.0",
}
extraFiles := map[string]string{
"scripts/yao/rag.ts": scriptPath,
}
zipData, err := PackDir(srcDir, manifest, extraFiles)
if err != nil {
t.Fatalf("PackDir with extras: %v", err)
}
files, err := ListZipFiles(zipData)
if err != nil {
t.Fatal(err)
}
hasScript := false
hasMCP := false
for _, f := range files {
if f == "scripts/yao/rag.ts" {
hasScript = true
}
if f == "main.mcp.yao" {
hasMCP = true
}
}
if !hasScript {
t.Error("expected scripts/yao/rag.ts in zip")
}
if !hasMCP {
t.Error("expected main.mcp.yao in zip")
}
}
func TestReadManifestMissing(t *testing.T) {
// Create a zip without pkg.yao
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "test.txt"), []byte("hello"), 0644)
manifest := &PkgManifest{Type: "test", Version: "1.0.0"}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatal(err)
}
// This should succeed because PackDir always writes pkg.yao
m, err := ReadManifest(zipData)
if err != nil {
t.Fatalf("ReadManifest should succeed: %v", err)
}
if m.Type != "test" {
t.Errorf("unexpected type: %s", m.Type)
}
}
func TestPackDirBuiltinIgnore(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "package.yao"), []byte(`{}`), 0644)
os.WriteFile(filepath.Join(srcDir, "prompts.md"), []byte("hello"), 0644)
// Files that should be excluded by built-in defaults
os.WriteFile(filepath.Join(srcDir, ".DS_Store"), []byte{}, 0644)
os.WriteFile(filepath.Join(srcDir, "debug.swp"), []byte{}, 0644)
os.WriteFile(filepath.Join(srcDir, "notes.bak"), []byte{}, 0644)
os.MkdirAll(filepath.Join(srcDir, ".git", "objects"), 0755)
os.WriteFile(filepath.Join(srcDir, ".git", "config"), []byte{}, 0644)
os.MkdirAll(filepath.Join(srcDir, ".vscode"), 0755)
os.WriteFile(filepath.Join(srcDir, ".vscode", "settings.json"), []byte{}, 0644)
os.MkdirAll(filepath.Join(srcDir, "node_modules", "foo"), 0755)
os.WriteFile(filepath.Join(srcDir, "node_modules", "foo", "index.js"), []byte{}, 0644)
manifest := &PkgManifest{Type: TypeAssistant, Scope: "test", Name: "ign", Version: "1.0.0"}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatalf("PackDir: %v", err)
}
files, err := ListZipFiles(zipData)
if err != nil {
t.Fatal(err)
}
fileSet := map[string]bool{}
for _, f := range files {
fileSet[f] = true
}
if !fileSet["package.yao"] {
t.Error("expected package.yao in zip")
}
if !fileSet["prompts.md"] {
t.Error("expected prompts.md in zip")
}
for _, excluded := range []string{".DS_Store", "debug.swp", "notes.bak", ".git/config", ".vscode/settings.json", "node_modules/foo/index.js"} {
if fileSet[excluded] {
t.Errorf("expected %s to be excluded from zip", excluded)
}
}
}
func TestPackDirYaoignoreFile(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "package.yao"), []byte(`{}`), 0644)
os.WriteFile(filepath.Join(srcDir, "keep.txt"), []byte("keep"), 0644)
os.WriteFile(filepath.Join(srcDir, "secret.key"), []byte("secret"), 0644)
os.MkdirAll(filepath.Join(srcDir, "drafts"), 0755)
os.WriteFile(filepath.Join(srcDir, "drafts", "notes.md"), []byte("draft"), 0644)
os.WriteFile(filepath.Join(srcDir, "test.log"), []byte("log"), 0644)
// .yaoignore excludes *.key and drafts/
os.WriteFile(filepath.Join(srcDir, ".yaoignore"), []byte("*.key\ndrafts/\n"), 0644)
manifest := &PkgManifest{Type: TypeAssistant, Scope: "test", Name: "ign2", Version: "1.0.0"}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatalf("PackDir: %v", err)
}
files, err := ListZipFiles(zipData)
if err != nil {
t.Fatal(err)
}
fileSet := map[string]bool{}
for _, f := range files {
fileSet[f] = true
}
if !fileSet["package.yao"] {
t.Error("expected package.yao")
}
if !fileSet["keep.txt"] {
t.Error("expected keep.txt")
}
if fileSet["secret.key"] {
t.Error("secret.key should be excluded by .yaoignore")
}
if fileSet["drafts/notes.md"] {
t.Error("drafts/notes.md should be excluded by .yaoignore")
}
if fileSet["test.log"] {
t.Error("test.log should be excluded by built-in *.log pattern")
}
if fileSet[".yaoignore"] {
t.Error(".yaoignore itself should be excluded")
}
}
func TestPackDirYaoignoreNegation(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "package.yao"), []byte(`{}`), 0644)
os.WriteFile(filepath.Join(srcDir, "a.tmp"), []byte("tmp"), 0644)
os.WriteFile(filepath.Join(srcDir, "important.tmp"), []byte("keep"), 0644)
// *.tmp is in defaults, but negate important.tmp
os.WriteFile(filepath.Join(srcDir, ".yaoignore"), []byte("!important.tmp\n"), 0644)
manifest := &PkgManifest{Type: TypeAssistant, Scope: "test", Name: "neg", Version: "1.0.0"}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatalf("PackDir: %v", err)
}
files, err := ListZipFiles(zipData)
if err != nil {
t.Fatal(err)
}
fileSet := map[string]bool{}
for _, f := range files {
fileSet[f] = true
}
if fileSet["a.tmp"] {
t.Error("a.tmp should be excluded by built-in *.tmp")
}
if !fileSet["important.tmp"] {
t.Error("important.tmp should be included via negation in .yaoignore")
}
}
func TestExtractFile(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(filepath.Join(srcDir, "data.json"), []byte(`{"key":"value"}`), 0644)
manifest := &PkgManifest{Type: "test", Version: "1.0.0"}
zipData, err := PackDir(srcDir, manifest, nil)
if err != nil {
t.Fatal(err)
}
data, err := ExtractFile(zipData, "data.json")
if err != nil {
t.Fatal(err)
}
if string(data) != `{"key":"value"}` {
t.Errorf("unexpected: %s", data)
}
_, err = ExtractFile(zipData, "missing.json")
if err == nil {
t.Error("expected error for missing file")
}
}