package tagcmp_test import ( "sort" "testing" "coopcloud.tech/tagcmp" ) var giteaTags = []string{ "latest", "1", "1-linux-amd64", "1-linux-amd64-rootless", "1-linux-arm64", "1-linux-arm64-rootless", "1-rootless", "1.0", "1.0.0", "1.0.1", "1.0.2", "1.1", "1.1.0", "1.1.1", "1.1.2", "1.1.3", "1.1.4", "1.10", "1.10-linux-amd64", "1.10-linux-arm64", "1.10.0", "1.10.0-linux-amd64", "1.10.0-linux-arm64", "1.10.0-rc1", "1.10.0-rc1-linux-amd64", "1.10.0-rc1-linux-arm64", "1.10.0-rc2", "1.10.0-rc2-linux-amd64", "1.10.0-rc2-linux-arm64", "1.10.1", "1.10.1-linux-amd64", "1.10.1-linux-arm64", "1.10.2", "1.10.2-linux-amd64", "1.10.2-linux-arm64", "1.10.3", "1.10.3-linux-amd64", "1.10.3-linux-arm64", "1.10.4", "1.10.4-linux-amd64", "1.10.4-linux-arm64", "1.10.5", "1.10.5-linux-amd64", "1.10.5-linux-arm64", "1.10.6", "1.10.6-linux-amd64", "1.10.6-linux-arm64", "1.11", "1.11-linux-amd64", "1.11-linux-arm64", "1.11.0", "1.11.0-linux-amd64", "1.11.0-linux-arm64", "1.11.0-rc1", "1.11.0-rc1-linux-amd64", "1.11.0-rc1-linux-arm64", "1.11.0-rc2", "1.11.0-rc2-linux-amd64", "1.11.0-rc2-linux-arm64", "1.11.1", "1.11.1-linux-amd64", "1.11.1-linux-arm64", "1.11.2", "1.11.2-linux-amd64", "1.11.2-linux-arm64", "1.11.3", "1.11.3-linux-amd64", "1.11.3-linux-arm64", "1.11.4", "1.11.4-linux-amd64", "1.11.4-linux-arm64", "1.11.5", "1.11.5-linux-amd64", "1.11.5-linux-arm64", "1.11.6", "1.11.6-linux-amd64", "1.11.6-linux-arm64", "1.11.7", "1.11.7-linux-amd64", "1.11.7-linux-arm64", "1.11.8", "1.11.8-linux-amd64", "1.11.8-linux-arm64", "1.12", "1.12-linux-amd64", "1.12-linux-arm64", "1.12.0", "1.12.0-linux-amd64", "1.12.0-linux-arm64", "1.12.0-rc1", "1.12.0-rc1-linux-amd64", "1.12.0-rc1-linux-arm64", "1.12.0-rc2", "1.12.0-rc2-linux-amd64", "1.12.0-rc2-linux-arm64", "1.12.1", "1.12.1-linux-amd64", "1.12.1-linux-arm64", "1.12.2", "1.12.2-linux-amd64", "1.12.2-linux-arm64", "1.12.3", "1.12.3-linux-amd64", "1.12.3-linux-arm64", "1.12.4", "1.12.4-linux-amd64", "1.12.4-linux-arm64", "1.12.5", "1.12.5-linux-amd64", "1.12.5-linux-arm64", "1.12.6", "1.12.6-linux-amd64", "1.12.6-linux-arm64", "1.13", "1.13-linux-amd64", "1.13-linux-arm64", "1.13.0", "1.13.0-linux-amd64", "1.13.0-linux-arm64", "1.13.0-rc1", "1.13.0-rc1-linux-amd64", "1.13.0-rc1-linux-arm64", "1.13.0-rc2", "1.13.0-rc2-linux-amd64", "1.13.0-rc2-linux-arm64", "1.13.1", "1.13.1-linux-amd64", "1.13.1-linux-arm64", "1.13.2", "1.13.2-linux-amd64", "1.13.2-linux-arm64", "1.13.3", "1.13.3-linux-amd64", "1.13.3-linux-arm64", "1.13.4", "1.13.4-linux-amd64", "1.13.4-linux-arm64", "1.13.5", "1.13.5-linux-amd64", "1.13.5-linux-arm64", "1.13.6", "1.13.6-linux-amd64", "1.13.6-linux-arm64", "1.13.7", "1.13.7-linux-amd64", "1.13.7-linux-arm64", "1.14", "1.14-linux-amd64", "1.14-linux-amd64-rootless", "1.14-linux-arm64", "1.14-linux-arm64-rootless", "1.14-rootless", "1.14.0", "1.14.0-linux-amd64", "1.14.0-linux-amd64-rootless", "1.14.0-linux-arm64", "1.14.0-linux-arm64-rootless", "1.14.0-rc1", "1.14.0-rc1-linux-amd64", "1.14.0-rc1-linux-amd64-rootless", "1.14.0-rc1-linux-arm64", "1.14.0-rc1-linux-arm64-rootless", "1.14.0-rc1-rootless", "1.14.0-rc2", "1.14.0-rc2-linux-amd64", "1.14.0-rc2-linux-amd64-rootless", "1.14.0-rc2-linux-arm64", "1.14.0-rc2-linux-arm64-rootless", "1.14.0-rc2-rootless", "1.14.0-rootless", "1.14.1", "1.14.1-linux-amd64", "1.14.1-linux-amd64-rootless", "1.14.1-linux-arm64", "1.14.1-linux-arm64-rootless", "1.14.1-rootless", "1.14.2", "1.14.2-linux-amd64", "1.14.2-linux-amd64-rootless", "1.14.2-linux-arm64", "1.14.2-linux-arm64-rootless", "1.14.2-rootless", "1.14.3", "1.14.3-linux-amd64", "1.14.3-linux-amd64-rootless", "1.14.3-linux-arm64", "1.14.3-linux-arm64-rootless", "1.14.3-rootless", "1.14.4", "1.14.4-linux-amd64", "1.14.4-linux-amd64-rootless", "1.14.4-linux-arm64", "1.14.4-linux-arm64-rootless", "1.14.4-rootless", "1.14.5", "1.14.5-linux-amd64", "1.14.5-linux-amd64-rootless", "1.14.5-linux-arm64", "1.14.5-linux-arm64-rootless", "1.14.5-rootless", "1.14.6", "1.14.6-linux-amd64", "1.14.6-linux-amd64-rootless", "1.14.6-linux-arm64", "1.14.6-linux-arm64-rootless", "1.14.6-rootless", "1.15.0-rc1", "1.15.0-rc1-linux-amd64", "1.15.0-rc1-linux-amd64-rootless", "1.15.0-rc1-linux-arm64", "1.15.0-rc1-linux-arm64-rootless", "1.15.0-rc1-rootless", "1.15.0-rc2", "1.15.0-rc2-linux-amd64", "1.15.0-rc2-linux-amd64-rootless", "1.15.0-rc2-linux-arm64", "1.15.0-rc2-linux-arm64-rootless", "1.15.0-rc2-rootless", "1.15.0-rc3", "1.15.0-rc3-linux-amd64", "1.15.0-rc3-linux-amd64-rootless", "1.15.0-rc3-linux-arm64", "1.15.0-rc3-linux-arm64-rootless", "1.15.0-rc3-rootless", "1.2", "1.2.0", "1.2.0-rc1", "1.2.0-rc2", "1.2.0-rc3", "1.2.0-rc4", "1.2.1", "1.2.2", "1.2.3", "1.3", "1.3.0", "1.3.0-rc1", "1.3.0-rc2", "1.3.1", "1.3.2", "1.3.3", "1.4", "1.4.0", "1.4.0-rc1", "1.4.0-rc2", "1.4.0-rc3", "1.4.1", "1.4.2", "1.4.3", "1.5", "1.5.0", "1.5.0-rc1", "1.5.0-rc2", "1.5.1", "1.5.2", "1.5.3", "1.6", "1.6.0", "1.6.0-rc1", "1.6.0-rc2", "1.6.1", "1.6.2", "1.6.3", "1.6.4", "1.7", "1.7.0", "1.7.0-rc1", "1.7.0-rc2", "1.7.0-rc3", "1.7.1", "1.7.2", "1.7.3", "1.7.4", "1.7.5", "1.7.6", "1.8", "1.8.0", "1.8.0-rc1", "1.8.0-rc2", "1.8.0-rc3", "1.8.1", "1.8.2", "1.8.3", "1.9", "1.9-linux-amd64", "1.9-linux-arm64", "1.9.0", "1.9.1", "1.9.2", "1.9.2-linux-amd64", "1.9.2-linux-arm64", "1.9.3", "1.9.3-linux-amd64", "1.9.3-linux-arm64", "1.9.4", "1.9.4-linux-amd64", "1.9.4-linux-arm64", "1.9.5", "1.9.5-linux-amd64", "1.9.5-linux-arm64", "1.9.6", "1.9.6-linux-amd64", "1.9.6-linux-arm64", "dev", "dev-linux-amd64", "dev-linux-amd64-rootless", "dev-linux-arm64", "dev-linux-arm64-rootless", "dev-rootless", "latest-rootless", "linux-amd64", "linux-amd64-rootless", "linux-arm64", "linux-arm64-rootless", } var supported = []string{ // semver "5", "2.6", "4.3.5", // semver with 'v' "v1", "v2.3", "v1.0.2", // semver with suffix "6-alpine", "6.2-alpine", "6.2.1-alpine", // semver with sufix and 'v' "v6-alpine", "v6.2-alpine", "v6.2.1-alpine", "v6.2.1-alpine", // semver with '+' "5+SDFJ_-2l4x.", "2.6+SDFJ_-2l4x.", "4.3.5+SDFJ_-2l4x.", // semver with '+' and 'v' "v5+SDFJ_-2l4x.", "v2.6+SDFJ_-2l4x.", "v4.3.5+SDFJ_-2l4x.", // semver with '+' and suffix "5-alpine+SDFJ_-2l4x.", "2.6-alpine+SDFJ_-2l4x.", "4.3.5-alpine+SDFJ_-2l4x.", // semver with 'v', '+' and suffix "v5-alpine+SDFJ_-2l4x.", "v2.6-alpine+SDFJ_-2l4x.", "v4.3.5-alpine+SDFJ_-2l4x.", // semver with multiple suffix values "v6.2.1-alpine-foo", // semver with multiple suffix values and '+' "v6.2.1-alpine-foo+68BC1E", } var unsupported = []string{ // empty "", // parametrized "${MAILU_VERSION:-master}", "${PHP_VERSION}-fpm-alpine3.13", // commit hash like "0a1b2c3d4e5f6a7b8c9d0a1b2c3d4e5f6a7b8c9d", // numeric "20191109", "e02267d", // not semver "3.0.6.0", "r1295", "version-r1070", // too much dots "1.0.0.0.0", // prerelease "3.7.0b1", "3.8.0b1-alpine", // multiple versions "5.36-backdrop-php7.4", "v1.0.5_3.4.0", "v1.0.5_3.4.0_openid-sso", // tz based "RELEASE.2021-04-22T15-44-28Z", // only text "alpine", "latest", "master", // multiple - delimters "apache-debian-1.8-prod", "version-znc-1.8.2", // unparsable major/minor/patch parts: "a.0.0", "1.a", "1.a.0", "1.0.a", } func TestParseUnsupported(t *testing.T) { for _, tag := range unsupported { if _, err := tagcmp.Parse(tag); err == nil { t.Errorf("'%s' was parsed but it is unsupported", tag) } } } func TestParseSupported(t *testing.T) { for _, tag := range supported { if _, err := tagcmp.Parse(tag); err != nil { t.Errorf("'%s' was not parsed but it is supported", tag) } } } func TestParseRetainZeroes(t *testing.T) { tag, err := tagcmp.Parse("18.04") if err != nil { t.Errorf("'18.04' should have parsed but didn't: %s", err) } if tag.Minor != "04" { t.Errorf("parsing '18.04' didn't retain zeroes: %s", tag.Minor) } } func TestDetectsV(t *testing.T) { tag1, err := tagcmp.Parse("v2") if !tag1.UsesV { t.Error("'v2' uses 'v' but wasn't detected as such") } if err != nil { t.Errorf("'v2' should have parsed but didn't: %s", err) } tag2, err := tagcmp.Parse("2") if tag2.UsesV { t.Error("'v2' doesn't use 'v' but wasn't detected as such") } if err != nil { t.Errorf("'2' should have parsed but didn't: %s", err) } } func TestMissingParts(t *testing.T) { tag1, err := tagcmp.Parse("v2") if !tag1.MissingMinor || !tag1.MissingPatch { t.Error("'v2' parsed without realising there are missing parts") } if err != nil { t.Errorf("'v2' should have parsed but didn't: %s", err) } tag2, err := tagcmp.Parse("v2.3") if !tag2.MissingPatch { t.Error("'v2.3' parsed without realising there are missing parts") } if err != nil { t.Errorf("'v2.3' should have parsed but didn't: %s", err) } } func TestMajorPart(t *testing.T) { majors := map[string]string{ "1.2.3": "1", "18.04": "18", "10.1": "10", "3": "3", } for m := range majors { if p, _ := tagcmp.Parse(m); p.Major != majors[m] { t.Errorf("'%s' didn't parse major part correctly: %s", m, p.Major) } } } func TestMinorPart(t *testing.T) { minors := map[string]string{ "1.2.3": "2", "18.04": "04", "10.1": "1", "3": "", } for m := range minors { if p, _ := tagcmp.Parse(m); p.Minor != minors[m] { t.Errorf("'%s' didn't parse minor part correctly: %s", m, p.Minor) } } } func TestPatchPart(t *testing.T) { patches := map[string]string{ "1.2.3": "3", "18.04": "", "10.1": "", "3": "", } for m := range patches { if p, _ := tagcmp.Parse(m); p.Patch != patches[m] { t.Errorf("'%s' didn't parse patch part correctly: %s", m, p.Patch) } } } func TestIsParsableSupported(t *testing.T) { for _, tag := range supported { if !tagcmp.IsParsable(tag) { t.Errorf("'%s' should be parsable but IsParsable returned false", tag) } } } func TestIsParsableUnsupported(t *testing.T) { for _, tag := range unsupported { if tagcmp.IsParsable(tag) { t.Errorf("'%s' should not be parsable but IsParsable returned true", tag) } } } func TestIsGreaterThan(t *testing.T) { pairs := []struct { t1 string t2 string expected bool }{ {"1.2.3", "1.2", false}, {"1.2.3", "1.2.4", false}, {"18.04", "18.1", true}, {"10.1", "10.1.2", true}, {"3", "2", true}, {"1.2.3", "1.2.3", false}, } for _, p := range pairs { p1, err := tagcmp.Parse(p.t1) if err != nil { t.Errorf("'%s' should have parsed", p.t1) } p2, err := tagcmp.Parse(p.t2) if err != nil { t.Errorf("'%s' should have parsed", p.t2) } res := p1.IsGreaterThan(p2) if res != p.expected { t.Errorf("(%s).IsGreatherThan(%s) gave %t but expected %t", p.t1, p.t2, res, p.expected) } } } func TestIsLessThan(t *testing.T) { pairs := []struct { t1 string t2 string expected bool }{ {"1.2.3", "2.0", true}, {"18.04", "18.1", false}, {"10.1", "10.0.4", false}, {"3", "4.0", false}, {"1.2", "1.3.4", false}, {"0.0.1", "0.2.1", true}, {"0.0.1", "2.0.0", true}, {"1.3.9", "1.4.8", true}, {"v0.2.1", "v16.0.1", true}, } for _, p := range pairs { p1, err := tagcmp.Parse(p.t1) if err != nil { t.Errorf("'%s' should have parsed", p.t1) } p2, err := tagcmp.Parse(p.t2) if err != nil { t.Errorf("'%s' should have parsed", p.t2) } res := p1.IsLessThan(p2) if res != p.expected { t.Errorf("(%s).IsLessThan(%s) gave %t but expected %t", p.t1, p.t2, res, p.expected) } } } func TestEquals(t *testing.T) { pairs := []struct { t1 string t2 string expected bool }{ {"1.2.3", "1.2.3", true}, {"1.2.3", "1.2", false}, {"1.2.3", "1", false}, {"18.04", "18.4", true}, {"10.0", "10.0.4", false}, {"3", "4.0", false}, {"1.2", "1.2.3", false}, {"3", "4", false}, {"1.3", "1.4", false}, {"3+FF812B", "3", false}, } for _, p := range pairs { p1, err := tagcmp.Parse(p.t1) if err != nil { t.Errorf("'%s' should have parsed", p.t1) } p2, err := tagcmp.Parse(p.t2) if err != nil { t.Errorf("'%s' should have parsed", p.t2) } res := p1.Equals(p2) if res != p.expected { t.Errorf("(%s).Equals(%s) gave %t but expected %t", p.t1, p.t2, res, p.expected) } } } func TestIsCompatible(t *testing.T) { pairs := []struct { t1 string t2 string expected bool }{ {"v1", "v2", true}, {"v1", "v2.4", false}, {"1", "2", true}, {"2.3.4", "v2.3.4", false}, {"1.2.3", "1.2.6", true}, {"1.2.3", "1.2.0", true}, {"5-alpine", "6-alpine", true}, {"5-alpine", "6.5-alpine", false}, {"5", "5-alpine", false}, } for _, p := range pairs { p1, err := tagcmp.Parse(p.t1) if err != nil { t.Errorf("'%s' should have parsed", p.t1) } p2, err := tagcmp.Parse(p.t2) if err != nil { t.Errorf("'%s' should have parsed", p.t2) } res := p1.IsCompatible(p2) if res != p.expected { t.Errorf("(%s).IsCompatible(%s) gave %t but expected %t", p.t1, p.t2, res, p.expected) } } } func TestSortAsc1(t *testing.T) { rawTags := []string{ "v1.4.8", "v1.3.9", "v2.0.0", "v16.0.1", "v0.0.1", "v0.2.1", "v5.9.1", } var tags []tagcmp.Tag for _, rawTag := range rawTags { tag, err := tagcmp.Parse(rawTag) if err != nil { t.Errorf("'%s' should have parsed but didn't: %s", tag, err) } tags = append(tags, tag) } sort.Sort(tagcmp.ByTagAsc(tags)) expected := []string{ "v0.0.1", "v0.2.1", "v1.3.9", "v1.4.8", "v2.0.0", "v5.9.1", "v16.0.1", } for idx, tag := range tags { if tag.String() != expected[idx] { t.Errorf("'%s' sorted out of order, saw '%s', expected '%s'", tag, tags, expected) } } } func TestSortAsc2(t *testing.T) { rawTags := []string{ "10.0", "10.6", "10.2", "10.1", "10.5", "5.5", } var tags []tagcmp.Tag for _, rawTag := range rawTags { tag, err := tagcmp.Parse(rawTag) if err != nil { t.Errorf("'%s' should have parsed but didn't: %s", tag, err) } tags = append(tags, tag) } sort.Sort(tagcmp.ByTagAsc(tags)) expected := []string{ "5.5", "10.0", "10.1", "10.2", "10.5", "10.6", } for idx, tag := range tags { if tag.String() != expected[idx] { t.Errorf("'%s' sorted out of order, saw '%s', expected '%s'", tag, tags, expected) } } } func TestSortDesc(t *testing.T) { rawTags := []string{ "10.0", "10.6", "10.2", "10.1", "10.5", "5.5", } var tags []tagcmp.Tag for _, rawTag := range rawTags { tag, err := tagcmp.Parse(rawTag) if err != nil { t.Errorf("'%s' should have parsed but didn't: %s", tag, err) } tags = append(tags, tag) } sort.Sort(tagcmp.ByTagDesc(tags)) expected := []string{ "10.6", "10.5", "10.2", "10.1", "10.0", "5.5", } for idx, tag := range tags { if tag.String() != expected[idx] { t.Errorf("'%s' sorted out of order, saw '%s', expected '%s'", tag, tags, expected) } } } func TestTagString(t *testing.T) { for _, tag := range supported { p, err := tagcmp.Parse(tag) if err != nil { t.Errorf("'%s' was not parsed but it is supported", tag) } if p.String() != tag { t.Errorf("String() of '%s' didn't render properly: %s", tag, p.String()) } } } func TestGiteaFilterCompatible(t *testing.T) { expected := []string{ "1.14.0-rootless", "1.14.1-rootless", "1.14.2-rootless", "1.14.3-rootless", "1.14.4-rootless", "1.14.5-rootless", "1.14.6-rootless", } tag, err := tagcmp.Parse("1.14.0-rootless") if err != nil { t.Errorf("'1.14.0-rootless' should have parsed but didn't: %s", err) } var filtered []tagcmp.Tag for _, giteaTag := range giteaTags { // not interested in unsupported tags right now p, _ := tagcmp.Parse(giteaTag) if tag.IsCompatible(p) { filtered = append(filtered, p) } } sort.Sort(tagcmp.ByTagAsc(filtered)) for idx, tag := range filtered { if tag.String() != expected[idx] { t.Errorf("'%s' out of order or incompatible, saw '%s', expected '%s'", tag, filtered, expected) } } } func TestDeltaParse(t *testing.T) { supportedDeltas := []string{ "1", "1.0", "1.0.0", "-1", "-1.0", "-1.0.0", "-1.0.5", "-1.2.5", "1.-2", } unsupportedDeltas := []string{ "AAAAAAAAAAAAAAAA", "1.2.3.4", "1.ab.2", "1.2.a", } for _, delta := range supportedDeltas { if _, err := tagcmp.ParseDelta(delta); err != nil { t.Errorf("'%s' wasn't parsed but it is supported: %s", delta, err) } } for _, delta := range unsupportedDeltas { if _, err := tagcmp.ParseDelta(delta); err == nil { t.Errorf("'%s' was parsed but it is not supported", delta) } } } func TestDeltaString(t *testing.T) { supportedDeltas := []struct { in string out string }{ {"1", "1.0.0"}, {"1.0", "1.0.0"}, {"1.0.0", "1.0.0"}, {"-1", "-1.0.0"}, {"-1.0", "-1.0.0"}, {"-1.0.0", "-1.0.0"}, {"-1.0.5", "-1.0.5"}, {"-1.2.5", "-1.2.5"}, {"1.-2", "1.-2.0"}, } for _, test := range supportedDeltas { parsedDelta, err := tagcmp.ParseDelta(test.in) if err != nil { t.Errorf("'%s' was not parsed but it is supported", test.in) } if parsedDelta.String() != test.out { t.Errorf("String() of '%s' didn't render properly: %s", test.in, parsedDelta.String()) } } } func TestIsUpgradeCompatible(t *testing.T) { testsTrue := [][]string{ {"22-fpm", "22-fpm"}, {"22-fpm", "22.0-fpm"}, {"22-fpm", "22.0.0-fpm"}, {"22-fpm", "22.2.0-fpm"}, {"22-fpm", "22.0.0-fpm"}, {"22.2-fpm", "22.2-fpm"}, {"22.2-fpm", "22.2.0-fpm"}, {"22.2.2-fpm", "22.2.2-fpm"}, } testsFalse := [][]string{ {"22-fpm", "22-alpine"}, {"22-fpm", "23-fpm"}, {"22-fpm", "21-fpm"}, {"22.2-fpm", "22.0.2-fpm"}, {"22.2.0-fpm", "22.2.2-fpm"}, } for _, test := range testsTrue { pin, err := tagcmp.Parse(test[0]) if err != nil { t.Error(err) } upTag, err := tagcmp.Parse(test[1]) if err != nil { t.Error(err) } if !pin.IsUpgradeCompatible(upTag) { t.Errorf("pin %s should be upgradable to %s but returned false", test[0], test[1]) } } for _, test := range testsFalse { pin, err := tagcmp.Parse(test[0]) if err != nil { t.Error(err) } upTag, err := tagcmp.Parse(test[1]) if err != nil { t.Error(err) } if pin.IsUpgradeCompatible(upTag) { t.Errorf("pin %s should not be upgradable to %s but returned true", test[0], test[1]) } } } func TestUpgradeDelta(t *testing.T) { pairs := []struct { t1 string t2 string expected string throwsErr bool }{ {"v1.0.0", "1.0.0", "", true}, {"1", "2", "1.0.0", false}, {"1.0", "1.1", "0.1.0", false}, {"1.1.0", "1.1.1", "0.0.1", false}, {"2", "1", "-1.0.0", false}, {"1.1", "1.0", "0.-1.0", false}, {"1.1.1", "1.1.0", "0.0.-1", false}, } for _, p := range pairs { p1, err := tagcmp.Parse(p.t1) if err != nil { t.Errorf("'%s' should have parsed", p.t1) } p2, err := tagcmp.Parse(p.t2) if err != nil { t.Errorf("'%s' should have parsed", p.t2) } pexpected, _ := tagcmp.ParseDelta(p.expected) res, err := p1.UpgradeDelta(p2) if p.throwsErr && (err == nil) { t.Errorf("(%s).UpgradeDelta(%s) didn't throw an error but should have", p.t1, p.t2) } else if !p.throwsErr && (err != nil) { t.Errorf("(%s).UpgradeDelta(%s) threw an error but shouldn't have", p.t1, p.t2) } if res != pexpected { t.Errorf("(%s).UpgradeDelta(%s) gave %s but expected %s", p.t1, p.t2, res.String(), p.expected) } } } func TestUpgradeType(t *testing.T) { testSet := []struct { in string out int }{ {"1.0.0", 4}, {"-1.0.0", -4}, {"1.2.0", 4}, {"-1.2.0", -4}, {"0.1.0", 2}, {"0.-1.0", -2}, {"0.1.2", 2}, {"0.-1.2", -2}, {"0.0.1", 1}, {"0.0.-1", -1}, {"0.0.0", 0}, {"-0.-0.-0", 0}, } for _, test := range testSet { tagDelta, err := tagcmp.ParseDelta(test.in) if err != nil { t.Errorf("tagcmp.ParseDelta couldn't parse '%s': '%s'", test.in, err) } upType := tagDelta.UpgradeType() if upType != test.out { t.Errorf("(%s).UpgradeType() returned '%d', expected '%d'", test.in, upType, test.out) } } }