diff --git a/github/locate.go b/github/locate.go index 72ce9e6..3309818 100644 --- a/github/locate.go +++ b/github/locate.go @@ -5,21 +5,29 @@ import ( "strings" "github.com/blang/semver" - "github.com/gocd-contrib/gocd-cli/utils" ) -func ResolveVersionJar(rels []Release, filterBy string, stableOnly bool) (asset *Asset, err error) { +func ResolveVersionJar(rels []Release, filterBy string, stableOnly bool) (*Asset, error) { if 0 == len(rels) { - return nil, nil + return nil, fmt.Errorf("Could not find any releases to filter") } if !stableOnly && "" == filterBy { return findJarAsset(&(rels[0])) } else { - filt := buildFilter(filterBy, !stableOnly) - for _, r := range rels { - if filt(&r) { - return findJarAsset(&r) + if filt, err := buildFilter(filterBy, !stableOnly); err != nil { + return nil, err + } else { + for _, r := range rels { + ok, e2 := filt(&r) + + if e2 != nil { + return nil, e2 + } + + if ok { + return findJarAsset(&r) + } } } } @@ -27,19 +35,17 @@ func ResolveVersionJar(rels []Release, filterBy string, stableOnly bool) (asset return nil, fmt.Errorf("Cannot find a release that matches version spec `%s` and stable-only=%t", filterBy, stableOnly) } -func buildFilter(filterSpec string, allowPrerelease bool) func(*Release) bool { +func buildFilter(filterSpec string, allowPrerelease bool) (func(*Release) (bool, error), error) { if r, err := semver.ParseRange(filterSpec); err != nil { - utils.DieLoudly(1, "Don't know how to parse version spec `%s`: %v", filterSpec, err) - return nil + return nil, fmt.Errorf("Don't know how to parse version spec `%s`: %v", filterSpec, err) } else { - return func(rel *Release) bool { + return func(rel *Release) (bool, error) { if v, err := semver.Parse(rel.Version); err != nil { - utils.DieLoudly(1, "Cannot parse release version `%s`; does not conform to semantic version (%v)", err) - return false + return false, fmt.Errorf("Cannot parse release version `%s`; does not conform to semantic version (%v)", rel.Version, err) } else { - return r(v) && (allowPrerelease || !rel.Prerelease) + return r(v) && (allowPrerelease || !rel.Prerelease), nil } - } + }, nil } } diff --git a/github/locate_test.go b/github/locate_test.go new file mode 100644 index 0000000..8d4dd60 --- /dev/null +++ b/github/locate_test.go @@ -0,0 +1,89 @@ +package github + +import ( + "testing" +) + +func jarname(version string) string { + return "test.name-" + version + ".jar" +} + +func jarurl(version string) string { + return "http://test.com/releases/download/" + version + "/" + jarname(version) +} + +func createRelease(version string, prerelease bool) Release { + return Release{Version: version, Prerelease: prerelease, Assets: []Asset{Asset{Name: jarname(version), Url: jarurl(version)}}} +} + +func TestResolveVersionJar(t *testing.T) { + as := asserts(t) + releases := []Release{createRelease("1.1.1", true)} + + asset, err := ResolveVersionJar(releases, "1.1.1", false) + as.ok(err) + + as.eq(Asset{Name: jarname("1.1.1"), Url: jarurl("1.1.1")}, *asset) + + asset, err = ResolveVersionJar(releases, "", false) + as.ok(err) + + as.eq(Asset{Name: jarname("1.1.1"), Url: jarurl("1.1.1")}, *asset) +} + +func TestResolveVersionJarCanSkipPreReleaseJars(t *testing.T) { + as := asserts(t) + releases := []Release{createRelease("1.1.0", false), createRelease("1.1.1", true)} + + asset, err := ResolveVersionJar(releases, "1.1.x", true) + as.ok(err) + + as.eq(jarname("1.1.0"), asset.Name) +} + +func TestResolveVersionJarCanFindVersionWithinRange(t *testing.T) { + as := asserts(t) + version := "0.8.2" + releases := []Release{createRelease(version, false)} + + asset, err := ResolveVersionJar(releases, ">=0.5.0 <0.8.0 || >=0.8.1 !0.8.3", false) + as.ok(err) + + as.eq(Asset{Name: jarname(version), Url: jarurl(version)}, *asset) +} + +func TestResolveVersionJarReturnsErrorIfNoMatchingJar(t *testing.T) { + as := asserts(t) + versionFilter := ">=0.5.0 <0.8.0" + releases := []Release{createRelease("0.8.2", false)} + + _, err := ResolveVersionJar(releases, versionFilter, false) + as.err("Cannot find a release that matches version spec `"+versionFilter+"` and stable-only=false", err) +} + +func TestResolveVersionJarReturnsErrorIfCannotParseFilterVersion(t *testing.T) { + as := asserts(t) + _, err := ResolveVersionJar([]Release{createRelease("1.1.1", true)}, "mumbojumborelease", true) + as.err("Don't know how to parse version spec `mumbojumborelease`: Could not get version from string: \"mumbojumborelease\"", err) +} + +func TestResolveVersionJarReturnsErrorIfCannotParseReleaseVersion(t *testing.T) { + as := asserts(t) + _, err := ResolveVersionJar([]Release{createRelease("mumbojumborelease", true)}, "1.1.1", true) + as.err("Cannot parse release version `mumbojumborelease`; does not conform to semantic version (No Major.Minor.Patch elements found)", err) +} + +func TestResolveVersionJarReturnErrorIfCannotFindAssetJarForRelease(t *testing.T) { + as := asserts(t) + release := createRelease("1.1.1", false) + release.Assets[0].Name = "not a jar" + + _, err := ResolveVersionJar([]Release{release}, "1.1.1", true) + as.err("Could not resolve jar asset for version 1.1.1", err) +} + +func TestResolveVersionJarReturnErrorIfThereAreNoReleases(t *testing.T) { + as := asserts(t) + _, err := ResolveVersionJar([]Release{}, "", true) + as.err("Could not find any releases to filter", err) +} diff --git a/github/test_helpers_test.go b/github/test_helpers_test.go new file mode 100644 index 0000000..7239ecc --- /dev/null +++ b/github/test_helpers_test.go @@ -0,0 +1,60 @@ +package github + +import ( + "testing" +) + +type asserter struct { + t *testing.T +} + +func (a *asserter) eq(expected, actual interface{}) { + a.t.Helper() + if expected != actual { + a.t.Errorf("Expected %v to equal %v", actual, expected) + } +} + +func (a *asserter) neq(expected, actual interface{}) { + a.t.Helper() + if expected == actual { + a.t.Errorf("Expected %v to not equal %v", actual, expected) + } +} + +func (a *asserter) err(expected string, e error) { + a.t.Helper() + if nil == e { + a.t.Errorf("Expected error %q, but got nil", expected) + return + } + + if e.Error() != expected { + a.t.Errorf("Expected error %q, but got %q", expected, e) + } +} + +func (a *asserter) ok(err error) { + a.t.Helper() + if nil != err { + a.t.Errorf("Expected no error, but got %v", err) + } +} + +func (a *asserter) is(b bool) { + a.t.Helper() + if !b { + a.t.Errorf("Expected to be true") + } +} + +func (a *asserter) not(b bool) { + a.t.Helper() + if b { + a.t.Errorf("Expected to be false") + } +} + +func asserts(t *testing.T) *asserter { + return &asserter{t: t} +}