mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
add poetry command (#67)
* add poetry command * add poetry.lock extractor & define cmd parser for poetry * feat: support Poetry caret/tilde version syntax * add wildcard constraint support & test cases * readme update * chore: small fixes
This commit is contained in:
@@ -251,6 +251,348 @@ func TestPipConvertCompatibleRelease(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPypiConvertPoetryVersionConstraints(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
wantErr bool
|
||||
}{
|
||||
// Basic functionality tests
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
expected: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "package name only",
|
||||
input: "pendulum",
|
||||
expected: "pendulum",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "inequality constraints",
|
||||
input: "pendulum>=2.0.0",
|
||||
expected: "pendulum>=2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty package name",
|
||||
input: "@^1.0.0",
|
||||
expected: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "whitespace handling",
|
||||
input: " pendulum@^2.0.5 ",
|
||||
expected: "pendulum>=2.0.5,<3.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// Caret constraint tests
|
||||
{
|
||||
name: "caret basic major version",
|
||||
input: "pendulum@^2.0.5",
|
||||
expected: "pendulum>=2.0.5,<3.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with major version 0",
|
||||
input: "requests@^0.2.3",
|
||||
expected: "requests>=0.2.3,<0.3.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with major and minor version 0",
|
||||
input: "numpy@^0.0.5",
|
||||
expected: "numpy>=0.0.5,<0.0.6",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with two parts",
|
||||
input: "flask@^1.1",
|
||||
expected: "flask>=1.1.0,<2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with one part",
|
||||
input: "pytest@^7",
|
||||
expected: "pytest>=7.0.0,<8.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with zero major & two parts",
|
||||
input: "package@^0.5",
|
||||
expected: "package>=0.5.0,<0.6.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret invalid version",
|
||||
input: "invalid@^abc.def",
|
||||
expected: "",
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// Tilde constraint tests
|
||||
{
|
||||
name: "tilde basic three parts",
|
||||
input: "pendulum@~2.0.5",
|
||||
expected: "pendulum>=2.0.5,<2.1.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tilde with two parts",
|
||||
input: "requests@~1.2",
|
||||
expected: "requests>=1.2.0,<1.3.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tilde with one part",
|
||||
input: "numpy@~2",
|
||||
expected: "numpy>=2.0.0,<3.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tilde with zero major",
|
||||
input: "package@~0.5.2",
|
||||
expected: "package>=0.5.2,<0.6.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tilde invalid version",
|
||||
input: "invalid@~abc.def",
|
||||
expected: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "tilde without @ separator",
|
||||
input: "requests~1.2.0",
|
||||
expected: "requests>=1.2.0,<1.3.0",
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// With extras
|
||||
{
|
||||
name: "caret with extras using @ format",
|
||||
input: "fastapi[all]@^0.68.0",
|
||||
expected: "fastapi[all]>=0.68.0,<0.69.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "caret with extras without @ separator",
|
||||
input: "django[mysql,redis]^3.0",
|
||||
expected: "django[mysql,redis]>=3.0.0,<4.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tilde with multiple extras",
|
||||
input: "uvicorn[standard,dev]~0.15.0",
|
||||
expected: "uvicorn[standard,dev]>=0.15.0,<0.16.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty extras with caret",
|
||||
input: "numpy[]^1.20.0",
|
||||
expected: "numpy[]>=1.20.0,<2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// Standard constraint pass-through tests (no Poetry operators)
|
||||
{
|
||||
name: "standard python format",
|
||||
input: "pendulum>=2.0.0",
|
||||
expected: "pendulum>=2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "standard exact version",
|
||||
input: "django==3.2.0",
|
||||
expected: "django==3.2.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "standard with extras",
|
||||
input: "fastapi[all]>=0.68.0",
|
||||
expected: "fastapi[all]>=0.68.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "complex version range",
|
||||
input: "requests>=2.0,<3.0",
|
||||
expected: "requests>=2.0,<3.0",
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// Wildcard constraint tests
|
||||
{
|
||||
name: "wildcard all versions",
|
||||
input: "requests@*",
|
||||
expected: "requests>=0.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard major version",
|
||||
input: "django@1.*",
|
||||
expected: "django>=1.0.0,<2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard minor version",
|
||||
input: "flask@1.2.*",
|
||||
expected: "flask>=1.2.0,<1.3.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard without @ separator",
|
||||
input: "numpy@2.*",
|
||||
expected: "numpy>=2.0.0,<3.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard with extras",
|
||||
input: "fastapi[all]@1.*",
|
||||
expected: "fastapi[all]>=1.0.0,<2.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard with extras without @ separator",
|
||||
input: "uvicorn[standard]@0.*",
|
||||
expected: "uvicorn[standard]>=0.0.0,<1.0.0",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result, err := pypiConvertPoetryVersionConstraints(tc.input)
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPypiConvertCaretConstraint(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "standard major version",
|
||||
input: "1.2.3",
|
||||
expected: ">=1.2.3,<2.0.0",
|
||||
},
|
||||
{
|
||||
name: "major version zero",
|
||||
input: "0.2.3",
|
||||
expected: ">=0.2.3,<0.3.0",
|
||||
},
|
||||
{
|
||||
name: "major and minor version zero",
|
||||
input: "0.0.3",
|
||||
expected: ">=0.0.3,<0.0.4",
|
||||
},
|
||||
{
|
||||
name: "two parts",
|
||||
input: "1.2",
|
||||
expected: ">=1.2.0,<2.0.0",
|
||||
},
|
||||
{
|
||||
name: "one part",
|
||||
input: "7",
|
||||
expected: ">=7.0.0,<8.0.0",
|
||||
},
|
||||
{
|
||||
name: "zero major two parts",
|
||||
input: "0.5",
|
||||
expected: ">=0.5.0,<0.6.0",
|
||||
},
|
||||
{
|
||||
name: "double digit versions",
|
||||
input: "10.15.22",
|
||||
expected: ">=10.15.22,<11.0.0",
|
||||
},
|
||||
{
|
||||
name: "invalid version",
|
||||
input: "abc.def",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "empty version",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := pypiConvertCaretConstraint(tc.input)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPypiConvertTildeConstraint(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "three parts",
|
||||
input: "1.2.3",
|
||||
expected: ">=1.2.3,<1.3.0",
|
||||
},
|
||||
{
|
||||
name: "two parts",
|
||||
input: "1.2",
|
||||
expected: ">=1.2.0,<1.3.0",
|
||||
},
|
||||
{
|
||||
name: "one part",
|
||||
input: "2",
|
||||
expected: ">=2.0.0,<3.0.0",
|
||||
},
|
||||
{
|
||||
name: "zero major version",
|
||||
input: "0.5.2",
|
||||
expected: ">=0.5.2,<0.6.0",
|
||||
},
|
||||
{
|
||||
name: "double digit versions",
|
||||
input: "10.15.22",
|
||||
expected: ">=10.15.22,<10.16.0",
|
||||
},
|
||||
{
|
||||
name: "large version numbers",
|
||||
input: "99.99.99",
|
||||
expected: ">=99.99.99,<99.100.0",
|
||||
},
|
||||
{
|
||||
name: "invalid version",
|
||||
input: "abc.def",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "empty version",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := pypiConvertTildeConstraint(tc.input)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUvParseCommand(t *testing.T) {
|
||||
pm, err := NewPypiPackageManager(DefaultUvPackageManagerConfig())
|
||||
assert.NoError(t, err)
|
||||
@@ -360,3 +702,64 @@ func TestUvParseCommand(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPypiConvertWildcardConstraint(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "all versions wildcard",
|
||||
input: "*",
|
||||
expected: ">=0.0.0",
|
||||
},
|
||||
{
|
||||
name: "major version wildcard",
|
||||
input: "1.*",
|
||||
expected: ">=1.0.0,<2.0.0",
|
||||
},
|
||||
{
|
||||
name: "minor version wildcard",
|
||||
input: "2.5.*",
|
||||
expected: ">=2.5.0,<2.6.0",
|
||||
},
|
||||
{
|
||||
name: "zero major version wildcard",
|
||||
input: "0.*",
|
||||
expected: ">=0.0.0,<1.0.0",
|
||||
},
|
||||
{
|
||||
name: "zero minor version wildcard",
|
||||
input: "1.0.*",
|
||||
expected: ">=1.0.0,<1.1.0",
|
||||
},
|
||||
{
|
||||
name: "high version numbers",
|
||||
input: "12.34.*",
|
||||
expected: ">=12.34.0,<12.35.0",
|
||||
},
|
||||
{
|
||||
name: "invalid wildcard format",
|
||||
input: "1.2.3.*",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "invalid non-numeric parts",
|
||||
input: "abc.*",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "wildcard without dot",
|
||||
input: "1*",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := pypiConvertWildcardConstraint(tc.input)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user