Files
at-container-registry/pkg/credhelper/version_compare_test.go
T
Evan JarrettandClaude Opus 5 c9f8b4178c credhelper: stop dev builds nagging about an update to themselves
isNewerVersion split versions on "." and ran each component through
strconv.Atoi, discarding the error and substituting 0. For a git-describe
build the last component is "4-18-g8f70cce", which does not parse, so it
became 0 and every published release compared as newer. Running
v0.1.4-18-g8f70cce printed "Update available: v0.1.4" on every single
invocation, naming a version the binary was already 18 commits past.

Versions are now parsed properly: the "-<commits>-g<sha>" tail is recognised
and kept as a count of commits past the tag, and a version that cannot be
read in full returns false rather than being silently treated as 0. That
second part is the actual root cause — the comparison could not distinguish
"this component is zero" from "I could not read this component".

Ordering for a git-describe build is deliberately not semver, where a
prerelease sorts below its release. Such a build is commits AHEAD of its tag,
so v0.1.4-18-g8f70cce is newer than v0.1.4 and older than v0.1.4-20-gabc1234.

The function had no tests. Both failing cases are pinned along with the
ordinary release comparisons, so the git-describe handling cannot regress the
normal upgrade path.

Pre-existing at efabb677 rather than introduced by this range.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:34:25 -05:00

51 lines
1.9 KiB
Go

package credhelper
import "testing"
// isNewerVersion had no tests, and a git-describe build ("v0.1.4-18-g8f70cce")
// nagged about an update on every single invocation. The cause was a silent
// default: strconv.Atoi("4-18-g8f70cce") fails, the failure was swallowed, the
// component became 0, and so every published release compared as newer than any
// dev build of the same tag.
func TestIsNewerVersion(t *testing.T) {
tests := []struct {
name string
latest string
current string
want bool
}{
// The reported case: a dev build 18 commits past v0.1.4 is AHEAD of
// v0.1.4, so there is nothing to update to.
{"git describe build is ahead of its own tag", "v0.1.4", "v0.1.4-18-g8f70cce", false},
{"git describe build vs a real later release", "v0.2.0", "v0.1.4-18-g8f70cce", true},
{"git describe build vs a later patch", "v0.1.5", "v0.1.4-18-g8f70cce", true},
{"further ahead of the same tag", "v0.1.4-2-gabc1234", "v0.1.4-18-g8f70cce", false},
{"behind another describe build", "v0.1.4-20-gabc1234", "v0.1.4-18-g8f70cce", true},
// Plain releases.
{"newer patch", "v0.1.5", "v0.1.4", true},
{"newer minor", "v0.2.0", "v0.1.9", true},
{"newer major", "v1.0.0", "v0.9.9", true},
{"same version", "v0.1.4", "v0.1.4", false},
{"older patch", "v0.1.3", "v0.1.4", false},
{"older major", "v0.9.9", "v1.0.0", false},
{"missing v prefix still compares", "0.1.5", "0.1.4", true},
// An untagged build always updates.
{"dev always updates", "v0.1.4", "dev", true},
// Nothing parseable means no nagging.
{"garbage latest does not nag", "not-a-version", "v0.1.4", false},
{"empty latest does not nag", "", "v0.1.4", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isNewerVersion(tt.latest, tt.current); got != tt.want {
t.Errorf("isNewerVersion(%q, %q) = %v, want %v",
tt.latest, tt.current, got, tt.want)
}
})
}
}