127 lines
3.7 KiB
Go
127 lines
3.7 KiB
Go
package pkgmgr
|
|
|
|
import "testing"
|
|
|
|
// TestParsePlainName verifies a plain npm reference parses.
|
|
func TestParsePlainName(t *testing.T) {
|
|
r, err := ParsePackageRef("npm:pi-mcp-adapter")
|
|
if err != nil {
|
|
t.Fatalf("ParsePackageRef: %v", err)
|
|
}
|
|
if r.Registry != RegistryNPM {
|
|
t.Errorf("Registry = %q, want npm", r.Registry)
|
|
}
|
|
if r.Name != "pi-mcp-adapter" {
|
|
t.Errorf("Name = %q, want pi-mcp-adapter", r.Name)
|
|
}
|
|
if r.Version != "" {
|
|
t.Errorf("Version = %q, want empty", r.Version)
|
|
}
|
|
}
|
|
|
|
// TestParseScopedName verifies a scoped npm reference parses with the leading
|
|
// '@' preserved and no false version split.
|
|
func TestParseScopedName(t *testing.T) {
|
|
r, err := ParsePackageRef("npm:@gotgenes/pi-subagents")
|
|
if err != nil {
|
|
t.Fatalf("ParsePackageRef: %v", err)
|
|
}
|
|
if r.Name != "@gotgenes/pi-subagents" {
|
|
t.Errorf("Name = %q, want @gotgenes/pi-subagents", r.Name)
|
|
}
|
|
if r.Version != "" {
|
|
t.Errorf("Version = %q, want empty", r.Version)
|
|
}
|
|
}
|
|
|
|
// TestParseWithVersion verifies the "@version" suffix splits off correctly for
|
|
// both plain and scoped names.
|
|
func TestParseWithVersion(t *testing.T) {
|
|
cases := []struct {
|
|
ref, name, version string
|
|
}{
|
|
{"npm:pi-mcp-adapter@1.2.3", "pi-mcp-adapter", "1.2.3"},
|
|
{"npm:@scope/name@0.1.0", "@scope/name", "0.1.0"},
|
|
{"npm:@scope/name", "@scope/name", ""},
|
|
{"npm:pkg@latest", "pkg", "latest"},
|
|
}
|
|
for _, c := range cases {
|
|
r, err := ParsePackageRef(c.ref)
|
|
if err != nil {
|
|
t.Errorf("ParsePackageRef(%q): %v", c.ref, err)
|
|
continue
|
|
}
|
|
if r.Name != c.name || r.Version != c.version {
|
|
t.Errorf("ParsePackageRef(%q) = {%q, %q}, want {%q, %q}", c.ref, r.Name, r.Version, c.name, c.version)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestParseMissingPrefix verifies a reference without npm: is rejected.
|
|
func TestParseMissingPrefix(t *testing.T) {
|
|
for _, ref := range []string{"pi-mcp-adapter", ""} {
|
|
if _, err := ParsePackageRef(ref); err == nil {
|
|
t.Errorf("ParsePackageRef(%q) = nil error, want error", ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestParseUnsupportedPrefix verifies non-npm sources are rejected (github:/file:).
|
|
func TestParseUnsupportedPrefix(t *testing.T) {
|
|
for _, ref := range []string{"github:owner/repo", "file:./local", "pypi:foo"} {
|
|
if _, err := ParsePackageRef(ref); err == nil {
|
|
t.Errorf("ParsePackageRef(%q) = nil error, want error", ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestParseInvalidName verifies illegal npm names are rejected.
|
|
func TestParseInvalidName(t *testing.T) {
|
|
cases := []string{
|
|
"npm:has space", // whitespace
|
|
"npm:UPPER", // uppercase
|
|
"npm:bad;rm -rf", // shell metacharacters
|
|
"npm:.hidden", // leading dot
|
|
"npm:_underscore", // leading underscore
|
|
"npm:@scope", // scope without name
|
|
"npm:@/name", // empty scope
|
|
"npm:@scope/", // empty name
|
|
"npm:a/b/c", // too many slashes (unscoped with slash)
|
|
"npm:", // empty name after prefix
|
|
}
|
|
for _, ref := range cases {
|
|
if _, err := ParsePackageRef(ref); err == nil {
|
|
t.Errorf("ParsePackageRef(%q) = nil error, want error", ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRefStringRoundTrips verifies String reconstructs the canonical reference.
|
|
func TestRefStringRoundTrips(t *testing.T) {
|
|
cases := []string{
|
|
"npm:pi-mcp-adapter",
|
|
"npm:pi-mcp-adapter@1.2.3",
|
|
"npm:@scope/name@0.1.0",
|
|
}
|
|
for _, ref := range cases {
|
|
r, err := ParsePackageRef(ref)
|
|
if err != nil {
|
|
t.Fatalf("ParsePackageRef(%q): %v", ref, err)
|
|
}
|
|
if got := r.String(); got != ref {
|
|
t.Errorf("String() = %q, want %q", got, ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestParseTrimsWhitespace verifies surrounding whitespace is tolerated.
|
|
func TestParseTrimsWhitespace(t *testing.T) {
|
|
r, err := ParsePackageRef(" npm:pi-web-access ")
|
|
if err != nil {
|
|
t.Fatalf("ParsePackageRef: %v", err)
|
|
}
|
|
if r.Name != "pi-web-access" {
|
|
t.Errorf("Name = %q, want pi-web-access", r.Name)
|
|
}
|
|
}
|