Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: Add DownloadFile function #790

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ import (
"encoding/base64"
"encoding/gob"
"fmt"
"io"
"net/http"
"os"
"path"
"sort"
"strings"
"time"

"github.com/godbus/dbus/v5"
"github.com/pkg/errors"
"github.com/purpleidea/mgmt/util/errwrap"
)

Expand Down Expand Up @@ -806,3 +810,24 @@ func B64ToValue(str string) (interface{}, error) {
}
return value, nil
}

func DownloadFile(url, filePath string) error {
resp, err := http.Get(url)
if err != nil {
return errors.Wrapf(err, "DownloadFile: unable to download a resource. url: %q", url)
}
defer resp.Body.Close()

file, err := os.Create(filePath)
if err != nil {
return errors.Wrapf(err, "DownloadFile: unable to create a file. filepath: %q", filePath)
}
defer file.Close()

_, err = io.Copy(file, resp.Body)
if err != nil {
return errors.Wrapf(err, "DownloadFile: unable to copy downloaded data to a file. url: %q, filepath: %q", url, filePath)
}

return nil
}
86 changes: 86 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@
package util

import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"slices"
"sort"
"strings"
"testing"
"time"
)

func TestNumToAlpha(t *testing.T) {
Expand Down Expand Up @@ -1636,3 +1642,83 @@ func TestSortMapStringValuesByUInt64Keys(t *testing.T) {
t.Errorf("input slice reordered to: %v", slice2)
}
}

func TestDownloadFile(t *testing.T) {
t.Run("empty url", func(t *testing.T) {
err := DownloadFile("", "/tmp/file.pdf")

wantErrMsg := `DownloadFile: unable to download a resource. url: ""`
if !strings.Contains(err.Error(), wantErrMsg) {
t.Errorf("wanted error to contain %q, but got %q", wantErrMsg, err.Error())
}
})

t.Run("empty filepath", func(t *testing.T) {
err := DownloadFile("https://fedoraproject.org/", "")

wantErrMsg := `DownloadFile: unable to create a file. filepath: ""`
if !strings.Contains(err.Error(), wantErrMsg) {
t.Errorf("wanted error to contain %q, but got %q", wantErrMsg, err.Error())
}
})

t.Run("unable to copy downloaded file due to closed connection", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.(http.Flusher).Flush()
time.Sleep(5 * time.Second)
w.Write([]byte("some data"))
}))
defer server.Close()

http.DefaultClient.Timeout = 1 * time.Second

err := DownloadFile(server.URL, "/tmp/file.pdf")

wantErrMsg := `DownloadFile: unable to copy downloaded data to a file.`
if !strings.Contains(err.Error(), wantErrMsg) {
t.Errorf("wanted error to contain %q, but got %q", wantErrMsg, err.Error())
}
})

t.Run("download file", func(t *testing.T) {
wantContent := []byte("this is a test file for DownloadFile function")
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(wantContent)
}))
defer server.Close()

testDir := "testtemp"
err := os.Mkdir(testDir, 0755)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(testDir)

fp := filepath.Join(testDir, "file.txt")
err = DownloadFile(server.URL, fp)

if err != nil {
t.Errorf("didn't expect error during downloading the file. got: %q", err.Error())
}

info, err := os.Stat(fp)
if err != nil {
t.Errorf("expected file to exist at %q", fp)
}

if info.Size() == 0 {
t.Error("expected non-empty file")
}

gotContent, err := os.ReadFile(fp)
if err != nil {
t.Fatal(err)
}

if !slices.Equal(gotContent, wantContent) {
t.Errorf("content mismatch, got: %q, want: %q", gotContent, wantContent)
}
})
}