Pulse/internal/updates/version_test.go
rcourtman 4f824ab148 style: Apply gofmt to 37 files
Standardize code formatting across test files and monitor.go.
No functional changes.
2025-12-02 17:21:48 +00:00

614 lines
16 KiB
Go

package updates
import "testing"
func TestNormalizeVersionString(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected string
}{
{
name: "release version stays the same",
input: "4.24.0-rc.3",
expected: "4.24.0-rc.3",
},
{
name: "git describe converts to build metadata",
input: "4.24.0-rc.3-45-gABCDEF",
expected: "4.24.0-rc.3+git.45.gabcdef",
},
{
name: "git describe dirty includes dirty flag",
input: "4.24.0-rc.3-45-gabc123-dirty",
expected: "4.24.0-rc.3+git.45.gabc123.dirty",
},
{
name: "branch name falls back to prerelease",
input: "issue-551",
expected: "0.0.0-issue-551",
},
{
name: "branch with slashes sanitized",
input: "feature/new-api",
expected: "0.0.0-feature-new-api",
},
{
name: "empty input defaults to dev",
input: "",
expected: "0.0.0-dev",
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := normalizeVersionString(tc.input); got != tc.expected {
t.Fatalf("normalizeVersionString(%q) = %q, expected %q", tc.input, got, tc.expected)
}
})
}
}
func TestParseVersion(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
wantErr bool
wantMajor int
wantMinor int
wantPatch int
wantPrerel string
wantBuild string
}{
{
name: "basic version",
input: "4.24.0",
wantMajor: 4, wantMinor: 24, wantPatch: 0,
},
{
name: "with v prefix",
input: "v4.24.0",
wantMajor: 4, wantMinor: 24, wantPatch: 0,
},
{
name: "with prerelease",
input: "4.24.0-rc.3",
wantMajor: 4, wantMinor: 24, wantPatch: 0,
wantPrerel: "rc.3",
},
{
name: "with build metadata",
input: "4.24.0+build.123",
wantMajor: 4, wantMinor: 24, wantPatch: 0,
wantBuild: "build.123",
},
{
name: "with prerelease and build",
input: "4.24.0-rc.3+build.123",
wantMajor: 4, wantMinor: 24, wantPatch: 0,
wantPrerel: "rc.3",
wantBuild: "build.123",
},
{
name: "invalid format",
input: "not-a-version",
wantErr: true,
},
{
name: "empty string",
input: "",
wantErr: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, err := ParseVersion(tc.input)
if tc.wantErr {
if err == nil {
t.Fatalf("ParseVersion(%q) expected error, got nil", tc.input)
}
return
}
if err != nil {
t.Fatalf("ParseVersion(%q) unexpected error: %v", tc.input, err)
}
if got.Major != tc.wantMajor || got.Minor != tc.wantMinor || got.Patch != tc.wantPatch {
t.Fatalf("ParseVersion(%q) = %d.%d.%d, want %d.%d.%d",
tc.input, got.Major, got.Minor, got.Patch, tc.wantMajor, tc.wantMinor, tc.wantPatch)
}
if got.Prerelease != tc.wantPrerel {
t.Fatalf("ParseVersion(%q).Prerelease = %q, want %q", tc.input, got.Prerelease, tc.wantPrerel)
}
if got.Build != tc.wantBuild {
t.Fatalf("ParseVersion(%q).Build = %q, want %q", tc.input, got.Build, tc.wantBuild)
}
})
}
}
func TestDetectChannelFromVersion(t *testing.T) {
t.Parallel()
if got := detectChannelFromVersion("4.24.0-rc.3"); got != "rc" {
t.Fatalf("detectChannelFromVersion rc = %s, expected rc", got)
}
if got := detectChannelFromVersion("0.0.0-feature-x"); got != "stable" {
t.Fatalf("detectChannelFromVersion stable = %s, expected stable", got)
}
}
func TestVersionString(t *testing.T) {
t.Parallel()
tests := []struct {
name string
version Version
expected string
}{
{
name: "basic version",
version: Version{Major: 4, Minor: 24, Patch: 0},
expected: "4.24.0",
},
{
name: "with prerelease",
version: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.3"},
expected: "4.24.0-rc.3",
},
{
name: "with build metadata",
version: Version{Major: 4, Minor: 24, Patch: 0, Build: "build.123"},
expected: "4.24.0+build.123",
},
{
name: "with prerelease and build",
version: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.3", Build: "build.123"},
expected: "4.24.0-rc.3+build.123",
},
{
name: "zero version",
version: Version{Major: 0, Minor: 0, Patch: 0},
expected: "0.0.0",
},
{
name: "large numbers",
version: Version{Major: 100, Minor: 200, Patch: 300},
expected: "100.200.300",
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.version.String(); got != tc.expected {
t.Fatalf("Version.String() = %q, expected %q", got, tc.expected)
}
})
}
}
func TestVersionCompare(t *testing.T) {
t.Parallel()
tests := []struct {
name string
v1 Version
v2 Version
expected int
}{
{
name: "equal versions",
v1: Version{Major: 4, Minor: 24, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: 0,
},
{
name: "v1 major greater",
v1: Version{Major: 5, Minor: 0, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: 1,
},
{
name: "v1 major less",
v1: Version{Major: 3, Minor: 99, Patch: 99},
v2: Version{Major: 4, Minor: 0, Patch: 0},
expected: -1,
},
{
name: "v1 minor greater",
v1: Version{Major: 4, Minor: 25, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: 1,
},
{
name: "v1 minor less",
v1: Version{Major: 4, Minor: 23, Patch: 99},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: -1,
},
{
name: "v1 patch greater",
v1: Version{Major: 4, Minor: 24, Patch: 1},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: 1,
},
{
name: "v1 patch less",
v1: Version{Major: 4, Minor: 24, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 1},
expected: -1,
},
{
name: "release > prerelease",
v1: Version{Major: 4, Minor: 24, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
expected: 1,
},
{
name: "prerelease < release",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: -1,
},
{
name: "RC comparison - rc.9 > rc.1",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.9"},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
expected: 1,
},
{
name: "RC comparison - rc.1 < rc.9",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.9"},
expected: -1,
},
{
name: "RC comparison - rc9 format",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc9"},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc1"},
expected: 1,
},
{
name: "equal prereleases",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.3"},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.3"},
expected: 0,
},
{
name: "string prerelease comparison",
v1: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "beta"},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "alpha"},
expected: 1, // "beta" > "alpha" lexically
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.v1.Compare(&tc.v2); got != tc.expected {
t.Fatalf("(%v).Compare(%v) = %d, expected %d", tc.v1, tc.v2, got, tc.expected)
}
})
}
}
func TestVersionIsNewerThan(t *testing.T) {
t.Parallel()
tests := []struct {
name string
v1 Version
v2 Version
expected bool
}{
{
name: "newer version",
v1: Version{Major: 5, Minor: 0, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: true,
},
{
name: "older version",
v1: Version{Major: 4, Minor: 0, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: false,
},
{
name: "equal versions",
v1: Version{Major: 4, Minor: 24, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0},
expected: false,
},
{
name: "release newer than prerelease",
v1: Version{Major: 4, Minor: 24, Patch: 0},
v2: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
expected: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.v1.IsNewerThan(&tc.v2); got != tc.expected {
t.Fatalf("(%v).IsNewerThan(%v) = %v, expected %v", tc.v1, tc.v2, got, tc.expected)
}
})
}
}
func TestVersionIsPrerelease(t *testing.T) {
t.Parallel()
tests := []struct {
name string
version Version
expected bool
}{
{
name: "release version",
version: Version{Major: 4, Minor: 24, Patch: 0},
expected: false,
},
{
name: "prerelease version",
version: Version{Major: 4, Minor: 24, Patch: 0, Prerelease: "rc.1"},
expected: true,
},
{
name: "alpha prerelease",
version: Version{Major: 1, Minor: 0, Patch: 0, Prerelease: "alpha"},
expected: true,
},
{
name: "build metadata only is not prerelease",
version: Version{Major: 4, Minor: 24, Patch: 0, Build: "build.123"},
expected: false,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.version.IsPrerelease(); got != tc.expected {
t.Fatalf("(%v).IsPrerelease() = %v, expected %v", tc.version, got, tc.expected)
}
})
}
}
func TestCompareInts(t *testing.T) {
t.Parallel()
tests := []struct {
name string
a int
b int
expected int
}{
{name: "a < b", a: 1, b: 2, expected: -1},
{name: "a > b", a: 2, b: 1, expected: 1},
{name: "a == b", a: 1, b: 1, expected: 0},
{name: "negative numbers a < b", a: -2, b: -1, expected: -1},
{name: "negative numbers a > b", a: -1, b: -2, expected: 1},
{name: "zero comparisons", a: 0, b: 1, expected: -1},
{name: "large numbers", a: 1000000, b: 1000001, expected: -1},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := compareInts(tc.a, tc.b); got != tc.expected {
t.Fatalf("compareInts(%d, %d) = %d, expected %d", tc.a, tc.b, got, tc.expected)
}
})
}
}
func TestExtractRCNumber(t *testing.T) {
t.Parallel()
tests := []struct {
name string
prerelease string
expected int
}{
{name: "rc.1", prerelease: "rc.1", expected: 1},
{name: "rc.9", prerelease: "rc.9", expected: 9},
{name: "rc1", prerelease: "rc1", expected: 1},
{name: "rc9", prerelease: "rc9", expected: 9},
{name: "RC.3 uppercase", prerelease: "RC.3", expected: 3},
{name: "RC5 uppercase no dot", prerelease: "RC5", expected: 5},
{name: "rc.10 double digit", prerelease: "rc.10", expected: 10},
{name: "rc.123 triple digit", prerelease: "rc.123", expected: 123},
{name: "alpha no RC", prerelease: "alpha", expected: -1},
{name: "beta.1 no RC", prerelease: "beta.1", expected: -1},
{name: "empty string", prerelease: "", expected: -1},
{name: "just rc no number", prerelease: "rc", expected: -1},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := extractRCNumber(tc.prerelease); got != tc.expected {
t.Fatalf("extractRCNumber(%q) = %d, expected %d", tc.prerelease, got, tc.expected)
}
})
}
}
func TestEnvBool(t *testing.T) {
tests := []struct {
name string
value string
expected bool
}{
{name: "1 is true", value: "1", expected: true},
{name: "true is true", value: "true", expected: true},
{name: "TRUE is true", value: "TRUE", expected: true},
{name: "True is true", value: "True", expected: true},
{name: "yes is true", value: "yes", expected: true},
{name: "YES is true", value: "YES", expected: true},
{name: "on is true", value: "on", expected: true},
{name: "ON is true", value: "ON", expected: true},
{name: "0 is false", value: "0", expected: false},
{name: "false is false", value: "false", expected: false},
{name: "FALSE is false", value: "FALSE", expected: false},
{name: "no is false", value: "no", expected: false},
{name: "off is false", value: "off", expected: false},
{name: "empty is false", value: "", expected: false},
{name: "random string is false", value: "random", expected: false},
{name: "whitespace true", value: " true ", expected: true},
{name: "whitespace 1", value: " 1 ", expected: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
key := "TEST_ENV_BOOL_" + tc.name
t.Setenv(key, tc.value)
if got := envBool(key); got != tc.expected {
t.Fatalf("envBool(%q) with value %q = %v, expected %v", key, tc.value, got, tc.expected)
}
})
}
}
func TestSanitizePrereleaseIdentifier(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected string
}{
{name: "simple branch", input: "feature-x", expected: "feature-x"},
{name: "branch with slash", input: "feature/new-api", expected: "feature-new-api"},
{name: "branch with multiple slashes", input: "user/feature/test", expected: "user-feature-test"},
{name: "uppercase converts to lowercase", input: "FEATURE-X", expected: "feature-x"},
{name: "mixed case", input: "Feature-X", expected: "feature-x"},
{name: "numbers preserved", input: "issue-123", expected: "issue-123"},
{name: "dots preserved", input: "v1.2.3", expected: "v1.2.3"},
{name: "special chars replaced", input: "feat@test#123", expected: "feat-test-123"},
{name: "consecutive separators collapsed", input: "feature//test", expected: "feature-test"},
{name: "leading separator trimmed", input: "-feature", expected: "feature"},
{name: "trailing separator trimmed", input: "feature-", expected: "feature"},
{name: "empty string", input: "", expected: "dev"},
{name: "whitespace only", input: " ", expected: "dev"},
{name: "only special chars", input: "@#$%", expected: "dev"},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := sanitizePrereleaseIdentifier(tc.input); got != tc.expected {
t.Fatalf("sanitizePrereleaseIdentifier(%q) = %q, expected %q", tc.input, got, tc.expected)
}
})
}
}
func TestNormalizeGitDescribeVersion(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected string
ok bool
}{
// Valid git describe outputs
{
name: "basic git describe",
input: "4.24.0-45-gabcdef",
expected: "4.24.0+git.45.gabcdef",
ok: true,
},
{
name: "with prerelease",
input: "4.24.0-rc.3-45-gABCDEF",
expected: "4.24.0-rc.3+git.45.gabcdef",
ok: true,
},
{
name: "with dirty flag",
input: "4.24.0-rc.3-45-gabc123-dirty",
expected: "4.24.0-rc.3+git.45.gabc123.dirty",
ok: true,
},
{
name: "uppercase hash normalized",
input: "4.24.0-1-gABCDEF",
expected: "4.24.0+git.1.gabcdef",
ok: true,
},
{
name: "dirty without prerelease",
input: "4.24.0-10-g1234567-dirty",
expected: "4.24.0+git.10.g1234567.dirty",
ok: true,
},
// Invalid inputs
{
name: "plain version",
input: "4.24.0",
expected: "",
ok: false,
},
{
name: "version with prerelease only",
input: "4.24.0-rc.1",
expected: "",
ok: false,
},
{
name: "empty string",
input: "",
expected: "",
ok: false,
},
{
name: "branch name",
input: "feature-branch",
expected: "",
ok: false,
},
{
name: "invalid base version",
input: "invalid-45-gabcdef",
expected: "",
ok: false,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, ok := normalizeGitDescribeVersion(tc.input)
if ok != tc.ok {
t.Fatalf("normalizeGitDescribeVersion(%q) ok = %v, expected %v", tc.input, ok, tc.ok)
}
if got != tc.expected {
t.Fatalf("normalizeGitDescribeVersion(%q) = %q, expected %q", tc.input, got, tc.expected)
}
})
}
}