From 5a3fda4bd6f1ab8fc309b27ceee625bddc2ea5b4 Mon Sep 17 00:00:00 2001 From: Randell Callahan Date: Mon, 17 Jul 2023 13:59:46 -0600 Subject: [PATCH] executors: Provide more context when the semantic version is invalid (#55024) The annoying error of `Invalid Semantic Version` tells us nothing and makes troubleshooting the issue difficult with lots of churn. I have update Executor usage of `semvar.NewVersion` to wrap the error with a message that include the actual version that causes the error. This will help us understand the specific version string that is causing the error and more quickly determine _why_ the version is getting injected. ## Test plan Update/add Go tests. --- cmd/frontend/graphqlbackend/BUILD.bazel | 1 + cmd/frontend/graphqlbackend/executor.go | 27 +- cmd/frontend/graphqlbackend/executor_test.go | 395 ++++++++++-------- .../internal/executorqueue/handler/handler.go | 2 +- .../executorqueue/handler/handler_test.go | 2 +- .../executorqueue/handler/multihandler.go | 2 +- .../handler/multihandler_test.go | 2 +- lib/api/BUILD.bazel | 5 + lib/api/version_check.go | 6 +- lib/api/version_check_test.go | 75 +++- 10 files changed, 322 insertions(+), 195 deletions(-) diff --git a/cmd/frontend/graphqlbackend/BUILD.bazel b/cmd/frontend/graphqlbackend/BUILD.bazel index 80d9903a868..9f1976e6387 100644 --- a/cmd/frontend/graphqlbackend/BUILD.bazel +++ b/cmd/frontend/graphqlbackend/BUILD.bazel @@ -323,6 +323,7 @@ go_library( "//internal/version", "//internal/version/upgradestore", "//internal/webhooks/outbound", + "//lib/api", "//lib/batches", "//lib/errors", "//lib/output", diff --git a/cmd/frontend/graphqlbackend/executor.go b/cmd/frontend/graphqlbackend/executor.go index cbf2baa0290..a2e1e278db6 100644 --- a/cmd/frontend/graphqlbackend/executor.go +++ b/cmd/frontend/graphqlbackend/executor.go @@ -11,6 +11,8 @@ import ( "github.com/sourcegraph/sourcegraph/internal/gqlutil" "github.com/sourcegraph/sourcegraph/internal/types" "github.com/sourcegraph/sourcegraph/internal/version" + "github.com/sourcegraph/sourcegraph/lib/api" + "github.com/sourcegraph/sourcegraph/lib/errors" ) const oneReleaseCycle = 35 * 24 * time.Hour @@ -122,12 +124,12 @@ func calculateExecutorCompatibility(ev string) (*string, error) { return compatibility.ToGraphQL(), nil } - s, err := semver.NewVersion(sv) + s, err := getSemVer("sourcegraph", sv) if err != nil { return nil, err } - e, err := semver.NewVersion(ev) + e, err := getSemVer("executor", ev) if err != nil { return nil, err } @@ -145,3 +147,24 @@ func calculateExecutorCompatibility(ev string) (*string, error) { return compatibility.ToGraphQL(), nil } + +func getSemVer(source string, version string) (*semver.Version, error) { + v, err := semver.NewVersion(version) + if err != nil { + // Maybe the version is a daily build and need to extract the version from there. + // We don't care about the error from getDailyBuildVersion because we already have the error. + v, _ = getDailyBuildVersion(version) + if v == nil { + return nil, errors.Wrapf(err, "failed to parse %s version %q", source, version) + } + } + return v, nil +} + +func getDailyBuildVersion(version string) (*semver.Version, error) { + matches := api.BuildDateRegex.FindStringSubmatch(version) + if len(matches) > 2 { + return semver.NewVersion(matches[2]) + } + return nil, nil +} diff --git a/cmd/frontend/graphqlbackend/executor_test.go b/cmd/frontend/graphqlbackend/executor_test.go index f46dfaeeaf5..9cc76832dfe 100644 --- a/cmd/frontend/graphqlbackend/executor_test.go +++ b/cmd/frontend/graphqlbackend/executor_test.go @@ -1,183 +1,236 @@ package graphqlbackend import ( + "errors" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/sourcegraph/sourcegraph/internal/version" ) -func TestExecutorResolver(t *testing.T) { - t.Run("isExecutorOutdated", func(t *testing.T) { - testCases := []struct { - executorVersion string - sourcegraphVersion string - isActive bool - expected *string - description string - }{ - // The executor isn't outdated when in dev mode. - { - executorVersion: "0.0.0+dev", - sourcegraphVersion: "0.0.0+dev", - isActive: true, - expected: nil, - description: "executor and the Sourcegraph instance are in dev mode", - }, - // The executor isn't outdated when it's inactive - { - executorVersion: "0.0.0+dev", - sourcegraphVersion: "0.0.0+dev", - isActive: false, - expected: nil, - description: "executor is inactive", - }, - // The executor is not outdated if it's one minor version behind the sourcegraph version (SEMVER) - { - executorVersion: "3.43.0", - sourcegraphVersion: "3.42.0", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor is one version ahead of the Sourcegraph instance", - }, - // The executor is not outdated if it's one minor version ahead of the sourcegraph version (SEMVER) - { - executorVersion: "3.42.0", - sourcegraphVersion: "3.43.0", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor is one minor version ahead of the Sourcegraph instance", - }, - // The executor is not outdated when both sourcegraph and executor are the same (SEMVER). - { - executorVersion: "3.43.0", - sourcegraphVersion: "3.43.0", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor and the sourcegraph instance are the same version (SEMVER)", - }, - // The executor is not outdated when both sourcegraph and executor are the same (insiders). - { - executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-25_e94e18c4ebcc_patch", - sourcegraphVersion: "169135_2022-08-25_4.4-a2b623dce148", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor and the sourcegraph instance are the same version (insiders)", - }, - { - executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-25_e94e18c4ebcc_patch", - sourcegraphVersion: "169135_2022-08-25_a2b623dce148", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor and the sourcegraph instance are the same version (insiders - old version)", - }, - // The executor is outdated if the sourcegraph version is more than one version - // greater than the executor version (SEMVER). - { - executorVersion: "3.40.0", - sourcegraphVersion: "3.43.0", - isActive: true, - expected: ExecutorCompatibilityOutdated.ToGraphQL(), - description: "executor is more than one minor version behind the Sourcegraph instance (SEMVER)", - }, - { - executorVersion: "3.43.0", - sourcegraphVersion: "4.0.0", - isActive: true, - expected: ExecutorCompatibilityOutdated.ToGraphQL(), - description: "Sourcegraph instance is a major version ahead of the executor", - }, - // The executor is too new if the executor is more than one version ahead of the sourcegraph version. - { - executorVersion: "3.43.0", - sourcegraphVersion: "3.40.0", - isActive: true, - expected: ExecutorCompatibilityVersionAhead.ToGraphQL(), - description: "executor is more than one minor version ahead of the Sourcegraph instance (SEMVER)", - }, - { - executorVersion: "4.0.0", - sourcegraphVersion: "3.43.0", - isActive: true, - expected: ExecutorCompatibilityVersionAhead.ToGraphQL(), - description: "executor is a major version ahead of the Sourcegraph instance", - }, - // The executor is outdated if the sourcegraph version is greater than the executor version (insiders). - { - executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-06-10_e94e18c4ebcc_patch", - sourcegraphVersion: "169135_2022-07-25_a2b623dce148", - isActive: true, - expected: ExecutorCompatibilityOutdated.ToGraphQL(), - description: "executor version is less than the Sourcegraph build date - one release cycle (insiders)", - }, - // The executor is too new if the executor version is greater than the one release cycle + sourcegraph build date (insiders) - { - executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-10-30_e94e18c4ebcc_patch", - sourcegraphVersion: "169135_2022-09-15_a2b623dce148", - isActive: true, - expected: ExecutorCompatibilityVersionAhead.ToGraphQL(), - description: "executor version is greater than the one release cycle + Sourcegraph build date (insiders)", - }, - // The executor is up to date if the build date isn't greater than one release cycle + sourcegraph build date (insiders) - { - executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-20_e94e18c4ebcc_patch", - sourcegraphVersion: "169135_2022-08-15_a2b623dce148", - isActive: true, - expected: ExecutorCompatibilityUpToDate.ToGraphQL(), - description: "executor version is a few days ahead of the Sourcegraph instance (insiders)", - }, - // version mismatch - { - executorVersion: "3.36.2", - sourcegraphVersion: "169135_2022-08-15_a2b623dce148", - isActive: true, - expected: nil, - description: "Sourcegraph instance is on insiders version while the executor is tagged", - }, - { - executorVersion: "169135_2022-08-15_a2b623dce148", - sourcegraphVersion: "3.39.2", - isActive: true, - expected: nil, - description: "executor is on insiders version while the Sourcegraph instance is tagged", - }, - { - executorVersion: "0.0.0+dev", - sourcegraphVersion: "3.39.2", - isActive: true, - expected: nil, - description: "executor is in dev mode while Sourcegraph instance is tagged", - }, - { - executorVersion: "3.39.2", - sourcegraphVersion: "0.0.0+dev", - isActive: true, - expected: nil, - description: "Sourcegraph instance is in dev mode while executor is tagged", - }, - { - executorVersion: "0.0.0+dev", - sourcegraphVersion: "169135_2022-08-15_a2b623dce148", - isActive: true, - expected: nil, - description: "executor is in dev mode while Sourcegraph instance is on insiders version", - }, - { - executorVersion: "169135_2022-08-15_a2b623dce148", - sourcegraphVersion: "0.0.0+dev", - isActive: true, - expected: nil, - description: "Sourcegraph instance is in dev mode while executor is on insiders version", - }, - } +func TestCalculateExecutorCompatibility(t *testing.T) { + tests := []struct { + name string + executorVersion string + sourcegraphVersion string + isActive bool + expectedCompatibility ExecutorCompatibility + expectedError error + }{ + { + name: "Dev mode", + executorVersion: "0.0.0+dev", + sourcegraphVersion: "0.0.0+dev", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Executor is inactive", + executorVersion: "0.0.0+dev", + sourcegraphVersion: "0.0.0+dev", + isActive: false, + expectedCompatibility: "", + }, + { + name: "Executor is one minor version behind", + executorVersion: "3.43.0", + sourcegraphVersion: "3.42.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is one minor version behind", + executorVersion: "3.42.0", + sourcegraphVersion: "3.43.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is the same version as the Sourcegraph instance", + executorVersion: "3.43.0", + sourcegraphVersion: "3.43.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is the same version as the Sourcegraph instance (insiders)", + executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-25_e94e18c4ebcc_patch", + sourcegraphVersion: "169135_2022-08-25_4.4-a2b623dce148", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is the same version as the Sourcegraph instance (insiders - old version)", + executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-25_e94e18c4ebcc_patch", + sourcegraphVersion: "169135_2022-08-25_a2b623dce148", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is multiple minor versions behind", + executorVersion: "3.40.0", + sourcegraphVersion: "3.43.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityOutdated, + }, + { + name: "Executor is major version behind", + executorVersion: "3.43.0", + sourcegraphVersion: "4.0.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityOutdated, + }, + { + name: "Executor is multiple patch versions behind", + executorVersion: "3.43.0", + sourcegraphVersion: "3.43.12", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor is multiple minor version ahead", + executorVersion: "3.43.0", + sourcegraphVersion: "3.40.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityVersionAhead, + }, + { + executorVersion: "4.0.0", + sourcegraphVersion: "3.43.0", + isActive: true, + expectedCompatibility: ExecutorCompatibilityVersionAhead, + }, + { + name: "Executor is one release cycle behind (insiders)", + executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-06-10_e94e18c4ebcc_patch", + sourcegraphVersion: "169135_2022-07-25_a2b623dce148", + isActive: true, + expectedCompatibility: ExecutorCompatibilityOutdated, + }, + { + name: "Executor is one release cycle ahead (insiders)", + executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-10-30_e94e18c4ebcc_patch", + sourcegraphVersion: "169135_2022-09-15_a2b623dce148", + isActive: true, + expectedCompatibility: ExecutorCompatibilityVersionAhead, + }, + { + name: "Execcutor build date is greater than one release cycle + sourcegraph build date (insiders)", + executorVersion: "executor-patch-notest-es-ignite-debug_168065_2022-08-20_e94e18c4ebcc_patch", + sourcegraphVersion: "169135_2022-08-15_a2b623dce148", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Sourcegrpah version mismatch", + executorVersion: "3.36.2", + sourcegraphVersion: "169135_2022-08-15_a2b623dce148", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Executor version mismatch", + executorVersion: "169135_2022-08-15_a2b623dce148", + sourcegraphVersion: "3.39.2", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Executor is in dev mode", + executorVersion: "0.0.0+dev", + sourcegraphVersion: "3.39.2", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Sourcegraph instance is in dev mode", + executorVersion: "3.39.2", + sourcegraphVersion: "0.0.0+dev", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Executor is in dev mode and Sourcegraph instance is on insiders version", + executorVersion: "0.0.0+dev", + sourcegraphVersion: "169135_2022-08-15_a2b623dce148", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Sourcegraph instance is in dev mode and executor is on insiders version", + executorVersion: "169135_2022-08-15_a2b623dce148", + sourcegraphVersion: "0.0.0+dev", + isActive: true, + expectedCompatibility: "", + }, + { + name: "Executor version is an invalid semver", + executorVersion: "\n1.2", + sourcegraphVersion: "3.39.2", + isActive: true, + expectedCompatibility: "", + expectedError: errors.New("failed to parse executor version \"\\n1.2\": Invalid Semantic Version"), + }, + { + name: "Sourcegraph version is an invalid semver", + executorVersion: "4.0.1", + sourcegraphVersion: "\n1.2", + isActive: true, + expectedCompatibility: "", + expectedError: errors.New("failed to parse sourcegraph version \"\\n1.2\": Invalid Semantic Version"), + }, + { + name: "Executor release branch build", + executorVersion: "5.1_231128_2023-06-27_5.0-7ac9ba347103", + sourcegraphVersion: "5.0.3", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Sourcegraph release branch build", + executorVersion: "5.0.3", + sourcegraphVersion: "5.1_231128_2023-06-27_5.0-7ac9ba347103", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor release candidate", + executorVersion: "5.1.3-rc.1", + sourcegraphVersion: "5.1.3", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + { + name: "Executor version missing patch", + executorVersion: "5.1", + sourcegraphVersion: "5.1.3", + isActive: true, + expectedCompatibility: ExecutorCompatibilityUpToDate, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + version.Mock(test.sourcegraphVersion) + actual, err := calculateExecutorCompatibility(test.executorVersion) - for _, tc := range testCases { - version.Mock(tc.sourcegraphVersion) - want, err := calculateExecutorCompatibility(tc.executorVersion) - - assert.NoError(t, err) - assert.Equal(t, tc.expected, want, tc.description) - } - }) + if test.expectedError != nil { + require.Error(t, err) + assert.Equal(t, test.expectedError.Error(), err.Error()) + assert.Nil(t, actual) + } else { + require.NoError(t, err) + // Once https://github.com/stretchr/testify/pull/1287 is merged, we can remove this and just use Equal. + // When they are not equal we are just given the addresses which doesn't mean much to us, and tell us + // how to fix the test. + if test.expectedCompatibility != "" { + require.NotNil(t, actual) + assert.Equal(t, test.expectedCompatibility, ExecutorCompatibility(*actual)) + } else { + assert.Nil(t, actual) + } + } + }) + } } diff --git a/enterprise/cmd/frontend/internal/executorqueue/handler/handler.go b/enterprise/cmd/frontend/internal/executorqueue/handler/handler.go index f8d8f812f5f..0af0d29e8c1 100644 --- a/enterprise/cmd/frontend/internal/executorqueue/handler/handler.go +++ b/enterprise/cmd/frontend/internal/executorqueue/handler/handler.go @@ -127,7 +127,7 @@ func (h *handler[T]) dequeue(ctx context.Context, queueName string, metadata exe var err error version2Supported, err = api.CheckSourcegraphVersion(metadata.version, "4.3.0-0", "2022-11-24") if err != nil { - return executortypes.Job{}, false, err + return executortypes.Job{}, false, errors.Wrapf(err, "failed to check version %q", metadata.version) } } diff --git a/enterprise/cmd/frontend/internal/executorqueue/handler/handler_test.go b/enterprise/cmd/frontend/internal/executorqueue/handler/handler_test.go index 0c36f8f6832..28cf0a75178 100644 --- a/enterprise/cmd/frontend/internal/executorqueue/handler/handler_test.go +++ b/enterprise/cmd/frontend/internal/executorqueue/handler/handler_test.go @@ -76,7 +76,7 @@ func TestHandler_HandleDequeue(t *testing.T) { name: "Invalid version", body: `{"executorName": "test-executor", "version":"\n1.2", "numCPUs": 1, "memory": "1GB", "diskSpace": "10GB"}`, expectedStatusCode: http.StatusInternalServerError, - expectedResponseBody: `{"error":"Invalid Semantic Version"}`, + expectedResponseBody: `{"error":"failed to check version \"\\n1.2\": Invalid Semantic Version"}`, assertionFunc: func(t *testing.T, mockStore *dbworkerstoremocks.MockStore[testRecord], jobTokenStore *executorstore.MockJobTokenStore) { require.Len(t, mockStore.DequeueFunc.History(), 0) require.Len(t, jobTokenStore.CreateFunc.History(), 0) diff --git a/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler.go b/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler.go index 0408d8e2769..840ded94ee4 100644 --- a/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler.go +++ b/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler.go @@ -91,7 +91,7 @@ func (m *MultiHandler) dequeue(ctx context.Context, req executortypes.DequeueReq var err error version2Supported, err = api.CheckSourcegraphVersion(req.Version, "4.3.0-0", "2022-11-24") if err != nil { - return executortypes.Job{}, false, err + return executortypes.Job{}, false, errors.Wrapf(err, "failed to check version %q", req.Version) } } diff --git a/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler_test.go b/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler_test.go index 0e1eab06e03..f34aa8de54e 100644 --- a/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler_test.go +++ b/enterprise/cmd/frontend/internal/executorqueue/handler/multihandler_test.go @@ -235,7 +235,7 @@ func TestMultiHandler_HandleDequeue(t *testing.T) { dequeueEvents: []dequeueEvent{ { expectedStatusCode: http.StatusInternalServerError, - expectedResponseBody: `{"error":"Invalid Semantic Version"}`, + expectedResponseBody: `{"error":"failed to check version \"\\n1.2\": Invalid Semantic Version"}`, }, }, }, diff --git a/lib/api/BUILD.bazel b/lib/api/BUILD.bazel index c486feb1688..8709e22fe1c 100644 --- a/lib/api/BUILD.bazel +++ b/lib/api/BUILD.bazel @@ -17,4 +17,9 @@ go_test( timeout = "short", srcs = ["version_check_test.go"], embed = [":api"], + deps = [ + "//lib/errors", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + ], ) diff --git a/lib/api/version_check.go b/lib/api/version_check.go index 7a20ab5513d..567cae8991b 100644 --- a/lib/api/version_check.go +++ b/lib/api/version_check.go @@ -6,8 +6,10 @@ import ( "github.com/Masterminds/semver" ) -var buildDate = regexp.MustCompile(`\d+_(\d{4}-\d{2}-\d{2})_(\d+\.\d+-)?[a-z0-9]{7,}(_patch)?$`) +// BuildDateRegex matches the build date in a Sourcegraph version string. +var BuildDateRegex = regexp.MustCompile(`\d+_(\d{4}-\d{2}-\d{2})_(\d+\.\d+)?-?[a-z0-9]{7,}(_patch)?$`) +// CheckSourcegraphVersion checks if the given version satisfies the given constraint. // NOTE: A version with a prerelease suffix (e.g. the "-rc.3" of "3.35.1-rc.3") is not // considered by semver to satisfy a constraint without a prerelease suffix, regardless of // whether or not the major/minor/patch version is greater than or equal to that of the @@ -28,7 +30,7 @@ func CheckSourcegraphVersion(version, constraint, minDate string) (bool, error) // version string, we match on 7 or more characters. Currently, the Sourcegraph version // is expected to return 12: // https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/enterprise/dev/ci/internal/ci/config.go?L96. - matches := buildDate.FindStringSubmatch(version) + matches := BuildDateRegex.FindStringSubmatch(version) if len(matches) > 1 { return matches[1] >= minDate, nil } diff --git a/lib/api/version_check_test.go b/lib/api/version_check_test.go index 78965775674..81b003870a2 100644 --- a/lib/api/version_check_test.go +++ b/lib/api/version_check_test.go @@ -1,126 +1,169 @@ package api -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/sourcegraph/sourcegraph/lib/errors" +) func TestCheckSourcegraphVersion(t *testing.T) { - for _, tc := range []struct { - currentVersion, constraint, minDate string - expected bool + tests := []struct { + name string + currentVersion string + constraint string + minDate string + expected bool + expectedErr error }{ { + name: "Version matches constraint", currentVersion: "3.12.6", constraint: ">= 3.12.6", minDate: "2020-01-19", expected: true, }, { + name: "Release candidate version matches constraint", currentVersion: "3.12.6-rc.1", constraint: ">= 3.12.6-0", minDate: "2020-01-19", expected: true, }, { + name: "Newer release candidate version matches constraint", currentVersion: "3.12.6-rc.3", constraint: ">= 3.10.6-0", minDate: "2020-01-19", expected: true, }, { + name: "Version does not match constraint", currentVersion: "3.12.6", constraint: ">= 3.13", minDate: "2020-01-19", expected: false, }, { + name: "Constraint without patch version", currentVersion: "3.13.0", constraint: ">= 3.13", minDate: "2020-01-19", expected: true, }, { + name: "Dev version", currentVersion: "dev", constraint: ">= 3.13", minDate: "2020-01-19", expected: true, }, { + name: "Newer dev version", currentVersion: "0.0.0+dev", constraint: ">= 3.13", minDate: "2020-01-19", expected: true, }, - // 7-character abbreviated hash { + name: "Seven character abbreviated hash", currentVersion: "54959_2020-01-29_9258595", minDate: "2020-01-19", constraint: ">= 999.13", expected: true, }, { + name: "Seven character abbreviated hash too old", currentVersion: "54959_2020-01-29_9258595", minDate: "2020-01-30", constraint: ">= 999.13", expected: false, }, { + name: "Seven character abbreviated hash matches date", currentVersion: "54959_2020-01-29_9258595", minDate: "2020-01-29", constraint: ">= 0.0", expected: true, }, - // 12-character abbreviated hash { + name: "Twelve character abbreviated hash", currentVersion: "54959_2020-01-29_925859585436", minDate: "2020-01-19", constraint: ">= 999.13", expected: true, }, { + name: "Twelve character abbreviated hash too old", currentVersion: "54959_2020-01-29_925859585436", minDate: "2020-01-30", constraint: ">= 999.13", expected: false, }, { + name: "Twelve character abbreviated hash matches date", currentVersion: "54959_2020-01-29_925859585436", minDate: "2020-01-29", constraint: ">= 0.0", expected: true, }, - // 12-character abbreviated hash with latest version tag { + name: "Twelve character abbreviated hash with tag", currentVersion: "54959_2020-01-29_4.4-925859585436", minDate: "2020-01-19", constraint: ">= 999.13", expected: true, }, { + name: "Twelve character abbreviated hash with tag too old and does not match constraint", currentVersion: "54959_2020-01-29_4.4-925859585436", minDate: "2020-01-30", constraint: ">= 999.13", expected: false, }, { + name: "Twelve character abbreviated hash with tag matches date", currentVersion: "54959_2020-01-29_4.4-925859585436", minDate: "2020-01-29", constraint: ">= 0.0", expected: true, }, - // Full 40-character hash, just for fun { + name: "Forty character hash", currentVersion: "54959_2020-01-29_7db7d396346284fd0f8f79f130f38b16fb1d3d70", minDate: "2020-01-29", constraint: ">= 0.0", expected: true, }, - } { - actual, err := CheckSourcegraphVersion(tc.currentVersion, tc.constraint, tc.minDate) - if err != nil { - t.Errorf("err: %s", err) - } + { + name: "Daily release build", + currentVersion: "5.1_231128_2023-06-27_5.0-7ac9ba347103", + minDate: "2020-01-29", + constraint: ">= 4.4", + expected: true, + }, + { + name: "Invalid semantic version", + currentVersion: "\n1.2", + minDate: "2020-01-29", + constraint: ">= 0.0", + expected: false, + expectedErr: errors.New("Invalid Semantic Version"), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual, err := CheckSourcegraphVersion(test.currentVersion, test.constraint, test.minDate) - if actual != tc.expected { - t.Errorf("wrong result. want=%t, got=%t (version=%q, constraint=%q)", tc.expected, actual, tc.currentVersion, tc.constraint) - } + if test.expectedErr != nil { + require.Error(t, err) + assert.Equal(t, test.expectedErr.Error(), err.Error()) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, actual) + } + }) } }