/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package semver import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) var ( v1_18_0 = Semver{major: 1, minor: 18, patch: 0} v1_18_0Pre = Semver{major: 1, minor: 18, patch: 0, prerelease: "pre"} v1_18_0PreExtra = Semver{major: 1, minor: 18, patch: 0, prerelease: "pre.1"} v1_19_0 = Semver{major: 1, minor: 19, patch: 0} v1_18_1 = Semver{major: 1, minor: 18, patch: 1} v1_20_0 = Semver{major: 1, minor: 20, patch: 0} v2_0_0 = Semver{major: 2, minor: 0, patch: 0} ) func TestNewVersion(t *testing.T) { testCases := map[string]struct { version string want Semver wantErr bool }{ "valid version": { version: "v1.18.0", want: Semver{ major: 1, minor: 18, patch: 0, }, wantErr: false, }, "valid version prerelease": { version: "v1.18.0-pre+yyyymmddhhmmss-abcdefabcdef", want: Semver{ major: 1, minor: 18, patch: 0, prerelease: "pre", }, wantErr: false, }, "only prerelease": {version: "v-pre.0.yyyymmddhhmmss-abcdefabcdef", wantErr: true}, "invalid version": {version: "v1.18. 0", wantErr: true}, "add prefix": { version: "1.18.0", want: Semver{ major: 1, minor: 18, patch: 0, }, wantErr: false, }, "only major.minor": { version: "v1.18", want: Semver{ major: 1, minor: 18, patch: 0, }, wantErr: false, }, "only major": { version: "v1", want: Semver{ major: 1, minor: 0, patch: 0, }, wantErr: false, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) ver, err := New(tc.version) if tc.wantErr { assert.Error(err) } else { assert.NoError(err) assert.Equal(tc.want, ver) } }) } } func TestJSONMarshal(t *testing.T) { testCases := map[string]struct { version Semver wantString string wantErr bool }{ "valid version": { version: v1_18_0, wantString: `"v1.18.0"`, }, "prerelease": { version: v1_18_0Pre, wantString: `"v1.18.0-pre"`, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) require := require.New(t) b, err := tc.version.MarshalJSON() if tc.wantErr { require.Error(err) } else { assert.NoError(err) assert.Equal(tc.wantString, string(b)) } }) } } func TestJSONUnmarshal(t *testing.T) { testCases := map[string]struct { version string wantString string wantErr bool }{ "valid version": { version: `"v1.18.0"`, wantString: "v1.18.0", }, "invalid version": { version: `"v1. 18.0"`, wantErr: true, }, "prerelease": { version: `"v1.18.0-pre"`, wantString: "v1.18.0-pre", }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) require := require.New(t) var version Semver err := version.UnmarshalJSON([]byte(tc.version)) if tc.wantErr { require.Error(err) } else { assert.NoError(err) assert.Equal(tc.wantString, version.String()) } }) } } func TestComparison(t *testing.T) { testCases := map[string]struct { version1 Semver version2 Semver want int }{ "equal": { version1: v1_18_0, version2: v1_18_0, want: 0, }, "less than": { version1: v1_18_0, version2: v1_18_1, want: -1, }, "greater than": { version1: v1_18_1, version2: v1_18_0, want: 1, }, "prerelease less than": { version1: v1_18_0Pre, version2: v1_18_0, want: -1, }, "prerelease greater than": { version1: v1_18_0, version2: v1_18_0Pre, want: 1, }, "prerelease equal": { version1: v1_18_0Pre, version2: v1_18_0Pre, want: 0, }, "prerelease extra less than": { version1: v1_18_0Pre, version2: v1_18_0PreExtra, want: -1, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) assert.Equal(tc.want, tc.version1.Compare(tc.version2)) }) } } func TestCanUpgrade(t *testing.T) { testCases := map[string]struct { version1 Semver version2 Semver wantUpgrade bool }{ "equal": { version1: v1_18_0, version2: v1_18_0, wantUpgrade: false, }, "patch less than": { version1: v1_18_0, version2: v1_18_1, wantUpgrade: true, }, "minor less then": { version1: v1_18_0, version2: v1_19_0, wantUpgrade: true, }, "minor too big drift": { version1: v1_18_0, version2: v1_20_0, wantUpgrade: false, }, "major too big drift": { version1: v1_18_0, version2: v2_0_0, wantUpgrade: false, }, "greater than": { version1: v1_18_1, version2: v1_18_0, wantUpgrade: false, }, "prerelease less than": { version1: v1_18_0Pre, version2: v1_18_0, wantUpgrade: true, }, "prerelease greater than": { version1: v1_18_0, version2: v1_18_0Pre, wantUpgrade: false, }, "prerelease equal": { version1: v1_18_0Pre, version2: v1_18_0Pre, wantUpgrade: false, }, "prerelease extra": { version1: v1_18_0Pre, version2: v1_18_0PreExtra, wantUpgrade: true, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) assert.Equal(tc.wantUpgrade, tc.version2.IsUpgradeTo(tc.version1) == nil) }) } } func TestNextMinor(t *testing.T) { testCases := map[string]struct { version Semver want string }{ "valid version": { version: v1_18_0, want: "v1.19", }, "prerelease": { version: v1_18_0Pre, want: "v1.19", }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) assert.Equal(tc.want, tc.version.NextMinor()) }) } } func TestVersionMarshalYAML(t *testing.T) { testCases := map[string]struct { version Semver want string }{ "simple": { version: Semver{ major: 1, minor: 18, patch: 0, prerelease: "", }, want: "v1.18.0\n", }, "with prerelease": { version: Semver{ major: 1, minor: 18, patch: 0, prerelease: "pre", }, want: "v1.18.0-pre\n", }, "empty semver": { version: Semver{}, want: "v0.0.0\n", }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { marshalled, err := yaml.Marshal(tc.version) require.NoError(t, err) require.Equal(t, tc.want, string(marshalled)) var unmarshalled Semver err = yaml.Unmarshal(marshalled, &unmarshalled) require.NoError(t, err) require.Equal(t, tc.version, unmarshalled) }) } } func TestVersionUnmarshalYAML(t *testing.T) { testCases := map[string]struct { version []byte want Semver wantError bool }{ "empty string": { version: []byte(""), }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { var actual Semver err := yaml.Unmarshal(tc.version, &actual) if tc.wantError { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tc.want.Compare(actual), 0, fmt.Sprintf("expected %s, got %s", tc.want, actual)) }) } } func TestSort(t *testing.T) { testCases := map[string]struct { input []Semver want []Semver }{ "": { input: []Semver{NewFromInt(2, 0, 0, ""), NewFromInt(0, 0, 0, ""), NewFromInt(1, 5, 0, "aa"), NewFromInt(1, 5, 0, "bb"), NewFromInt(1, 0, 0, "")}, want: []Semver{NewFromInt(0, 0, 0, ""), NewFromInt(1, 0, 0, ""), NewFromInt(1, 5, 0, "aa"), NewFromInt(1, 5, 0, "bb"), NewFromInt(2, 0, 0, "")}, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { Sort(tc.input) require.Equal(t, tc.want, tc.input, fmt.Sprintf("expected %s, got %s", tc.want, tc.input)) }) } }