Files
BlackBean/pigo/internal/pkgmgr/install_test.go
T
2026-08-14 23:41:57 +08:00

141 lines
4.3 KiB
Go

package pkgmgr
import (
"os"
"path/filepath"
"runtime"
"testing"
)
// fakeNPMForInstall writes a fake `npm` onto PATH that packs a prebuilt tarball
// (built from files) into --pack-destination, mimicking `npm pack`.
func fakeNPMForInstall(t *testing.T, tarballFiles map[string]string, packName string) {
t.Helper()
binDir := t.TempDir()
srcTarball := filepath.Join(binDir, "src.tgz")
makeTarGz(t, srcTarball, tarballFiles)
script := `#!/bin/sh
dest=""
prev=""
for a in "$@"; do
if [ "$prev" = "--pack-destination" ]; then dest="$a"; fi
prev="$a"
done
cp "` + srcTarball + `" "$dest/` + packName + `"
echo "` + packName + `"
`
if err := os.WriteFile(filepath.Join(binDir, "npm"), []byte(script), 0o755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
}
// TestInstallExtensionEndToEnd drives Install for an extension package through
// fetch (fake npm) → classify → distribute → lockfile.
func TestInstallExtensionEndToEnd(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("fake npm shell script + extension install are POSIX-only")
}
home := t.TempDir()
t.Setenv("PIGO_HOME", home)
fakeNPMForInstall(t, map[string]string{
"package/package.json": `{"name":"pi-demo","version":"1.2.0","bin":"./cli.js"}`,
"package/cli.js": "#!/usr/bin/env node\nconsole.log('hi')\n",
}, "pi-demo-1.2.0.tgz")
lockPath := filepath.Join(home, "packages.json")
res, err := Install("npm:pi-demo", lockPath, nil)
if err != nil {
t.Fatalf("Install: %v", err)
}
if res.Name != "pi-demo" || res.Version != "1.2.0" {
t.Errorf("result = %+v, want pi-demo@1.2.0", res)
}
if len(res.Types) != 1 || res.Types[0] != TypeExtension {
t.Errorf("types = %v, want [extension]", res.Types)
}
// Launcher exists in plugins.
if _, err := os.Stat(filepath.Join(home, "plugins", "pi-demo")); err != nil {
t.Errorf("launcher not installed: %v", err)
}
// Lockfile records the package.
lf, err := Load(lockPath)
if err != nil {
t.Fatalf("Load lockfile: %v", err)
}
p, ok := lf.Get("pi-demo")
if !ok {
t.Fatal("lockfile missing pi-demo")
}
if p.Source != "npm:pi-demo" || p.Version != "1.2.0" {
t.Errorf("lockfile entry = %+v", p)
}
if len(p.Files) == 0 {
t.Error("lockfile entry has no files")
}
}
// TestInstallMultiType drives Install for a package that is both extension and
// skill, verifying both distributions happen and both types are recorded.
func TestInstallMultiType(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("POSIX-only")
}
home := t.TempDir()
skills := t.TempDir()
t.Setenv("PIGO_HOME", home)
t.Setenv("PIGO_SKILLS_DIR", skills)
fakeNPMForInstall(t, map[string]string{
"package/package.json": `{"name":"combo","version":"1.0.0","bin":"./x.js","pi":{"types":["extension","skill"]}}`,
"package/x.js": "#!/usr/bin/env node\n",
"package/SKILL.md": "---\nname: combo\ndescription: d\n---\nbody",
}, "combo-1.0.0.tgz")
res, err := Install("npm:combo", filepath.Join(home, "packages.json"), nil)
if err != nil {
t.Fatalf("Install: %v", err)
}
if len(res.Types) != 2 {
t.Errorf("types = %v, want extension+skill", res.Types)
}
if _, err := os.Stat(filepath.Join(home, "plugins", "combo")); err != nil {
t.Errorf("extension launcher missing: %v", err)
}
if _, err := os.Stat(filepath.Join(skills, "combo", "SKILL.md")); err != nil {
t.Errorf("skill not installed: %v", err)
}
}
// TestInstallUnrecognized verifies a non-pi package fails install with a clear
// error and writes no lockfile entry.
func TestInstallUnrecognized(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("POSIX-only")
}
home := t.TempDir()
t.Setenv("PIGO_HOME", home)
fakeNPMForInstall(t, map[string]string{
"package/package.json": `{"name":"lodash","version":"4.0.0"}`,
}, "lodash-4.0.0.tgz")
lockPath := filepath.Join(home, "packages.json")
if _, err := Install("npm:lodash", lockPath, nil); err == nil {
t.Fatal("Install of non-pi package = nil error, want error")
}
if _, err := os.Stat(lockPath); !os.IsNotExist(err) {
t.Errorf("lockfile written for failed install: %v", err)
}
}
// TestInstallBadRef verifies an invalid reference is rejected before any fetch.
func TestInstallBadRef(t *testing.T) {
if _, err := Install("github:owner/repo", filepath.Join(t.TempDir(), "packages.json"), nil); err == nil {
t.Fatal("Install with non-npm ref = nil error, want error")
}
}