blob: 521cc38fa4f4a604734102171109003957d594ac [file]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package selfupdate
import (
"bytes"
"errors"
"io"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"go.fuchsia.dev/jiri/gitutil"
"go.fuchsia.dev/jiri/jiritest"
"go.fuchsia.dev/jiri/jiritest/xtest"
)
func TestMain(m *testing.M) {
jiritest.IsolateGitEnv()
os.Exit(m.Run())
}
func TestGetCurrentCommit(t *testing.T) {
t.Parallel()
ctx := xtest.NewContext(t)
repoDir := t.TempDir()
g := gitutil.New(ctx, gitutil.RootDirOpt(repoDir))
if err := g.Init(ctx, repoDir); err != nil {
t.Fatal(err)
}
if err := g.Config(ctx, "user.email", "alice@example.com"); err != nil {
t.Fatal(err)
}
if err := g.Config(ctx, "user.name", "Alice"); err != nil {
t.Fatal(err)
}
if err := g.CommitWithMessage(ctx, "initial commit"); err != nil {
t.Fatal(err)
}
want, err := g.CurrentRevision(ctx)
if err != nil {
t.Fatal(err)
}
got, err := getCurrentCommit(ctx, repoDir)
if err != nil {
t.Fatal(err)
}
if got != want {
t.Errorf("wrong commit, want: %s, got: %s", want, got)
}
}
func TestHasPrebuilt(t *testing.T) {
t.Parallel()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
_, err := downloadBinary(t.Context(), ts.URL, "abc123")
if err != nil {
t.Fatal(err)
}
}
func TestDoesNotHavePrebuilt(t *testing.T) {
t.Parallel()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer ts.Close()
_, err := downloadBinary(t.Context(), ts.URL, "abc123")
if err != updateNotAvailableErr {
t.Fatal(err)
}
}
func TestDownloadBinary(t *testing.T) {
t.Parallel()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream")
b := bytes.NewBuffer([]byte("jiri"))
b.WriteTo(w)
}))
defer ts.Close()
b, err := downloadBinary(t.Context(), ts.URL, "abc123")
if err != nil {
t.Fatal(err)
}
if want, got := []byte("jiri"), b; !bytes.Equal(want, got) {
t.Errorf("wrong file content, want: %s, got: %s\n", want, got)
}
}
func TestDownloadBinaryHTTPError(t *testing.T) {
t.Parallel()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "internal server error", http.StatusInternalServerError)
}))
defer ts.Close()
_, err := downloadBinary(t.Context(), ts.URL, "abc123")
if err == nil {
t.Fatal("expected error, got nil")
}
errMsg := err.Error()
if !strings.Contains(errMsg, "HTTP request failed") {
t.Errorf("error message missing 'HTTP request failed', got: %s", errMsg)
}
if !strings.Contains(errMsg, "500") {
t.Errorf("error message missing status '500', got: %s", errMsg)
}
if !strings.Contains(errMsg, "internal server error") {
t.Errorf("error message missing body 'internal server error', got: %s", errMsg)
}
}
func TestMaybeTruncateString(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
maxLen int
want string
}{
{
name: "shorter than maxLen",
input: "short string",
maxLen: 50,
want: "short string",
},
{
name: "exact maxLen",
input: "exact",
maxLen: 5,
want: "exact",
},
{
name: "longer than maxLen",
input: "0123456789",
maxLen: 6,
want: "012345...",
},
{
name: "multibyte unicode characters truncated",
input: "Hello, δΈ–η•Œ πŸš€πŸŽ‰",
maxLen: 9,
want: "Hello, δΈ–η•Œ...",
},
{
name: "multibyte unicode characters shorter than maxLen",
input: "Hello, δΈ–η•Œ πŸš€πŸŽ‰",
maxLen: 12,
want: "Hello, δΈ–η•Œ πŸš€πŸŽ‰",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := maybeTruncateString(tc.input, tc.maxLen)
if got != tc.want {
t.Errorf("maybeTruncateString(%q, %d) = %q, want %q", tc.input, tc.maxLen, got, tc.want)
}
})
}
}
type errorReader struct{}
func (errorReader) Read(p []byte) (n int, err error) {
return 0, errors.New("read failure")
}
func (errorReader) Close() error {
return nil
}
func TestCheckHTTPResponseErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
response *http.Response
errContains []string
}{
{
name: "StatusOK returns nil",
response: &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Body: io.NopCloser(strings.NewReader("ok")),
},
},
{
name: "StatusError with body",
response: &http.Response{
StatusCode: http.StatusServiceUnavailable,
Status: "503 Service Unavailable",
Body: io.NopCloser(strings.NewReader("rate limit exceeded")),
},
errContains: []string{"HTTP request failed", "503 Service Unavailable", "rate limit exceeded"},
},
{
name: "StatusError with long body",
response: &http.Response{
StatusCode: http.StatusServiceUnavailable,
Status: "503 Service Unavailable",
Body: io.NopCloser(strings.NewReader(strings.Repeat("πŸš€", 4000))),
},
errContains: []string{"HTTP request failed", "503 Service Unavailable", "πŸš€..."},
},
{
name: "StatusError with empty body",
response: &http.Response{
StatusCode: http.StatusBadRequest,
Status: "400 Bad Request",
Body: io.NopCloser(strings.NewReader("")),
},
errContains: []string{"HTTP request failed", "400 Bad Request"},
},
{
name: "StatusError with failing body reader",
response: &http.Response{
StatusCode: http.StatusInternalServerError,
Status: "500 Internal Server Error",
Body: errorReader{},
},
errContains: []string{"HTTP request failed", "failed to read body", "read failure"},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := checkHTTPResponseErrors(tc.response)
if len(tc.errContains) == 0 {
if err != nil {
t.Fatalf("expected nil error, got: %v", err)
}
return
}
if err == nil {
t.Fatal("expected error, got nil")
}
errMsg := err.Error()
for _, substr := range tc.errContains {
if !strings.Contains(errMsg, substr) {
t.Errorf("error message %q missing expected substring %q", errMsg, substr)
}
}
})
}
}
func TestUpdateExecutable(t *testing.T) {
t.Parallel()
content := []byte("old")
f, err := os.CreateTemp(t.TempDir(), "jiri")
if err != nil {
t.Fatal(err)
}
if _, err := f.Write(content); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
if err := updateExecutable(f.Name(), []byte("new")); err != nil {
t.Fatal(err)
}
b, err := os.ReadFile(f.Name())
if err != nil {
t.Fatal(err)
}
if want, got := []byte("new"), b; !bytes.Equal(want, got) {
t.Errorf("wrong file content, want: %s, got: %s\n", want, got)
}
}