Skip to content

Commit

Permalink
Adding tests for version.go (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcollom authored Jul 9, 2024
1 parent c052ef3 commit c2a0128
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 11 deletions.
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
github.com/gofri/go-github-ratelimit v1.1.0
github.com/google/go-containerregistry v0.19.0
github.com/google/go-github/v58 v58.0.0
github.com/stretchr/testify v1.9.0
)

require (
Expand Down Expand Up @@ -98,9 +99,10 @@ require (
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.54.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect
Expand Down
15 changes: 8 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8=
github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand All @@ -208,8 +208,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down
5 changes: 4 additions & 1 deletion pkg/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ func (v *Version) Fetch(ctx context.Context, imageURL string, _ *api.Options) (i
// latestSemver will return the latest ImageTag based on the given options
// restriction, using semver. This should not be used is UseSHA has been
// enabled.
// TODO: add tests..
func latestSemver(opts *api.Options, tags []api.ImageTag) (*api.ImageTag, error) {
var (
latestImageTag *api.ImageTag
Expand Down Expand Up @@ -153,6 +152,10 @@ func latestSemver(opts *api.Options, tags []api.ImageTag) (*api.ImageTag, error)
}
}

if latestImageTag == nil {
return nil, fmt.Errorf("no suitable version found")
}

return latestImageTag, nil
}

Expand Down
275 changes: 275 additions & 0 deletions pkg/version/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
package version

import (
"regexp"
"testing"
"time"

"github.com/jetstack/version-checker/pkg/api"

"github.com/stretchr/testify/assert"
)

// Helper function to parse time
func parseTime(t string) time.Time {
parsedTime, _ := time.Parse(time.RFC3339, t)
return parsedTime
}

func TestLatestSemver(t *testing.T) {
// Ideal Set of Tags
tags := []api.ImageTag{
{Tag: "v1.0.0", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "v1.1.0", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "v1.1.1-alpha", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "v1.1.1", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "v2.0.0", Timestamp: parseTime("2023-06-05T00:00:00Z")},
}
tagsNoPrefix := []api.ImageTag{
{Tag: "1.0.0", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "1.1.0", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "1.1.1-alpha", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "1.1.1", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "2.0.0", Timestamp: parseTime("2023-06-05T00:00:00Z")},
}
// Include More Alpha/Beta/RC
alphaBetaTags := []api.ImageTag{
{Tag: "v1.0.0", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "v1.1.0", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "v1.1.1-alpha", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "v1.1.1", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "v2.0.0-alpha", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "v2.0.0-beta", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "v2.0.0-rc1", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "v2.0.0-rc2", Timestamp: parseTime("2023-06-05T00:00:00Z")},
}
// Images that are all numerical
nonSemVer := []api.ImageTag{
{Tag: "20230601", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "20230602", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "20230603", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "20230604", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "20230605", Timestamp: parseTime("2023-06-05T00:00:00Z")},
}
// This is to simulate an image that USED to SemVer but stopped
stoppedSemVer := []api.ImageTag{
{Tag: "v1.0.0", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "v1.1.0", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "202306030", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "202306031", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "202306040", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "202306050", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "202306060", Timestamp: parseTime("2023-06-06T00:00:00Z")},
}
// This is to simulate an image that USED to SemVer but stopped
startedSemVer := []api.ImageTag{
{Tag: "20230603", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "202306031", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "20230604", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "20230605", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "20230606", Timestamp: parseTime("2023-06-06T00:00:00Z")},
{Tag: "v1.0.0", Timestamp: parseTime("2023-06-09T00:00:00Z")},
{Tag: "v1.1.0", Timestamp: parseTime("2023-06-10T00:00:00Z")},
}
// Mixed Numerical and SemVer along with Older images pushed more recently
badTags := []api.ImageTag{
{Tag: "v1.0.0", Timestamp: parseTime("2023-06-01T00:00:00Z")},
{Tag: "v1.1.0", Timestamp: parseTime("2023-06-02T00:00:00Z")},
{Tag: "9999999", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "v1.1.1-alpha", Timestamp: parseTime("2023-06-03T00:00:00Z")},
{Tag: "v1.1.1", Timestamp: parseTime("2023-06-04T00:00:00Z")},
{Tag: "v2.0.0", Timestamp: parseTime("2023-06-05T00:00:00Z")},
{Tag: "v1.1.1", Timestamp: parseTime("2023-06-06T00:00:00Z")},
}

tests := []struct {
name string
opts *api.Options
expected string
tags []api.ImageTag
}{
{
name: "No constraints",
opts: &api.Options{},
expected: "v2.0.0",
},
{
name: "Regex match v1.*",
opts: &api.Options{
RegexMatcher: regexp.MustCompile("v1.*"),
},
expected: "v1.1.1",
},
{
name: "Pin major version 1",
opts: &api.Options{
PinMajor: intPtr(1),
},
expected: "v1.1.1",
},
{
name: "Pin minor version 1.1",
opts: &api.Options{
PinMajor: intPtr(1),
PinMinor: intPtr(1),
},
expected: "v1.1.1",
},
{
name: "Pin patch version 1.1.1",
opts: &api.Options{
PinMajor: intPtr(1),
PinMinor: intPtr(1),
PinPatch: intPtr(1),
},
expected: "v1.1.1",
},
{
name: "Exclude metadata",
opts: &api.Options{
UseMetaData: false,
},
expected: "v2.0.0",
},
{
name: "Include metadata",
opts: &api.Options{
UseMetaData: true,
},
expected: "v2.0.0",
},
{
name: "NoPrefixed Tags",
opts: &api.Options{},
tags: tagsNoPrefix,
expected: "2.0.0",
},
// Some Bad/Miss-behaving tags
{
name: "Bad Tags",
opts: &api.Options{
RegexMatcher: regexp.MustCompile(`^v(\d+)(\.\d+)?(\.\d+)?(.*)$`),
},
tags: badTags,
expected: "v2.0.0",
},
// None SemVer tags
{
name: "Non SemVer",
opts: &api.Options{
RegexMatcher: regexp.MustCompile(`^(\d+)`),
},
tags: nonSemVer,
expected: "20230605",
},
{
name: "Stopped SemVer",
opts: &api.Options{
RegexMatcher: regexp.MustCompile(`^(\d+)`),
},
tags: stoppedSemVer,
expected: "202306060",
},
{
name: "Started SemVer",
opts: &api.Options{
RegexMatcher: regexp.MustCompile(`^v(\d+)(\.\d+)?(\.\d+)?(.*)$`),
},
tags: startedSemVer,
expected: "v1.1.0",
},
{
name: "Alpha/Beta SemVer",
opts: &api.Options{
UseMetaData: true,
},
tags: alphaBetaTags,
expected: "v1.1.1",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if len(tt.tags) > 0 {
tags = tt.tags
}
tag, err := latestSemver(tt.opts, tags)
assert.NoError(t, err)
assert.NotNil(t, tag)
assert.Equal(t, tt.expected, tag.Tag)
})
}
}

func TestLatestSHA(t *testing.T) {
tests := []struct {
name string
tags []api.ImageTag
expectedSHA *string
}{
{
name: "Single tag",
tags: []api.ImageTag{
{SHA: "sha1", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
},
expectedSHA: strPtr("sha1"),
},
{
name: "Multiple tags, latest in the middle",
tags: []api.ImageTag{
{SHA: "sha1", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha2", Timestamp: time.Date(2023, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha3", Timestamp: time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)},
},
expectedSHA: strPtr("sha2"),
},
{
name: "Multiple tags, latest at the end",
tags: []api.ImageTag{
{SHA: "sha1", Timestamp: time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha2", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha3", Timestamp: time.Date(2023, time.January, 1, 0, 0, 0, 0, time.UTC)},
},
expectedSHA: strPtr("sha3"),
},
{
name: "No tags",
tags: []api.ImageTag{},
expectedSHA: nil,
},
{
name: "All tags with the same timestamp",
tags: []api.ImageTag{
{SHA: "sha1", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha2", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
{SHA: "sha3", Timestamp: time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)},
},
expectedSHA: strPtr("sha1"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := latestSHA(tt.tags)
if err != nil {
t.Errorf("latestSHA() error = %v", err)
return
}
if (got == nil && tt.expectedSHA != nil) || (got != nil && tt.expectedSHA == nil) {
t.Errorf("latestSHA() = %v, want %v", got, tt.expectedSHA)
return
}
if got != nil && got.SHA != *tt.expectedSHA {
t.Errorf("latestSHA() = %v, want %v", got.SHA, *tt.expectedSHA)
}
})
}
}

func intPtr(i int64) *int64 {
return &i
}

func strPtr(s string) *string {
return &s
}

0 comments on commit c2a0128

Please sign in to comment.