a73x

internal/version/order_test.go

Ref:   Size: 5.4 KiB   History

package version

import "testing"

func TestLess(t *testing.T) {
	cases := []struct {
		a, b string
		want bool
	}{
		{"v0.0.1", "v0.0.2", true},
		{"v0.0.2", "v0.0.1", false},
		{"v0.0.2", "v0.0.2", false},
		{"v0.9.0", "v0.10.0", true}, // numeric, not lexicographic
		{"dev", "v0.0.2", false},    // unparsable never upgrades
		{"v0.0.1", "dev", false},
		{"", "v0.0.2", false},
		{"v0.0.2", "", false},
	}
	for _, c := range cases {
		if got := Less(c.a, c.b); got != c.want {
			t.Errorf("Less(%q,%q) = %v, want %v", c.a, c.b, got, c.want)
		}
	}
}

// TestLessOrdersGitDescribeBuilds pins the ordering of a git-describe build
// ("vX.Y.Z-N-g<hex>", N commits past tag vX.Y.Z) against a release tag: it
// sorts by (X, Y, Z, N), so a build derived from an older tag takes the
// upgrade while a build ahead of the latest release is never offered one.
func TestLessOrdersGitDescribeBuilds(t *testing.T) {
	cases := []struct {
		a, b string
		want bool
	}{
		// A hand-deployed agent two commits past v0.0.2 upgrades to v0.0.3.
		{"v0.0.2-2-g68f804d", "v0.0.3", true},
		{"v0.0.3", "v0.0.2-2-g68f804d", false},
		// A dev build ahead of the latest release is never downgraded onto it.
		{"v0.0.3-5-gabc1234", "v0.0.3", false},
		{"v0.0.3", "v0.0.3-5-gabc1234", true},
		// The commit count breaks ties within one tag.
		{"v0.0.3-2-gabc1234", "v0.0.3-5-gabc1234", true},
		{"v0.0.3-5-gabc1234", "v0.0.3-2-gabc1234", false},
		{"v0.0.3-5-gabc1234", "v0.0.3-5-gabc1234", false},
		// A plain tag is the same build as zero commits past it.
		{"v0.0.3-0-gabc1234", "v0.0.3", false},
		{"v0.0.3", "v0.0.3-0-gabc1234", false},
		// Release components still outrank the commit count.
		{"v0.0.2-99-gabc1234", "v0.1.0", true},
		{"v0.1.0-1-gabc1234", "v0.0.2-99-gabc1234", false},
		// Uppercase hex is still a hex abbrev.
		{"v0.0.2-2-gABC1234", "v0.0.3", true},
	}
	for _, c := range cases {
		if got := Less(c.a, c.b); got != c.want {
			t.Errorf("Less(%q,%q) = %v, want %v", c.a, c.b, got, c.want)
		}
	}
}

// TestLessOrdersPreReleases pins the chain a release cycle walks: each
// pre-release, the builds derived from it, the next pre-release, the release
// itself, and builds past that. An agent on a pre-release takes the next one
// and eventually the release, which is what lets a staging plane rehearse the
// upgrade path on the same tags the cycle produces.
func TestLessOrdersPreReleases(t *testing.T) {
	// Every entry orders strictly before every entry after it.
	chain := []string{
		"v0.0.3",
		"v0.0.4-pre.1",
		"v0.0.4-pre.1-3-gabc1234",
		"v0.0.4-pre.2",
		"v0.0.4-pre.10", // numeric, not lexicographic
		"v0.0.4",
		"v0.0.4-2-gabc1234",
		"v0.0.5-pre.1",
	}
	for i, a := range chain {
		for j, b := range chain {
			want := i < j
			if got := Less(a, b); got != want {
				t.Errorf("Less(%q,%q) = %v, want %v", a, b, got, want)
			}
		}
	}
}

// TestLessLeavesUnstampedBuildsUnordered pins the shapes that stay
// unparsable: a dirty working tree or a malformed version never orders before
// anything, in either direction, so it is never offered an upgrade.
func TestLessLeavesUnstampedBuildsUnordered(t *testing.T) {
	unstamped := []string{
		"",
		"dev",
		"v0.0.3-dirty",
		"v0.0.2-2-g68f804d-dirty",
		"v0.0.4-pre",                       // no pre number
		"v0.0.4-pre.",                      // empty pre number
		"v0.0.4-pre.x",                     // pre number is not a number
		"v0.0.4-pre.1.2",                   // pre number is not one component
		"v0.0.4-pre.-1",                    // negative pre number
		"v0.0.4-pre.1-dirty",               // dirty past a pre-release
		"v0.0.4-pre.1-3",                   // no g-abbrev past a pre-release
		"v0.0.4-pre.1-g abc",               // no commit count past a pre-release
		"v0.0.4-prerelease.1",              // not the pre. prefix
		"v0.0.4-rc.1",                      // only "pre" is spelled this way
		"v0.0.4-pre.99999999999999999999",  // pre number out of range
		"v0.0.2-x-g123",                    // commit count is not a number
		"v0.0.2--2-g68f804d",               // negative commit count
		"v0.0.2-2",                         // no g-abbrev
		"v0.0.2-2-g",                       // empty abbrev
		"v0.0.2-2-gZZZ",                    // abbrev is not hex
		"v0.0.2-2-68f804d",                 // abbrev missing its g
		"v0.0.2-2-g68f804d-3",              // trailing junk
		"v-1.0.0",                          // negative major
		"v0.-1.2",                          // negative minor
		"v0.0",                             // too few components
		"v0.0.2.1",                         // too many components
		"v0.0.+2",                          // signed component
		"v0.0.99999999999999999999",        // component out of range
		"v0.0.2-99999999999999999999-gabc", // commit count out of range
	}
	for _, u := range unstamped {
		if Less(u, "v0.0.3") {
			t.Errorf("Less(%q, %q) = true, want false", u, "v0.0.3")
		}
		if Less("v0.0.3", u) {
			t.Errorf("Less(%q, %q) = true, want false", "v0.0.3", u)
		}
	}
}

// Ordered is the predicate behind Less's unparsable rule: it must agree with
// what Less can and cannot order, so a constant asserted well-formed through it
// is one Less will really rank.
func TestOrderedAgreesWithLess(t *testing.T) {
	for _, v := range []string{"v0.0.3", "v0.0.4-pre.1", "v0.0.2-2-g68f804d"} {
		if !Ordered(v) {
			t.Errorf("Ordered(%q) = false, want true", v)
		}
	}
	for _, v := range []string{"", "dev", "v0.0.3-dirty", "v0.0.4-rc.1"} {
		if Ordered(v) {
			t.Errorf("Ordered(%q) = true, want false", v)
		}
	}
}