blob: 61579f53106f5c1098a34e7ec32de7efd32833fb [file] [log] [blame] [edit]
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"testing"
"github.com/bazelbuild/bazelisk/config"
"github.com/bazelbuild/bazelisk/core"
"github.com/bazelbuild/bazelisk/httputil"
"github.com/bazelbuild/bazelisk/repositories"
"github.com/bazelbuild/bazelisk/versions"
)
const (
rollingReleaseIdentifier = "rolling"
)
var (
tmpDir = ""
)
func TestMain(m *testing.M) {
var err error
tmpDir, err = ioutil.TempDir("", "version_test")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir)
code := m.Run()
os.Exit(code)
}
func TestResolveVersion(t *testing.T) {
s := setUp(t)
s.AddVersion("4.0.0", false, nil, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "4.0.0", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedRC := "4.0.0"
if version != expectedRC {
t.Fatalf("Expected version %s, but got %s", expectedRC, version)
}
}
func TestResolvePatchVersion(t *testing.T) {
s := setUp(t)
s.AddVersion("4.0.0-patch1", false, nil, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "4.0.0-patch1", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedRC := "4.0.0-patch1"
if version != expectedRC {
t.Fatalf("Expected version %s, but got %s", expectedRC, version)
}
}
func TestResolveLatestRcVersion(t *testing.T) {
s := setUp(t)
s.AddVersion("4.0.0", false, nil, nil)
s.AddVersion("10.0.0", false, nil, nil)
s.AddVersion("11.0.0", true, nil, nil)
s.AddVersion("11.11.0", false, []int{1, 2}, nil)
s.AddVersion("12.0.0", false, nil, []string{"12.0.0-pre.20210504.1"})
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "last_rc", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedRC := "11.11.0rc2"
if version != expectedRC {
t.Fatalf("Expected version %s, but got %s", expectedRC, version)
}
}
func TestResolveLatestRcVersion_WithFullRelease(t *testing.T) {
s := setUp(t)
s.AddVersion("4.0.0", true, []int{1, 2, 3}, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "last_rc", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedRC := "4.0.0rc3"
if version != expectedRC {
t.Fatalf("Expected version %s, but got %s", expectedRC, version)
}
}
func TestResolveLatestVersion_TwoLatestVersionsDoNotHaveAReleaseYet(t *testing.T) {
s := setUp(t)
s.AddVersion("4.0.0", true, nil, nil)
s.AddVersion("5.0.0", false, nil, nil)
s.AddVersion("6.0.0", false, nil, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedVersion := "4.0.0"
if version != expectedVersion {
t.Fatalf("Expected version %s, but got %s", expectedVersion, version)
}
}
func TestResolveLatestVersion_ShouldOnlyReturnStableReleases(t *testing.T) {
s := setUp(t)
s.AddVersion("3.0.0", true, []int{1}, nil)
s.AddVersion("4.0.0", false, nil, nil)
s.AddVersion("5.0.0", false, nil, nil)
s.AddVersion("6.0.0", true, []int{1, 2}, nil)
s.AddVersion("7.0.0", false, nil, []string{"12.0.0-pre.20210504.1"})
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest-1", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedVersion := "3.0.0"
if version != expectedVersion {
t.Fatalf("Expected version %s, but got %s", expectedVersion, version)
}
}
func TestResolveLatestVersion_ShouldFailIfNotEnoughReleases(t *testing.T) {
s := setUp(t)
s.AddVersion("3.0.0", true, nil, nil)
s.AddVersion("4.0.0", false, nil, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
_, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest-1", config.Null())
if err == nil {
t.Fatal("Expected ResolveVersion() to fail.")
}
expectedError := "cannot resolve version \"latest-1\": There are not enough matching Bazel releases (1)"
if err.Error() != expectedError {
t.Fatalf("Expected error message %q, but got '%v'", expectedError, err)
}
}
func TestResolveLatestVersion_GCSIsDown(t *testing.T) {
g := setUp(t).WithError().Finish()
g.Transport.AddResponse("https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/", 500, "", nil)
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
_, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest", config.Null())
if err == nil {
t.Fatal("Expected resolveLatestVersion() to fail.")
}
expectedPrefix := "unable to determine latest version: could not list Bazel versions in GCS bucket"
if !strings.HasPrefix(err.Error(), expectedPrefix) {
t.Fatalf("Expected error message that starts with %q, but got '%v'", expectedPrefix, err)
}
}
func TestResolveLatestVersion_GitHubIsDown(t *testing.T) {
transport := installTransport()
transport.AddResponse("https://api.github.com/repos/bazelbuild/bazel/releases", 500, "", nil)
gh := repositories.CreateGitHubRepo("test_token")
repos := core.CreateRepositories(nil, nil, gh, nil, nil, false)
_, _, err := repos.ResolveVersion(tmpDir, "some_fork", "latest", config.Null())
if err == nil {
t.Fatal("Expected resolveLatestVersion() to fail.")
}
expectedPrefix := "unable to determine latest version: unable to determine 'some_fork' releases: could not download list of Bazel releases from github.com/some_fork"
if !strings.HasPrefix(err.Error(), expectedPrefix) {
t.Fatalf("Expected error message that starts with %q, but got '%v'", expectedPrefix, err)
}
}
func TestAcceptRollingReleaseName(t *testing.T) {
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, nil, nil, nil, gcs, false)
for _, version := range []string{"10.0.0-pre.20201103.4", "10.0.0-pre.20201103.4.2"} {
resolvedVersion, _, err := repos.ResolveVersion(tmpDir, "", version, config.Null())
if err != nil {
t.Fatalf("ResolveVersion(%q, \"\", %q): expected no error, but got %v", tmpDir, version, err)
}
if resolvedVersion != version {
t.Fatalf("ResolveVersion(%q, \"\", %q) = %v, but expected %v", tmpDir, version, resolvedVersion, version)
}
}
}
func TestResolveLatestRollingRelease(t *testing.T) {
s := setUp(t)
s.AddVersion("11.0.0", false, nil, []string{"11.0.0/rolling/11.0.0-pre.20210503.1"})
s.AddVersion("12.0.0", false, nil, []string{"12.0.0/rolling/12.0.0-pre.20210504.1"})
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, nil, nil, nil, gcs, false)
version, _, err := repos.ResolveVersion(tmpDir, "", rollingReleaseIdentifier, config.Null())
if err != nil {
t.Fatalf("ResolveVersion(%q, \"\", %q): expected no error, but got %v", tmpDir, rollingReleaseIdentifier, err)
}
want := "12.0.0-pre.20210504.1"
if version != want {
t.Fatalf("ResolveVersion(%q, \"\", %q) = %v, but expected %v", tmpDir, rollingReleaseIdentifier, version, want)
}
}
func TestAcceptFloatingReleaseVersions(t *testing.T) {
s := setUp(t)
s.AddVersion("3.0.0", true, nil, []string{"4.0.0-pre.20210504.1"})
s.AddVersion("4.0.0", true, nil, nil)
s.AddVersion("4.1.0", true, nil, nil)
s.AddVersion("4.2.0", true, nil, nil)
s.AddVersion("4.2.1", true, []int{1, 2}, nil)
s.AddVersion("5.0.0", true, nil, nil)
s.Finish()
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "4.x", config.Null())
if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedVersion := "4.2.1"
if version != expectedVersion {
t.Fatalf("Expected version %s, but got %s", expectedVersion, version)
}
}
type gcsSetup struct {
baseURL string
versionPrefixes []string
status int
test *testing.T
Transport *httputil.FakeTransport
}
func (g *gcsSetup) AddVersion(version string, hasRelease bool, rcs []int, rolling []string) *gcsSetup {
g.versionPrefixes = append(g.versionPrefixes, fmt.Sprintf("%s/", version))
prefixes := make([]string, 0)
register := func(subDir string) {
path := fmt.Sprintf("%s/%s/", version, subDir)
prefixes = append(prefixes, path)
g.addURL(path, false)
}
for _, rc := range rcs {
register(fmt.Sprintf("rc%d", rc))
}
g.addURL(fmt.Sprintf("%s/rolling/", version), false, rolling...)
// The /release/ URLs have to exist, even if there is no release. In this case GCS returns no items, though.
releasePrefix := fmt.Sprintf("%s/release/", version)
g.addURL(releasePrefix, hasRelease)
if hasRelease {
prefixes = append(prefixes, releasePrefix)
}
g.addURL(fmt.Sprintf("%s/", version), false, prefixes...)
return g
}
func (g *gcsSetup) addURL(prefix string, containsItem bool, childPrefixes ...string) {
items := make([]interface{}, 0)
if containsItem {
items = append(items, "this_is_a_release")
}
resp := buildGCSResponseOrFail(g.test, childPrefixes, items)
g.Transport.AddResponse(fmt.Sprintf("%s&prefix=%s", g.baseURL, prefix), 200, resp, nil)
}
func setUp(t *testing.T) *gcsSetup {
return &gcsSetup{
baseURL: "https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/",
status: 200,
versionPrefixes: make([]string, 0),
test: t,
Transport: installTransport(),
}
}
func installTransport() *httputil.FakeTransport {
ft := httputil.NewFakeTransport()
httputil.DefaultTransport = ft
return ft
}
func (g *gcsSetup) WithError() *gcsSetup {
g.status = 500
return g
}
func (g *gcsSetup) Finish() *gcsSetup {
// TODO: sort and deduplicate versionPrefixes
listBody := buildGCSResponseOrFail(g.test, g.versionPrefixes, []interface{}{})
g.Transport.AddResponse(g.baseURL, g.status, listBody, nil)
return g
}
func buildGCSResponseOrFail(t *testing.T, prefixes []string, items []interface{}) string {
r := &repositories.GcsListResponse{
Prefixes: prefixes,
Items: items,
}
byteValue, err := json.Marshal(r)
if err != nil {
t.Fatalf("Could not build GCS json response: %v", err)
return ""
}
return string(byteValue)
}