225 lines
5.4 KiB
Go
225 lines
5.4 KiB
Go
package version
|
|
|
|
import "testing"
|
|
|
|
func TestNormalize(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want string
|
|
}{
|
|
// Numeric versions
|
|
{"1.0", "1.0"},
|
|
{"1.0.0", "1.0.0"},
|
|
{"1.0.0.0", "1.0.0.0"},
|
|
{"5.3.2", "5.3.2"},
|
|
|
|
// Trunk
|
|
{"trunk", "dev-trunk"},
|
|
{"Trunk", "dev-trunk"},
|
|
{"TRUNK", "dev-trunk"},
|
|
{"dev-trunk", "dev-trunk"},
|
|
{"vtrunk", "dev-trunk"},
|
|
{"Vtrunk", "dev-trunk"},
|
|
|
|
// Valid pre-release suffixes
|
|
{"1.0-beta1", "1.0-beta1"},
|
|
{"1.0-RC2", "1.0-RC2"},
|
|
{"1.0-alpha", "1.0-alpha"},
|
|
{"2.0.0-beta.1", "2.0.0-beta.1"},
|
|
{"1.0-alpha3", "1.0-alpha3"},
|
|
{"1.0-a1", "1.0-a1"},
|
|
{"1.0-b2", "1.0-b2"},
|
|
{"1.0-p", "1.0-p"},
|
|
{"1.0-p1", "1.0-p1"},
|
|
{"1.0-pl", "1.0-pl"},
|
|
{"1.0-pl1", "1.0-pl1"},
|
|
{"1.0-patch", "1.0-patch"},
|
|
{"1.0-patch1", "1.0-patch1"},
|
|
{"1.0-dev", "1.0-dev"},
|
|
{"1.0-dev.1", "1.0-dev.1"},
|
|
{"1.0-stable", "1.0-stable"},
|
|
{"1.0-stable.1", "1.0-stable.1"},
|
|
|
|
// Case-insensitive pre-release
|
|
{"1.0-Beta1", "1.0-Beta1"},
|
|
{"1.0-rc2", "1.0-rc2"},
|
|
{"1.0-ALPHA", "1.0-ALPHA"},
|
|
{"1.0-DEV", "1.0-DEV"},
|
|
|
|
// Invalid: empty/whitespace
|
|
{"", ""},
|
|
{" ", ""},
|
|
|
|
// Invalid: non-version strings
|
|
{"stable", ""},
|
|
{"latest", ""},
|
|
{"not a version", ""},
|
|
|
|
// Leading v stripped (Composer VersionParser compatibility, issue #19)
|
|
{"v1.0", "1.0"},
|
|
{"v1.0.0", "1.0.0"},
|
|
{"V2.0.0", "2.0.0"},
|
|
{"v1.13.11-beta.0", "1.13.11-beta.0"},
|
|
{"v20100102", "20100102"},
|
|
|
|
// Invalid: structural
|
|
{"1.0.0.0.1", ""}, // 5+ parts
|
|
|
|
// Invalid: non-Composer pre-release suffixes (issue #17)
|
|
{"3.1.0-dev1", ""}, // dev + bare number
|
|
{"3.1.0-dev2", ""},
|
|
{"3.1.0-free", ""}, // not a Composer keyword
|
|
{"1.0f", ""}, // trailing letter, no hyphen
|
|
{"1.0-foo", ""}, // arbitrary suffix
|
|
|
|
// Invalid: trailing dot with no digits
|
|
{"1.0-beta.", ""},
|
|
{"1.0-rc.", ""},
|
|
{"1.0-alpha.", ""},
|
|
{"1.0-dev.", ""},
|
|
{"1.0-stable.", ""},
|
|
|
|
// Invalid: Wordfence corpus samples
|
|
{"1.0 12319", ""}, // space in version
|
|
{"1.0.1 Lite", ""},
|
|
{"1.1(Beta)", ""},
|
|
{"1.3..4", ""}, // double dot
|
|
{"08-03-2018", ""}, // date format
|
|
{"2.24080000-WP6.6.1", ""},
|
|
{"2025r1", ""},
|
|
{"3.0 (Beta r7)", ""},
|
|
{"3.1.37.11.L", ""}, // 5 parts + letter
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.input, func(t *testing.T) {
|
|
got := Normalize(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("Normalize(%q) = %q, want %q", tt.input, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsValid(t *testing.T) {
|
|
valid := []string{
|
|
"1.0", "1.0.0", "1.0.0.0", "trunk", "dev-trunk",
|
|
"1.0-beta1", "1.0-RC2", "1.0-alpha",
|
|
"1.0-dev", "1.0-dev.1", "1.0-stable",
|
|
}
|
|
for _, v := range valid {
|
|
if !IsValid(v) {
|
|
t.Errorf("IsValid(%q) = false, want true", v)
|
|
}
|
|
}
|
|
|
|
invalid := []string{
|
|
"", "stable", "1.0.0.0.1", "not valid",
|
|
"3.1.0-dev1", "3.1.0-dev2", "3.1.0-free",
|
|
"1.0f", "1.0-foo",
|
|
"1.0-beta.", "1.0-rc.", "1.0-alpha.", "1.0-dev.", "1.0-stable.",
|
|
}
|
|
for _, v := range invalid {
|
|
if IsValid(v) {
|
|
t.Errorf("IsValid(%q) = true, want false", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeVersions(t *testing.T) {
|
|
input := map[string]string{
|
|
"1.0": "https://example.com/1.0.zip",
|
|
"2.0": "https://example.com/2.0.zip",
|
|
"dev-trunk": "https://example.com/trunk.zip",
|
|
"v3.0": "https://example.com/v3.0.zip",
|
|
"": "https://example.com/empty.zip",
|
|
"bad!": "https://example.com/bad.zip",
|
|
"3.1.0-dev1": "https://example.com/dev1.zip",
|
|
}
|
|
|
|
got := NormalizeVersions(input)
|
|
if len(got) != 4 {
|
|
t.Fatalf("NormalizeVersions returned %d entries, want 4", len(got))
|
|
}
|
|
if got["1.0"] != "https://example.com/1.0.zip" {
|
|
t.Error("missing 1.0")
|
|
}
|
|
if got["dev-trunk"] != "https://example.com/trunk.zip" {
|
|
t.Error("missing dev-trunk")
|
|
}
|
|
if got["3.0"] != "https://example.com/v3.0.zip" {
|
|
t.Error("v3.0 should have been normalized to 3.0")
|
|
}
|
|
if _, ok := got["3.1.0-dev1"]; ok {
|
|
t.Error("3.1.0-dev1 should have been filtered out")
|
|
}
|
|
}
|
|
|
|
func TestIsStable(t *testing.T) {
|
|
stable := []string{"1.0", "1.0.0", "10.6.2", "5.3.2"}
|
|
for _, v := range stable {
|
|
if !IsStable(v) {
|
|
t.Errorf("IsStable(%q) = false, want true", v)
|
|
}
|
|
}
|
|
|
|
unstable := []string{"1.0-beta1", "10.7.0-beta.1", "1.0-RC2", "1.0-alpha", "1.0-dev", "1.0-dev.1"}
|
|
for _, v := range unstable {
|
|
if IsStable(v) {
|
|
t.Errorf("IsStable(%q) = true, want false", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLatest(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
versions map[string]string
|
|
want string
|
|
}{
|
|
{
|
|
name: "stable wins over higher beta",
|
|
versions: map[string]string{"10.6.2": "", "10.7.0-beta.1": "", "10.6.1": ""},
|
|
want: "10.6.2",
|
|
},
|
|
{
|
|
name: "only betas returns highest beta",
|
|
versions: map[string]string{"1.0-beta1": "", "1.0-beta2": ""},
|
|
want: "1.0-beta2",
|
|
},
|
|
{
|
|
name: "dev-trunk excluded",
|
|
versions: map[string]string{"dev-trunk": "", "1.0": ""},
|
|
want: "1.0",
|
|
},
|
|
{
|
|
name: "only dev-trunk returns empty",
|
|
versions: map[string]string{"dev-trunk": ""},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "empty map",
|
|
versions: map[string]string{},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "stable with RC",
|
|
versions: map[string]string{"2.0": "", "2.1-RC1": "", "1.9": ""},
|
|
want: "2.0",
|
|
},
|
|
{
|
|
name: "multiple stable picks highest",
|
|
versions: map[string]string{"1.0": "", "2.0": "", "3.0": ""},
|
|
want: "3.0",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := Latest(tt.versions)
|
|
if got != tt.want {
|
|
t.Errorf("Latest() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|